saml undertow sp

This commit is contained in:
Bill Burke 2015-09-22 20:27:08 -04:00
parent 861a13501a
commit cc9d6d0cf7
34 changed files with 3157 additions and 70 deletions

View file

@ -990,6 +990,11 @@
<artifactId>keycloak-saml-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-saml-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>

View file

@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-saml-adapter-core</artifactId>
<name>Keycloak SAML Adapter Core</name>
<name>Keycloak SAML Client Adapter Core</name>
<description/>
<properties>

View file

@ -0,0 +1,363 @@
package org.keycloak.adapters.saml;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.enums.SslRequired;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DefaultSamlDeployment implements SamlDeployment {
public static class DefaultSingleSignOnService implements IDP.SingleSignOnService {
private boolean signRequest;
private boolean validateResponseSignature;
private String signatureCanonicalizationMethod;
private Binding requestBinding;
private Binding responseBinding;
private String requestBindingUrl;
@Override
public boolean signRequest() {
return signRequest;
}
@Override
public boolean validateResponseSignature() {
return validateResponseSignature;
}
@Override
public String getSignatureCanonicalizationMethod() {
return signatureCanonicalizationMethod;
}
@Override
public Binding getRequestBinding() {
return requestBinding;
}
@Override
public Binding getResponseBinding() {
return responseBinding;
}
@Override
public String getRequestBindingUrl() {
return requestBindingUrl;
}
public void setSignRequest(boolean signRequest) {
this.signRequest = signRequest;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
}
public void setRequestBinding(Binding requestBinding) {
this.requestBinding = requestBinding;
}
public void setResponseBinding(Binding responseBinding) {
this.responseBinding = responseBinding;
}
public void setRequestBindingUrl(String requestBindingUrl) {
this.requestBindingUrl = requestBindingUrl;
}
}
public static class DefaultSingleLogoutService implements IDP.SingleLogoutService {
private boolean validateRequestSignature;
private boolean validateResponseSignature;
private boolean signRequest;
private boolean signResponse;
private String signatureCanonicalizationMethod;
private Binding requestBinding;
private Binding responseBinding;
private String requestBindingUrl;
private String responseBindingUrl;
@Override
public boolean validateRequestSignature() {
return validateRequestSignature;
}
@Override
public boolean validateResponseSignature() {
return validateResponseSignature;
}
@Override
public boolean signRequest() {
return signRequest;
}
@Override
public boolean signResponse() {
return signResponse;
}
@Override
public String getSignatureCanonicalizationMethod() {
return signatureCanonicalizationMethod;
}
@Override
public Binding getRequestBinding() {
return requestBinding;
}
@Override
public Binding getResponseBinding() {
return responseBinding;
}
@Override
public String getRequestBindingUrl() {
return requestBindingUrl;
}
@Override
public String getResponseBindingUrl() {
return responseBindingUrl;
}
public void setValidateRequestSignature(boolean validateRequestSignature) {
this.validateRequestSignature = validateRequestSignature;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
public void setSignRequest(boolean signRequest) {
this.signRequest = signRequest;
}
public void setSignResponse(boolean signResponse) {
this.signResponse = signResponse;
}
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
}
public void setRequestBinding(Binding requestBinding) {
this.requestBinding = requestBinding;
}
public void setResponseBinding(Binding responseBinding) {
this.responseBinding = responseBinding;
}
public void setRequestBindingUrl(String requestBindingUrl) {
this.requestBindingUrl = requestBindingUrl;
}
public void setResponseBindingUrl(String responseBindingUrl) {
this.responseBindingUrl = responseBindingUrl;
}
}
public static class DefaultIDP implements IDP {
private String entityID;
private PublicKey signatureValidationKey;
private SingleSignOnService singleSignOnService;
private SingleLogoutService singleLogoutService;
@Override
public String getEntityID() {
return entityID;
}
@Override
public SingleSignOnService getSingleSignOnService() {
return singleSignOnService;
}
@Override
public SingleLogoutService getSingleLogoutService() {
return singleLogoutService;
}
@Override
public PublicKey getSignatureValidationKey() {
return signatureValidationKey;
}
public void setEntityID(String entityID) {
this.entityID = entityID;
}
public void setSignatureValidationKey(PublicKey signatureValidationKey) {
this.signatureValidationKey = signatureValidationKey;
}
public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
this.singleSignOnService = singleSignOnService;
}
public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
this.singleLogoutService = singleLogoutService;
}
}
private IDP idp;
private boolean configured;
private SslRequired sslRequired = SslRequired.EXTERNAL;
private String entityID;
private String nameIDPolicyFormat;
private boolean forceAuthentication;
private PrivateKey decryptionKey;
private KeyPair signingKeyPair;
private String assertionConsumerServiceUrl;
private Set<String> roleAttributeNames;
private Set<String> roleFriendlyAttributeNames;
private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
private String principalAttributeName;
private String logoutPage;
@Override
public IDP getIDP() {
return idp;
}
@Override
public boolean isConfigured() {
return configured;
}
@Override
public SslRequired getSslRequired() {
return sslRequired;
}
@Override
public String getEntityID() {
return entityID;
}
@Override
public String getNameIDPolicyFormat() {
return nameIDPolicyFormat;
}
@Override
public boolean isForceAuthentication() {
return forceAuthentication;
}
@Override
public PrivateKey getDecryptionKey() {
return decryptionKey;
}
@Override
public KeyPair getSigningKeyPair() {
return signingKeyPair;
}
@Override
public String getAssertionConsumerServiceUrl() {
return assertionConsumerServiceUrl;
}
@Override
public Set<String> getRoleAttributeNames() {
return roleAttributeNames;
}
@Override
public Set<String> getRoleAttributeFriendlyNames() {
return roleFriendlyAttributeNames;
}
@Override
public PrincipalNamePolicy getPrincipalNamePolicy() {
return principalNamePolicy;
}
@Override
public String getPrincipalAttributeName() {
return principalAttributeName;
}
public void setIdp(IDP idp) {
this.idp = idp;
}
public void setConfigured(boolean configured) {
this.configured = configured;
}
public void setSslRequired(SslRequired sslRequired) {
this.sslRequired = sslRequired;
}
public void setEntityID(String entityID) {
this.entityID = entityID;
}
public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
this.nameIDPolicyFormat = nameIDPolicyFormat;
}
public void setForceAuthentication(boolean forceAuthentication) {
this.forceAuthentication = forceAuthentication;
}
public void setDecryptionKey(PrivateKey decryptionKey) {
this.decryptionKey = decryptionKey;
}
public void setSigningKeyPair(KeyPair signingKeyPair) {
this.signingKeyPair = signingKeyPair;
}
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
}
public void setRoleAttributeNames(Set<String> roleAttributeNames) {
this.roleAttributeNames = roleAttributeNames;
}
public void setRoleFriendlyAttributeNames(Set<String> roleFriendlyAttributeNames) {
this.roleFriendlyAttributeNames = roleFriendlyAttributeNames;
}
public void setPrincipalNamePolicy(PrincipalNamePolicy principalNamePolicy) {
this.principalNamePolicy = principalNamePolicy;
}
public void setPrincipalAttributeName(String principalAttributeName) {
this.principalAttributeName = principalAttributeName;
}
@Override
public String getLogoutPage() {
return logoutPage;
}
public void setLogoutPage(String logoutPage) {
this.logoutPage = logoutPage;
}
}

