saml undertow sp
This commit is contained in:
parent
861a13501a
commit
cc9d6d0cf7
34 changed files with 3157 additions and 70 deletions
5
pom.xml
5
pom.xml
|
@ -990,6 +990,11 @@
|
||||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
<artifactId>keycloak-saml-adapter-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-undertow-saml-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-services</artifactId>
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
<artifactId>keycloak-saml-adapter-core</artifactId>
|
||||||
<name>Keycloak SAML Adapter Core</name>
|
<name>Keycloak SAML Client Adapter Core</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,8 +34,8 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
@Override
|
@Override
|
||||||
public boolean challenge(HttpFacade httpFacade) {
|
public boolean challenge(HttpFacade httpFacade) {
|
||||||
try {
|
try {
|
||||||
String issuerURL = deployment.getIssuer();
|
String issuerURL = deployment.getEntityID();
|
||||||
String actionUrl = deployment.getSingleSignOnServiceUrl();
|
String actionUrl = deployment.getIDP().getSingleSignOnService().getRequestBindingUrl();
|
||||||
String destinationUrl = actionUrl;
|
String destinationUrl = actionUrl;
|
||||||
String nameIDPolicyFormat = deployment.getNameIDPolicyFormat();
|
String nameIDPolicyFormat = deployment.getNameIDPolicyFormat();
|
||||||
|
|
||||||
|
@ -45,28 +45,30 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
|
|
||||||
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
|
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();
|
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
||||||
.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl())
|
|
||||||
.destination(destinationUrl)
|
.destination(destinationUrl)
|
||||||
.issuer(issuerURL)
|
.issuer(issuerURL)
|
||||||
.forceAuthn(deployment.isForceAuthentication())
|
.forceAuthn(deployment.isForceAuthentication())
|
||||||
.protocolBinding(protocolBinding)
|
.protocolBinding(protocolBinding)
|
||||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
||||||
|
if (deployment.getAssertionConsumerServiceUrl() != null) {
|
||||||
|
authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
|
||||||
|
}
|
||||||
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
|
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
|
||||||
|
|
||||||
if (deployment.isRequestsSigned()) {
|
if (deployment.getIDP().getSingleSignOnService().signRequest()) {
|
||||||
|
|
||||||
|
|
||||||
KeyPair keypair = deployment.getSigningKeyPair();
|
KeyPair keypair = deployment.getSigningKeyPair();
|
||||||
if (keypair == null) {
|
if (keypair == null) {
|
||||||
throw new RuntimeException("Signing keys not configured");
|
throw new RuntimeException("Signing keys not configured");
|
||||||
}
|
}
|
||||||
if (deployment.getSignatureCanonicalizationMethod() != null) {
|
if (deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod() != null) {
|
||||||
binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
binding.canonicalizationMethod(deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.signWith(keypair);
|
binding.signWith(keypair);
|
||||||
|
@ -75,7 +77,7 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
sessionStore.saveRequest();
|
sessionStore.saveRequest();
|
||||||
|
|
||||||
Document document = authnRequestBuilder.toDocument();
|
Document document = authnRequestBuilder.toDocument();
|
||||||
SamlDeployment.Binding samlBinding = deployment.getRequestBinding();
|
SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
|
||||||
SamlUtil.sendSaml(httpFacade, actionUrl, binding, document, samlBinding);
|
SamlUtil.sendSaml(httpFacade, actionUrl, binding, document, samlBinding);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Could not create authentication request.", e);
|
throw new RuntimeException("Could not create authentication request.", e);
|
||||||
|
|
|
@ -90,12 +90,12 @@ public abstract class SamlAuthenticator {
|
||||||
SamlSession account = sessionStore.getAccount();
|
SamlSession account = sessionStore.getAccount();
|
||||||
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
||||||
.assertionExpiration(30)
|
.assertionExpiration(30)
|
||||||
.issuer(deployment.getIssuer())
|
.issuer(deployment.getEntityID())
|
||||||
.sessionIndex(account.getSessionIndex())
|
.sessionIndex(account.getSessionIndex())
|
||||||
.userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
|
.userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
|
||||||
.destination(deployment.getSingleLogoutServiceUrl());
|
.destination(deployment.getIDP().getSingleLogoutService().getRequestBindingUrl());
|
||||||
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
|
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
|
||||||
if (deployment.isRequestsSigned()) {
|
if (deployment.getIDP().getSingleLogoutService().signRequest()) {
|
||||||
binding.signWith(deployment.getSigningKeyPair())
|
binding.signWith(deployment.getSigningKeyPair())
|
||||||
.signDocument();
|
.signDocument();
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public abstract class SamlAuthenticator {
|
||||||
binding.relayState("logout");
|
binding.relayState("logout");
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
|
@ -129,9 +129,11 @@ public abstract class SamlAuthenticator {
|
||||||
if (!facade.getRequest().getURI().toString().equals(requestAbstractType.getDestination())) {
|
if (!facade.getRequest().getURI().toString().equals(requestAbstractType.getDestination())) {
|
||||||
throw new RuntimeException("destination not equal to request");
|
throw new RuntimeException("destination not equal to request");
|
||||||
}
|
}
|
||||||
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
|
||||||
|
|
||||||
if (requestAbstractType instanceof LogoutRequestType) {
|
if (requestAbstractType instanceof LogoutRequestType) {
|
||||||
|
if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
|
||||||
|
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
||||||
|
}
|
||||||
LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
|
LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
|
||||||
return logoutRequest(logout, relayState);
|
return logoutRequest(logout, relayState);
|
||||||
|
|
||||||
|
@ -147,21 +149,22 @@ public abstract class SamlAuthenticator {
|
||||||
sessionStore.logoutBySsoId(request.getSessionIndex());
|
sessionStore.logoutBySsoId(request.getSessionIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
String issuerURL = deployment.getIssuer();
|
String issuerURL = deployment.getEntityID();
|
||||||
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
|
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
|
||||||
builder.logoutRequestID(request.getID());
|
builder.logoutRequestID(request.getID());
|
||||||
builder.destination(deployment.getSingleLogoutServiceUrl());
|
builder.destination(deployment.getIDP().getSingleLogoutService().getResponseBindingUrl());
|
||||||
builder.issuer(issuerURL);
|
builder.issuer(issuerURL);
|
||||||
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
|
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
|
||||||
if (deployment.isRequestsSigned()) {
|
if (deployment.getIDP().getSingleLogoutService().signResponse()) {
|
||||||
binding.signWith(deployment.getSigningKeyPair())
|
binding.signWith(deployment.getSigningKeyPair())
|
||||||
.signDocument();
|
.signDocument();
|
||||||
|
binding.canonicalizationMethod(deployment.getIDP().getSingleLogoutService().getSignatureCanonicalizationMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SamlUtil.sendSaml(facade, deployment.getSingleLogoutServiceUrl(), binding, builder.buildDocument(),
|
SamlUtil.sendSaml(facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
|
||||||
deployment.getResponseBinding());
|
deployment.getIDP().getSingleLogoutService().getResponseBinding());
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
|
@ -188,11 +191,16 @@ public abstract class SamlAuthenticator {
|
||||||
if (!facade.getRequest().getURI().toString().equals(statusResponse.getDestination())) {
|
if (!facade.getRequest().getURI().toString().equals(statusResponse.getDestination())) {
|
||||||
throw new RuntimeException("destination not equal to request");
|
throw new RuntimeException("destination not equal to request");
|
||||||
}
|
}
|
||||||
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
|
|
||||||
if (statusResponse instanceof ResponseType) {
|
if (statusResponse instanceof ResponseType) {
|
||||||
|
if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
|
||||||
|
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
||||||
|
}
|
||||||
return handleLoginResponse((ResponseType)statusResponse);
|
return handleLoginResponse((ResponseType)statusResponse);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
|
||||||
|
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
||||||
|
}
|
||||||
// todo need to check that it is actually a LogoutResponse
|
// todo need to check that it is actually a LogoutResponse
|
||||||
return handleLogoutResponse(holder, statusResponse, relayState);
|
return handleLogoutResponse(holder, statusResponse, relayState);
|
||||||
}
|
}
|
||||||
|
@ -200,24 +208,22 @@ public abstract class SamlAuthenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) {
|
private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) {
|
||||||
if (deployment.isValidateSignatures()) {
|
try {
|
||||||
try {
|
if (postBinding) {
|
||||||
if (postBinding) {
|
verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
|
||||||
verifyPostBindingSignature(holder.getSamlDocument(), deployment.getSignatureValidationKey());
|
} else {
|
||||||
} else {
|
verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
|
||||||
verifyRedirectBindingSignature(deployment.getSignatureValidationKey(), paramKey);
|
|
||||||
}
|
|
||||||
} catch (VerificationException e) {
|
|
||||||
log.error("validation failed", e);
|
|
||||||
throw new RuntimeException("invalid document signature");
|
|
||||||
}
|
}
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
log.error("validation failed", e);
|
||||||
|
throw new RuntimeException("invalid document signature");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AuthOutcome handleLoginResponse(ResponseType responseType) {
|
protected AuthOutcome handleLoginResponse(ResponseType responseType) {
|
||||||
AssertionType assertion = null;
|
AssertionType assertion = null;
|
||||||
try {
|
try {
|
||||||
assertion = AssertionUtil.getAssertion(responseType, deployment.getAssertionDecryptionKey());
|
assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
|
||||||
if (AssertionUtil.hasExpired(assertion)) {
|
if (AssertionUtil.hasExpired(assertion)) {
|
||||||
return initiateLogin();
|
return initiateLogin();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -14,26 +14,53 @@ import java.util.Set;
|
||||||
public interface SamlDeployment {
|
public interface SamlDeployment {
|
||||||
enum Binding {
|
enum Binding {
|
||||||
POST,
|
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();
|
public boolean isConfigured();
|
||||||
SslRequired getSslRequired();
|
SslRequired getSslRequired();
|
||||||
String getSingleSignOnServiceUrl();
|
String getEntityID();
|
||||||
String getSingleLogoutServiceUrl();
|
|
||||||
String getIssuer();
|
|
||||||
String getNameIDPolicyFormat();
|
String getNameIDPolicyFormat();
|
||||||
String getAssertionConsumerServiceUrl();
|
|
||||||
Binding getRequestBinding();
|
|
||||||
Binding getResponseBinding();
|
|
||||||
KeyPair getSigningKeyPair();
|
|
||||||
String getSignatureCanonicalizationMethod();
|
|
||||||
boolean isForceAuthentication();
|
boolean isForceAuthentication();
|
||||||
boolean isRequestsSigned();
|
PrivateKey getDecryptionKey();
|
||||||
|
KeyPair getSigningKeyPair();
|
||||||
boolean isValidateSignatures();
|
String getAssertionConsumerServiceUrl();
|
||||||
PublicKey getSignatureValidationKey();
|
String getLogoutPage();
|
||||||
PrivateKey getAssertionDecryptionKey();
|
|
||||||
|
|
||||||
Set<String> getRoleAttributeNames();
|
Set<String> getRoleAttributeNames();
|
||||||
Set<String> getRoleAttributeFriendlyNames();
|
Set<String> getRoleAttributeFriendlyNames();
|
||||||
|
|
|
@ -7,7 +7,13 @@ import org.keycloak.adapters.HttpFacade;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SamlDeploymentContext {
|
public class SamlDeploymentContext {
|
||||||
|
private SamlDeployment deployment = null;
|
||||||
|
|
||||||
|
public SamlDeploymentContext(SamlDeployment deployment) {
|
||||||
|
this.deployment = deployment;
|
||||||
|
}
|
||||||
|
|
||||||
public SamlDeployment resolveDeployment(HttpFacade facade) {
|
public SamlDeployment resolveDeployment(HttpFacade facade) {
|
||||||
return null;
|
return deployment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class SamlPrincipal implements Serializable, Principal {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return null;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
126
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
Executable file
126
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
Executable 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
61
saml/client-adapter/core/src/test/resources/keycloak-saml.xml
Executable file
61
saml/client-adapter/core/src/test/resources/keycloak-saml.xml
Executable 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>
|
|
@ -42,16 +42,13 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
|
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
|
||||||
protected SamlDeploymentContext deploymentContext;
|
protected SamlDeploymentContext deploymentContext;
|
||||||
protected UndertowUserSessionManagement sessionManagement;
|
protected UndertowUserSessionManagement sessionManagement;
|
||||||
protected String logoutPage;
|
|
||||||
protected String errorPage;
|
protected String errorPage;
|
||||||
|
|
||||||
public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
|
public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
|
||||||
String logoutPage,
|
|
||||||
String errorPage) {
|
String errorPage) {
|
||||||
this.deploymentContext = deploymentContext;
|
this.deploymentContext = deploymentContext;
|
||||||
this.sessionManagement = sessionManagement;
|
this.sessionManagement = sessionManagement;
|
||||||
this.errorPage = errorPage;
|
this.errorPage = errorPage;
|
||||||
this.logoutPage = logoutPage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,7 +59,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
Integer code = servePage(exchange, errorPage);
|
Integer code = servePage(exchange, errorPage);
|
||||||
return new ChallengeResult(true, code);
|
return new ChallengeResult(true, code);
|
||||||
}
|
}
|
||||||
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
UndertowHttpFacade facade = createFacade(exchange);
|
||||||
if (challenge.challenge(facade)) {
|
if (challenge.challenge(facade)) {
|
||||||
return new ChallengeResult(true, exchange.getResponseCode());
|
return new ChallengeResult(true, exchange.getResponseCode());
|
||||||
}
|
}
|
||||||
|
@ -91,7 +88,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
|
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
|
||||||
|
|
||||||
HttpServerExchange exchange = notification.getExchange();
|
HttpServerExchange exchange = notification.getExchange();
|
||||||
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
UndertowHttpFacade facade = createFacade(exchange);
|
||||||
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||||
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
|
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
|
||||||
sessionStore.logoutAccount();
|
sessionStore.logoutAccount();
|
||||||
|
@ -105,7 +102,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
* Call this inside your authenticate method.
|
* Call this inside your authenticate method.
|
||||||
*/
|
*/
|
||||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||||
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
UndertowHttpFacade facade = createFacade(exchange);
|
||||||
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||||
if (!deployment.isConfigured()) {
|
if (!deployment.isConfigured()) {
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||||
|
@ -120,10 +117,8 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
}
|
}
|
||||||
if (outcome == AuthOutcome.LOGGED_OUT) {
|
if (outcome == AuthOutcome.LOGGED_OUT) {
|
||||||
securityContext.logout();
|
securityContext.logout();
|
||||||
if (logoutPage != null) {
|
if (deployment.getLogoutPage() != null) {
|
||||||
sendRedirect(exchange, logoutPage);
|
redirectLogout(deployment, exchange);
|
||||||
exchange.setResponseCode(302);
|
|
||||||
exchange.endExchange();
|
|
||||||
}
|
}
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||||
}
|
}
|
||||||
|
@ -138,5 +133,16 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
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);
|
protected abstract SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext);
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,24 +2,67 @@ package org.keycloak.adapters.saml.undertow;
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
import io.undertow.security.api.SecurityContext;
|
||||||
import io.undertow.server.HttpServerExchange;
|
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.HttpFacade;
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
import org.keycloak.adapters.saml.SamlDeployment;
|
||||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
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 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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
|
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
|
||||||
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
|
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
||||||
String logoutPage, String errorPage) {
|
super(deploymentContext, sessionManagement, errorPage);
|
||||||
super(deploymentContext, sessionManagement, logoutPage, errorPage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
|
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
|
||||||
return new ServletSamlSessionStore(exchange, sessionManagement, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,23 @@ public class StaxParserUtil {
|
||||||
return result;
|
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
|
* Given that the {@code XMLEventReader} is in {@code XMLStreamConstants.START_ELEMENT} mode, we parse into a DOM
|
||||||
* Element
|
* Element
|
||||||
|
|
|
@ -101,6 +101,14 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
Binary file not shown.
310
testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
Executable file
310
testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
Executable 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,7 +69,6 @@
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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=="
|
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -92,7 +91,6 @@
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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=="
|
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -114,7 +112,6 @@
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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=="
|
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -139,7 +136,6 @@
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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=="
|
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -157,7 +153,6 @@
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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=="
|
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -175,7 +170,6 @@
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.authnstatement": "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.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -198,9 +192,7 @@
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.encrypt": "true",
|
"saml.encrypt": "true",
|
||||||
"saml.authnstatement": "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.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=="
|
"saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -219,7 +211,6 @@
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA1",
|
"saml.signature.algorithm": "RSA_SHA1",
|
||||||
"saml.authnstatement": "true",
|
"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"
|
"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.client.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA1",
|
"saml.signature.algorithm": "RSA_SHA1",
|
||||||
"saml.authnstatement": "true",
|
"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"
|
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue