keycloak-aplcache

KEYCLOAK-1881 Update client adapter configuration Client

11/3/2016 11:55:12 AM

Changes

adapters/saml/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java 133(+0 -133)

Details

diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
index 3960b46..de95d87 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
@@ -19,6 +19,7 @@ package org.keycloak.adapters.saml.config;
 
 import java.io.Serializable;
 import java.util.List;
+import org.keycloak.adapters.cloned.AdapterHttpClientConfig;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -157,12 +158,97 @@ public class IDP implements Serializable {
         }
     }
 
+    public static class HttpClientConfig implements AdapterHttpClientConfig {
+
+        private String truststore;
+        private String truststorePassword;
+        private String clientKeystore;
+        private String clientKeystorePassword;
+        private boolean allowAnyHostname;
+        private boolean disableTrustManager;
+        private int connectionPoolSize;
+        private String proxyUrl;
+
+        @Override
+        public String getTruststore() {
+            return truststore;
+        }
+
+        public void setTruststore(String truststore) {
+            this.truststore = truststore;
+        }
+
+        @Override
+        public String getTruststorePassword() {
+            return truststorePassword;
+        }
+
+        public void setTruststorePassword(String truststorePassword) {
+            this.truststorePassword = truststorePassword;
+        }
+
+        @Override
+        public String getClientKeystore() {
+            return clientKeystore;
+        }
+
+        public void setClientKeystore(String clientKeystore) {
+            this.clientKeystore = clientKeystore;
+        }
+
+        @Override
+        public String getClientKeystorePassword() {
+            return clientKeystorePassword;
+        }
+
+        public void setClientKeystorePassword(String clientKeystorePassword) {
+            this.clientKeystorePassword = clientKeystorePassword;
+        }
+
+        @Override
+        public boolean isAllowAnyHostname() {
+            return allowAnyHostname;
+        }
+
+        public void setAllowAnyHostname(boolean allowAnyHostname) {
+            this.allowAnyHostname = allowAnyHostname;
+        }
+
+        @Override
+        public boolean isDisableTrustManager() {
+            return disableTrustManager;
+        }
+
+        public void setDisableTrustManager(boolean disableTrustManager) {
+            this.disableTrustManager = disableTrustManager;
+        }
+
+        @Override
+        public int getConnectionPoolSize() {
+            return connectionPoolSize;
+        }
+
+        public void setConnectionPoolSize(int connectionPoolSize) {
+            this.connectionPoolSize = connectionPoolSize;
+        }
+
+        @Override
+        public String getProxyUrl() {
+            return proxyUrl;
+        }
+
+        public void setProxyUrl(String proxyUrl) {
+            this.proxyUrl = proxyUrl;
+        }
+    }
+
     private String entityID;
     private String signatureAlgorithm;
     private String signatureCanonicalizationMethod;
     private SingleSignOnService singleSignOnService;
     private SingleLogoutService singleLogoutService;
     private List<Key> keys;
+    private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig();
 
     public String getEntityID() {
         return entityID;
@@ -212,4 +298,12 @@ public class IDP implements Serializable {
         this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
     }
 
+    public AdapterHttpClientConfig getHttpClientConfig() {
+        return httpClientConfig;
+    }
+
+    public void setHttpClientConfig(AdapterHttpClientConfig httpClientConfig) {
+        this.httpClientConfig = httpClientConfig;
+    }
+
 }
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index 0085a6a..1a3dd04 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -72,4 +72,15 @@ public class ConfigXmlConstants {
     public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
     public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
     public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
+
+    public static final String HTTP_CLIENT_ELEMENT = "HttpClient";
+    public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname";
+    public static final String CLIENT_KEYSTORE_ATTR = "clientKeystore";
+    public static final String CLIENT_KEYSTORE_PASSWORD_ATTR = "clientKeystorePassword";
+    public static final String CONNECTION_POOL_SIZE_ATTR = "connectionPoolSize";
+    public static final String DISABLE_TRUST_MANAGER_ATTR = "disableTrustManager";
+    public static final String PROXY_URL_ATTR = "proxyUrl";
+    public static final String TRUSTSTORE_ATTR = "truststore";
+    public static final String TRUSTSTORE_PASSWORD_ATTR = "truststorePassword";
+
 }
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index ee21620..7af71ba 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -40,6 +40,7 @@ import java.security.PublicKey;
 import java.security.cert.Certificate;
 import java.util.HashSet;
 import java.util.Set;