View file

@ -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);

View file

@ -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());
verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
} else {
verifyRedirectBindingSignature(deployment.getSignatureValidationKey(), paramKey);
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();
}

View file

@ -0,0 +1,39 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.saml;
import org.keycloak.adapters.HttpFacade.Request;
/**
* On multi-tenant scenarios, Keycloak will defer the resolution of a
* SamlDeployment to the target application at the request-phase.
*
* A Request object is passed to the resolver and callers expect a complete
* SamlDeployment. Based on this SamlDeployment, Keycloak will resume
* authenticating and authorizing the request.
*
* The easiest way to build a SamlDeployment is to use
* DeploymentBuilder , passing the InputStream of an existing
* keycloak-saml.xml to the build() method.
*
* @author Juraci Paixão Kröhling <juraci at kroehling.de>
*/
public interface SamlConfigResolver {
public SamlDeployment resolve(Request facade);
}

View file

@ -14,26 +14,53 @@ import java.util.Set;
public interface SamlDeployment {
enum Binding {
POST,
REDIRECT
REDIRECT;
public static Binding parseBinding(String val) {
if (val == null) return POST;
return Binding.valueOf(val);
}
}
public interface IDP {
String getEntityID();
SingleSignOnService getSingleSignOnService();
SingleLogoutService getSingleLogoutService();
PublicKey getSignatureValidationKey();
public interface SingleSignOnService {
boolean signRequest();
boolean validateResponseSignature();
String getSignatureCanonicalizationMethod();
Binding getRequestBinding();
Binding getResponseBinding();
String getRequestBindingUrl();
}
public interface SingleLogoutService {
boolean validateRequestSignature();
boolean validateResponseSignature();
boolean signRequest();
boolean signResponse();
String getSignatureCanonicalizationMethod();
Binding getRequestBinding();
Binding getResponseBinding();
String getRequestBindingUrl();
String getResponseBindingUrl();
}
}
public IDP getIDP();
public boolean isConfigured();
SslRequired getSslRequired();
String getSingleSignOnServiceUrl();
String getSingleLogoutServiceUrl();
String getIssuer();
String getEntityID();
String getNameIDPolicyFormat();
String getAssertionConsumerServiceUrl();
Binding getRequestBinding();
Binding getResponseBinding();
KeyPair getSigningKeyPair();
String getSignatureCanonicalizationMethod();
boolean isForceAuthentication();
boolean isRequestsSigned();
boolean isValidateSignatures();
PublicKey getSignatureValidationKey();
PrivateKey getAssertionDecryptionKey();
PrivateKey getDecryptionKey();
KeyPair getSigningKeyPair();
String getAssertionConsumerServiceUrl();
String getLogoutPage();
Set<String> getRoleAttributeNames();
Set<String> getRoleAttributeFriendlyNames();

View file

@ -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;
}
}

View file

@ -40,7 +40,7 @@ public class SamlPrincipal implements Serializable, Principal {
@Override
public String getName() {
return null;
return name;
}

View file

@ -0,0 +1,190 @@
package org.keycloak.adapters.saml.config;
import org.keycloak.adapters.saml.SamlDeployment;
import java.io.Serializable;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IDP implements Serializable {
public static class SingleSignOnService implements Serializable {
private boolean signRequest;
private boolean validateResponseSignature;
private String signatureCanonicalizationMethod;
private String requestBinding;
private String responseBinding;
private String bindingUrl;
public boolean isSignRequest() {
return signRequest;
}
public void setSignRequest(boolean signRequest) {
this.signRequest = signRequest;
}
public boolean isValidateResponseSignature() {
return validateResponseSignature;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
public String getSignatureCanonicalizationMethod() {
return signatureCanonicalizationMethod;
}
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
}
public String getRequestBinding() {
return requestBinding;
}
public void setRequestBinding(String requestBinding) {
this.requestBinding = requestBinding;
}
public String getResponseBinding() {
return responseBinding;
}
public void setResponseBinding(String responseBinding) {
this.responseBinding = responseBinding;
}
public String getBindingUrl() {
return bindingUrl;
}
public void setBindingUrl(String bindingUrl) {
this.bindingUrl = bindingUrl;
}
}
public static class SingleLogoutService implements Serializable {
private boolean signRequest;
private boolean signResponse;
private boolean validateRequestSignature;
private boolean validateResponseSignature;
private String signatureCanonicalizationMethod;
private String requestBinding;
private String responseBinding;
private String postBindingUrl;
private String redirectBindingUrl;
public boolean isSignRequest() {
return signRequest;
}
public void setSignRequest(boolean signRequest) {
this.signRequest = signRequest;
}
public boolean isSignResponse() {
return signResponse;
}
public void setSignResponse(boolean signResponse) {
this.signResponse = signResponse;
}
public boolean isValidateRequestSignature() {
return validateRequestSignature;
}
public void setValidateRequestSignature(boolean validateRequestSignature) {
this.validateRequestSignature = validateRequestSignature;
}
public boolean isValidateResponseSignature() {
return validateResponseSignature;
}
public void setValidateResponseSignature(boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature;
}
public String getSignatureCanonicalizationMethod() {
return signatureCanonicalizationMethod;
}
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
}
public String getRequestBinding() {
return requestBinding;
}
public void setRequestBinding(String requestBinding) {
this.requestBinding = requestBinding;
}
public String getResponseBinding() {
return responseBinding;
}
public void setResponseBinding(String responseBinding) {
this.responseBinding = responseBinding;
}
public String getPostBindingUrl() {
return postBindingUrl;
}
public void setPostBindingUrl(String postBindingUrl) {
this.postBindingUrl = postBindingUrl;
}
public String getRedirectBindingUrl() {
return redirectBindingUrl;
}
public void setRedirectBindingUrl(String redirectBindingUrl) {
this.redirectBindingUrl = redirectBindingUrl;
}
}
private String entityID;
private SingleSignOnService singleSignOnService;
private SingleLogoutService singleLogoutService;
private List<Key> keys;
public String getEntityID() {
return entityID;
}
public void setEntityID(String entityID) {
this.entityID = entityID;
}
public SingleSignOnService getSingleSignOnService() {
return singleSignOnService;
}
public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
this.singleSignOnService = singleSignOnService;
}
public SingleLogoutService getSingleLogoutService() {
return singleLogoutService;
}
public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
this.singleLogoutService = singleLogoutService;
}
public List<Key> getKeys() {
return keys;
}
public void setKeys(List<Key> keys) {
this.keys = keys;
}
}

View file

