From cc9d6d0cf777e2aa64f1daae7152caa3b82d6e70 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Tue, 22 Sep 2015 20:27:08 -0400 Subject: [PATCH] saml undertow sp --- pom.xml | 5 + saml/client-adapter/core/pom.xml | 2 +- .../adapters/saml/DefaultSamlDeployment.java | 363 +++++++++++++ .../keycloak/adapters/saml/InitiateLogin.java | 18 +- .../adapters/saml/SamlAuthenticator.java | 50 +- .../adapters/saml/SamlConfigResolver.java | 39 ++ .../adapters/saml/SamlDeployment.java | 55 +- .../adapters/saml/SamlDeploymentContext.java | 8 +- .../keycloak/adapters/saml/SamlPrincipal.java | 2 +- .../keycloak/adapters/saml/config/IDP.java | 190 +++++++ .../keycloak/adapters/saml/config/Key.java | 143 +++++ .../saml/config/KeycloakSamlAdapter.java | 21 + .../org/keycloak/adapters/saml/config/SP.java | 126 +++++ .../config/parsers/ConfigXmlConstants.java | 56 ++ .../config/parsers/DeploymentBuilder.java | 195 +++++++ .../saml/config/parsers/IDPXmlParser.java | 100 ++++ .../saml/config/parsers/KeyXmlParser.java | 128 +++++ .../parsers/KeycloakSamlAdapterXMLParser.java | 46 ++ .../saml/config/parsers/KeysXmlParser.java | 60 +++ .../saml/config/parsers/ResourceLoader.java | 11 + .../saml/config/parsers/SPXmlParser.java | 139 +++++ .../test/adapters/saml/XmlParserTest.java | 80 +++ .../core/src/test/resources/keycloak-saml.xml | 61 +++ .../saml/undertow/AbstractSamlAuthMech.java | 26 +- .../saml/undertow/SamlServletExtension.java | 187 +++++++ .../saml/undertow/ServletSamlAuthMech.java | 49 +- .../saml/common/util/StaxParserUtil.java | 17 + testsuite/integration/pom.xml | 8 + .../keycloaksaml/SamlBindingTest.java | 510 ++++++++++++++++++ .../keycloaksaml/SamlKeycloakRule.java | 167 ++++++ .../signed-post/WEB-INF/keycloak-saml.xml | 45 ++ .../signed-post/WEB-INF/keystore.jks | Bin 0 -> 1705 bytes .../resources/keycloak-saml/testsaml.json | 310 +++++++++++ .../src/test/resources/saml/testsaml.json | 10 - 34 files changed, 3157 insertions(+), 70 deletions(-) create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java create mode 100755 saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java create mode 100755 saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java create mode 100755 saml/client-adapter/core/src/test/resources/keycloak-saml.xml create mode 100755 saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java create mode 100755 testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java create mode 100755 testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java create mode 100755 testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml create mode 100755 testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks create mode 100755 testsuite/integration/src/test/resources/keycloak-saml/testsaml.json diff --git a/pom.xml b/pom.xml index ce53528a0f..c0a1cc1004 100755 --- a/pom.xml +++ b/pom.xml @@ -990,6 +990,11 @@ keycloak-saml-adapter-core ${project.version} + + org.keycloak + keycloak-undertow-saml-adapter + ${project.version} + org.keycloak keycloak-services diff --git a/saml/client-adapter/core/pom.xml b/saml/client-adapter/core/pom.xml index 59bd4d523b..605d87fd14 100755 --- a/saml/client-adapter/core/pom.xml +++ b/saml/client-adapter/core/pom.xml @@ -10,7 +10,7 @@ 4.0.0 keycloak-saml-adapter-core - Keycloak SAML Adapter Core + Keycloak SAML Client Adapter Core 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 0000000000..65612b3822 --- /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 Bill Burke + * @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 roleAttributeNames; + private Set 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 getRoleAttributeNames() { + return roleAttributeNames; + } + + @Override + public Set 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 roleAttributeNames) { + this.roleAttributeNames = roleAttributeNames; + } + + public void setRoleFriendlyAttributeNames(Set 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 d5f86d7490..690a99cc13 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 8c393bdff7..ca2da00ea5 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 0000000000..2479a2349d --- /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 + */ +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 1de9d727d7..9363624df8 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 getRoleAttributeNames(); Set 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 b8ea94df3c..cb8ea7794b 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 f336c923a8..68b23dafdb 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/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 0000000000..813f52e0eb --- /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 Bill Burke + * @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 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 getKeys() { + return keys; + } + + public void setKeys(List 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 0000000000..3d94f78f38 --- /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 Bill Burke + * @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 0000000000..9370dba807 --- /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 Bill Burke + * @version $Revision: 1 $ + */ +public class KeycloakSamlAdapter implements Serializable { + private List sps = new LinkedList<>(); + + public List getSps() { + return sps; + } + + public void setSps(List sps) { + this.sps = sps; + } +} 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 0000000000..addafdb9bf --- /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 Bill Burke + * @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 keys; + private String nameIDPolicyFormat; + private PrincipalNameMapping principalNameMapping; + private Set roleAttributes; + private Set 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 getKeys() { + return keys; + } + + public void setKeys(List 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 getRoleAttributes() { + return roleAttributes; + } + + public void setRoleAttributes(Set roleAttributes) { + this.roleAttributes = roleAttributes; + } + + public Set getRoleFriendlyAttributes() { + return roleFriendlyAttributes; + } + + public void setRoleFriendlyAttributes(Set 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/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 0000000000..a714b0a483 --- /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 Bill Burke + * @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 0000000000..24f9101fca --- /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 Bill Burke + * @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 0000000000..c4e9a6e263 --- /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 Bill Burke + * @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 keys = (List)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/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java new file mode 100755 index 0000000000..787af6993d --- /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 Bill Burke + * @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/KeycloakSamlAdapterXMLParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java new file mode 100755 index 0000000000..d5b9ee6ab0 --- /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 Bill Burke + * @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 0000000000..a63a6ac922 --- /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 Bill Burke + * @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 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/ResourceLoader.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java new file mode 100755 index 0000000000..296f31e651 --- /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 Bill Burke + * @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 0000000000..8df85001a7 --- /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 Bill Burke + * @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 keys = (List)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 roleAttributes = new HashSet<>(); + Set 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/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 0000000000..84f03ea65c --- /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 Bill Burke + * @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 0000000000..5755fd7276 --- /dev/null +++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + private pem + + + public pem + + + + + + + + + + + + + + + + cert pem + + + + + + + + + + + + \ 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 6a21c5ef83..9b29618bf8 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 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 0000000000..ac3da618ae --- /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 Bill Burke + * @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 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 ec2444ab4e..cd5253621f 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 Bill Burke * @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 fb32feff85..79417293a1 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 @@ -104,6 +104,23 @@ public class StaxParserUtil { return result; } + /** + * 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 diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 7948e97729..166fa4b2d6 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -101,6 +101,14 @@ org.keycloak keycloak-undertow-adapter + + org.keycloak + keycloak-saml-adapter-core + + + org.keycloak + keycloak-undertow-saml-adapter + org.keycloak keycloak-jaxrs-oauth-client 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 0000000000..9639aab0f8 --- /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 Bill Burke + * @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 0000000000..17b849fdce --- /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 Bill Burke + * @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 0000000000..0a907ced34 --- /dev/null +++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 0000000000000000000000000000000000000000..144830bc77683d1d0a3d29f8793471d4f395bdb0 GIT binary patch literal 1705 zcmezO_TO6u1_mZ5W@J#!C@Cqh($~+)PfpCq$S*FjvM{hP&@WERNiEhb0P=N1M z7#KYz`B=UJb?!81V%loJ$Ht}2#>m2`#U#kc$jZRd#I(>@`_HHQr3*BE{x`hDyEHld z+K~ja7y5T|r=%}Fu;`1zi=v(re>&z=o_dkol)j+zYO>t=Qy1S}v3en%vP3563G;>m zf%$6H#y=LWo_E+v&g1u@xzTso16CaPbkO_D8o@;(`MobV^Igq3BNxZ5w|jA2W>>?O zsK)o5D^C2naN_4wlZOiFZx$ZYW2t4}JhWx=_GhgLGruICtx3+%d3V!9>Dt7nXAhlc z$;{X;mJr8NzU5%3ud}q*;Tc<&7_HRg&F#_Q?wWFKHOG|)@xdBe`K>+ek+V}K-){E) zl)cDAKzFrg_KD*rPhJ#0xY%188oeS%DBgct!6JRDwVn#urq)k%GX7O$-%BXGHp?M$ zPK(%~JsS=itGtnGNeXRIJS=enoqTH}>U?=&w0Rv-!V zt0IaI7=L{C`R*=P35_m^|6)DbnI1Rom?{^itz&v>PMs${YwWF4+mV{{ zB3p#_4CDJAHQ!e4#HZJ%dA%($R{WrMXd~~o9sJ4l4<@p_UZ&<;=%+j9w)>u?FXr2x z;Qi$;R(a*~n?JwFjfqj;bam5w#iu)2cStlBuRNH-DYCa*SFTj`Ruc2O%^{1v zS^p3Un$kT>@>nGEyG?KA#5`*A-Q)TDd5mCQN4nqL|I4L=R^IMCEM*}Xua~%r;rLwD z(rqehS=sAVMB2P;SIyh9w+fi%85vk3^h^ybfvg(_O^jE7*p`Wn(Np4IY>ELd8>d#A zN85K^Mn-N{1_J{_Jp&y!=1>-9Vd650ft)z6p^2e^p_!qPiHVVMlsK;ujB93K97TqS zjq{OR1z|$TGQU@sN$9wB)LN$MwU<2= zd)SlRE7$3UyG=O%*+B2uv-3&H4597J6WaBsZpb_&f7j)l$IXYTF6-xcsr5`yWnyMz zU_^EvFwmHR?&_X(ZsE4#TgmHFu2rXfU6s0)lCKe^<7iiUsaqK$zd9h>c57vO3iO-78%EoLL<8ZmZxNJeq&U-KBw5j(T{eI-t zj}(o0{2w&pRSxr|MK;+T;phADf{DX@d*|wohzX72{tZ3*&sbbC2N!KDDXF>n@KOsN z=~ICbKGC4@Hz>{}*<-lToNvHy$ZNn2iF2?C$Wdx)Vu&22rlzLAD8`N=&Wn#AY>fF{RdOu_iDQssmI(9hPO(2Uiv*4ZQlGJH!&z=`K zw%L^5bkqb1@@gO=+rT(BxQD(6^h&8PDm@hVJA%Qxx$H92KBf6;Q! zeu+xYdBwlV_8D(8vle0u7ZYR_ZJGO3A6Ja^r!9<;Jf^6B_V*`!pI4_%{2#46Qrcoa z{d4O3!wr>pceY+y88$ipliuM2X9G4KozS*BUZS)4a=%Gc^OV}R*}*rh8m{{;m0Oa) z^Ysn?xlP?KeF94gT3Fr`zuJ}MUVH6cw8xR2&A~UG*V~pyDQR4_ymoT_iPrx=pDW$^ gV7obl)$b?cUQd>i8HM*{tk@@=IXfvlri|w}0JG8182|tP literal 0 HcmV?d00001 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 0000000000..e929c24ac7 --- /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 4db2adfaf4..e929c24ac7 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" } }