+import org.keycloak.adapters.cloned.HttpClientBuilder;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -178,36 +179,39 @@ public class DeploymentBuilder {
         if (sp.getIdp().getKeys() != null) {
             for (Key key : sp.getIdp().getKeys()) {
                 if (key.isSigning()) {
-                    if (key.getKeystore() != null) {
-                        KeyStore keyStore = loadKeystore(resourceLoader, key);
-                        Certificate cert = null;
-                        try {
-                            cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
-                        } catch (KeyStoreException e) {
-                            throw new RuntimeException(e);
-                        }
-                        idp.setSignatureValidationKey(cert.getPublicKey());
-                    } else {
-                        if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
-                            throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
-                        }
-                        try {
-                            PublicKey publicKey = getPublicKeyFromPem(key);
-                            idp.setSignatureValidationKey(publicKey);
-                        } catch (Exception e) {
-                            throw new RuntimeException(e);
-                        }
-                    }
+                    processSigningKey(idp, key, resourceLoader);
                 }
             }
         }
 
+        idp.setClient(new HttpClientBuilder().build(sp.getIdp().getHttpClientConfig()));
         idp.refreshKeyLocatorConfiguration();
 
         return deployment;
     }
 
-    protected static PublicKey getPublicKeyFromPem(Key key) throws Exception {
+    private void processSigningKey(DefaultSamlDeployment.DefaultIDP idp, Key key, ResourceLoader resourceLoader) throws RuntimeException {
+        PublicKey publicKey;
+        if (key.getKeystore() != null) {
+            KeyStore keyStore = loadKeystore(resourceLoader, key);
+            Certificate cert = null;
+            try {
+                cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
+            } catch (KeyStoreException e) {
+                throw new RuntimeException(e);
+            }
+            publicKey = cert.getPublicKey();
+        } else {
+            if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
+                throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
+            }
+            publicKey = getPublicKeyFromPem(key);
+        }
+
+        idp.addSignatureValidationKey(publicKey);
+    }
+
+    protected static PublicKey getPublicKeyFromPem(Key key) {
         PublicKey publicKey;
         if (key.getPublicKeyPem() != null) {
             publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
index e649d1c..be54223 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -29,6 +29,10 @@ import javax.xml.stream.events.EndElement;
 import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
 import java.util.List;
+import org.keycloak.adapters.saml.config.IDP.HttpClientConfig;
+import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getAttributeValue;
+import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getBooleanAttributeValue;
+import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getIntegerAttributeValue;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -41,16 +45,16 @@ public class IDPXmlParser extends AbstractParser {
         StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
         StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
         IDP idp = new IDP();
-        String entityID = SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+        String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
         if (entityID == null) {
             throw new ParsingException("entityID must be set on IDP");
 
         }
         idp.setEntityID(entityID);
 
-        boolean signaturesRequired = SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
-        idp.setSignatureCanonicalizationMethod(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
-        idp.setSignatureAlgorithm(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
+        boolean signaturesRequired = getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
+        idp.setSignatureCanonicalizationMethod(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+        idp.setSignatureAlgorithm(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
         while (xmlEventReader.hasNext()) {
             XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
             if (xmlEvent == null)
@@ -75,6 +79,10 @@ public class IDPXmlParser extends AbstractParser {
                 IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader, signaturesRequired);
                 idp.setSingleLogoutService(slo);
 
+            } else if (tag.equals(ConfigXmlConstants.HTTP_CLIENT_ELEMENT)) {
+                HttpClientConfig config = parseHttpClientElement(xmlEventReader);
+                idp.setHttpClientConfig(config);
+
             } else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
                 KeysXmlParser parser = new KeysXmlParser();
                 List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
@@ -90,29 +98,63 @@ public class IDPXmlParser extends AbstractParser {
     protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
         IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
         StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
-        slo.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
-        slo.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
-        slo.setValidateRequestSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
-        slo.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
-        slo.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
-        slo.setSignResponse(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
-        slo.setPostBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
-        slo.setRedirectBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
+        slo.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+        slo.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+        slo.setValidateRequestSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
+        slo.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+        slo.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+        slo.setSignResponse(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
+        slo.setPostBindingUrl(getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
+        slo.setRedirectBindingUrl(getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
         return slo;
     }
 
     protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
         IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
         StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
-        sso.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
-        sso.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
-        sso.setValidateAssertionSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR));
-        sso.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
-        sso.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
-        sso.setBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+        sso.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+        sso.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+        sso.setValidateAssertionSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR));
+        sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+        sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+        sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
         return sso;
     }
 
+    private HttpClientConfig parseHttpClientElement(XMLEventReader xmlEventReader) throws ParsingException {
+        StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+        StaxParserUtil.validate(startElement, ConfigXmlConstants.HTTP_CLIENT_ELEMENT);
+        HttpClientConfig config = new HttpClientConfig();
+
+        config.setAllowAnyHostname(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
+        config.setClientKeystore(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_ATTR));
+        config.setClientKeystorePassword(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_PASSWORD_ATTR));
+        config.setConnectionPoolSize(getIntegerAttributeValue(startElement, ConfigXmlConstants.CONNECTION_POOL_SIZE_ATTR, 0));
+        config.setDisableTrustManager(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false));
+        config.setProxyUrl(getAttributeValue(startElement, ConfigXmlConstants.PROXY_URL_ATTR));
+        config.setTruststore(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_ATTR));
+        config.setTruststorePassword(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_PASSWORD_ATTR));
+
+        while (xmlEventReader.hasNext()) {
+            XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+            if (xmlEvent == null)
+                break;
+            if (xmlEvent instanceof EndElement) {
+                EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+                String endElementName = StaxParserUtil.getEndElementName(endElement);
+                if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
+                    break;
+                else
+                    continue;
+            }
+
+            String tag = StaxParserUtil.getStartElementName(startElement);
+            StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+        }
+
+        return config;
+    }
+
     @Override
     public boolean supports(QName qname) {
         return false;
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
index 3eeb1f7..be6d682 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -48,6 +48,13 @@ public class SPXmlParser extends AbstractParser {
             return str;
     }
 
+    public static int getIntegerAttributeValue(StartElement startElement, String tag, int defaultValue) {
+        String result = getAttributeValue(startElement, tag);
+        if (result == null)
+            return defaultValue;
+        return Integer.valueOf(result);
+    }
+
     public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
         String result = getAttributeValue(startElement, tag);
         if (result == null)
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
index fcbe1e9..a52cdc2 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -23,9 +23,10 @@ import org.keycloak.saml.SignatureAlgorithm;
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 import org.apache.http.client.HttpClient;
-import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.adapters.saml.rotation.SamlDescriptorPublicKeyLocator;
 import org.keycloak.rotation.CompositeKeyLocator;
 import org.keycloak.rotation.HardcodedKeyLocator;
@@ -191,8 +192,9 @@ public class DefaultSamlDeployment implements SamlDeployment {
         private final CompositeKeyLocator signatureValidationKeyLocator = new CompositeKeyLocator();
         private SingleSignOnService singleSignOnService;
         private SingleLogoutService singleLogoutService;
-        private HardcodedKeyLocator hardcodedKeyLocator;
+        private final List<PublicKey> signatureValidationKeys = new LinkedList<>();
         private int minTimeBetweenDescriptorRequests;
+        private HttpClient client;
 
         @Override
         public String getEntityID() {
@@ -227,14 +229,12 @@ public class DefaultSamlDeployment implements SamlDeployment {
             this.entityID = entityID;
         }
 
-        public void setSignatureValidationKey(PublicKey signatureValidationKey) {
-            this.hardcodedKeyLocator = signatureValidationKey == null ? null : new HardcodedKeyLocator(signatureValidationKey);
-            refreshKeyLocatorConfiguration();
+        public void addSignatureValidationKey(PublicKey signatureValidationKey) {
+            this.signatureValidationKeys.add(signatureValidationKey);
         }
 
         public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
             this.singleSignOnService = singleSignOnService;
-            refreshKeyLocatorConfiguration();
         }
 
         public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
@@ -245,18 +245,26 @@ public class DefaultSamlDeployment implements SamlDeployment {
             this.signatureValidationKeyLocator.clear();
 
             // When key is set, use that (and only that), otherwise configure dynamic key locator
-            if (this.hardcodedKeyLocator != null) {
-                this.signatureValidationKeyLocator.add(this.hardcodedKeyLocator);
+            if (! this.signatureValidationKeys.isEmpty()) {
+                this.signatureValidationKeyLocator.add(new HardcodedKeyLocator(this.signatureValidationKeys));
             } else if (this.singleSignOnService != null) {
                 String samlDescriptorUrl = singleSignOnService.getRequestBindingUrl() + "/descriptor";
-                // TODO
-                HttpClient httpClient = new HttpClientBuilder().build();
+                HttpClient httpClient = getClient();
                 SamlDescriptorPublicKeyLocator samlDescriptorPublicKeyLocator =
                   new SamlDescriptorPublicKeyLocator(
                     samlDescriptorUrl, this.minTimeBetweenDescriptorRequests, DEFAULT_CACHE_TTL, httpClient);
                 this.signatureValidationKeyLocator.add(samlDescriptorPublicKeyLocator);
             }
         }
+
+        @Override
+        public HttpClient getClient() {
+            return this.client;
+        }
+
+        public void setClient(HttpClient client) {
+            this.client = client;
+        }
     }
 
     private IDP idp;
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index f01b6a1..4442177 100755
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -23,6 +23,7 @@ import org.keycloak.saml.SignatureAlgorithm;
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.util.Set;
+import org.apache.http.client.HttpClient;
 import org.keycloak.rotation.KeyLocator;
 
 /**
@@ -77,6 +78,12 @@ public interface SamlDeployment {
          */
         int getMinTimeBetweenDescriptorRequests();
 
+        /**
+         * Returns {@link HttpClient} instance that will be used for http communication with this IdP.
+         * @return see description
+         */
+        HttpClient getClient();
+
         public interface SingleSignOnService {
             /**
              * Returns {@code true} if the requests to IdP need to be signed by SP key.
diff --git a/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd
new file mode 100644
index 0000000..174ea17
--- /dev/null
+++ b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<xs:schema version="1.0"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="urn:keycloak:saml:adapter"
+           targetNamespace="urn:keycloak:saml:adapter"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified">
+
+    <xs:element name="keycloak-saml-adapter" type="adapter-type"/>
+    <xs:complexType name="adapter-type">
+        <xs:annotation>
+            <xs:documentation>
+                <![CDATA[
+                    The Keycloak SAML Adapter keycloak-saml.xml config file
+                ]]>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:all>
+            <xs:element name="SP" maxOccurs="1" minOccurs="0" type="sp-type"/>
+        </xs:all>
+    </xs:complexType>
+
+    <xs:complexType name="sp-type">
+        <xs:all>
+            <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="PrincipalNameMapping" type="principal-name-mapping-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="RoleIdentifiers" type="role-identifiers-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="IDP" type="idp-type" minOccurs="1" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="entityID" type="xs:string" use="required"/>
+        <xs:attribute name="sslPolicy" type="xs:string" use="optional"/>
+        <xs:attribute name="nameIDPolicyFormat" type="xs:string" use="optional"/>
+        <xs:attribute name="logoutPage" type="xs:string" use="optional"/>
+        <xs:attribute name="forceAuthentication" type="xs:boolean" use="optional"/>
+        <xs:attribute name="isPassive" type="xs:boolean" use="optional"/>
+        <xs:attribute name="turnOffChangeSessionIdOnLogin" type="xs:boolean" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="keys-type">
+        <xs:sequence>
+            <xs:element name="Key" type="key-type" minOccurs="1" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="key-type">
+        <xs:all>
+            <xs:element name="KeyStore" maxOccurs="1" minOccurs="0" type="key-store-type"/>
+            <xs:element name="PrivateKeyPem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="PublicKeyPem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="CertificatePem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="signing" type="xs:boolean" use="optional"/>
+        <xs:attribute name="encryption" type="xs:boolean" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="key-store-type">
+        <xs:all>
+            <xs:element name="PrivateKey" maxOccurs="1" minOccurs="0" type="private-key-type"/>
+            <xs:element name="Certificate" type="certificate-type" minOccurs="0" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="file" type="xs:string" use="optional"/>
+        <xs:attribute name="resource" type="xs:string" use="optional"/>
+        <xs:attribute name="password" type="xs:string" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="private-key-type">
+        <xs:attribute name="alias" type="xs:string" use="required"/>
+        <xs:attribute name="password" type="xs:string" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="certificate-type">
+        <xs:attribute name="alias" type="xs:string" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="principal-name-mapping-type">
+        <xs:attribute name="policy" type="xs:string" use="required"/>
+        <xs:attribute name="attribute" type="xs:string" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="role-identifiers-type">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="Attribute" maxOccurs="unbounded" minOccurs="0" type="attribute-type"/>
+        </xs:choice>
+    </xs:complexType>
+    <xs:complexType name="attribute-type">
+        <xs:attribute name="name" type="xs:string" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="idp-type">
+        <xs:sequence minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="SingleSignOnService" maxOccurs="1" minOccurs="1" type="sign-on-type"/>
+            <xs:element name="SingleLogoutService" type="logout-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="HttpClient" type="http-client-type" minOccurs="0" maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute name="entityID" type="xs:string" use="required"/>
+        <xs:attribute name="signaturesRequired" type="xs:boolean" use="required"/>
+        <xs:attribute name="signatureAlgorithm" type="xs:string" use="optional"/>
+        <xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional"/>
+        <xs:attribute name="encryption" type="xs:boolean" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="sign-on-type">
+        <xs:attribute name="signRequest" type="xs:boolean" use="optional"/>
+        <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional"/>
+        <xs:attribute name="validateAssertionSignature" type="xs:boolean" use="optional"/>
+        <xs:attribute name="requestBinding" type="xs:string" use="optional"/>
+        <xs:attribute name="responseBinding" type="xs:string" use="optional"/>
+        <xs:attribute name="bindingUrl" type="xs:string" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="logout-type">
+        <xs:attribute name="signRequest" type="xs:boolean" use="optional"/>
+        <xs:attribute name="signResponse" type="xs:boolean" use="optional"/>
+        <xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional"/>
+        <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional"/>
+        <xs:attribute name="requestBinding" type="xs:string" use="optional"/>
+        <xs:attribute name="responseBinding" type="xs:string" use="optional"/>
+        <xs:attribute name="postBindingUrl" type="xs:string" use="optional"/>
+        <xs:attribute name="redirectBindingUrl" type="xs:string" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="http-client-type">
+        <xs:attribute name="allowAnyHostname" type="xs:boolean" use="optional"/>
+        <xs:attribute name="clientKeystore" type="xs:string" use="optional"/>
+        <xs:attribute name="clientKeystorePassword" type="xs:string" use="optional"/>
+        <xs:attribute name="connectionPoolSize" type="xs:int" use="optional"/>
+        <xs:attribute name="disableTrustManager" type="xs:boolean" use="optional"/>
+        <xs:attribute name="proxyUrl" type="xs:string" use="optional"/>
+        <xs:attribute name="truststore" type="xs:string" use="optional"/>
+        <xs:attribute name="truststorePassword" type="xs:string" use="optional"/>
+    </xs:complexType>
+
+</xs:schema>
diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java
new file mode 100755
index 0000000..10537b3
--- /dev/null
+++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.config.parsers;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.Test;
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import java.io.InputStream;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlAdapterXMLParserTest {
+
+    private static final String CURRENT_XSD_LOCATION = "/schema/keycloak_saml_adapter_1_7.xsd";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    private void testValidationValid(String fileName) throws Exception {
+        InputStream schema = getClass().getResourceAsStream(CURRENT_XSD_LOCATION);
+        InputStream is = getClass().getResourceAsStream(fileName);
+        assertNotNull(is);
+        assertNotNull(schema);
+        StaxParserUtil.validate(is, schema);
+    }
+
+    @Test
+    public void testValidationSimpleFile() throws Exception {
+        testValidationValid("keycloak-saml.xml");
+    }
+
+    @Test
+    public void testValidationMultipleKeys() throws Exception {
+        testValidationValid("keycloak-saml-multiple-signing-keys.xml");
+    }
+
+    @Test
+    public void testValidationWithHttpClient() throws Exception {
+        testValidationValid("keycloak-saml-wth-http-client-settings.xml");
+    }
+
+    @Test
+    public void testValidationKeyInvalid() throws Exception {
+        InputStream schemaIs = KeycloakSamlAdapterXMLParser.class.getResourceAsStream(CURRENT_XSD_LOCATION);
+        InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml");
+        assertNotNull(is);
+        assertNotNull(schemaIs);
+
+        expectedException.expect(ParsingException.class);
+        StaxParserUtil.validate(is, schemaIs);
+    }
+
+    @Test
+    public void testXmlParser() throws Exception {
+        InputStream is = getClass().getResourceAsStream("keycloak-saml.xml");
+        assertNotNull(is);
+        KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
+
+        KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
+        assertNotNull(config);
+        assertEquals(1, config.getSps().size());
+        SP sp = config.getSps().get(0);
+        assertEquals("sp", sp.getEntityID());
+        assertEquals("ssl", sp.getSslPolicy());
+        assertEquals("format", sp.getNameIDPolicyFormat());
+        assertTrue(sp.isForceAuthentication());
+        assertTrue(sp.isIsPassive());
+        assertEquals(2, sp.getKeys().size());
+        Key signing = sp.getKeys().get(0);
+        assertTrue(signing.isSigning());
+        Key.KeyStoreConfig keystore = signing.getKeystore();
+        assertNotNull(keystore);
+        assertEquals("file", keystore.getFile());
+        assertEquals("cp", keystore.getResource());
+        assertEquals("pw", keystore.getPassword());
+        assertEquals("private alias", keystore.getPrivateKeyAlias());
+        assertEquals("private pw", keystore.getPrivateKeyPassword());
+        assertEquals("cert alias", keystore.getCertificateAlias());
+        Key encryption = sp.getKeys().get(1);
+        assertTrue(encryption.isEncryption());
+        assertEquals("private pem", encryption.getPrivateKeyPem());
+        assertEquals("public pem", encryption.getPublicKeyPem());
+        assertEquals("policy", sp.getPrincipalNameMapping().getPolicy());
+        assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
+        assertTrue(sp.getRoleAttributes().size() == 1);
+        assertTrue(sp.getRoleAttributes().contains("member"));
+
+        IDP idp = sp.getIdp();
+        assertEquals("idp", idp.getEntityID());
+        assertEquals("RSA", idp.getSignatureAlgorithm());
+        assertEquals("canon", idp.getSignatureCanonicalizationMethod());
+        assertTrue(idp.getSingleSignOnService().isSignRequest());
+        assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
+        assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
+        assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
+
+        assertTrue(idp.getSingleLogoutService().isSignRequest());
+        assertTrue(idp.getSingleLogoutService().isSignResponse());
+        assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
+        assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
+        assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
+        assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
+        assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
+        assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
+
+        assertTrue(idp.getKeys().size() == 1);
+        assertTrue(idp.getKeys().get(0).isSigning());
+        assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
+    }
+
+
+    @Test
+    public void testXmlParserMultipleSigningKeys() throws Exception {
+        InputStream is = getClass().getResourceAsStream("keycloak-saml-multiple-signing-keys.xml");
+        assertNotNull(is);
+        KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
+
+        KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
+        assertNotNull(config);
+        assertEquals(1, config.getSps().size());
+        SP sp = config.getSps().get(0);
+        IDP idp = sp.getIdp();
+
+        assertTrue(idp.getKeys().size() == 4);
+        for (int i = 0; i < 4; i ++) {
+            Key key = idp.getKeys().get(i);
+            assertTrue(key.isSigning());
+            assertEquals("cert pem " + i, idp.getKeys().get(i).getCertificatePem());
+        }
+    }
+
+    @Test
+    public void testXmlParserHttpClientSettings() throws Exception {
+        InputStream is = getClass().getResourceAsStream("keycloak-saml-wth-http-client-settings.xml");
+        assertNotNull(is);
+        KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
+
+        KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
+        assertNotNull(config);
+        assertEquals(1, config.getSps().size());
+        SP sp = config.getSps().get(0);
+        IDP idp = sp.getIdp();
+
+        assertThat(idp.getHttpClientConfig(), notNullValue());
+        assertThat(idp.getHttpClientConfig().getClientKeystore(), is("ks"));
+        assertThat(idp.getHttpClientConfig().getClientKeystorePassword(), is("ks-pwd"));
+        assertThat(idp.getHttpClientConfig().getProxyUrl(), is("pu"));
+        assertThat(idp.getHttpClientConfig().getTruststore(), is("ts"));
+        assertThat(idp.getHttpClientConfig().getTruststorePassword(), is("tsp"));
+        assertThat(idp.getHttpClientConfig().getConnectionPoolSize(), is(42));
+        assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true));
+        assertThat(idp.getHttpClientConfig().isDisableTrustManager(), is(true));
+    }
+}
diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml
new file mode 100644
index 0000000..33b2f73
--- /dev/null
+++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml
@@ -0,0 +1,81 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter">
+    <SP entityID="sp"
+        sslPolicy="ssl"
+        nameIDPolicyFormat="format"
+        forceAuthentication="true"
+        isPassive="true">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore file="file" resource="cp" password="pw">
+                    <PrivateKey alias="private alias" password="private pw"/>
+                    <Certificate alias="cert alias"/>
+                </KeyStore>
+            </Key>
+            <Key encryption="true">
+                <PrivateKeyPem>
+                    private pem
+                </PrivateKeyPem>
+                <PublicKeyPem>
+                    public pem
+                </PublicKeyPem>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="policy" attribute="attribute"/>
+        <RoleIdentifiers>
+            <Attribute name="member"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signatureAlgorithm="RSA"
+             signatureCanonicalizationMethod="canon"
+             signaturesRequired="true"
+                >
+            <SingleSignOnService signRequest="true"
+                                 validateResponseSignature="true"
+                                 requestBinding="post"
+                                 bindingUrl="url"
+                    />
+
+            <SingleLogoutService
+                    validateRequestSignature="true"
+                    validateResponseSignature="true"
+                    signRequest="true"
+                    signResponse="true"
+                    requestBinding="redirect"
+                    responseBinding="post"
+                    postBindingUrl="posturl"
+                    redirectBindingUrl="redirecturl"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <CertificatePem>cert pem 0</CertificatePem>
+                </Key>
+                <Key signing="true">
+                    <CertificatePem>cert pem 1</CertificatePem>
+                </Key>
+                <Key signing="true">
+                    <CertificatePem>cert pem 2</CertificatePem>
+                </Key>
+                <Key signing="true">
+                    <CertificatePem>cert pem 3</CertificatePem>
+                </Key>
+            </Keys>
+        </IDP>
+    </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml
new file mode 100644
index 0000000..36410e5
--- /dev/null
+++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml
@@ -0,0 +1,81 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter">
+    <SP entityID="sp"
+        sslPolicy="ssl"
+        nameIDPolicyFormat="format"
+        forceAuthentication="true"
+        isPassive="true">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore file="file" resource="cp" password="pw">
+                    <PrivateKey alias="private alias" password="private pw"/>
+                    <Certificate alias="cert alias"/>
+                </KeyStore>
+            </Key>
+            <Key encryption="true">
+                <PrivateKeyPem>
+                    private pem
+                </PrivateKeyPem>
+                <PublicKeyPem>
+                    public pem
+                </PublicKeyPem>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="policy" attribute="attribute"/>
+        <RoleIdentifiers>
+            <Attribute name="member"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signatureAlgorithm="RSA"
+             signatureCanonicalizationMethod="canon"
+             signaturesRequired="true"
+                >
+            <SingleSignOnService signRequest="true"
+                                 validateResponseSignature="true"
+                                 requestBinding="post"
+                                 bindingUrl="url"
+                    />
+
+            <SingleLogoutService
+                    validateRequestSignature="true"
+                    validateResponseSignature="true"
+                    signRequest="true"
+                    signResponse="true"
+                    requestBinding="redirect"
+                    responseBinding="post"
+                    postBindingUrl="posturl"
+                    redirectBindingUrl="redirecturl"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <CertificatePem>
+                        cert pem
+                    </CertificatePem>
+                </Key>
+            </Keys>
+            <HttpClient allowAnyHostname="true"
+                        clientKeystore="ks" clientKeystorePassword="ks-pwd"
+                        connectionPoolSize="42"
+                        disableTrustManager="true"
+                        proxyUrl="pu"
+                        truststore="ts" truststorePassword="tsp"
+                        />
+        </IDP>
+    </SP>
+</keycloak-saml-adapter>
\ No newline at end of file