@ -0,0 +1,143 @@
package org.keycloak.adapters.saml.config;
import java.io.Serializable;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Key implements Serializable {
public static class KeyStoreConfig implements Serializable {
private String file;
private String resource;
private String password;
private String type;
private String alias;
private String privateKeyAlias;
private String privateKeyPassword;
private String certificateAlias;
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPrivateKeyAlias() {
return privateKeyAlias;
}
public void setPrivateKeyAlias(String privateKeyAlias) {
this.privateKeyAlias = privateKeyAlias;
}
public String getPrivateKeyPassword() {
return privateKeyPassword;
}
public void setPrivateKeyPassword(String privateKeyPassword) {
this.privateKeyPassword = privateKeyPassword;
}
public String getCertificateAlias() {
return certificateAlias;
}
public void setCertificateAlias(String certificateAlias) {
this.certificateAlias = certificateAlias;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
}
private boolean signing;
private boolean encryption;
private KeyStoreConfig keystore;
private String privateKeyPem;
private String publicKeyPem;
private String certificatePem;
public boolean isSigning() {
return signing;
}
public void setSigning(boolean signing) {
this.signing = signing;
}
public boolean isEncryption() {
return encryption;
}
public void setEncryption(boolean encryption) {
this.encryption = encryption;
}
public KeyStoreConfig getKeystore() {
return keystore;
}
public void setKeystore(KeyStoreConfig keystore) {
this.keystore = keystore;
}
public String getPrivateKeyPem() {
return privateKeyPem;
}
public void setPrivateKeyPem(String privateKeyPem) {
this.privateKeyPem = privateKeyPem;
}
public String getPublicKeyPem() {
return publicKeyPem;
}
public void setPublicKeyPem(String publicKeyPem) {
this.publicKeyPem = publicKeyPem;
}
public String getCertificatePem() {
return certificatePem;
}
public void setCertificatePem(String certificatePem) {
this.certificatePem = certificatePem;
}
}

View file

@ -0,0 +1,21 @@
package org.keycloak.adapters.saml.config;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSamlAdapter implements Serializable {
private List<SP> sps = new LinkedList<>();
public List<SP> getSps() {
return sps;
}
public void setSps(List<SP> sps) {
this.sps = sps;
}
}

View file

@ -0,0 +1,126 @@
package org.keycloak.adapters.saml.config;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.enums.SslRequired;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SP implements Serializable {
public static class PrincipalNameMapping implements Serializable {
private String policy;
private String attributeName;
public String getPolicy() {
return policy;
}
public void setPolicy(String policy) {
this.policy = policy;
}
public String getAttributeName() {
return attributeName;
}
public void setAttributeName(String attributeName) {
this.attributeName = attributeName;
}
}
private String entityID;
private String sslPolicy;
private boolean forceAuthentication;
private String logoutPage;
private List<Key> keys;
private String nameIDPolicyFormat;
private PrincipalNameMapping principalNameMapping;
private Set<String> roleAttributes;
private Set<String> roleFriendlyAttributes;
private IDP idp;
public String getEntityID() {
return entityID;
}
public void setEntityID(String entityID) {
this.entityID = entityID;
}
public String getSslPolicy() {
return sslPolicy;
}
public void setSslPolicy(String sslPolicy) {
this.sslPolicy = sslPolicy;
}
public boolean isForceAuthentication() {
return forceAuthentication;
}
public void setForceAuthentication(boolean forceAuthentication) {
this.forceAuthentication = forceAuthentication;
}
public List<Key> getKeys() {
return keys;
}
public void setKeys(List<Key> keys) {
this.keys = keys;
}
public String getNameIDPolicyFormat() {
return nameIDPolicyFormat;
}
public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
this.nameIDPolicyFormat = nameIDPolicyFormat;
}
public PrincipalNameMapping getPrincipalNameMapping() {
return principalNameMapping;
}
public void setPrincipalNameMapping(PrincipalNameMapping principalNameMapping) {
this.principalNameMapping = principalNameMapping;
}
public Set<String> getRoleAttributes() {
return roleAttributes;
}
public void setRoleAttributes(Set<String> roleAttributes) {
this.roleAttributes = roleAttributes;
}
public Set<String> getRoleFriendlyAttributes() {
return roleFriendlyAttributes;
}
public void setRoleFriendlyAttributes(Set<String> roleFriendlyAttributes) {
this.roleFriendlyAttributes = roleFriendlyAttributes;
}
public IDP getIdp() {
return idp;
}
public void setIdp(IDP idp) {
this.idp = idp;
}
public String getLogoutPage() {
return logoutPage;
}
public void setLogoutPage(String logoutPage) {
this.logoutPage = logoutPage;
}
}

View file

@ -0,0 +1,56 @@
package org.keycloak.adapters.saml.config.parsers;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ConfigXmlConstants {
public static final String KEYCLOAK_SAML_ADAPTER ="keycloak-saml-adapter";
public static final String SP_ELEMENT="SP";
public static final String ENTITY_ID_ATTR = "entityID";
public static final String SSL_POLICY_ATTR = "sslPolicy";
public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
public static final String LOGOUT_PAGE_ATTR = "logoutPage";
public static final String KEYS_ELEMENT = "Keys";
public static final String KEY_ELEMENT = "Key";
public static final String SIGNING_ATTR = "signing";
public static final String ENCRYPTION_ATTR = "encryption";
public static final String CERTIFICATE_PEM_ELEMENT = "CertificatePem";
public static final String PRIVATE_KEY_PEM_ELEMENT = "PrivateKeyPem";
public static final String PUBLIC_KEY_PEM_ELEMENT = "PublicKeyPem";
public static final String FILE_ATTR = "file";
public static final String TYPE_ATTR = "type";
public static final String RESOURCE_ATTR = "resource";
public static final String PASSWORD_ATTR = "password";
public static final String ALIAS_ATTR = "alias";
public static final String KEYS_STORE_ELEMENT = "KeyStore";
public static final String CERTIFICATE_ELEMENT = "Certificate";
public static final String PRIVATE_KEY_ELEMENT = "PrivateKey";
public static final String PRINCIPAL_NAME_MAPPING_ELEMENT = "PrincipalNameMapping";
public static final String POLICY_ATTR = "policy";
public static final String ATTRIBUTE_ATTR = "attribute";
public static final String ROLE_MAPPING_ELEMENT = "RoleMapping";
public static final String ATTRIBUTE_ELEMENT = "Attribute";
public static final String FRIENDLY_ATTRIBUTE_ELEMENT = "FriendlyAttribute";
public static final String NAME_ATTR = "name";
public static final String IDP_ELEMENT = "IDP";
public static final String SINGLE_SIGN_ON_SERVICE_ELEMENT = "SingleSignOnService";
public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService";
public static final String SIGN_REQUEST_ATTR = "signRequest";
public static final String SIGN_RESPONSE_ATTR = "signResponse";
public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
public static final String REQUEST_BINDING_ATTR = "requestBinding";
public static final String RESPONSE_BINDING_ATTR = "responseBinding";
public static final String BINDING_URL_ATTR = "bindingUrl";
public static final String VALIDATE_RESPONSE_SIGNATURE_ATTR = "validateResponseSignature";
public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
}

View file

