keycloak-aplcache
Changes
pom.xml 5(+5 -0)
saml/client-adapter/core/pom.xml 2(+1 -1)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java 21(+21 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java 56(+56 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java 195(+195 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java 100(+100 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java 46(+46 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java 60(+60 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java 128(+128 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java 11(+11 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java 139(+139 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java 363(+363 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java 50(+28 -22)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java 39(+39 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java 8(+7 -1)
saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java 80(+80 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java 26(+16 -10)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java 187(+187 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java 49(+46 -3)
testsuite/integration/pom.xml 8(+8 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java 510(+510 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java 167(+167 -0)
Details
pom.xml 5(+5 -0)
diff --git a/pom.xml b/pom.xml
index ce53528..c0a1cc1 100755
--- a/pom.xml
+++ b/pom.xml
@@ -992,6 +992,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-saml-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
</dependency>
saml/client-adapter/core/pom.xml 2(+1 -1)
diff --git a/saml/client-adapter/core/pom.xml b/saml/client-adapter/core/pom.xml
index 59bd4d5..605d87f 100755
--- a/saml/client-adapter/core/pom.xml
+++ b/saml/client-adapter/core/pom.xml
@@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-saml-adapter-core</artifactId>
- <name>Keycloak SAML Adapter Core</name>
+ <name>Keycloak SAML Client Adapter Core</name>
<description/>
<properties>
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
new file mode 100755
index 0000000..813f52e
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
@@ -0,0 +1,190 @@
+package org.keycloak.adapters.saml.config;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IDP implements Serializable {
+ public static class SingleSignOnService implements Serializable {
+ private boolean signRequest;
+ private boolean validateResponseSignature;
+ private String signatureCanonicalizationMethod;
+ private String requestBinding;
+ private String responseBinding;
+ private String bindingUrl;
+
+ public boolean isSignRequest() {
+ return signRequest;
+ }
+
+ public void setSignRequest(boolean signRequest) {
+ this.signRequest = signRequest;
+ }
+
+ public boolean isValidateResponseSignature() {
+ return validateResponseSignature;
+ }
+
+ public void setValidateResponseSignature(boolean validateResponseSignature) {
+ this.validateResponseSignature = validateResponseSignature;
+ }
+
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
+ public String getRequestBinding() {
+ return requestBinding;
+ }
+
+ public void setRequestBinding(String requestBinding) {
+ this.requestBinding = requestBinding;
+ }
+
+ public String getResponseBinding() {
+ return responseBinding;
+ }
+
+ public void setResponseBinding(String responseBinding) {
+ this.responseBinding = responseBinding;
+ }
+
+ public String getBindingUrl() {
+ return bindingUrl;
+ }
+
+ public void setBindingUrl(String bindingUrl) {
+ this.bindingUrl = bindingUrl;
+ }
+ }
+
+ public static class SingleLogoutService implements Serializable {
+ private boolean signRequest;
+ private boolean signResponse;
+ private boolean validateRequestSignature;
+ private boolean validateResponseSignature;
+ private String signatureCanonicalizationMethod;
+ private String requestBinding;
+ private String responseBinding;
+ private String postBindingUrl;
+ private String redirectBindingUrl;
+
+ public boolean isSignRequest() {
+ return signRequest;
+ }
+
+ public void setSignRequest(boolean signRequest) {
+ this.signRequest = signRequest;
+ }
+
+ public boolean isSignResponse() {
+ return signResponse;
+ }
+
+ public void setSignResponse(boolean signResponse) {
+ this.signResponse = signResponse;
+ }
+
+ public boolean isValidateRequestSignature() {
+ return validateRequestSignature;
+ }
+
+ public void setValidateRequestSignature(boolean validateRequestSignature) {
+ this.validateRequestSignature = validateRequestSignature;
+ }
+
+ public boolean isValidateResponseSignature() {
+ return validateResponseSignature;
+ }
+
+ public void setValidateResponseSignature(boolean validateResponseSignature) {
+ this.validateResponseSignature = validateResponseSignature;
+ }
+
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
+ public String getRequestBinding() {
+ return requestBinding;
+ }
+
+ public void setRequestBinding(String requestBinding) {
+ this.requestBinding = requestBinding;
+ }
+
+ public String getResponseBinding() {
+ return responseBinding;
+ }
+
+ public void setResponseBinding(String responseBinding) {
+ this.responseBinding = responseBinding;
+ }
+
+ public String getPostBindingUrl() {
+ return postBindingUrl;
+ }
+
+ public void setPostBindingUrl(String postBindingUrl) {
+ this.postBindingUrl = postBindingUrl;
+ }
+
+ public String getRedirectBindingUrl() {
+ return redirectBindingUrl;
+ }
+
+ public void setRedirectBindingUrl(String redirectBindingUrl) {
+ this.redirectBindingUrl = redirectBindingUrl;
+ }
+ }
+
+ private String entityID;
+ private SingleSignOnService singleSignOnService;
+ private SingleLogoutService singleLogoutService;
+ private List<Key> keys;
+
+ public String getEntityID() {
+ return entityID;
+ }
+
+ public void setEntityID(String entityID) {
+ this.entityID = entityID;
+ }
+
+ public SingleSignOnService getSingleSignOnService() {
+ return singleSignOnService;
+ }
+
+ public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
+ this.singleSignOnService = singleSignOnService;
+ }
+
+ public SingleLogoutService getSingleLogoutService() {
+ return singleLogoutService;
+ }
+
+ public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
+ this.singleLogoutService = singleLogoutService;
+ }
+
+ public List<Key> getKeys() {
+ return keys;
+ }
+
+ public void setKeys(List<Key> keys) {
+ this.keys = keys;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java
new file mode 100755
index 0000000..3d94f78
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java
@@ -0,0 +1,143 @@
+package org.keycloak.adapters.saml.config;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Key implements Serializable {
+
+ public static class KeyStoreConfig implements Serializable {
+ private String file;
+ private String resource;
+ private String password;
+ private String type;
+ private String alias;
+ private String privateKeyAlias;
+ private String privateKeyPassword;
+ private String certificateAlias;
+
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPrivateKeyAlias() {
+ return privateKeyAlias;
+ }
+
+ public void setPrivateKeyAlias(String privateKeyAlias) {
+ this.privateKeyAlias = privateKeyAlias;
+ }
+
+ public String getPrivateKeyPassword() {
+ return privateKeyPassword;
+ }
+
+ public void setPrivateKeyPassword(String privateKeyPassword) {
+ this.privateKeyPassword = privateKeyPassword;
+ }
+
+ public String getCertificateAlias() {
+ return certificateAlias;
+ }
+
+ public void setCertificateAlias(String certificateAlias) {
+ this.certificateAlias = certificateAlias;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+ }
+
+
+ private boolean signing;
+ private boolean encryption;
+ private KeyStoreConfig keystore;
+ private String privateKeyPem;
+ private String publicKeyPem;
+ private String certificatePem;
+
+
+ public boolean isSigning() {
+ return signing;
+ }
+
+ public void setSigning(boolean signing) {
+ this.signing = signing;
+ }
+
+ public boolean isEncryption() {
+ return encryption;
+ }
+
+ public void setEncryption(boolean encryption) {
+ this.encryption = encryption;
+ }
+
+ public KeyStoreConfig getKeystore() {
+ return keystore;
+ }
+
+ public void setKeystore(KeyStoreConfig keystore) {
+ this.keystore = keystore;
+ }
+
+ public String getPrivateKeyPem() {
+ return privateKeyPem;
+ }
+
+ public void setPrivateKeyPem(String privateKeyPem) {
+ this.privateKeyPem = privateKeyPem;
+ }
+
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+
+ public String getCertificatePem() {
+ return certificatePem;
+ }
+
+ public void setCertificatePem(String certificatePem) {
+ this.certificatePem = certificatePem;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java
new file mode 100755
index 0000000..9370dba
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java
@@ -0,0 +1,21 @@
+package org.keycloak.adapters.saml.config;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlAdapter implements Serializable {
+ private List<SP> sps = new LinkedList<>();
+
+ public List<SP> getSps() {
+ return sps;
+ }
+
+ public void setSps(List<SP> sps) {
+ this.sps = sps;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
new file mode 100755
index 0000000..a714b0a
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -0,0 +1,56 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConfigXmlConstants {
+ public static final String KEYCLOAK_SAML_ADAPTER ="keycloak-saml-adapter";
+ public static final String SP_ELEMENT="SP";
+ public static final String ENTITY_ID_ATTR = "entityID";
+ public static final String SSL_POLICY_ATTR = "sslPolicy";
+ public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
+ public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
+ public static final String LOGOUT_PAGE_ATTR = "logoutPage";
+
+
+ public static final String KEYS_ELEMENT = "Keys";
+ public static final String KEY_ELEMENT = "Key";
+ public static final String SIGNING_ATTR = "signing";
+ public static final String ENCRYPTION_ATTR = "encryption";
+ public static final String CERTIFICATE_PEM_ELEMENT = "CertificatePem";
+ public static final String PRIVATE_KEY_PEM_ELEMENT = "PrivateKeyPem";
+ public static final String PUBLIC_KEY_PEM_ELEMENT = "PublicKeyPem";
+ public static final String FILE_ATTR = "file";
+ public static final String TYPE_ATTR = "type";
+ public static final String RESOURCE_ATTR = "resource";
+ public static final String PASSWORD_ATTR = "password";
+ public static final String ALIAS_ATTR = "alias";
+ public static final String KEYS_STORE_ELEMENT = "KeyStore";
+ public static final String CERTIFICATE_ELEMENT = "Certificate";
+ public static final String PRIVATE_KEY_ELEMENT = "PrivateKey";
+
+ public static final String PRINCIPAL_NAME_MAPPING_ELEMENT = "PrincipalNameMapping";
+ public static final String POLICY_ATTR = "policy";
+ public static final String ATTRIBUTE_ATTR = "attribute";
+
+
+ public static final String ROLE_MAPPING_ELEMENT = "RoleMapping";
+ public static final String ATTRIBUTE_ELEMENT = "Attribute";
+ public static final String FRIENDLY_ATTRIBUTE_ELEMENT = "FriendlyAttribute";
+ public static final String NAME_ATTR = "name";
+
+ public static final String IDP_ELEMENT = "IDP";
+ public static final String SINGLE_SIGN_ON_SERVICE_ELEMENT = "SingleSignOnService";
+ public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService";
+ public static final String SIGN_REQUEST_ATTR = "signRequest";
+ public static final String SIGN_RESPONSE_ATTR = "signResponse";
+ public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
+ public static final String REQUEST_BINDING_ATTR = "requestBinding";
+ public static final String RESPONSE_BINDING_ATTR = "responseBinding";
+ public static final String BINDING_URL_ATTR = "bindingUrl";
+ public static final String VALIDATE_RESPONSE_SIGNATURE_ATTR = "validateResponseSignature";
+ 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";
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
new file mode 100755
index 0000000..24f9101
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -0,0 +1,195 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.enums.SslRequired;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.util.PemUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DeploymentBuilder {
+ public SamlDeployment build(InputStream xml, ResourceLoader resourceLoader) throws ParsingException {
+ DefaultSamlDeployment deployment = new DefaultSamlDeployment();
+ DefaultSamlDeployment.DefaultIDP idp = new DefaultSamlDeployment.DefaultIDP();
+ DefaultSamlDeployment.DefaultSingleSignOnService sso = new DefaultSamlDeployment.DefaultSingleSignOnService();
+ DefaultSamlDeployment.DefaultSingleLogoutService slo = new DefaultSamlDeployment.DefaultSingleLogoutService();
+ idp.setSingleSignOnService(sso);
+ idp.setSingleLogoutService(slo);
+
+ KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml));
+ SP sp = adapter.getSps().get(0);
+ deployment.setConfigured(true);
+ deployment.setEntityID(sp.getEntityID());
+ deployment.setForceAuthentication(sp.isForceAuthentication());
+ deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
+ deployment.setLogoutPage(sp.getLogoutPage());
+ if (sp.getPrincipalNameMapping() != null) {
+ SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy());
+ deployment.setPrincipalNamePolicy(policy);
+ deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName());
+ }
+ deployment.setRoleAttributeNames(sp.getRoleAttributes());
+ deployment.setRoleFriendlyAttributeNames(sp.getRoleFriendlyAttributes());
+ if (sp.getSslPolicy() != null) {
+ SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
+ deployment.setSslRequired(ssl);
+ }
+ for (Key key : sp.getKeys()) {
+ if (key.isSigning()) {
+ PrivateKey privateKey = null;
+ PublicKey publicKey = null;
+ if (key.getKeystore() != null) {
+ KeyStore keyStore = loadKeystore(resourceLoader, key);
+ Certificate cert = null;
+ try {
+ cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
+ privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ publicKey = cert.getPublicKey();
+ } else {
+ if (key.getPrivateKeyPem() == null) {
+ throw new RuntimeException("SP signing key must have a PrivateKey defined");
+ }
+ try {
+ privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
+ if (key.getPublicKeyPem() == null &&key.getCertificatePem() == null) {
+ throw new RuntimeException("Sp signing key must have a PublicKey or Certificate defined");
+ }
+ publicKey = getPublicKeyFromPem(key);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ KeyPair keyPair = new KeyPair(publicKey, privateKey);
+ deployment.setSigningKeyPair(keyPair);
+
+ } else if (key.isEncryption()) {
+ KeyStore keyStore = loadKeystore(resourceLoader, key);
+ try {
+ PrivateKey privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
+ deployment.setDecryptionKey(privateKey);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ deployment.setIdp(idp);
+ idp.setEntityID(sp.getIdp().getEntityID());
+ sso.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getRequestBinding()));
+ sso.setRequestBindingUrl(sp.getIdp().getSingleSignOnService().getBindingUrl());
+ sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
+ sso.setSignatureCanonicalizationMethod(sp.getIdp().getSingleSignOnService().getSignatureCanonicalizationMethod());
+ sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
+ sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
+
+ slo.setSignRequest(sp.getIdp().getSingleLogoutService().isSignRequest());
+ slo.setSignResponse(sp.getIdp().getSingleLogoutService().isSignResponse());
+ slo.setValidateResponseSignature(sp.getIdp().getSingleLogoutService().isValidateResponseSignature());
+ slo.setValidateRequestSignature(sp.getIdp().getSingleLogoutService().isValidateRequestSignature());
+ slo.setSignatureCanonicalizationMethod(sp.getIdp().getSingleLogoutService().getSignatureCanonicalizationMethod());
+ slo.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getRequestBinding()));
+ slo.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getResponseBinding()));
+ if (slo.getRequestBinding() == SamlDeployment.Binding.POST) {
+ slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl());
+ } else {
+ slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
+ }
+ if (slo.getResponseBinding() == SamlDeployment.Binding.POST) {
+ slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl());
+ } else {
+ slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
+ }
+ 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);
+ }
+ }
+ }
+ }
+
+
+ return deployment;
+ }
+
+ protected static PublicKey getPublicKeyFromPem(Key key) throws Exception {
+ PublicKey publicKey;
+ if (key.getPublicKeyPem() != null) {
+ publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
+ } else {
+ Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim());
+ publicKey = cert.getPublicKey();
+ }
+ return publicKey;
+ }
+
+ protected static KeyStore loadKeystore(ResourceLoader resourceLoader, Key key) {
+ String type = key.getKeystore().getType();
+ if (type == null) type = "JKS";
+ KeyStore keyStore = null;
+ try {
+ keyStore = KeyStore.getInstance(type);
+ } catch (KeyStoreException e) {
+ throw new RuntimeException(e);
+ }
+ InputStream is = null;
+ if (key.getKeystore().getFile() != null) {
+ File fp = new File(key.getKeystore().getFile());
+ if (!fp.exists()) {
+ }
+ try {
+ is = new FileInputStream(fp);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("KeyStore " + key.getKeystore().getFile() + " does not exist");
+ }
+
+ } else {
+ is = resourceLoader.getResourceAsStream(key.getKeystore().getResource());
+ if (is == null) {
+ throw new RuntimeException("KeyStore " + key.getKeystore().getResource() + " does not exist");
+ }
+ }
+ try {
+ keyStore.load(is, key.getKeystore().getPassword().toCharArray());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return keyStore;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
new file mode 100755
index 0000000..c4e9a6e
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -0,0 +1,100 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IDPXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
+ IDP idp = new IDP();
+ String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ if (entityID == null) {
+ throw new ParsingException("entityID must be set on IDP");
+
+ }
+ idp.setEntityID(entityID);
+ 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.IDP_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) {
+ IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader);
+ idp.setSingleSignOnService(sso);
+
+ } else if (tag.equals(ConfigXmlConstants.SINGLE_LOGOUT_SERVICE_ELEMENT)) {
+ IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader);
+ idp.setSingleLogoutService(slo);
+
+ } else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
+ KeysXmlParser parser = new KeysXmlParser();
+ List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
+ idp.setKeys(keys);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return idp;
+ }
+
+ protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader) throws ParsingException {
+ IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
+ slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
+ slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR));
+ slo.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR));
+ slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
+ slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
+ return slo;
+ }
+
+ protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader) throws ParsingException {
+ IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
+ sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
+ sso.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+ return sso;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java
new file mode 100755
index 0000000..d5b9ee6
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java
@@ -0,0 +1,46 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.StartElement;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlAdapterXMLParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ KeycloakSamlAdapter adapter = new KeycloakSamlAdapter();
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYCLOAK_SAML_ADAPTER);
+ while (xmlEventReader.hasNext()) {
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) {
+ SPXmlParser parser = new SPXmlParser();
+ SP sp = (SP)parser.parse(xmlEventReader);
+ if (sp != null) adapter.getSps().add(sp);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return adapter;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java
new file mode 100755
index 0000000..a63a6ac
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java
@@ -0,0 +1,60 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeysXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_ELEMENT);
+ List<Key> keys = new LinkedList<>();
+ 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.KEYS_ELEMENT))
+ break;
+ else
+ throw logger.parserUnknownEndElement(endElementName);
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) {
+ KeyXmlParser parser = new KeyXmlParser();
+ Key key = (Key)parser.parse(xmlEventReader);
+ keys.add(key);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return keys;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
new file mode 100755
index 0000000..787af69
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
@@ -0,0 +1,128 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeyXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
+ Key key = new Key();
+ key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
+ key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_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.KEY_ELEMENT))
+ break;
+ else
+ throw logger.parserUnknownEndElement(endElementName);
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) {
+ key.setKeystore(parseKeyStore(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return key;
+ }
+
+ protected Key.KeyStoreConfig parseKeyStore(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
+ Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
+ keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
+ keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
+ keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
+ if (keyStore.getFile() == null && keyStore.getResource() == null) {
+ throw new ParsingException("KeyStore element must have the url or classpath attribute set");
+ }
+ keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
+ if (keyStore.getPassword() == null) {
+ throw new ParsingException("KeyStore element must have the password attribute set");
+ }
+
+
+
+ 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.KEYS_STORE_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ if (keyStore.getCertificateAlias() == null) {
+ throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
+
+ }
+ } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ if (keyStore.getPrivateKeyAlias() == null) {
+ throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
+
+ }
+ keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
+ if (keyStore.getPrivateKeyPassword() == null) {
+ throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
+
+ }
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return keyStore;
+
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java
new file mode 100755
index 0000000..296f31e
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java
@@ -0,0 +1,11 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ResourceLoader {
+ InputStream getResourceAsStream(String resource);
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
new file mode 100755
index 0000000..8df8500
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -0,0 +1,139 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SPXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT);
+ SP sp = new SP();
+ String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ if (entityID == null) {
+ throw new ParsingException("entityID must be set on SP");
+
+ }
+ sp.setEntityID(entityID);
+ sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
+ sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
+ sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
+ sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_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.SP_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
+ KeysXmlParser parser = new KeysXmlParser();
+ List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
+ sp.setKeys(keys);
+ } else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
+ if (policy == null) {
+ throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
+
+ }
+ String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
+ SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
+ mapping.setPolicy(policy);
+ mapping.setAttributeName(attribute);
+ sp.setPrincipalNameMapping(mapping);
+
+ } else if (tag.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT)) {
+ parseRoleMapping(xmlEventReader, sp);
+ } else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
+ IDPXmlParser parser = new IDPXmlParser();
+ IDP idp = (IDP)parser.parse(xmlEventReader);
+ sp.setIdp(idp);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return sp;
+ }
+
+ protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
+ Set<String> roleAttributes = new HashSet<>();
+ Set<String> roleFriendlyAttributes = new HashSet<>();
+ 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_MAPPING_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ if (attributeValue == null) {
+ throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
+
+ }
+ roleAttributes.add(attributeValue);
+ } else if (tag.equals(ConfigXmlConstants.FRIENDLY_ATTRIBUTE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ if (attributeValue == null) {
+ throw new ParsingException("RoleMapping FriendlyAttribute element must have the name attribute set");
+
+ }
+ roleFriendlyAttributes.add(attributeValue);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ sp.setRoleAttributes(roleAttributes);
+ sp.setRoleFriendlyAttributes(roleFriendlyAttributes);
+ }
+
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
new file mode 100755
index 0000000..addafdb
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
@@ -0,0 +1,126 @@
+package org.keycloak.adapters.saml.config;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.enums.SslRequired;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SP implements Serializable {
+ public static class PrincipalNameMapping implements Serializable {
+ private String policy;
+ private String attributeName;
+
+ public String getPolicy() {
+ return policy;
+ }
+
+ public void setPolicy(String policy) {
+ this.policy = policy;
+ }
+
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ public void setAttributeName(String attributeName) {
+ this.attributeName = attributeName;
+ }
+ }
+
+ private String entityID;
+ private String sslPolicy;
+ private boolean forceAuthentication;
+ private String logoutPage;
+ private List<Key> keys;
+ private String nameIDPolicyFormat;
+ private PrincipalNameMapping principalNameMapping;
+ private Set<String> roleAttributes;
+ private Set<String> roleFriendlyAttributes;
+ private IDP idp;
+
+ public String getEntityID() {
+ return entityID;
+ }
+
+ public void setEntityID(String entityID) {
+ this.entityID = entityID;
+ }
+
+ public String getSslPolicy() {
+ return sslPolicy;
+ }
+
+ public void setSslPolicy(String sslPolicy) {
+ this.sslPolicy = sslPolicy;
+ }
+
+ public boolean isForceAuthentication() {
+ return forceAuthentication;
+ }
+
+ public void setForceAuthentication(boolean forceAuthentication) {
+ this.forceAuthentication = forceAuthentication;
+ }
+
+ public List<Key> getKeys() {
+ return keys;
+ }
+
+ public void setKeys(List<Key> keys) {
+ this.keys = keys;
+ }
+
+ public String getNameIDPolicyFormat() {
+ return nameIDPolicyFormat;
+ }
+
+ public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
+ this.nameIDPolicyFormat = nameIDPolicyFormat;
+ }
+
+ public PrincipalNameMapping getPrincipalNameMapping() {
+ return principalNameMapping;
+ }
+
+ public void setPrincipalNameMapping(PrincipalNameMapping principalNameMapping) {
+ this.principalNameMapping = principalNameMapping;
+ }
+
+ public Set<String> getRoleAttributes() {
+ return roleAttributes;
+ }
+
+ public void setRoleAttributes(Set<String> roleAttributes) {
+ this.roleAttributes = roleAttributes;
+ }
+
+ public Set<String> getRoleFriendlyAttributes() {
+ return roleFriendlyAttributes;
+ }
+
+ public void setRoleFriendlyAttributes(Set<String> roleFriendlyAttributes) {
+ this.roleFriendlyAttributes = roleFriendlyAttributes;
+ }
+
+ public IDP getIdp() {
+ return idp;
+ }
+
+ public void setIdp(IDP idp) {
+ this.idp = idp;
+ }
+
+ public String getLogoutPage() {
+ return logoutPage;
+ }
+
+ public void setLogoutPage(String logoutPage) {
+ this.logoutPage = logoutPage;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
new file mode 100755
index 0000000..65612b3
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -0,0 +1,363 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.enums.SslRequired;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DefaultSamlDeployment implements SamlDeployment {
+
+ public static class DefaultSingleSignOnService implements IDP.SingleSignOnService {
+ private boolean signRequest;
+ private boolean validateResponseSignature;
+ private String signatureCanonicalizationMethod;
+ private Binding requestBinding;
+ private Binding responseBinding;
+ private String requestBindingUrl;
+
+ @Override
+ public boolean signRequest() {
+ return signRequest;
+ }
+
+ @Override
+ public boolean validateResponseSignature() {
+ return validateResponseSignature;
+ }
+
+ @Override
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ @Override
+ public Binding getRequestBinding() {
+ return requestBinding;
+ }
+
+ @Override
+ public Binding getResponseBinding() {
+ return responseBinding;
+ }
+
+ @Override
+ public String getRequestBindingUrl() {
+ return requestBindingUrl;
+ }
+
+ public void setSignRequest(boolean signRequest) {
+ this.signRequest = signRequest;
+ }
+
+ public void setValidateResponseSignature(boolean validateResponseSignature) {
+ this.validateResponseSignature = validateResponseSignature;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
+ public void setRequestBinding(Binding requestBinding) {
+ this.requestBinding = requestBinding;
+ }
+
+ public void setResponseBinding(Binding responseBinding) {
+ this.responseBinding = responseBinding;
+ }
+
+ public void setRequestBindingUrl(String requestBindingUrl) {
+ this.requestBindingUrl = requestBindingUrl;
+ }
+ }
+
+ public static class DefaultSingleLogoutService implements IDP.SingleLogoutService {
+ private boolean validateRequestSignature;
+ private boolean validateResponseSignature;
+ private boolean signRequest;
+ private boolean signResponse;
+ private String signatureCanonicalizationMethod;
+ private Binding requestBinding;
+ private Binding responseBinding;
+ private String requestBindingUrl;
+ private String responseBindingUrl;
+
+ @Override
+ public boolean validateRequestSignature() {
+ return validateRequestSignature;
+ }
+
+ @Override
+ public boolean validateResponseSignature() {
+ return validateResponseSignature;
+ }
+
+ @Override
+ public boolean signRequest() {
+ return signRequest;
+ }
+
+ @Override
+ public boolean signResponse() {
+ return signResponse;
+ }
+
+ @Override
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ @Override
+ public Binding getRequestBinding() {
+ return requestBinding;
+ }
+
+ @Override
+ public Binding getResponseBinding() {
+ return responseBinding;
+ }
+
+ @Override
+ public String getRequestBindingUrl() {
+ return requestBindingUrl;
+ }
+
+ @Override
+ public String getResponseBindingUrl() {
+ return responseBindingUrl;
+ }
+
+ public void setValidateRequestSignature(boolean validateRequestSignature) {
+ this.validateRequestSignature = validateRequestSignature;
+ }
+
+ public void setValidateResponseSignature(boolean validateResponseSignature) {
+ this.validateResponseSignature = validateResponseSignature;
+ }
+
+ public void setSignRequest(boolean signRequest) {
+ this.signRequest = signRequest;
+ }
+
+ public void setSignResponse(boolean signResponse) {
+ this.signResponse = signResponse;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
+ public void setRequestBinding(Binding requestBinding) {
+ this.requestBinding = requestBinding;
+ }
+
+ public void setResponseBinding(Binding responseBinding) {
+ this.responseBinding = responseBinding;
+ }
+
+ public void setRequestBindingUrl(String requestBindingUrl) {
+ this.requestBindingUrl = requestBindingUrl;
+ }
+
+ public void setResponseBindingUrl(String responseBindingUrl) {
+ this.responseBindingUrl = responseBindingUrl;
+ }
+ }
+
+
+
+ public static class DefaultIDP implements IDP {
+
+
+
+ private String entityID;
+ private PublicKey signatureValidationKey;
+ private SingleSignOnService singleSignOnService;
+ private SingleLogoutService singleLogoutService;
+
+ @Override
+ public String getEntityID() {
+ return entityID;
+ }
+
+ @Override
+ public SingleSignOnService getSingleSignOnService() {
+ return singleSignOnService;
+ }
+
+ @Override
+ public SingleLogoutService getSingleLogoutService() {
+ return singleLogoutService;
+ }
+
+ @Override
+ public PublicKey getSignatureValidationKey() {
+ return signatureValidationKey;
+ }
+
+ public void setEntityID(String entityID) {
+ this.entityID = entityID;
+ }
+
+ public void setSignatureValidationKey(PublicKey signatureValidationKey) {
+ this.signatureValidationKey = signatureValidationKey;
+ }
+
+ public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
+ this.singleSignOnService = singleSignOnService;
+ }
+
+ public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
+ this.singleLogoutService = singleLogoutService;
+ }
+ }
+
+ private IDP idp;
+ private boolean configured;
+ private SslRequired sslRequired = SslRequired.EXTERNAL;
+ private String entityID;
+ private String nameIDPolicyFormat;
+ private boolean forceAuthentication;
+ private PrivateKey decryptionKey;
+ private KeyPair signingKeyPair;
+ private String assertionConsumerServiceUrl;
+ private Set<String> roleAttributeNames;
+ private Set<String> roleFriendlyAttributeNames;
+ private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
+ private String principalAttributeName;
+ private String logoutPage;
+
+
+ @Override
+ public IDP getIDP() {
+ return idp;
+ }
+
+ @Override
+ public boolean isConfigured() {
+ return configured;
+ }
+
+ @Override
+ public SslRequired getSslRequired() {
+ return sslRequired;
+ }
+
+ @Override
+ public String getEntityID() {
+ return entityID;
+ }
+
+ @Override
+ public String getNameIDPolicyFormat() {
+ return nameIDPolicyFormat;
+ }
+
+ @Override
+ public boolean isForceAuthentication() {
+ return forceAuthentication;
+ }
+
+ @Override
+ public PrivateKey getDecryptionKey() {
+ return decryptionKey;
+ }
+
+ @Override
+ public KeyPair getSigningKeyPair() {
+ return signingKeyPair;
+ }
+
+ @Override
+ public String getAssertionConsumerServiceUrl() {
+ return assertionConsumerServiceUrl;
+ }
+
+ @Override
+ public Set<String> getRoleAttributeNames() {
+ return roleAttributeNames;
+ }
+
+ @Override
+ public Set<String> getRoleAttributeFriendlyNames() {
+ return roleFriendlyAttributeNames;
+ }
+
+ @Override
+ public PrincipalNamePolicy getPrincipalNamePolicy() {
+ return principalNamePolicy;
+ }
+
+ @Override
+ public String getPrincipalAttributeName() {
+ return principalAttributeName;
+ }
+
+ public void setIdp(IDP idp) {
+ this.idp = idp;
+ }
+
+ public void setConfigured(boolean configured) {
+ this.configured = configured;
+ }
+
+ public void setSslRequired(SslRequired sslRequired) {
+ this.sslRequired = sslRequired;
+ }
+
+ public void setEntityID(String entityID) {
+ this.entityID = entityID;
+ }
+
+ public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
+ this.nameIDPolicyFormat = nameIDPolicyFormat;
+ }
+
+ public void setForceAuthentication(boolean forceAuthentication) {
+ this.forceAuthentication = forceAuthentication;
+ }
+
+ public void setDecryptionKey(PrivateKey decryptionKey) {
+ this.decryptionKey = decryptionKey;
+ }
+
+ public void setSigningKeyPair(KeyPair signingKeyPair) {
+ this.signingKeyPair = signingKeyPair;
+ }
+
+ public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
+ this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
+ }
+
+ public void setRoleAttributeNames(Set<String> roleAttributeNames) {
+ this.roleAttributeNames = roleAttributeNames;
+ }
+
+ public void setRoleFriendlyAttributeNames(Set<String> roleFriendlyAttributeNames) {
+ this.roleFriendlyAttributeNames = roleFriendlyAttributeNames;
+ }
+
+ public void setPrincipalNamePolicy(PrincipalNamePolicy principalNamePolicy) {
+ this.principalNamePolicy = principalNamePolicy;
+ }
+
+ public void setPrincipalAttributeName(String principalAttributeName) {
+ this.principalAttributeName = principalAttributeName;
+ }
+
+ @Override
+ public String getLogoutPage() {
+ return logoutPage;
+ }
+
+ public void setLogoutPage(String logoutPage) {
+ this.logoutPage = logoutPage;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
index d5f86d7..690a99c 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
@@ -34,8 +34,8 @@ public class InitiateLogin implements AuthChallenge {
@Override
public boolean challenge(HttpFacade httpFacade) {
try {
- String issuerURL = deployment.getIssuer();
- String actionUrl = deployment.getSingleSignOnServiceUrl();
+ String issuerURL = deployment.getEntityID();
+ String actionUrl = deployment.getIDP().getSingleSignOnService().getRequestBindingUrl();
String destinationUrl = actionUrl;
String nameIDPolicyFormat = deployment.getNameIDPolicyFormat();
@@ -45,28 +45,30 @@ public class InitiateLogin implements AuthChallenge {
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
- if (deployment.getResponseBinding() == SamlDeployment.Binding.POST) {
+ if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
}
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
- .assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl())
.destination(destinationUrl)
.issuer(issuerURL)
.forceAuthn(deployment.isForceAuthentication())
.protocolBinding(protocolBinding)
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
+ if (deployment.getAssertionConsumerServiceUrl() != null) {
+ authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
+ }
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
- if (deployment.isRequestsSigned()) {
+ if (deployment.getIDP().getSingleSignOnService().signRequest()) {
KeyPair keypair = deployment.getSigningKeyPair();
if (keypair == null) {
throw new RuntimeException("Signing keys not configured");
}
- if (deployment.getSignatureCanonicalizationMethod() != null) {
- binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
+ if (deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod() != null) {
+ binding.canonicalizationMethod(deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod());
}
binding.signWith(keypair);
@@ -75,7 +77,7 @@ public class InitiateLogin implements AuthChallenge {
sessionStore.saveRequest();
Document document = authnRequestBuilder.toDocument();
- SamlDeployment.Binding samlBinding = deployment.getRequestBinding();
+ SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
SamlUtil.sendSaml(httpFacade, actionUrl, binding, document, samlBinding);
} catch (Exception e) {
throw new RuntimeException("Could not create authentication request.", e);
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index 8c393bd..ca2da00 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -90,12 +90,12 @@ public abstract class SamlAuthenticator {
SamlSession account = sessionStore.getAccount();
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
.assertionExpiration(30)
- .issuer(deployment.getIssuer())
+ .issuer(deployment.getEntityID())
.sessionIndex(account.getSessionIndex())
.userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
- .destination(deployment.getSingleLogoutServiceUrl());
+ .destination(deployment.getIDP().getSingleLogoutService().getRequestBindingUrl());
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
- if (deployment.isRequestsSigned()) {
+ if (deployment.getIDP().getSingleLogoutService().signRequest()) {
binding.signWith(deployment.getSigningKeyPair())
.signDocument();
}
@@ -103,7 +103,7 @@ public abstract class SamlAuthenticator {
binding.relayState("logout");
try {
- SamlUtil.sendSaml(facade, deployment.getSingleLogoutServiceUrl(), binding, logoutBuilder.buildDocument(), deployment.getRequestBinding());
+ SamlUtil.sendSaml(facade, deployment.getIDP().getSingleLogoutService().getRequestBindingUrl(), binding, logoutBuilder.buildDocument(), deployment.getIDP().getSingleLogoutService().getRequestBinding());
} catch (ProcessingException e) {
throw new RuntimeException(e);
} catch (ConfigurationException e) {
@@ -129,9 +129,11 @@ public abstract class SamlAuthenticator {
if (!facade.getRequest().getURI().toString().equals(requestAbstractType.getDestination())) {
throw new RuntimeException("destination not equal to request");
}
- validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
if (requestAbstractType instanceof LogoutRequestType) {
+ if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
+ }
LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
return logoutRequest(logout, relayState);
@@ -147,21 +149,22 @@ public abstract class SamlAuthenticator {
sessionStore.logoutBySsoId(request.getSessionIndex());
}
- String issuerURL = deployment.getIssuer();
+ String issuerURL = deployment.getEntityID();
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
builder.logoutRequestID(request.getID());
- builder.destination(deployment.getSingleLogoutServiceUrl());
+ builder.destination(deployment.getIDP().getSingleLogoutService().getResponseBindingUrl());
builder.issuer(issuerURL);
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
- if (deployment.isRequestsSigned()) {
+ if (deployment.getIDP().getSingleLogoutService().signResponse()) {
binding.signWith(deployment.getSigningKeyPair())
.signDocument();
+ binding.canonicalizationMethod(deployment.getIDP().getSingleLogoutService().getSignatureCanonicalizationMethod());
}
try {
- SamlUtil.sendSaml(facade, deployment.getSingleLogoutServiceUrl(), binding, builder.buildDocument(),
- deployment.getResponseBinding());
+ SamlUtil.sendSaml(facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
+ deployment.getIDP().getSingleLogoutService().getResponseBinding());
} catch (ConfigurationException e) {
throw new RuntimeException(e);
} catch (ProcessingException e) {
@@ -188,11 +191,16 @@ public abstract class SamlAuthenticator {
if (!facade.getRequest().getURI().toString().equals(statusResponse.getDestination())) {
throw new RuntimeException("destination not equal to request");
}
- validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
if (statusResponse instanceof ResponseType) {
+ if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
+ }
return handleLoginResponse((ResponseType)statusResponse);
} else {
+ if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
+ }
// todo need to check that it is actually a LogoutResponse
return handleLogoutResponse(holder, statusResponse, relayState);
}
@@ -200,24 +208,22 @@ public abstract class SamlAuthenticator {
}
private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) {
- if (deployment.isValidateSignatures()) {
- try {
- if (postBinding) {
- verifyPostBindingSignature(holder.getSamlDocument(), deployment.getSignatureValidationKey());
- } else {
- verifyRedirectBindingSignature(deployment.getSignatureValidationKey(), paramKey);
- }
- } catch (VerificationException e) {
- log.error("validation failed", e);
- throw new RuntimeException("invalid document signature");
+ try {
+ if (postBinding) {
+ verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
+ } else {
+ verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
}
+ } catch (VerificationException e) {
+ log.error("validation failed", e);
+ throw new RuntimeException("invalid document signature");
}
}
protected AuthOutcome handleLoginResponse(ResponseType responseType) {
AssertionType assertion = null;
try {
- assertion = AssertionUtil.getAssertion(responseType, deployment.getAssertionDecryptionKey());
+ assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
if (AssertionUtil.hasExpired(assertion)) {
return initiateLogin();
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java
new file mode 100755
index 0000000..2479a23
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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;
+
+import org.keycloak.adapters.HttpFacade.Request;
+
+/**
+ * On multi-tenant scenarios, Keycloak will defer the resolution of a
+ * SamlDeployment to the target application at the request-phase.
+ *
+ * A Request object is passed to the resolver and callers expect a complete
+ * SamlDeployment. Based on this SamlDeployment, Keycloak will resume
+ * authenticating and authorizing the request.
+ *
+ * The easiest way to build a SamlDeployment is to use
+ * DeploymentBuilder , passing the InputStream of an existing
+ * keycloak-saml.xml to the build() method.
+ *
+ * @author Juraci Paixão Kröhling <juraci at kroehling.de>
+ */
+public interface SamlConfigResolver {
+
+ public SamlDeployment resolve(Request facade);
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 1de9d72..9363624 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -14,26 +14,53 @@ import java.util.Set;
public interface SamlDeployment {
enum Binding {
POST,
- REDIRECT
+ REDIRECT;
+
+ public static Binding parseBinding(String val) {
+ if (val == null) return POST;
+ return Binding.valueOf(val);
+ }
+ }
+
+ public interface IDP {
+ String getEntityID();
+
+ SingleSignOnService getSingleSignOnService();
+ SingleLogoutService getSingleLogoutService();
+ PublicKey getSignatureValidationKey();
+
+ public interface SingleSignOnService {
+ boolean signRequest();
+ boolean validateResponseSignature();
+ String getSignatureCanonicalizationMethod();
+ Binding getRequestBinding();
+ Binding getResponseBinding();
+ String getRequestBindingUrl();
+ }
+ public interface SingleLogoutService {
+ boolean validateRequestSignature();
+ boolean validateResponseSignature();
+ boolean signRequest();
+ boolean signResponse();
+ String getSignatureCanonicalizationMethod();
+ Binding getRequestBinding();
+ Binding getResponseBinding();
+ String getRequestBindingUrl();
+ String getResponseBindingUrl();
+ }
}
+ public IDP getIDP();
+
public boolean isConfigured();
SslRequired getSslRequired();
- String getSingleSignOnServiceUrl();
- String getSingleLogoutServiceUrl();
- String getIssuer();
+ String getEntityID();
String getNameIDPolicyFormat();
- String getAssertionConsumerServiceUrl();
- Binding getRequestBinding();
- Binding getResponseBinding();
- KeyPair getSigningKeyPair();
- String getSignatureCanonicalizationMethod();
boolean isForceAuthentication();
- boolean isRequestsSigned();
-
- boolean isValidateSignatures();
- PublicKey getSignatureValidationKey();
- PrivateKey getAssertionDecryptionKey();
+ PrivateKey getDecryptionKey();
+ KeyPair getSigningKeyPair();
+ String getAssertionConsumerServiceUrl();
+ String getLogoutPage();
Set<String> getRoleAttributeNames();
Set<String> getRoleAttributeFriendlyNames();
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java
index b8ea94d..cb8ea77 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java
@@ -7,7 +7,13 @@ import org.keycloak.adapters.HttpFacade;
* @version $Revision: 1 $
*/
public class SamlDeploymentContext {
+ private SamlDeployment deployment = null;
+
+ public SamlDeploymentContext(SamlDeployment deployment) {
+ this.deployment = deployment;
+ }
+
public SamlDeployment resolveDeployment(HttpFacade facade) {
- return null;
+ return deployment;
}
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
index f336c92..68b23da 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
@@ -40,7 +40,7 @@ public class SamlPrincipal implements Serializable, Principal {
@Override
public String getName() {
- return null;
+ return name;
}
diff --git a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
new file mode 100755
index 0000000..84f03ea
--- /dev/null
+++ b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
@@ -0,0 +1,80 @@
+package org.keycloak.test.adapters.saml;
+
+import org.junit.Assert;
+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.adapters.saml.config.parsers.KeycloakSamlAdapterXMLParser;
+
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class XmlParserTest {
+
+ @Test
+ public void testXmlParser() throws Exception {
+ InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
+ Assert.assertNotNull(is);
+ KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
+ KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
+ Assert.assertNotNull(config);
+ Assert.assertEquals(1, config.getSps().size());
+ SP sp = config.getSps().get(0);
+ Assert.assertEquals("sp", sp.getEntityID());
+ Assert.assertEquals("ssl", sp.getSslPolicy());
+ Assert.assertEquals("format", sp.getNameIDPolicyFormat());
+ Assert.assertTrue(sp.isForceAuthentication());
+ Assert.assertEquals(2, sp.getKeys().size());
+ Key signing = sp.getKeys().get(0);
+ Assert.assertTrue(signing.isSigning());
+ Key.KeyStoreConfig keystore = signing.getKeystore();
+ Assert.assertNotNull(keystore);
+ Assert.assertEquals("file", keystore.getFile());
+ Assert.assertEquals("cp", keystore.getResource());
+ Assert.assertEquals("pw", keystore.getPassword());
+ Assert.assertEquals("private alias", keystore.getPrivateKeyAlias());
+ Assert.assertEquals("private pw", keystore.getPrivateKeyPassword());
+ Assert.assertEquals("cert alias", keystore.getCertificateAlias());
+ Key encryption = sp.getKeys().get(1);
+ Assert.assertTrue(encryption.isEncryption());
+ Assert.assertEquals("private pem", encryption.getPrivateKeyPem());
+ Assert.assertEquals("public pem", encryption.getPublicKeyPem());
+ Assert.assertEquals("policy", sp.getPrincipalNameMapping().getPolicy());
+ Assert.assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
+ Assert.assertTrue(sp.getRoleAttributes().size() == 1);
+ Assert.assertTrue(sp.getRoleAttributes().contains("member"));
+ Assert.assertTrue(sp.getRoleFriendlyAttributes().size() == 1);
+ Assert.assertTrue(sp.getRoleFriendlyAttributes().contains("memberOf"));
+
+ IDP idp = sp.getIdp();
+ Assert.assertEquals("idp", idp.getEntityID());
+ Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
+ Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
+ Assert.assertEquals("canon", idp.getSingleSignOnService().getSignatureCanonicalizationMethod());
+ Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
+ Assert.assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
+
+ Assert.assertTrue(idp.getSingleLogoutService().isSignRequest());
+ Assert.assertTrue(idp.getSingleLogoutService().isSignResponse());
+ Assert.assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
+ Assert.assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
+ Assert.assertEquals("canon", idp.getSingleLogoutService().getSignatureCanonicalizationMethod());
+ Assert.assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
+ Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
+ Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
+ Assert.assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
+
+ Assert.assertTrue(idp.getKeys().size() == 1);
+ Assert.assertTrue(idp.getKeys().get(0).isSigning());
+ Assert.assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
+
+
+
+
+ }
+}
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
new file mode 100755
index 0000000..5755fd7
--- /dev/null
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -0,0 +1,61 @@
+<keycloak-saml-adapter>
+ <SP entityID="sp"
+ sslPolicy="ssl"
+ nameIDPolicyFormat="format"
+ forceAuthentication="true">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore file="file" classpath="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"/>
+ <RoleMapping>
+ <Attribute name="member"/>
+ <FriendlyAttribute name="memberOf"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ signatureCanonicalizationMethod="canon"
+ requestBinding="post"
+ bindingUrl="url"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ signatureCanonicalizationMethod="canon"
+ requestBinding="redirect"
+ responseBinding="post"
+ postBindingUrl="posturl"
+ redirectBindingUrl="redirecturl"
+ />
+ <Keys>
+ <Key signing="true">
+ <CertificatePem>
+ cert pem
+ </CertificatePem>
+ </Key>
+ </Keys>
+ </IDP>
+ <Keys>
+ <KeyStore>
+
+ </KeyStore>
+ </Keys>
+
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
index 6a21c5e..9b29618 100755
--- a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
@@ -42,16 +42,13 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
protected SamlDeploymentContext deploymentContext;
protected UndertowUserSessionManagement sessionManagement;
- protected String logoutPage;
protected String errorPage;
public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
- String logoutPage,
String errorPage) {
this.deploymentContext = deploymentContext;
this.sessionManagement = sessionManagement;
this.errorPage = errorPage;
- this.logoutPage = logoutPage;
}
@Override
@@ -62,7 +59,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
Integer code = servePage(exchange, errorPage);
return new ChallengeResult(true, code);
}
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = createFacade(exchange);
if (challenge.challenge(facade)) {
return new ChallengeResult(true, exchange.getResponseCode());
}
@@ -91,7 +88,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
HttpServerExchange exchange = notification.getExchange();
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = createFacade(exchange);
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
sessionStore.logoutAccount();
@@ -105,7 +102,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
* Call this inside your authenticate method.
*/
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = createFacade(exchange);
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
@@ -120,10 +117,8 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
}
if (outcome == AuthOutcome.LOGGED_OUT) {
securityContext.logout();
- if (logoutPage != null) {
- sendRedirect(exchange, logoutPage);
- exchange.setResponseCode(302);
- exchange.endExchange();
+ if (deployment.getLogoutPage() != null) {
+ redirectLogout(deployment, exchange);
}
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
@@ -138,5 +133,16 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
+ protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
+ String page = deployment.getLogoutPage();
+ sendRedirect(exchange, page);
+ exchange.setResponseCode(302);
+ exchange.endExchange();
+ }
+
+ protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
+ return new UndertowHttpFacade(exchange);
+ }
+
protected abstract SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext);
}
\ No newline at end of file
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java
new file mode 100755
index 0000000..ac3da61
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.AuthenticationMechanismFactory;
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.server.handlers.form.FormParserFactory;
+import io.undertow.servlet.ServletExtension;
+import io.undertow.servlet.api.AuthMethodConfig;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.LoginConfig;
+import io.undertow.servlet.api.ServletSessionConfig;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlConfigResolver;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
+import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+import javax.servlet.ServletContext;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlServletExtension implements ServletExtension {
+
+ protected static Logger log = Logger.getLogger(SamlServletExtension.class);
+
+ // todo when this DeploymentInfo method of the same name is fixed.
+ public boolean isAuthenticationMechanismPresent(DeploymentInfo deploymentInfo, final String mechanismName) {
+ LoginConfig loginConfig = deploymentInfo.getLoginConfig();
+ if (loginConfig != null) {
+ for (AuthMethodConfig method : loginConfig.getAuthMethods()) {
+ if (method.getName().equalsIgnoreCase(mechanismName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static InputStream getConfigInputStream(ServletContext context) {
+ InputStream is = null;
+ if (is == null) {
+ String path = context.getInitParameter("keycloak.config.file");
+ if (path == null) {
+ log.debug("using /WEB-INF/keycloak-saml.xml");
+ is = context.getResourceAsStream("/WEB-INF/keycloak-saml.xml");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return is;
+ }
+
+
+ @Override
+ @SuppressWarnings("UseSpecificCatch")
+ public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
+ if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK-SAML")) {
+ log.debug("auth-method is not keycloak saml!");
+ return;
+ }
+ log.debug("SamlServletException initialization");
+
+ // Possible scenarios:
+ // 1) The deployment has a keycloak.config.resolver specified and it exists:
+ // Outcome: adapter uses the resolver
+ // 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exists, isn't a resolver, ...) :
+ // Outcome: adapter is left unconfigured
+ // 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent)
+ // Outcome: adapter uses it
+ // 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
+ // Outcome: adapter is left unconfigured
+
+ SamlConfigResolver configResolver;
+ String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
+ SamlDeploymentContext deploymentContext = null;
+ if (configResolverClass != null) {
+ try {
+ throw new RuntimeException("Not implemented yet");
+ //configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
+ //deploymentContext = new AdapterDeploymentContext(configResolver);
+ //log.info("Using " + configResolverClass + " to resolve Keycloak configuration on a per-request basis.");
+ } catch (Exception ex) {
+ log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
+ //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ InputStream is = getConfigInputStream(servletContext);
+ final SamlDeployment deployment;
+ if (is == null) {
+ log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ deployment = new DefaultSamlDeployment();
+ } else {
+ try {
+ ResourceLoader loader = new ResourceLoader() {
+ @Override
+ public InputStream getResourceAsStream(String resource) {
+ return servletContext.getResourceAsStream(resource);
+ }
+ };
+ deployment = new DeploymentBuilder().build(is, loader);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ deploymentContext = new SamlDeploymentContext(deployment);
+ log.debug("Keycloak is using a per-deployment configuration.");
+ }
+
+ servletContext.setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
+ UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
+ final ServletSamlAuthMech mech = new ServletSamlAuthMech(deploymentContext, userSessionManagement, getErrorPage(deploymentInfo));
+
+
+ // setup handlers
+
+ deploymentInfo.addAuthenticationMechanism("KEYCLOAK-SAML", new AuthenticationMechanismFactory() {
+ @Override
+ public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
+ return mech;
+ }
+ }); // authentication
+
+ deploymentInfo.setIdentityManager(new IdentityManager() {
+ @Override
+ public Account verify(Account account) {
+ return account;
+ }
+
+ @Override
+ public Account verify(String id, Credential credential) {
+ throw new IllegalStateException("Should never be called in Keycloak flow");
+ }
+
+ @Override
+ public Account verify(Credential credential) {
+ throw new IllegalStateException("Should never be called in Keycloak flow");
+ }
+ });
+
+ log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
+ ServletSessionConfig cookieConfig = new ServletSessionConfig();
+ cookieConfig.setPath(deploymentInfo.getContextPath());
+ deploymentInfo.setServletSessionConfig(cookieConfig);
+
+ }
+
+ protected String getErrorPage(DeploymentInfo deploymentInfo) {
+ LoginConfig loginConfig = deploymentInfo.getLoginConfig();
+ String errorPage = null;
+ if (loginConfig != null) {
+ errorPage = loginConfig.getErrorPage();
+ }
+ return errorPage;
+ }
+}
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java
index ec2444a..cd52536 100755
--- a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java
@@ -2,24 +2,67 @@ package org.keycloak.adapters.saml.undertow;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import io.undertow.util.Headers;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.SamlDeploymentContext;
import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.undertow.ServletHttpFacade;
+import org.keycloak.adapters.undertow.UndertowHttpFacade;
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
- public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
- String logoutPage, String errorPage) {
- super(deploymentContext, sessionManagement, logoutPage, errorPage);
+ public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
+ super(deploymentContext, sessionManagement, errorPage);
}
@Override
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
return new ServletSamlSessionStore(exchange, sessionManagement, securityContext);
}
+
+ @Override
+ protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
+ return new ServletHttpFacade(exchange);
+ }
+
+ @Override
+ protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
+ servePage(exchange, deployment.getLogoutPage());
+ }
+
+ @Override
+ protected Integer servePage(HttpServerExchange exchange, String location) {
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ ServletRequest req = servletRequestContext.getServletRequest();
+ ServletResponse resp = servletRequestContext.getServletResponse();
+ RequestDispatcher disp = req.getRequestDispatcher(location);
+ //make sure the login page is never cached
+ exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
+ exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
+ exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
+
+
+ try {
+ disp.forward(req, resp);
+ } catch (ServletException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
index fb32fef..7941729 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
@@ -105,6 +105,23 @@ public class StaxParserUtil {
}
/**
+ * Get the Attribute value
+ *
+ * @param startElement
+ * @param tag localpart of the qname of the attribute
+ *
+ * @return false if attribute not set
+ */
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
+ String result = null;
+ Attribute attr = startElement.getAttributeByName(new QName(tag));
+ if (attr != null)
+ result = getAttributeValue(attr);
+ if (result == null) return false;
+ return Boolean.valueOf(result);
+ }
+
+ /**
* Given that the {@code XMLEventReader} is in {@code XMLStreamConstants.START_ELEMENT} mode, we parse into a DOM
* Element
*
testsuite/integration/pom.xml 8(+8 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 7948e97..166fa4b 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -103,6 +103,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-saml-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
</dependency>
<dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java
new file mode 100755
index 0000000..9639aab
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java
@@ -0,0 +1,510 @@
+package org.keycloak.testsuite.keycloaksaml;
+
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.Config;
+import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.dom.saml.v2.assertion.AttributeType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
+import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper;
+import org.keycloak.protocol.saml.mappers.HardcodedRole;
+import org.keycloak.protocol.saml.mappers.RoleListMapper;
+import org.keycloak.protocol.saml.mappers.RoleNameMapper;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
+import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
+import org.keycloak.saml.processing.web.util.PostBindingUtil;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.admin.AdminRoot;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlBindingTest {
+
+ @ClassRule
+ public static SamlKeycloakRule keycloakRule = new SamlKeycloakRule() {
+ @Override
+ public void initWars() {
+ ClassLoader classLoader = SamlBindingTest.class.getClassLoader();
+
+ //initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post", "post.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email", "post-sig-email.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient", "post-sig-transient.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
+ //initializeSamlSecuredWar("/saml/simple-get", "/employee", "employee.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
+ //initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
+ //uploadSP();
+ //server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
+
+
+
+ }
+
+ @Override
+ public String getRealmJson() {
+ return "/saml/testsaml.json";
+ }
+ };
+
+ public static class SamlSPFacade extends HttpServlet {
+ public static String samlResponse;
+ public static String RELAY_STATE = "http://test.com/foo/bar";
+ public static String sentRelayState;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ handler(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ handler(req, resp);
+ }
+
+ private void handler(HttpServletRequest req, HttpServletResponse resp) {
+ System.out.println("********* HERE ******");
+ if (req.getParameterMap().isEmpty()) {
+ System.out.println("redirecting");
+ resp.setStatus(302);
+ // Redirect
+ // UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVLRTsIwFP2Vpe%2BjG4wxG0YyWYxL0BBAH3wx3XYnTbp29nYof%2B8YEvEBNOlD03vOveec2ynyWjYsae1WreC9BbTOZy0Vsr4Qk9YopjkKZIrXgMwWbJ08LNhw4LHGaKsLLcmRch3MEcFYoRVxktN1rhW2NZg1mJ0o4Gm1iMnW2oZRKnXB5VajZZEX%2BRTqRuo9ACVO2mkUih%2F4l9C8s0MNcFkjLaHW9KSUHlwR506bAnrPMam4RCBOlsYkS1%2BD3MvLcDJxAx9KN4jCkXszrG5cP%2BCVH4y8IM8PYFx2dsQOfuiILWQKLVc2JkPPH7te6HrRxh%2BzUdidwSSIXoiz%2FBZyK1Qp1Nv1yPIjCNn9ZrN0V1AKA4UlzjMY7N13IDKbHjyxXoA5291%2FtzH7I%2FApPet%2FHNawx65hli61FMXeSaTUH%2FMubtvlYU0LfcA1t5cl%2BAO%2FfxGlW%2FVQ1ipsoBCVgJLQ2XHo7385%2BwI%3D");
+ UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVJbT8IwFP4rS99HuwluNIwEIUYSLwugD76Y2h2kSdfOng7l31uGRn0ATfrQ9HznfJfTEYpaN3zS%2Bo1ZwGsL6KP3WhvkXaEgrTPcClTIjagBuZd8Obm55mmP8cZZb6XV5NByGiwQwXllDYkmX9epNdjW4JbgtkrC%2FeK6IBvvG06ptlLojUXPc5YnFOpG2x0AJdEsaFRG7PuPoUWwQx0IXSOtoLb0SynduyLRpXUSOs8FWQuNQKL5rCDz2VO%2FymEgIY2zlJ3H%2FSx9jkU%2BzOK0ys8yNmSSsUEAYxnsqC18tyO2MDfohfEFSVkyiNlZzM5XacrDSbJePug%2Fkqj8FHKhTKXMy%2BnIng8g5FerVRmXd8sViR7AYec8AMh4tPfDO3L3Y2%2F%2F3cT4j7BH9Mf8A1nDb8PA%2Bay0WsldNNHavk1D1D5k4V0LXbi18MclJL2ke1FVvO6gvDXYgFRrBRWh4wPp7z85%2FgA%3D");
+ builder.queryParam("RelayState", RELAY_STATE);
+ resp.setHeader("Location", builder.build().toString());
+ return;
+ }
+ System.out.println("received response");
+ samlResponse = req.getParameter("SAMLResponse");
+ sentRelayState = req.getParameter("RelayState");
+ }
+ }
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+ @WebResource
+ protected WebDriver driver;
+ @WebResource
+ protected LoginPage loginPage;
+
+ protected void checkLoggedOut(String mainUrl) {
+ String pageSource = driver.getPageSource();
+ System.out.println("*** logout pagesource ***");
+ System.out.println(pageSource);
+ System.out.println("driver url: " + driver.getCurrentUrl());
+ Assert.assertTrue(pageSource.contains("request-path: /logout.jsp"));
+ driver.navigate().to(mainUrl);
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ }
+
+ //@Test
+ public void ideTesting() throws Exception {
+ Thread.sleep(100000000);
+ }
+
+ @Test
+ public void testPostSimpleLoginLogout() {
+ driver.navigate().to("http://localhost:8081/sales-post/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post/");
+ System.out.println(driver.getPageSource());
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post/");
+ }
+ @Test
+ public void testPostSimpleLoginLogoutIdpInitiated() {
+ driver.navigate().to("http://localhost:8081/auth/realms/demo/protocol/saml/clients/sales-post");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post/");
+ System.out.println(driver.getPageSource());
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post/");
+ }
+
+ @Test
+ public void testPostSignedLoginLogout() {
+ driver.navigate().to("http://localhost:8081/sales-post-sig/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-post-sig?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post-sig/");
+
+ }
+ @Test
+ public void testPostSignedLoginLogoutTransientNameID() {
+ driver.navigate().to("http://localhost:8081/sales-post-sig-transient/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-transient/");
+ System.out.println(driver.getPageSource());
+ Assert.assertFalse(driver.getPageSource().contains("bburke"));
+ Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
+ driver.navigate().to("http://localhost:8081/sales-post-sig-transient?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post-sig-transient/");
+
+ }
+ @Test
+ public void testPostSignedLoginLogoutPersistentNameID() {
+ driver.navigate().to("http://localhost:8081/sales-post-sig-persistent/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-persistent/");
+ System.out.println(driver.getPageSource());
+ Assert.assertFalse(driver.getPageSource().contains("bburke"));
+ Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
+ driver.navigate().to("http://localhost:8081/sales-post-sig-persistent?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post-sig-persistent/");
+
+ }
+ @Test
+ public void testPostSignedLoginLogoutEmailNameID() {
+ driver.navigate().to("http://localhost:8081/sales-post-sig-email/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-email/");
+ System.out.println(driver.getPageSource());
+ Assert.assertTrue(driver.getPageSource().contains("principal=bburke@redhat.com"));
+ driver.navigate().to("http://localhost:8081/sales-post-sig-email?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post-sig-email/");
+
+ }
+
+ @Test
+ public void testRelayStateEncoding() throws Exception {
+ // this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
+ // at the relay state
+ SamlSPFacade.samlResponse = null;
+ driver.navigate().to("http://localhost:8081/employee/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ System.out.println(driver.getCurrentUrl());
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
+ Assert.assertEquals(SamlSPFacade.sentRelayState, SamlSPFacade.RELAY_STATE);
+ Assert.assertNotNull(SamlSPFacade.samlResponse);
+
+ }
+
+
+ @Test
+ public void testAttributes() throws Exception {
+ // this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
+ // at the assertions sent. This is because Picketlink, AFAICT, does not give you any way to get access to
+ // the assertion.
+
+ {
+ SamlSPFacade.samlResponse = null;
+ driver.navigate().to("http://localhost:8081/employee/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ System.out.println(driver.getCurrentUrl());
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
+ Assert.assertNotNull(SamlSPFacade.samlResponse);
+ SAML2Response saml2Response = new SAML2Response();
+ byte[] samlResponse = PostBindingUtil.base64Decode(SamlSPFacade.samlResponse);
+ ResponseType rt = saml2Response.getResponseType(new ByteArrayInputStream(samlResponse));
+ Assert.assertTrue(rt.getAssertions().size() == 1);
+ AssertionType assertion = rt.getAssertions().get(0).getAssertion();
+
+ // test attributes and roles
+
+ boolean email = false;
+ boolean phone = false;
+ boolean userRole = false;
+ boolean managerRole = false;
+ for (AttributeStatementType statement : assertion.getAttributeStatements()) {
+ for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
+ AttributeType attr = choice.getAttribute();
+ if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attr.getFriendlyName())) {
+ Assert.assertEquals(X500SAMLProfileConstants.EMAIL.get(), attr.getName());
+ Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(), attr.getNameFormat());
+ Assert.assertEquals(attr.getAttributeValue().get(0), "bburke@redhat.com");
+ email = true;
+ } else if (attr.getName().equals("phone")) {
+ Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get(), attr.getNameFormat());
+ Assert.assertEquals(attr.getAttributeValue().get(0), "617");
+ phone = true;
+ } else if (attr.getName().equals("Role")) {
+ if (attr.getAttributeValue().get(0).equals("manager")) managerRole = true;
+ if (attr.getAttributeValue().get(0).equals("user")) userRole = true;
+ }
+ }
+
+ }
+
+ Assert.assertTrue(email);
+ Assert.assertTrue(phone);
+ Assert.assertTrue(userRole);
+ Assert.assertTrue(managerRole);
+ }
+
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ ClientModel app = appRealm.getClientByClientId("http://localhost:8081/employee/");
+ for (ProtocolMapperModel mapper : app.getProtocolMappers()) {
+ if (mapper.getName().equals("role-list")) {
+ app.removeProtocolMapper(mapper);
+ mapper.setId(null);
+ mapper.getConfig().put(RoleListMapper.SINGLE_ROLE_ATTRIBUTE, "true");
+ mapper.getConfig().put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "memberOf");
+ app.addProtocolMapper(mapper);
+ }
+ }
+ app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard", false, null));
+ app.addProtocolMapper(HardcodedRole.create("hardcoded-role", "hardcoded-role"));
+ app.addProtocolMapper(RoleNameMapper.create("renamed-role", "manager", "el-jefe"));
+ app.addProtocolMapper(RoleNameMapper.create("renamed-employee-role", "http://localhost:8081/employee/.employee", "pee-on"));
+ }
+ }, "demo");
+
+ System.out.println(">>>>>>>>>> single role attribute <<<<<<<<");
+
+ {
+ SamlSPFacade.samlResponse = null;
+ driver.navigate().to("http://localhost:8081/employee/");
+ System.out.println(driver.getCurrentUrl());
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
+ Assert.assertNotNull(SamlSPFacade.samlResponse);
+ SAML2Response saml2Response = new SAML2Response();
+ byte[] samlResponse = PostBindingUtil.base64Decode(SamlSPFacade.samlResponse);
+ ResponseType rt = saml2Response.getResponseType(new ByteArrayInputStream(samlResponse));
+ Assert.assertTrue(rt.getAssertions().size() == 1);
+ AssertionType assertion = rt.getAssertions().get(0).getAssertion();
+
+ // test attributes and roles
+
+ boolean userRole = false;
+ boolean managerRole = false;
+ boolean single = false;
+ boolean hardcodedRole = false;
+ boolean hardcodedAttribute = false;
+ boolean peeOn = false;
+ for (AttributeStatementType statement : assertion.getAttributeStatements()) {
+ for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
+ AttributeType attr = choice.getAttribute();
+ if (attr.getName().equals("memberOf")) {
+ if (single) Assert.fail("too many role attributes");
+ single = true;
+ for (Object value : attr.getAttributeValue()) {
+ if (value.equals("el-jefe")) managerRole = true;
+ if (value.equals("user")) userRole = true;
+ if (value.equals("hardcoded-role")) hardcodedRole = true;
+ if (value.equals("pee-on")) peeOn = true;
+ }
+ } else if (attr.getName().equals("hardcoded-attribute")) {
+ hardcodedAttribute = true;
+ Assert.assertEquals(attr.getAttributeValue().get(0), "hard");
+ }
+ }
+
+ }
+
+ Assert.assertTrue(single);
+ Assert.assertTrue(hardcodedAttribute);
+ Assert.assertTrue(hardcodedRole);
+ Assert.assertTrue(peeOn);
+ Assert.assertTrue(userRole);
+ Assert.assertTrue(managerRole);
+ }
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogout() {
+ driver.navigate().to("http://localhost:8081/employee-sig/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
+ checkLoggedOut("http://localhost:8081/employee-sig/");
+
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogoutFrontNoSSO() {
+ driver.navigate().to("http://localhost:8081/employee-sig-front/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/employee-sig-front?GLO=true");
+ checkLoggedOut("http://localhost:8081/employee-sig-front/");
+
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogoutFront() {
+ // visit 1st app an logg in
+ System.out.println("visit 1st app ");
+ driver.navigate().to("http://localhost:8081/employee-sig/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ System.out.println("login to form");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+
+ // visit 2nd app
+ System.out.println("visit 2nd app ");
+ driver.navigate().to("http://localhost:8081/employee-sig-front/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+
+ // visit 3rd app
+ System.out.println("visit 3rd app ");
+ driver.navigate().to("http://localhost:8081/sales-post-sig/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+
+ // logout of first app
+ System.out.println("GLO");
+ driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
+ checkLoggedOut("http://localhost:8081/employee-sig/");
+ driver.navigate().to("http://localhost:8081/employee-sig-front/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ driver.navigate().to("http://localhost:8081/sales-post-sig/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+
+ }
+
+ @Test
+ public void testPostEncryptedLoginLogout() {
+ driver.navigate().to("http://localhost:8081/sales-post-enc/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-enc/");
+ Assert.assertTrue(driver.getPageSource().contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-post-enc?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-post-enc/");
+
+ }
+ @Test
+ public void testPostBadClientSignature() {
+ driver.navigate().to("http://localhost:8081/bad-client-sales-post-sig/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ Assert.assertEquals(driver.getTitle(), "We're sorry...");
+
+ }
+
+ @Test
+ public void testPostBadRealmSignature() {
+ driver.navigate().to("http://localhost:8081/bad-realm-sales-post-sig/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/bad-realm-sales-post-sig/");
+ Assert.assertTrue(driver.getPageSource().contains("null"));
+ }
+
+ private static String createToken() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ RealmManager manager = new RealmManager(session);
+
+ RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
+ ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
+ TokenManager tm = new TokenManager();
+ UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+ ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
+ clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/master");
+ UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
+ AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
+ return tm.encodeToken(adminRealm, token);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+ }
+
+
+ @Test
+ public void testMetadataPostSignedLoginLogout() throws Exception {
+
+ driver.navigate().to("http://localhost:8081/sales-metadata/");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-metadata/");
+ String pageSource = driver.getPageSource();
+ Assert.assertTrue(pageSource.contains("bburke"));
+ driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true");
+ checkLoggedOut("http://localhost:8081/sales-metadata/");
+
+ }
+
+ public static void uploadSP() {
+ String token = createToken();
+ final String authHeader = "Bearer " + token;
+ ClientRequestFilter authFilter = new ClientRequestFilter() {
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+ };
+ Client client = ClientBuilder.newBuilder().register(authFilter).build();
+ UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
+ WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
+
+
+ MultipartFormDataOutput formData = new MultipartFormDataOutput();
+ InputStream is = SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml");
+ Assert.assertNotNull(is);
+ formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
+
+ WebTarget upload = adminRealms.path("demo/client-importers/saml2-entity-descriptor/upload");
+ System.out.println(upload.getUri());
+ Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
+ Assert.assertEquals(204, response.getStatus());
+ response.close();
+ client.close();
+ }
+
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java
new file mode 100755
index 0000000..17b849f
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java
@@ -0,0 +1,167 @@
+package org.keycloak.testsuite.keycloaksaml;
+
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.server.handlers.resource.Resource;
+import io.undertow.server.handlers.resource.ResourceChangeListener;
+import io.undertow.server.handlers.resource.ResourceManager;
+import io.undertow.server.handlers.resource.URLResource;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.LoginConfig;
+import io.undertow.servlet.api.SecurityConstraint;
+import io.undertow.servlet.api.ServletInfo;
+import io.undertow.servlet.api.WebResourceCollection;
+import org.keycloak.adapters.saml.undertow.SamlServletExtension;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.picketlink.identity.federation.bindings.wildfly.sp.SPServletExtension;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.security.Principal;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
+
+ public static class SendUsernameServlet extends HttpServlet {
+ @Override
+ protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ OutputStream stream = resp.getOutputStream();
+ Principal principal = req.getUserPrincipal();
+ stream.write("request-path: ".getBytes());
+ stream.write(req.getPathInfo().getBytes());
+ stream.write("\n".getBytes());
+ stream.write("principal=".getBytes());
+ if (principal == null) {
+ stream.write("null".getBytes());
+ return;
+ }
+ String name = principal.getName();
+ stream.write(name.getBytes());
+ }
+ @Override
+ protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ OutputStream stream = resp.getOutputStream();
+ Principal principal = req.getUserPrincipal();
+ stream.write("request-path: ".getBytes());
+ stream.write(req.getPathInfo().getBytes());
+ stream.write("\n".getBytes());
+ stream.write("principal=".getBytes());
+ if (principal == null) {
+ stream.write("null".getBytes());
+ return;
+ }
+ String name = principal.getName();
+ stream.write(name.getBytes());
+ }
+ }
+
+ public static class TestResourceManager implements ResourceManager {
+
+ private final String basePath;
+
+ public TestResourceManager(String basePath){
+ this.basePath = basePath;
+ }
+
+ @Override
+ public Resource getResource(String path) throws IOException {
+ String temp = path;
+ String fullPath = basePath + temp;
+ URL url = getClass().getResource(fullPath);
+ if (url == null) {
+ System.out.println("url is null: " + fullPath);
+ }
+ return new URLResource(url, url.openConnection(), path);
+ }
+
+ @Override
+ public boolean isResourceChangeListenerSupported() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void registerResourceChangeListener(ResourceChangeListener listener) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void removeResourceChangeListener(ResourceChangeListener listener) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void close() throws IOException {
+ throw new RuntimeException();
+ }
+ }
+
+ public static class TestIdentityManager implements IdentityManager {
+ @Override
+ public Account verify(Account account) {
+ return account;
+ }
+
+ @Override
+ public Account verify(String userName, Credential credential) {
+ throw new RuntimeException("WTF");
+ }
+
+ @Override
+ public Account verify(Credential credential) {
+ throw new RuntimeException();
+ }
+ }
+
+ @Override
+ protected void setupKeycloak() {
+ String realmJson = getRealmJson();
+ server.importRealm(getClass().getResourceAsStream(realmJson));
+ initWars();
+ }
+
+ public abstract void initWars();
+
+ public void initializeSamlSecuredWar(String warResourcePath, String contextPath, String warDeploymentName, ClassLoader classLoader) {
+
+ ServletInfo regularServletInfo = new ServletInfo("servlet", SendUsernameServlet.class)
+ .addMapping("/*");
+
+ SecurityConstraint constraint = new SecurityConstraint();
+ WebResourceCollection collection = new WebResourceCollection();
+ collection.addUrlPattern("/*");
+ constraint.addWebResourceCollection(collection);
+ constraint.addRoleAllowed("manager");
+ LoginConfig loginConfig = new LoginConfig("KEYCLOAK-SAML", "Test Realm");
+
+ ResourceManager resourceManager = new TestResourceManager(warResourcePath);
+
+ DeploymentInfo deploymentInfo = new DeploymentInfo()
+ .setClassLoader(classLoader)
+ .setIdentityManager(new TestIdentityManager())
+ .setContextPath(contextPath)
+ .setDeploymentName(warDeploymentName)
+ .setLoginConfig(loginConfig)
+ .setResourceManager(resourceManager)
+ .addServlets(regularServletInfo)
+ .addSecurityConstraint(constraint)
+ .addServletExtension(new SamlServletExtension());
+ server.getServer().deploy(deploymentInfo);
+ }
+
+ public String getRealmJson() {
+ return "/keycloak-saml/testsaml.json";
+ }
+
+
+}
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..0a907ce
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
new file mode 100755
index 0000000..e929c24
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
@@ -0,0 +1,310 @@
+{
+ "id": "demo",
+ "realm": "demo",
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": true,
+ "resetPasswordAllowed": true,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "defaultRoles": [ "user" ],
+ "smtpServer": {
+ "from": "auto@keycloak.org",
+ "host": "localhost",
+ "port":"3025"
+ },
+ "users" : [
+ {
+ "username" : "bburke",
+ "enabled": true,
+ "email" : "bburke@redhat.com",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "attributes" : {
+ "phone": "617"
+ },
+ "realmRoles": ["manager", "user"],
+ "applicationRoles": {
+ "http://localhost:8081/employee/": [ "employee" ]
+ }
+ }
+ ],
+ "applications": [
+ {
+ "name": "http://localhost:8081/sales-post/",
+ "enabled": true,
+ "fullScopeAllowed": true,
+ "protocol": "saml",
+ "baseUrl": "http://localhost:8081/sales-post",
+ "redirectUris": [
+ "http://localhost:8081/sales-post/*"
+ ],
+ "attributes": {
+ "saml.authnstatement": "true",
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post/",
+ "saml_idp_initiated_sso_url_name": "sales-post"
+ }
+ },
+ {
+ "name": "http://localhost:8081/sales-post-sig/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/sales-post-sig",
+ "redirectUris": [
+ "http://localhost:8081/sales-post-sig/*"
+ ],
+ "attributes": {
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig/",
+ "saml.server.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA256",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/sales-post-sig-transient/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/sales-post-sig-transient",
+ "adminUrl": "http://localhost:8081/sales-post-sig-transient",
+ "redirectUris": [
+ "http://localhost:8081/sales-post-sig-transient/*"
+ ],
+ "attributes": {
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-transient/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-transient/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-transient/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-transient/",
+ "saml.server.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA256",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/sales-post-sig-persistent/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/sales-post-sig-persistent",
+ "redirectUris": [
+ "http://localhost:8081/sales-post-sig-persistent/*"
+ ],
+ "attributes": {
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-persistent/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-persistent/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-persistent/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-persistent/",
+ "saml.server.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA256",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/sales-post-sig-email/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/sales-post-sig-email",
+ "adminUrl": "http://localhost:8081/sales-post-sig-email",
+ "redirectUris": [
+ "http://localhost:8081/sales-post-sig-email/*"
+ ],
+ "attributes": {
+ "saml_force_name_id_format": "true",
+ "saml_name_id_format": "email",
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-email/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-email/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-email/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-email/",
+ "saml.server.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA256",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/bad-realm-sales-post-sig/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/bad-realm-sales-post-sig/",
+ "adminUrl": "http://localhost:8081/bad-realm-sales-post-sig/",
+ "redirectUris": [
+ "http://localhost:8081/bad-realm-sales-post-sig/*"
+ ],
+ "attributes": {
+ "saml.server.signature": "true",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/bad-client-sales-post-sig/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/bad-client-sales-post-sig/",
+ "adminUrl": "http://localhost:8081/bad-client-sales-post-sig/",
+ "redirectUris": [
+ "http://localhost:8081/bad-client-sales-post-sig/*"
+ ],
+ "attributes": {
+ "saml.server.signature": "true",
+ "saml.client.signature": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/sales-post-enc/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/sales-post-enc",
+ "redirectUris": [
+ "http://localhost:8081/sales-post-enc/*"
+ ],
+ "attributes": {
+ "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-enc/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-enc/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-enc/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-enc/",
+ "saml.server.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA512",
+ "saml.client.signature": "true",
+ "saml.encrypt": "true",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
+ "saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+ }
+ },
+ {
+ "name": "http://localhost:8081/employee-sig/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/employee-sig",
+ "redirectUris": [
+ "http://localhost:8081/employee-sig/*"
+ ],
+ "adminUrl": "http://localhost:8081/employee-sig/",
+ "attributes": {
+ "saml.server.signature": "true",
+ "saml.client.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA1",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
+ }
+ },
+ {
+ "name": "http://localhost:8081/employee/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/employee",
+ "redirectUris": [
+ "http://localhost:8081/employee/*"
+ ],
+ "adminUrl": "http://localhost:8081/employee/",
+ "attributes": {
+ "saml.authnstatement": "true"
+ },
+ "protocolMappers": [
+ {
+ "name": "email",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "email",
+ "friendly.name": "email",
+ "attribute.name": "urn:oid:1.2.840.113549.1.9.1",
+ "attribute.nameformat": "URI Reference"
+ }
+ },
+ {
+ "name": "phone",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "phone",
+ "attribute.name": "phone",
+ "attribute.nameformat": "Basic"
+ }
+ },
+ {
+ "name": "role-list",
+ "protocol": "saml",
+ "protocolMapper": "saml-role-list-mapper",
+ "consentRequired": false,
+ "config": {
+ "attribute.name": "Role",
+ "attribute.nameformat": "Basic",
+ "single": "false"
+ }
+ }
+ ]
+ },
+ {
+ "name": "http://localhost:8081/employee-sig-front/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "frontchannelLogout": true,
+ "baseUrl": "http://localhost:8081/employee-sig-front/",
+ "redirectUris": [
+ "http://localhost:8081/employee-sig-front/*"
+ ],
+ "attributes": {
+ "saml_assertion_consumer_url_post": "http://localhost:8081/employee-sig-front/",
+ "saml_assertion_consumer_url_redirect": "http://localhost:8081/employee-sig-front/",
+ "saml_single_logout_service_url_post": "http://localhost:8081/employee-sig-front/",
+ "saml_single_logout_service_url_redirect": "http://localhost:8081/employee-sig-front/",
+ "saml.server.signature": "true",
+ "saml.client.signature": "true",
+ "saml.signature.algorithm": "RSA_SHA1",
+ "saml.authnstatement": "true",
+ "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "manager",
+ "description": "Have Manager privileges"
+ },
+ {
+ "name": "user",
+ "description": "Have User privileges"
+ }
+ ],
+ "application" : {
+ "http://localhost:8081/employee/" : [
+ {
+ "name": "employee",
+ "description": "Have Employee privileges"
+ }
+ ]
+ }
+ }
+}
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 4db2adf..e929c24 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -69,7 +69,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -92,7 +91,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -114,7 +112,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -139,7 +136,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -157,7 +153,6 @@
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -175,7 +170,6 @@
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
}
},
@@ -198,9 +192,7 @@
"saml.client.signature": "true",
"saml.encrypt": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
- "saml.encryption.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
}
},
@@ -219,7 +211,6 @@
"saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
},
@@ -292,7 +283,6 @@
"saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
}