@ -0,0 +1,195 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.DefaultSamlDeployment;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.enums.SslRequired;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.util.PemUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DeploymentBuilder {
public SamlDeployment build(InputStream xml, ResourceLoader resourceLoader) throws ParsingException {
DefaultSamlDeployment deployment = new DefaultSamlDeployment();
DefaultSamlDeployment.DefaultIDP idp = new DefaultSamlDeployment.DefaultIDP();
DefaultSamlDeployment.DefaultSingleSignOnService sso = new DefaultSamlDeployment.DefaultSingleSignOnService();
DefaultSamlDeployment.DefaultSingleLogoutService slo = new DefaultSamlDeployment.DefaultSingleLogoutService();
idp.setSingleSignOnService(sso);
idp.setSingleLogoutService(slo);
KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml));
SP sp = adapter.getSps().get(0);
deployment.setConfigured(true);
deployment.setEntityID(sp.getEntityID());
deployment.setForceAuthentication(sp.isForceAuthentication());
deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
deployment.setLogoutPage(sp.getLogoutPage());
if (sp.getPrincipalNameMapping() != null) {
SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy());
deployment.setPrincipalNamePolicy(policy);
deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName());
}
deployment.setRoleAttributeNames(sp.getRoleAttributes());
deployment.setRoleFriendlyAttributeNames(sp.getRoleFriendlyAttributes());
if (sp.getSslPolicy() != null) {
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
deployment.setSslRequired(ssl);
}
for (Key key : sp.getKeys()) {
if (key.isSigning()) {
PrivateKey privateKey = null;
PublicKey publicKey = null;
if (key.getKeystore() != null) {
KeyStore keyStore = loadKeystore(resourceLoader, key);
Certificate cert = null;
try {
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
publicKey = cert.getPublicKey();
} else {
if (key.getPrivateKeyPem() == null) {
throw new RuntimeException("SP signing key must have a PrivateKey defined");
}
try {
privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
if (key.getPublicKeyPem() == null &&key.getCertificatePem() == null) {
throw new RuntimeException("Sp signing key must have a PublicKey or Certificate defined");
}
publicKey = getPublicKeyFromPem(key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
KeyPair keyPair = new KeyPair(publicKey, privateKey);
deployment.setSigningKeyPair(keyPair);
} else if (key.isEncryption()) {
KeyStore keyStore = loadKeystore(resourceLoader, key);
try {
PrivateKey privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
deployment.setDecryptionKey(privateKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
deployment.setIdp(idp);
idp.setEntityID(sp.getIdp().getEntityID());
sso.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getRequestBinding()));
sso.setRequestBindingUrl(sp.getIdp().getSingleSignOnService().getBindingUrl());
sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
sso.setSignatureCanonicalizationMethod(sp.getIdp().getSingleSignOnService().getSignatureCanonicalizationMethod());
sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
slo.setSignRequest(sp.getIdp().getSingleLogoutService().isSignRequest());
slo.setSignResponse(sp.getIdp().getSingleLogoutService().isSignResponse());
slo.setValidateResponseSignature(sp.getIdp().getSingleLogoutService().isValidateResponseSignature());
slo.setValidateRequestSignature(sp.getIdp().getSingleLogoutService().isValidateRequestSignature());
slo.setSignatureCanonicalizationMethod(sp.getIdp().getSingleLogoutService().getSignatureCanonicalizationMethod());
slo.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getRequestBinding()));
slo.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getResponseBinding()));
if (slo.getRequestBinding() == SamlDeployment.Binding.POST) {
slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl());
} else {
slo.setRequestBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
}
if (slo.getResponseBinding() == SamlDeployment.Binding.POST) {
slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getPostBindingUrl());
} else {
slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
}
for (Key key : sp.getIdp().getKeys()) {
if (key.isSigning()) {
if (key.getKeystore() != null) {
KeyStore keyStore = loadKeystore(resourceLoader, key);
Certificate cert = null;
try {
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
idp.setSignatureValidationKey(cert.getPublicKey());
} else {
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
}
try {
PublicKey publicKey = getPublicKeyFromPem(key);
idp.setSignatureValidationKey(publicKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
return deployment;
}
protected static PublicKey getPublicKeyFromPem(Key key) throws Exception {
PublicKey publicKey;
if (key.getPublicKeyPem() != null) {
publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
} else {
Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim());
publicKey = cert.getPublicKey();
}
return publicKey;
}
protected static KeyStore loadKeystore(ResourceLoader resourceLoader, Key key) {
String type = key.getKeystore().getType();
if (type == null) type = "JKS";
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(type);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
InputStream is = null;
if (key.getKeystore().getFile() != null) {
File fp = new File(key.getKeystore().getFile());
if (!fp.exists()) {
}
try {
is = new FileInputStream(fp);
} catch (FileNotFoundException e) {
throw new RuntimeException("KeyStore " + key.getKeystore().getFile() + " does not exist");
}
} else {
is = resourceLoader.getResourceAsStream(key.getKeystore().getResource());
if (is == null) {
throw new RuntimeException("KeyStore " + key.getKeystore().getResource() + " does not exist");
}
}
try {
keyStore.load(is, key.getKeystore().getPassword().toCharArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
return keyStore;
}
}

View file

@ -0,0 +1,100 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IDPXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
IDP idp = new IDP();
String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on IDP");
}
idp.setEntityID(entityID);
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.IDP_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) {
IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader);
idp.setSingleSignOnService(sso);
} else if (tag.equals(ConfigXmlConstants.SINGLE_LOGOUT_SERVICE_ELEMENT)) {
IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader);
idp.setSingleLogoutService(slo);
} else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
KeysXmlParser parser = new KeysXmlParser();
List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
idp.setKeys(keys);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return idp;
}
protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader) throws ParsingException {
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR));
slo.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR));
slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
return slo;
}
protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader) throws ParsingException {
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
sso.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
return sso;
}
@Override
public boolean supports(QName qname) {
return false;
}
}

View file

@ -0,0 +1,128 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
Key key = new Key();
key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEY_ELEMENT))
break;
else
throw logger.parserUnknownEndElement(endElementName);
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) {
key.setKeystore(parseKeyStore(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader));
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return key;
}
protected Key.KeyStoreConfig parseKeyStore(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
if (keyStore.getFile() == null && keyStore.getResource() == null) {
throw new ParsingException("KeyStore element must have the url or classpath attribute set");
}
keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPassword() == null) {
throw new ParsingException("KeyStore element must have the password attribute set");
}
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getCertificateAlias() == null) {
throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
}
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getPrivateKeyAlias() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
}
keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPrivateKeyPassword() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
}
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return keyStore;
}
@Override
public boolean supports(QName qname) {
return false;
}
}

View file

@ -0,0 +1,46 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartElement;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSamlAdapterXMLParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
KeycloakSamlAdapter adapter = new KeycloakSamlAdapter();
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYCLOAK_SAML_ADAPTER);
while (xmlEventReader.hasNext()) {
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) {
SPXmlParser parser = new SPXmlParser();
SP sp = (SP)parser.parse(xmlEventReader);
if (sp != null) adapter.getSps().add(sp);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return adapter;
}
@Override
public boolean supports(QName qname) {
return false;
}
}

View file

@ -0,0 +1,60 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeysXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_ELEMENT);
List<Key> keys = new LinkedList<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.KEYS_ELEMENT))
break;
else
throw logger.parserUnknownEndElement(endElementName);
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) {
KeyXmlParser parser = new KeyXmlParser();
Key key = (Key)parser.parse(xmlEventReader);
keys.add(key);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return keys;
}
@Override
public boolean supports(QName qname) {
return false;
}
}

View file

@ -0,0 +1,11 @@
package org.keycloak.adapters.saml.config.parsers;
import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ResourceLoader {
InputStream getResourceAsStream(String resource);
}

View file

@ -0,0 +1,139 @@
package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SPXmlParser extends AbstractParser {
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT);
SP sp = new SP();
String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on SP");
}
sp.setEntityID(entityID);
sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.SP_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
KeysXmlParser parser = new KeysXmlParser();
List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
sp.setKeys(keys);
} else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
if (policy == null) {
throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
}
String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
mapping.setPolicy(policy);
mapping.setAttributeName(attribute);
sp.setPrincipalNameMapping(mapping);
} else if (tag.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT)) {
parseRoleMapping(xmlEventReader, sp);
} else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
IDPXmlParser parser = new IDPXmlParser();
IDP idp = (IDP)parser.parse(xmlEventReader);
sp.setIdp(idp);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
return sp;
}
protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
Set<String> roleAttributes = new HashSet<>();
Set<String> roleFriendlyAttributes = new HashSet<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
break;
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
if (endElementName.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT))
break;
else
continue;
}
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
if (startElement == null)
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
if (attributeValue == null) {
throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
}
roleAttributes.add(attributeValue);
} else if (tag.equals(ConfigXmlConstants.FRIENDLY_ATTRIBUTE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
if (attributeValue == null) {
throw new ParsingException("RoleMapping FriendlyAttribute element must have the name attribute set");
}
roleFriendlyAttributes.add(attributeValue);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
sp.setRoleAttributes(roleAttributes);
sp.setRoleFriendlyAttributes(roleFriendlyAttributes);
}
@Override
public boolean supports(QName qname) {
return false;
}
}

View file

@ -0,0 +1,80 @@
package org.keycloak.test.adapters.saml;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.adapters.saml.config.IDP;
import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterXMLParser;
import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class XmlParserTest {
@Test
public void testXmlParser() throws Exception {
InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
Assert.assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
Assert.assertNotNull(config);
Assert.assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0);
Assert.assertEquals("sp", sp.getEntityID());
Assert.assertEquals("ssl", sp.getSslPolicy());
Assert.assertEquals("format", sp.getNameIDPolicyFormat());
Assert.assertTrue(sp.isForceAuthentication());
Assert.assertEquals(2, sp.getKeys().size());
Key signing = sp.getKeys().get(0);
Assert.assertTrue(signing.isSigning());
Key.KeyStoreConfig keystore = signing.getKeystore();
Assert.assertNotNull(keystore);
Assert.assertEquals("file", keystore.getFile());
Assert.assertEquals("cp", keystore.getResource());
Assert.assertEquals("pw", keystore.getPassword());
Assert.assertEquals("private alias", keystore.getPrivateKeyAlias());
Assert.assertEquals("private pw", keystore.getPrivateKeyPassword());
Assert.assertEquals("cert alias", keystore.getCertificateAlias());
Key encryption = sp.getKeys().get(1);
Assert.assertTrue(encryption.isEncryption());
Assert.assertEquals("private pem", encryption.getPrivateKeyPem());
Assert.assertEquals("public pem", encryption.getPublicKeyPem());
Assert.assertEquals("policy", sp.getPrincipalNameMapping().getPolicy());
Assert.assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
Assert.assertTrue(sp.getRoleAttributes().size() == 1);
Assert.assertTrue(sp.getRoleAttributes().contains("member"));
Assert.assertTrue(sp.getRoleFriendlyAttributes().size() == 1);
Assert.assertTrue(sp.getRoleFriendlyAttributes().contains("memberOf"));
IDP idp = sp.getIdp();
Assert.assertEquals("idp", idp.getEntityID());
Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
Assert.assertEquals("canon", idp.getSingleSignOnService().getSignatureCanonicalizationMethod());
Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
Assert.assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
Assert.assertTrue(idp.getSingleLogoutService().isSignRequest());
Assert.assertTrue(idp.getSingleLogoutService().isSignResponse());
Assert.assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
Assert.assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
Assert.assertEquals("canon", idp.getSingleLogoutService().getSignatureCanonicalizationMethod());
Assert.assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
Assert.assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
Assert.assertTrue(idp.getKeys().size() == 1);
Assert.assertTrue(idp.getKeys().get(0).isSigning());
Assert.assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
}
}

View file

@ -0,0 +1,61 @@
<keycloak-saml-adapter>
<SP entityID="sp"
sslPolicy="ssl"
nameIDPolicyFormat="format"
forceAuthentication="true">
<Keys>
<Key signing="true" >
<KeyStore file="file" classpath="cp" password="pw">
<PrivateKey alias="private alias" password="private pw"/>
<Certificate alias="cert alias"/>
</KeyStore>
</Key>
<Key encryption="true">
<PrivateKeyPem>
private pem
</PrivateKeyPem>
<PublicKeyPem>
public pem
</PublicKeyPem>
</Key>
</Keys>
<PrincipalNameMapping policy="policy" attribute="attribute"/>
<RoleMapping>
<Attribute name="member"/>
<FriendlyAttribute name="memberOf"/>
</RoleMapping>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
signatureCanonicalizationMethod="canon"
requestBinding="post"
bindingUrl="url"
/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
signatureCanonicalizationMethod="canon"
requestBinding="redirect"
responseBinding="post"
postBindingUrl="posturl"
redirectBindingUrl="redirecturl"
/>
<Keys>
<Key signing="true">
<CertificatePem>
cert pem
</CertificatePem>
</Key>
</Keys>
</IDP>
<Keys>
<KeyStore>
</KeyStore>
</Keys>
</SP>
</keycloak-saml-adapter>

View file

@ -42,16 +42,13 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
protected SamlDeploymentContext deploymentContext;
protected UndertowUserSessionManagement sessionManagement;
protected String logoutPage;
protected String errorPage;
public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
String logoutPage,
String errorPage) {
this.deploymentContext = deploymentContext;
this.sessionManagement = sessionManagement;
this.errorPage = errorPage;
this.logoutPage = logoutPage;
}
@Override
@ -62,7 +59,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
Integer code = servePage(exchange, errorPage);
return new ChallengeResult(true, code);
}
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = createFacade(exchange);
if (challenge.challenge(facade)) {
return new ChallengeResult(true, exchange.getResponseCode());
}
@ -91,7 +88,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
HttpServerExchange exchange = notification.getExchange();
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = createFacade(exchange);
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
sessionStore.logoutAccount();
@ -105,7 +102,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
* Call this inside your authenticate method.
*/
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = createFacade(exchange);
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
@ -120,10 +117,8 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
}
if (outcome == AuthOutcome.LOGGED_OUT) {
securityContext.logout();
if (logoutPage != null) {
sendRedirect(exchange, logoutPage);
exchange.setResponseCode(302);
exchange.endExchange();
if (deployment.getLogoutPage() != null) {
redirectLogout(deployment, exchange);
}
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
@ -138,5 +133,16 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
String page = deployment.getLogoutPage();
sendRedirect(exchange, page);
exchange.setResponseCode(302);
exchange.endExchange();
}
protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
return new UndertowHttpFacade(exchange);
}
protected abstract SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext);
}

View file

@ -0,0 +1,187 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.adapters.saml.undertow;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.AuthenticationMechanismFactory;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.server.handlers.form.FormParserFactory;
import io.undertow.servlet.ServletExtension;
import io.undertow.servlet.api.AuthMethodConfig;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.ServletSessionConfig;
import org.jboss.logging.Logger;
import org.keycloak.adapters.saml.DefaultSamlDeployment;
import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.SamlDeploymentContext;
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import org.keycloak.saml.common.exceptions.ParsingException;
import javax.servlet.ServletContext;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SamlServletExtension implements ServletExtension {
protected static Logger log = Logger.getLogger(SamlServletExtension.class);
// todo when this DeploymentInfo method of the same name is fixed.
public boolean isAuthenticationMechanismPresent(DeploymentInfo deploymentInfo, final String mechanismName) {
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
if (loginConfig != null) {
for (AuthMethodConfig method : loginConfig.getAuthMethods()) {
if (method.getName().equalsIgnoreCase(mechanismName)) {
return true;
}
}
}
return false;
}
private static InputStream getConfigInputStream(ServletContext context) {
InputStream is = null;
if (is == null) {
String path = context.getInitParameter("keycloak.config.file");
if (path == null) {
log.debug("using /WEB-INF/keycloak-saml.xml");
is = context.getResourceAsStream("/WEB-INF/keycloak-saml.xml");
} else {
try {
is = new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
}
return is;
}
@Override
@SuppressWarnings("UseSpecificCatch")
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK-SAML")) {
log.debug("auth-method is not keycloak saml!");
return;
}
log.debug("SamlServletException initialization");
// Possible scenarios:
// 1) The deployment has a keycloak.config.resolver specified and it exists:
// Outcome: adapter uses the resolver
// 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exists, isn't a resolver, ...) :
// Outcome: adapter is left unconfigured
// 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent)
// Outcome: adapter uses it
// 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
// Outcome: adapter is left unconfigured
SamlConfigResolver configResolver;
String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
SamlDeploymentContext deploymentContext = null;
if (configResolverClass != null) {
try {
throw new RuntimeException("Not implemented yet");
//configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
//deploymentContext = new AdapterDeploymentContext(configResolver);
//log.info("Using " + configResolverClass + " to resolve Keycloak configuration on a per-request basis.");
} catch (Exception ex) {
log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
//deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
}
} else {
InputStream is = getConfigInputStream(servletContext);
final SamlDeployment deployment;
if (is == null) {
log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
deployment = new DefaultSamlDeployment();
} else {
try {
ResourceLoader loader = new ResourceLoader() {
@Override
public InputStream getResourceAsStream(String resource) {
return servletContext.getResourceAsStream(resource);
}
};
deployment = new DeploymentBuilder().build(is, loader);
} catch (ParsingException e) {
throw new RuntimeException(e);
}
}
deploymentContext = new SamlDeploymentContext(deployment);
log.debug("Keycloak is using a per-deployment configuration.");
}
servletContext.setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
final ServletSamlAuthMech mech = new ServletSamlAuthMech(deploymentContext, userSessionManagement, getErrorPage(deploymentInfo));
// setup handlers
deploymentInfo.addAuthenticationMechanism("KEYCLOAK-SAML", new AuthenticationMechanismFactory() {
@Override
public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
return mech;
}
}); // authentication
deploymentInfo.setIdentityManager(new IdentityManager() {
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(String id, Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
@Override
public Account verify(Credential credential) {
throw new IllegalStateException("Should never be called in Keycloak flow");
}
});
log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
ServletSessionConfig cookieConfig = new ServletSessionConfig();
cookieConfig.setPath(deploymentInfo.getContextPath());
deploymentInfo.setServletSessionConfig(cookieConfig);
}
protected String getErrorPage(DeploymentInfo deploymentInfo) {
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
String errorPage = null;
if (loginConfig != null) {
errorPage = loginConfig.getErrorPage();
}
return errorPage;
}
}

View file

@ -2,24 +2,67 @@ package org.keycloak.adapters.saml.undertow;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.handlers.ServletRequestContext;
import io.undertow.util.Headers;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.SamlDeploymentContext;
import org.keycloak.adapters.saml.SamlSessionStore;
import org.keycloak.adapters.undertow.ServletHttpFacade;
import org.keycloak.adapters.undertow.UndertowHttpFacade;
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
String logoutPage, String errorPage) {
super(deploymentContext, sessionManagement, logoutPage, errorPage);
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
super(deploymentContext, sessionManagement, errorPage);
}
@Override
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
return new ServletSamlSessionStore(exchange, sessionManagement, securityContext);
}
@Override
protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
return new ServletHttpFacade(exchange);
}
@Override
protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
servePage(exchange, deployment.getLogoutPage());
}
@Override
protected Integer servePage(HttpServerExchange exchange, String location) {
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
ServletRequest req = servletRequestContext.getServletRequest();
ServletResponse resp = servletRequestContext.getServletResponse();
RequestDispatcher disp = req.getRequestDispatcher(location);
//make sure the login page is never cached
exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
try {
disp.forward(req, resp);
} catch (ServletException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}

View file

@ -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

View file

@ -101,6 +101,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-saml-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>

View file

@ -0,0 +1,510 @@
package org.keycloak.testsuite.keycloaksaml;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper;
import org.keycloak.protocol.saml.mappers.HardcodedRole;
import org.keycloak.protocol.saml.mappers.RoleListMapper;
import org.keycloak.protocol.saml.mappers.RoleNameMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SamlBindingTest {
@ClassRule
public static SamlKeycloakRule keycloakRule = new SamlKeycloakRule() {
@Override
public void initWars() {
ClassLoader classLoader = SamlBindingTest.class.getClassLoader();
//initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post", "post.war", classLoader);
initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email", "post-sig-email.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient", "post-sig-transient.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
//initializeSamlSecuredWar("/saml/simple-get", "/employee", "employee.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
//initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
//uploadSP();
//server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
}
@Override
public String getRealmJson() {
return "/saml/testsaml.json";
}
};
public static class SamlSPFacade extends HttpServlet {
public static String samlResponse;
public static String RELAY_STATE = "http://test.com/foo/bar";
public static String sentRelayState;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
handler(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
handler(req, resp);
}
private void handler(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("********* HERE ******");
if (req.getParameterMap().isEmpty()) {
System.out.println("redirecting");
resp.setStatus(302);
// Redirect
// UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVLRTsIwFP2Vpe%2BjG4wxG0YyWYxL0BBAH3wx3XYnTbp29nYof%2B8YEvEBNOlD03vOveec2ynyWjYsae1WreC9BbTOZy0Vsr4Qk9YopjkKZIrXgMwWbJ08LNhw4LHGaKsLLcmRch3MEcFYoRVxktN1rhW2NZg1mJ0o4Gm1iMnW2oZRKnXB5VajZZEX%2BRTqRuo9ACVO2mkUih%2F4l9C8s0MNcFkjLaHW9KSUHlwR506bAnrPMam4RCBOlsYkS1%2BD3MvLcDJxAx9KN4jCkXszrG5cP%2BCVH4y8IM8PYFx2dsQOfuiILWQKLVc2JkPPH7te6HrRxh%2BzUdidwSSIXoiz%2FBZyK1Qp1Nv1yPIjCNn9ZrN0V1AKA4UlzjMY7N13IDKbHjyxXoA5291%2FtzH7I%2FApPet%2FHNawx65hli61FMXeSaTUH%2FMubtvlYU0LfcA1t5cl%2BAO%2FfxGlW%2FVQ1ipsoBCVgJLQ2XHo7385%2BwI%3D");
UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVJbT8IwFP4rS99HuwluNIwEIUYSLwugD76Y2h2kSdfOng7l31uGRn0ATfrQ9HznfJfTEYpaN3zS%2Bo1ZwGsL6KP3WhvkXaEgrTPcClTIjagBuZd8Obm55mmP8cZZb6XV5NByGiwQwXllDYkmX9epNdjW4JbgtkrC%2FeK6IBvvG06ptlLojUXPc5YnFOpG2x0AJdEsaFRG7PuPoUWwQx0IXSOtoLb0SynduyLRpXUSOs8FWQuNQKL5rCDz2VO%2FymEgIY2zlJ3H%2FSx9jkU%2BzOK0ys8yNmSSsUEAYxnsqC18tyO2MDfohfEFSVkyiNlZzM5XacrDSbJePug%2Fkqj8FHKhTKXMy%2BnIng8g5FerVRmXd8sViR7AYec8AMh4tPfDO3L3Y2%2F%2F3cT4j7BH9Mf8A1nDb8PA%2Bay0WsldNNHavk1D1D5k4V0LXbi18MclJL2ke1FVvO6gvDXYgFRrBRWh4wPp7z85%2FgA%3D");
builder.queryParam("RelayState", RELAY_STATE);
resp.setHeader("Location", builder.build().toString());
return;
}
System.out.println("received response");
samlResponse = req.getParameter("SAMLResponse");
sentRelayState = req.getParameter("RelayState");
}
}
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected LoginPage loginPage;
protected void checkLoggedOut(String mainUrl) {
String pageSource = driver.getPageSource();
System.out.println("*** logout pagesource ***");
System.out.println(pageSource);
System.out.println("driver url: " + driver.getCurrentUrl());
Assert.assertTrue(pageSource.contains("request-path: /logout.jsp"));
driver.navigate().to(mainUrl);
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
}
//@Test
public void ideTesting() throws Exception {
Thread.sleep(100000000);
}
@Test
public void testPostSimpleLoginLogout() {
driver.navigate().to("http://localhost:8081/sales-post/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post/");
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post/");
}
@Test
public void testPostSimpleLoginLogoutIdpInitiated() {
driver.navigate().to("http://localhost:8081/auth/realms/demo/protocol/saml/clients/sales-post");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post/");
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post/");
}
@Test
public void testPostSignedLoginLogout() {
driver.navigate().to("http://localhost:8081/sales-post-sig/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post-sig?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post-sig/");
}
@Test
public void testPostSignedLoginLogoutTransientNameID() {
driver.navigate().to("http://localhost:8081/sales-post-sig-transient/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-transient/");
System.out.println(driver.getPageSource());
Assert.assertFalse(driver.getPageSource().contains("bburke"));
Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
driver.navigate().to("http://localhost:8081/sales-post-sig-transient?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post-sig-transient/");
}
@Test
public void testPostSignedLoginLogoutPersistentNameID() {
driver.navigate().to("http://localhost:8081/sales-post-sig-persistent/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-persistent/");
System.out.println(driver.getPageSource());
Assert.assertFalse(driver.getPageSource().contains("bburke"));
Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
driver.navigate().to("http://localhost:8081/sales-post-sig-persistent?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post-sig-persistent/");
}
@Test
public void testPostSignedLoginLogoutEmailNameID() {
driver.navigate().to("http://localhost:8081/sales-post-sig-email/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig-email/");
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("principal=bburke@redhat.com"));
driver.navigate().to("http://localhost:8081/sales-post-sig-email?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post-sig-email/");
}
@Test
public void testRelayStateEncoding() throws Exception {
// this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
// at the relay state
SamlSPFacade.samlResponse = null;
driver.navigate().to("http://localhost:8081/employee/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
System.out.println(driver.getCurrentUrl());
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
Assert.assertEquals(SamlSPFacade.sentRelayState, SamlSPFacade.RELAY_STATE);
Assert.assertNotNull(SamlSPFacade.samlResponse);
}
@Test
public void testAttributes() throws Exception {
// this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
// at the assertions sent. This is because Picketlink, AFAICT, does not give you any way to get access to
// the assertion.
{
SamlSPFacade.samlResponse = null;
driver.navigate().to("http://localhost:8081/employee/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
System.out.println(driver.getCurrentUrl());
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
Assert.assertNotNull(SamlSPFacade.samlResponse);
SAML2Response saml2Response = new SAML2Response();
byte[] samlResponse = PostBindingUtil.base64Decode(SamlSPFacade.samlResponse);
ResponseType rt = saml2Response.getResponseType(new ByteArrayInputStream(samlResponse));
Assert.assertTrue(rt.getAssertions().size() == 1);
AssertionType assertion = rt.getAssertions().get(0).getAssertion();
// test attributes and roles
boolean email = false;
boolean phone = false;
boolean userRole = false;
boolean managerRole = false;
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attr.getFriendlyName())) {
Assert.assertEquals(X500SAMLProfileConstants.EMAIL.get(), attr.getName());
Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(), attr.getNameFormat());
Assert.assertEquals(attr.getAttributeValue().get(0), "bburke@redhat.com");
email = true;
} else if (attr.getName().equals("phone")) {
Assert.assertEquals(JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get(), attr.getNameFormat());
Assert.assertEquals(attr.getAttributeValue().get(0), "617");
phone = true;
} else if (attr.getName().equals("Role")) {
if (attr.getAttributeValue().get(0).equals("manager")) managerRole = true;
if (attr.getAttributeValue().get(0).equals("user")) userRole = true;
}
}
}
Assert.assertTrue(email);
Assert.assertTrue(phone);
Assert.assertTrue(userRole);
Assert.assertTrue(managerRole);
}
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ClientModel app = appRealm.getClientByClientId("http://localhost:8081/employee/");
for (ProtocolMapperModel mapper : app.getProtocolMappers()) {
if (mapper.getName().equals("role-list")) {
app.removeProtocolMapper(mapper);
mapper.setId(null);
mapper.getConfig().put(RoleListMapper.SINGLE_ROLE_ATTRIBUTE, "true");
mapper.getConfig().put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "memberOf");
app.addProtocolMapper(mapper);
}
}
app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard", false, null));
app.addProtocolMapper(HardcodedRole.create("hardcoded-role", "hardcoded-role"));
app.addProtocolMapper(RoleNameMapper.create("renamed-role", "manager", "el-jefe"));
app.addProtocolMapper(RoleNameMapper.create("renamed-employee-role", "http://localhost:8081/employee/.employee", "pee-on"));
}
}, "demo");
System.out.println(">>>>>>>>>> single role attribute <<<<<<<<");
{
SamlSPFacade.samlResponse = null;
driver.navigate().to("http://localhost:8081/employee/");
System.out.println(driver.getCurrentUrl());
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee/");
Assert.assertNotNull(SamlSPFacade.samlResponse);
SAML2Response saml2Response = new SAML2Response();
byte[] samlResponse = PostBindingUtil.base64Decode(SamlSPFacade.samlResponse);
ResponseType rt = saml2Response.getResponseType(new ByteArrayInputStream(samlResponse));
Assert.assertTrue(rt.getAssertions().size() == 1);
AssertionType assertion = rt.getAssertions().get(0).getAssertion();
// test attributes and roles
boolean userRole = false;
boolean managerRole = false;
boolean single = false;
boolean hardcodedRole = false;
boolean hardcodedAttribute = false;
boolean peeOn = false;
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (attr.getName().equals("memberOf")) {
if (single) Assert.fail("too many role attributes");
single = true;
for (Object value : attr.getAttributeValue()) {
if (value.equals("el-jefe")) managerRole = true;
if (value.equals("user")) userRole = true;
if (value.equals("hardcoded-role")) hardcodedRole = true;
if (value.equals("pee-on")) peeOn = true;
}
} else if (attr.getName().equals("hardcoded-attribute")) {
hardcodedAttribute = true;
Assert.assertEquals(attr.getAttributeValue().get(0), "hard");
}
}
}
Assert.assertTrue(single);
Assert.assertTrue(hardcodedAttribute);
Assert.assertTrue(hardcodedRole);
Assert.assertTrue(peeOn);
Assert.assertTrue(userRole);
Assert.assertTrue(managerRole);
}
}
@Test
public void testRedirectSignedLoginLogout() {
driver.navigate().to("http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
checkLoggedOut("http://localhost:8081/employee-sig/");
}
@Test
public void testRedirectSignedLoginLogoutFrontNoSSO() {
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/employee-sig-front?GLO=true");
checkLoggedOut("http://localhost:8081/employee-sig-front/");
}
@Test
public void testRedirectSignedLoginLogoutFront() {
// visit 1st app an logg in
System.out.println("visit 1st app ");
driver.navigate().to("http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
System.out.println("login to form");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
// visit 2nd app
System.out.println("visit 2nd app ");
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
// visit 3rd app
System.out.println("visit 3rd app ");
driver.navigate().to("http://localhost:8081/sales-post-sig/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
// logout of first app
System.out.println("GLO");
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
checkLoggedOut("http://localhost:8081/employee-sig/");
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
driver.navigate().to("http://localhost:8081/sales-post-sig/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
}
@Test
public void testPostEncryptedLoginLogout() {
driver.navigate().to("http://localhost:8081/sales-post-enc/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-enc/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post-enc?GLO=true");
checkLoggedOut("http://localhost:8081/sales-post-enc/");
}
@Test
public void testPostBadClientSignature() {
driver.navigate().to("http://localhost:8081/bad-client-sales-post-sig/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
Assert.assertEquals(driver.getTitle(), "We're sorry...");
}
@Test
public void testPostBadRealmSignature() {
driver.navigate().to("http://localhost:8081/bad-realm-sales-post-sig/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/bad-realm-sales-post-sig/");
Assert.assertTrue(driver.getPageSource().contains("null"));
}
private static String createToken() {
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
TokenManager tm = new TokenManager();
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/master");
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
}
}
@Test
public void testMetadataPostSignedLoginLogout() throws Exception {
driver.navigate().to("http://localhost:8081/sales-metadata/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-metadata/");
String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true");
checkLoggedOut("http://localhost:8081/sales-metadata/");
}
public static void uploadSP() {
String token = createToken();
final String authHeader = "Bearer " + token;
ClientRequestFilter authFilter = new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
}
};
Client client = ClientBuilder.newBuilder().register(authFilter).build();
UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
MultipartFormDataOutput formData = new MultipartFormDataOutput();
InputStream is = SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml");
Assert.assertNotNull(is);
formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
WebTarget upload = adminRealms.path("demo/client-importers/saml2-entity-descriptor/upload");
System.out.println(upload.getUri());
Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
Assert.assertEquals(204, response.getStatus());
response.close();
client.close();
}
}

View file

@ -0,0 +1,167 @@
package org.keycloak.testsuite.keycloaksaml;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.server.handlers.resource.ResourceChangeListener;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.server.handlers.resource.URLResource;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.WebResourceCollection;
import org.keycloak.adapters.saml.undertow.SamlServletExtension;
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import org.picketlink.identity.federation.bindings.wildfly.sp.SPServletExtension;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.security.Principal;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
public static class SendUsernameServlet extends HttpServlet {
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
OutputStream stream = resp.getOutputStream();
Principal principal = req.getUserPrincipal();
stream.write("request-path: ".getBytes());
stream.write(req.getPathInfo().getBytes());
stream.write("\n".getBytes());
stream.write("principal=".getBytes());
if (principal == null) {
stream.write("null".getBytes());
return;
}
String name = principal.getName();
stream.write(name.getBytes());
}
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
OutputStream stream = resp.getOutputStream();
Principal principal = req.getUserPrincipal();
stream.write("request-path: ".getBytes());
stream.write(req.getPathInfo().getBytes());
stream.write("\n".getBytes());
stream.write("principal=".getBytes());
if (principal == null) {
stream.write("null".getBytes());
return;
}
String name = principal.getName();
stream.write(name.getBytes());
}
}
public static class TestResourceManager implements ResourceManager {
private final String basePath;
public TestResourceManager(String basePath){
this.basePath = basePath;
}
@Override
public Resource getResource(String path) throws IOException {
String temp = path;
String fullPath = basePath + temp;
URL url = getClass().getResource(fullPath);
if (url == null) {
System.out.println("url is null: " + fullPath);
}
return new URLResource(url, url.openConnection(), path);
}
@Override
public boolean isResourceChangeListenerSupported() {
throw new RuntimeException();
}
@Override
public void registerResourceChangeListener(ResourceChangeListener listener) {
throw new RuntimeException();
}
@Override
public void removeResourceChangeListener(ResourceChangeListener listener) {
throw new RuntimeException();
}
@Override
public void close() throws IOException {
throw new RuntimeException();
}
}
public static class TestIdentityManager implements IdentityManager {
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(String userName, Credential credential) {
throw new RuntimeException("WTF");
}
@Override
public Account verify(Credential credential) {
throw new RuntimeException();
}
}
@Override
protected void setupKeycloak() {
String realmJson = getRealmJson();
server.importRealm(getClass().getResourceAsStream(realmJson));
initWars();
}
public abstract void initWars();
public void initializeSamlSecuredWar(String warResourcePath, String contextPath, String warDeploymentName, ClassLoader classLoader) {
ServletInfo regularServletInfo = new ServletInfo("servlet", SendUsernameServlet.class)
.addMapping("/*");
SecurityConstraint constraint = new SecurityConstraint();
WebResourceCollection collection = new WebResourceCollection();
collection.addUrlPattern("/*");
constraint.addWebResourceCollection(collection);
constraint.addRoleAllowed("manager");
LoginConfig loginConfig = new LoginConfig("KEYCLOAK-SAML", "Test Realm");
ResourceManager resourceManager = new TestResourceManager(warResourcePath);
DeploymentInfo deploymentInfo = new DeploymentInfo()
.setClassLoader(classLoader)
.setIdentityManager(new TestIdentityManager())
.setContextPath(contextPath)
.setDeploymentName(warDeploymentName)
.setLoginConfig(loginConfig)
.setResourceManager(resourceManager)
.addServlets(regularServletInfo)
.addSecurityConstraint(constraint)
.addServletExtension(new SamlServletExtension());
server.getServer().deploy(deploymentInfo);
}
public String getRealmJson() {
return "/keycloak-saml/testsaml.json";
}
}

View file

@ -0,0 +1,45 @@
<keycloak-saml-adapter>
<SP entityID="http://localhost:8081/sales-post-sig/"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
logoutPage="/logout.jsp"
forceAuthentication="false">
<Keys>
<Key signing="true" >
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
</KeyStore>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
<RoleMapping>
<Attribute name="Role"/>
</RoleMapping>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
/>
<Keys>
<Key signing="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="demo"/>
</KeyStore>
</Key>
</Keys>
</IDP>
</SP>
</keycloak-saml-adapter>

View file

@ -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"
}
]
}
}
}

View file

@ -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"
}
}