keycloak-aplcache
Changes
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml 20(+20 -0)
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml 1(+1 -0)
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml 1(+1 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml 20(+20 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml 1(+1 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml 1(+1 -0)
distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml 1(+1 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml 20(+20 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml 1(+1 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml 1(+1 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml 1(+1 -0)
distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml 1(+1 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-core/main/module.xml 1(+1 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-spi/main/module.xml 22(+22 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-jboss-adapter-core/main/module.xml 1(+1 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-servlet-oauth-client/main/module.xml 1(+1 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-undertow-adapter/main/module.xml 1(+1 -0)
distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-wildfly-adapter/main/module.xml 1(+1 -0)
examples/multi-tenant/pom.xml 4(+4 -0)
examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java 5(+2 -3)
integration/adapter-core/pom.xml 5(+5 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java 4(+2 -2)
integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java 11(+5 -6)
integration/adapter-spi/pom.xml 93(+93 -0)
integration/installed/pom.xml 4(+4 -0)
integration/jetty/jetty8.1/pom.xml 4(+4 -0)
integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/JettyAdapterSessionStore.java 15(+7 -8)
integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java 3(+2 -1)
integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettyAdapterSessionStore.java 16(+7 -9)
integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java 3(+2 -1)
integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettyAdapterSessionStore.java 15(+7 -8)
integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java 3(+2 -1)
integration/jetty/jetty-adapter-spi/pom.xml 115(+115 -0)
integration/jetty/jetty-adapter-spi/src/main/java/org/keycloak/adapters/jetty/core/JettyHttpFacade.java 60(+30 -30)
integration/jetty/jetty-adapter-spi/src/main/java/org/keycloak/adapters/jetty/core/JettyUserSessionManagement.java 0(+0 -0)
integration/jetty/jetty-adapter-spi/src/main/java/org/keycloak/adapters/jetty/core/WrappingSessionHandler.java 0(+0 -0)
integration/jetty/jetty-core/pom.xml 8(+8 -0)
integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java 4(+2 -2)
integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettySessionTokenStore.java 20(+16 -4)
integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/OIDCJettyHttpFacade.java 23(+23 -0)
integration/jetty/pom.xml 1(+1 -0)
integration/pom.xml 2(+2 -0)
integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java 11(+7 -4)
integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java 4(+2 -2)
integration/spring-security/pom.xml 4(+4 -0)
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java 2(+1 -1)
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java 8(+3 -5)
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java 5(+5 -0)
integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/token/SpringSecurityAdapterTokenStoreFactoryTest.java 5(+2 -3)
integration/tomcat/pom.xml 1(+1 -0)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/CatalinaHttpFacade.java 59(+29 -30)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/CatalinaUserSessionManagement.java 0(+0 -0)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/CatalinaUserSessionManagementWrapper.java 0(+0 -0)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java 0(+0 -0)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java 0(+0 -0)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java 13(+3 -10)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AuthenticatedActionsValve.java 4(+2 -2)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaAdapterSessionStore.java 32(+32 -0)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java 20(+2 -18)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/OIDCCatalinaHttpFacade.java 23(+23 -0)
integration/undertow/pom.xml 8(+8 -0)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java 8(+2 -6)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCUndertowHttpFacade.java 40(+40 -0)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java 4(+1 -3)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPreAuthActionsHandler.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticatedActionsHandler.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticationMechanism.java 2(+1 -1)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowPreAuthActionsHandler.java 2(+1 -1)
integration/undertow-adapter-spi/pom.xml 65(+65 -0)
integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/ServletHttpFacade.java 39(+39 -0)
integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/SessionManagementBridge.java 0(+0 -0)
integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/UndertowHttpFacade.java 80(+30 -50)
integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/UndertowUserSessionManagement.java 0(+0 -0)
pom.xml 30(+30 -0)
saml/client-adapter/core/pom.xml 60(+60 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java 21(+21 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java 57(+57 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java 206(+206 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java 98(+98 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java 46(+46 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java 60(+60 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java 128(+128 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java 11(+11 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java 141(+141 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java 364(+364 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java 458(+458 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java 39(+39 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java 19(+19 -0)
saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java 78(+78 -0)
saml/client-adapter/pom.xml 20(+20 -0)
saml/client-adapter/undertow/pom.xml 87(+87 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java 148(+148 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java 187(+187 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java 71(+71 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java 180(+180 -0)
saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/UndertowSamlAuthenticator.java 43(+43 -0)
saml/pom.xml 1(+1 -0)
saml/saml-core/pom.xml 5(+5 -0)
saml/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java 58(+58 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java 1(+1 -0)
saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/JaxrsSAML2BindingBuilder.java 76(+76 -0)
testsuite/integration/pom.xml 8(+8 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java 450(+450 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java 190(+190 -0)
testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 44(+44 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml 44(+44 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 44(+44 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keystore.jks 0(+0 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 45(+45 -0)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keystore.jks 0(+0 -0)
Details
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index f15a23f..24b56bb 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -10,7 +10,6 @@ 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.assertion.AuthnStatementType;
-import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
@@ -24,31 +23,23 @@ import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
-import org.keycloak.protocol.saml.SAMLRequestParser;
+import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.protocol.saml.SamlProtocolUtils;
import org.keycloak.saml.common.constants.GeneralConstants;
-import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException;
-import org.keycloak.saml.common.util.DocumentUtil;
-import org.keycloak.saml.common.util.StaxParserUtil;
-import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
-import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
-import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
-import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
+import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
@@ -61,9 +52,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import javax.xml.namespace.QName;
import java.io.IOException;
-import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.List;
@@ -155,7 +144,7 @@ public class SAMLEndpoint {
}
protected abstract String getBindingType();
- protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
+ protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException;
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
protected PublicKey getIDPKey() {
@@ -188,7 +177,7 @@ public class SAMLEndpoint {
}
if (config.isValidateSignature()) {
try {
- verifySignature(holder);
+ verifySignature(GeneralConstants.SAML_REQUEST_KEY, holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
@@ -247,17 +236,18 @@ public class SAMLEndpoint {
builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL);
- builder.relayState(relayState);
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
+ .relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
- builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+ binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signatureAlgorithm(provider.getSignatureAlgorithm())
.signDocument();
}
try {
if (config.isPostBindingResponse()) {
- return builder.postBinding().response();
+ return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
} else {
- return builder.redirectBinding().response();
+ return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@@ -275,7 +265,7 @@ public class SAMLEndpoint {
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
try {
- AssertionType assertion = getAssertion(responseType);
+ AssertionType assertion = AssertionUtil.getAssertion(responseType, realm.getPrivateKey());
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
@@ -335,22 +325,6 @@ public class SAMLEndpoint {
- private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
- List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
-
- if (assertions.isEmpty()) {
- throw new IdentityBrokerException("No assertion from response.");
- }
-
- ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
- EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
-
- if (encryptedAssertion != null) {
- decryptAssertion(responseType, realm.getPrivateKey());
-
- }
- return responseType.getAssertions().get(0).getAssertion();
- }
public Response handleSamlResponse(String samlResponse, String relayState) {
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
@@ -364,7 +338,7 @@ public class SAMLEndpoint {
}
if (config.isValidateSignature()) {
try {
- verifySignature(holder);
+ verifySignature(GeneralConstants.SAML_RESPONSE_KEY, holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
@@ -407,43 +381,14 @@ public class SAMLEndpoint {
}
- protected ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
- SAML2Response saml2Response = new SAML2Response();
-
- try {
- Document doc = saml2Response.convert(responseType);
- Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
-
- if (enc == null) {
- throw new IdentityBrokerException("No encrypted assertion found.");
- }
-
- String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
- Document newDoc = DocumentUtil.createDocument();
- Node importedNode = newDoc.importNode(enc, true);
- newDoc.appendChild(importedNode);
-
- Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
- SAMLParser parser = new SAMLParser();
-
- JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
- AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
- .getNodeAsStream(decryptedDocumentElement)));
- responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
-
- return responseType;
- } catch (Exception e) {
- throw new IdentityBrokerException("Could not decrypt assertion.", e);
- }
- }
}
protected class PostBinding extends Binding {
@Override
- protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
+ protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
}
@@ -466,9 +411,9 @@ public class SAMLEndpoint {
protected class RedirectBinding extends Binding {
@Override
- protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
+ protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
PublicKey publicKey = getIDPKey();
- SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
+ SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, key);
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 52959f8..e28aa97 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -33,21 +33,18 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
-import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
-import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
-import org.keycloak.protocol.saml.SignatureAlgorithm;
+import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
+import org.keycloak.saml.SAML2AuthnRequestBuilder;
+import org.keycloak.saml.SAML2LogoutRequestBuilder;
+import org.keycloak.saml.SAML2NameIDPolicyBuilder;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
-import org.keycloak.saml.common.exceptions.ConfigurationException;
-import org.keycloak.saml.common.exceptions.ParsingException;
-import org.keycloak.saml.common.exceptions.ProcessingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -93,7 +90,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(issuerURL)
.forceAuthn(getConfig().isForceAuthn())
.protocolBinding(protocolBinding)
- .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
+ .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(request.getState());
if (getConfig().isWantAuthnRequestsSigned()) {
@@ -110,15 +108,15 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
KeyPair keypair = new KeyPair(publicKey, privateKey);
- authnRequestBuilder.signWith(keypair);
- authnRequestBuilder.signatureAlgorithm(getSignatureAlgorithm());
- authnRequestBuilder.signDocument();
+ binding.signWith(keypair);
+ binding.signatureAlgorithm(getSignatureAlgorithm());
+ binding.signDocument();
}
if (getConfig().isPostBindingAuthnRequest()) {
- return authnRequestBuilder.postBinding().request();
+ return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
} else {
- return authnRequestBuilder.redirectBinding().request();
+ return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
}
} catch (Exception e) {
throw new IdentityBrokerException("Could not create authentication request.", e);
@@ -155,9 +153,10 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
+ JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
- .param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
+ .param(GeneralConstants.SAML_REQUEST_KEY, binding.postBinding(logoutBuilder.buildDocument()).encoded())
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
boolean success = status >=200 && status < 400;
if (!success) {
@@ -180,7 +179,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
} else {
try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
- return logoutBuilder.postBinding().request();
+ JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
+ return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -194,14 +194,19 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(getEntityId(uriInfo, realm))
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
- .destination(singleLogoutServiceUrl)
+ .destination(singleLogoutServiceUrl);
+ return logoutBuilder;
+ }
+
+ private JaxrsSAML2BindingBuilder buildLogoutBinding(UserSessionModel userSession, RealmModel realm) {
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
- logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+ binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signatureAlgorithm(getSignatureAlgorithm())
.signDocument();
}
- return logoutBuilder;
+ return binding;
}
@Override
@@ -257,4 +262,5 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
}
return SignatureAlgorithm.RSA_SHA256;
}
+
}
diff --git a/distribution/adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml b/distribution/adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
index 59ae243..dba2c93 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
@@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/build.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/build.xml
index 9ab6fab..c3e0add 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/build.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/build.xml
@@ -53,6 +53,11 @@
<!-- subsystems -->
+ <module-def name="org.keycloak.keycloak-adapter-spi">
+ <maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-tomcat-adapter-spi"/>
+ </module-def>
+
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/pom.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/pom.xml
index 04724d4..1628f9b 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/pom.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/pom.xml
@@ -23,6 +23,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-tomcat-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
index 5e3e5c3..c2975ba 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -14,6 +14,7 @@
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="net.iharder.base64"/>
</dependencies>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
new file mode 100755
index 0000000..08d4d5e
--- /dev/null
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.as.web"/>
+ <module name="javax.servlet.api"/>
+ <module name="org.apache.httpcomponents"/>
+ <module name="org.bouncycastle" />
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="net.iharder.base64"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml
index 463ff43..05053c1 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-as7-adapter/main/module.xml
@@ -18,6 +18,7 @@
<module name="org.jboss.as.security"/>
<module name="org.jboss.as.web"/>
<module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
index beac07b..f1ee530 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
index bc48988..c906cc9 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml b/distribution/adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
index 0f6c462..5ea03d1 100755
--- a/distribution/adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
+++ b/distribution/adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
@@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include>
diff --git a/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml b/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml
index da4e127..0d134dc 100755
--- a/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml
+++ b/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml
@@ -14,6 +14,7 @@
<include>net/iharder/base64/**</include>
<include>org/apache/httpcomponents/**</include>
<include>org/keycloak/keycloak-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-undertow-adapter/**</include>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/build.xml b/distribution/adapters/wf8-adapter/wf8-modules/build.xml
index 30e0254..1601598 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/build.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/build.xml
@@ -49,6 +49,11 @@
<!-- subsystems -->
+ <module-def name="org.keycloak.keycloak-adapter-spi">
+ <maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
+ </module-def>
+
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/pom.xml b/distribution/adapters/wf8-adapter/wf8-modules/pom.xml
index e98c41c..e652ede 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/pom.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/pom.xml
@@ -23,6 +23,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
index 1be1486..cfa8a0e 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
new file mode 100755
index 0000000..28c0543
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.apache.httpcomponents" slot="4.3" />
+ <module name="javax.servlet.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.xnio"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ <module name="org.keycloak.keycloak-core"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
index beac07b..f1ee530 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
index 2d3876e..1e92a95 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents" slot="4.3"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
index 1772a22..c3f1775 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
index 2b0e537..c0786cf 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf9-adapter/wf9-adapter-zip/assembly.xml b/distribution/adapters/wf9-adapter/wf9-adapter-zip/assembly.xml
index 764b76d..e839021 100755
--- a/distribution/adapters/wf9-adapter/wf9-adapter-zip/assembly.xml
+++ b/distribution/adapters/wf9-adapter/wf9-adapter-zip/assembly.xml
@@ -14,6 +14,7 @@
<include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-undertow-adapter/**</include>
<include>org/keycloak/keycloak-wildfly-adapter/**</include>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/build.xml b/distribution/adapters/wf9-adapter/wf9-modules/build.xml
index 8c1d41a..ce67858 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/build.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/build.xml
@@ -49,6 +49,11 @@
<!-- subsystems -->
+ <module-def name="org.keycloak.keycloak-adapter-spi">
+ <maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
+ </module-def>
+
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/pom.xml b/distribution/adapters/wf9-adapter/wf9-modules/pom.xml
index 9277184..270cc55 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/pom.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/pom.xml
@@ -23,6 +23,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
index 5e3e5c3..6c25a03 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
new file mode 100755
index 0000000..850c46a
--- /dev/null
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="org.apache.httpcomponents"/>
+ <module name="javax.servlet.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.xnio"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
index beac07b..f1ee530 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
index bc48988..c906cc9 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
index 4b3a4c9..4dd6f2d 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
index 3d65f2b..853ca8d 100755
--- a/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
+++ b/distribution/adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml
old mode 100644
new mode 100755
index 180d47b..37a4c6c
--- a/distribution/feature-packs/adapter-feature-pack/pom.xml
+++ b/distribution/feature-packs/adapter-feature-pack/pom.xml
@@ -46,6 +46,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
</dependency>
<dependency>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-core/main/module.xml
old mode 100644
new mode 100755
index 6407abc..0c854b9
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" />
<module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-spi/main/module.xml
new file mode 100755
index 0000000..ad826da
--- /dev/null
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-spi">
+ <resources>
+ <artifact name="${org.keycloak:keycloak-adapter-spi}"/>
+ <artifact name="${org.keycloak:keycloak-undertow-adapter-spi}"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.xnio"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ <module name="javax.servlet.api"/>
+ <module name="org.bouncycastle" />
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="net.iharder.base64"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
old mode 100644
new mode 100755
index 243a5b9..c836034
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-servlet-oauth-client/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
index 73fccad..fb39438 100755
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-servlet-oauth-client/main/module.xml
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-undertow-adapter/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-undertow-adapter/main/module.xml
old mode 100644
new mode 100755
index 75ac27e..b813b7f
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-undertow-adapter/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-undertow-adapter/main/module.xml
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-wildfly-adapter/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-wildfly-adapter/main/module.xml
index 88eaafa..4610766 100755
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-wildfly-adapter/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-wildfly-adapter/main/module.xml
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
examples/multi-tenant/pom.xml 4(+4 -0)
diff --git a/examples/multi-tenant/pom.xml b/examples/multi-tenant/pom.xml
index b75c76d..ac316a5 100755
--- a/examples/multi-tenant/pom.xml
+++ b/examples/multi-tenant/pom.xml
@@ -39,6 +39,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
<!-- Contains KeycloakPrincipal -->
<dependency>
diff --git a/examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java b/examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
old mode 100644
new mode 100755
index 44f7e78..b722300
--- a/examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
+++ b/examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
@@ -17,14 +17,13 @@
package org.keycloak.example.multitenant.control;
import java.io.InputStream;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.OIDCHttpFacade;
/**
*
@@ -35,7 +34,7 @@ public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
@Override
- public KeycloakDeployment resolve(HttpFacade.Request request) {
+ public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI();
int multitenantIndex = path.indexOf("multitenant/");
if (multitenantIndex == -1) {
integration/adapter-core/pom.xml 5(+5 -0)
diff --git a/integration/adapter-core/pom.xml b/integration/adapter-core/pom.xml
index 973003e..92308c4 100755
--- a/integration/adapter-core/pom.xml
+++ b/integration/adapter-core/pom.xml
@@ -40,6 +40,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
index d07bffa..066a32a 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
@@ -5,7 +5,7 @@ package org.keycloak.adapters;
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public interface AdapterTokenStore {
+public interface AdapterTokenStore extends AdapterSessionStore {
/**
* Impl can validate if current token exists and perform refreshing if it exists and is expired
@@ -39,6 +39,4 @@ public interface AdapterTokenStore {
*/
void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
- void saveRequest();
- boolean restoreRequest();
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
index 0a73404..f4eced9 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
@@ -23,9 +23,9 @@ import java.util.Set;
public class AuthenticatedActionsHandler {
private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
protected KeycloakDeployment deployment;
- protected HttpFacade facade;
+ protected OIDCHttpFacade facade;
- public AuthenticatedActionsHandler(KeycloakDeployment deployment, HttpFacade facade) {
+ public AuthenticatedActionsHandler(KeycloakDeployment deployment, OIDCHttpFacade facade) {
this.deployment = deployment;
this.facade = facade;
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
index f5e145f..73d58bc 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
@@ -34,7 +34,7 @@ public class CookieTokenStore {
}
public static KeycloakPrincipal<RefreshableKeycloakSecurityContext> getPrincipalFromCookie(KeycloakDeployment deployment, HttpFacade facade, AdapterTokenStore tokenStore) {
- HttpFacade.Cookie cookie = facade.getRequest().getCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
+ OIDCHttpFacade.Cookie cookie = facade.getRequest().getCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
if (cookie == null) {
log.debug("Not found adapter state cookie in current request");
return null;
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index 3189ab2..d077d7d 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -14,7 +14,6 @@ import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import java.io.IOException;
-import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@@ -27,7 +26,7 @@ public class OAuthRequestAuthenticator {
protected KeycloakDeployment deployment;
protected RequestAuthenticator reqAuthenticator;
protected int sslRedirectPort;
- protected AdapterTokenStore tokenStore;
+ protected AdapterSessionStore tokenStore;
protected String tokenString;
protected String idTokenString;
protected IDToken idToken;
@@ -37,7 +36,7 @@ public class OAuthRequestAuthenticator {
protected String refreshToken;
protected String strippedOauthParametersRequestUri;
- public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterTokenStore tokenStore) {
+ public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterSessionStore tokenStore) {
this.reqAuthenticator = requestAuthenticator;
this.facade = facade;
this.deployment = deployment;
@@ -93,12 +92,12 @@ public class OAuthRequestAuthenticator {
return facade.getRequest().isSecure();
}
- protected HttpFacade.Cookie getCookie(String cookieName) {
+ protected OIDCHttpFacade.Cookie getCookie(String cookieName) {
return facade.getRequest().getCookie(cookieName);
}
protected String getCookieValue(String cookieName) {
- HttpFacade.Cookie cookie = getCookie(cookieName);
+ OIDCHttpFacade.Cookie cookie = getCookie(cookieName);
if (cookie == null) return null;
return cookie.getValue();
}
@@ -204,7 +203,7 @@ public class OAuthRequestAuthenticator {
}
protected AuthChallenge checkStateCookie() {
- HttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
+ OIDCHttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
if (stateCookie == null) {
log.warn("No state cookie");
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCHttpFacade.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCHttpFacade.java
new file mode 100755
index 0000000..5b1cadc
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCHttpFacade.java
@@ -0,0 +1,14 @@
+package org.keycloak.adapters;
+
+import org.keycloak.KeycloakSecurityContext;
+
+/**
+ * Bridge between core adapter and HTTP Engine
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface OIDCHttpFacade extends HttpFacade {
+
+ KeycloakSecurityContext getSecurityContext();
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index 04cf79e..daf205a 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -9,11 +9,11 @@ import org.keycloak.KeycloakPrincipal;
*/
public abstract class RequestAuthenticator {
protected static Logger log = Logger.getLogger(RequestAuthenticator.class);
-
protected HttpFacade facade;
+ protected AuthChallenge challenge;
+
protected KeycloakDeployment deployment;
protected AdapterTokenStore tokenStore;
- protected AuthChallenge challenge;
protected int sslRedirectPort;
public RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort) {
integration/adapter-spi/pom.xml 93(+93 -0)
diff --git a/integration/adapter-spi/pom.xml b/integration/adapter-spi/pom.xml
new file mode 100755
index 0000000..1e18e76
--- /dev/null
+++ b/integration/adapter-spi/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <name>Keycloak Adapter SPI</name>
+ <description/>
+
+ <properties>
+ <keycloak.osgi.export>
+ org.keycloak.adapters.*
+ </keycloak.osgi.export>
+ <keycloak.osgi.import>
+ org.keycloak.*;version="${project.version}",
+ org.apache.http.*;version=${apache.httpcomponents.version},
+ org.apache.karaf.jaas.boot.principal;resolution:=optional,
+ org.apache.karaf.jaas.modules;resolution:=optional,
+ *;resolution:=optional
+ </keycloak.osgi.import>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+
+ <!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <id>bundle-manifest</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <instructions>
+ <Bundle-ClassPath>.</Bundle-ClassPath>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>${keycloak.osgi.import}</Import-Package>
+ <Export-Package>${keycloak.osgi.export}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/AdapterSessionStore.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AdapterSessionStore.java
new file mode 100755
index 0000000..8c5f2e2
--- /dev/null
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AdapterSessionStore.java
@@ -0,0 +1,10 @@
+package org.keycloak.adapters;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AdapterSessionStore {
+ void saveRequest();
+ boolean restoreRequest();
+}
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
new file mode 100755
index 0000000..4470c15
--- /dev/null
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
@@ -0,0 +1,69 @@
+package org.keycloak.adapters;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Maps external principal and SSO id to internal local http session id
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class InMemorySessionIdMapper implements SessionIdMapper {
+ ConcurrentHashMap<String, String> ssoToSession = new ConcurrentHashMap<>();
+ ConcurrentHashMap<String, String> sessionToSso = new ConcurrentHashMap<>();
+ ConcurrentHashMap<String, Set<String>> principalToSession = new ConcurrentHashMap<>();
+ ConcurrentHashMap<String, String> sessionToPrincipal = new ConcurrentHashMap<>();
+
+ @Override
+ public Set<String> getUserSessions(String principal) {
+ Set<String> lookup = principalToSession.get(principal);
+ if (lookup == null) return null;
+ Set<String> copy = new HashSet<>();
+ copy.addAll(lookup);
+ return copy;
+ }
+
+ @Override
+ public String getSessionFromSSO(String sso) {
+ return ssoToSession.get(sso);
+ }
+
+ @Override
+ public void map(String sso, String principal, String session) {
+ if (sso != null) {
+ ssoToSession.put(sso, session);
+ sessionToSso.put(session, sso);
+ }
+ Set<String> userSessions = principalToSession.get(principal);
+ if (userSessions == null) {
+ final Set<String> tmp = Collections.synchronizedSet(new HashSet<String>());
+ userSessions = principalToSession.putIfAbsent(principal, tmp);
+ if (userSessions == null) {
+ userSessions = tmp;
+ }
+ }
+ userSessions.add(session);
+ sessionToPrincipal.put(session, principal);
+ }
+
+ @Override
+ public void removeSession(String session) {
+ String sso = sessionToSso.remove(session);
+ if (sso != null) {
+ ssoToSession.remove(sso);
+ }
+ String principal = sessionToPrincipal.remove(session);
+ if (principal != null) {
+ Set<String> sessions = principalToSession.get(principal);
+ sessions.remove(session);
+ if (sessions.isEmpty()) {
+ principalToSession.remove(principal, sessions);
+ }
+ }
+ }
+
+
+}
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
new file mode 100755
index 0000000..646f71b
--- /dev/null
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
@@ -0,0 +1,17 @@
+package org.keycloak.adapters;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface SessionIdMapper {
+ Set<String> getUserSessions(String principal);
+
+ String getSessionFromSSO(String sso);
+
+ void map(String sso, String principal, String session);
+
+ void removeSession(String session);
+}
diff --git a/integration/as7-eap6/as7-adapter/pom.xml b/integration/as7-eap6/as7-adapter/pom.xml
index e13542b..74c35f2 100755
--- a/integration/as7-eap6/as7-adapter/pom.xml
+++ b/integration/as7-eap6/as7-adapter/pom.xml
@@ -20,6 +20,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
integration/installed/pom.xml 4(+4 -0)
diff --git a/integration/installed/pom.xml b/integration/installed/pom.xml
index 379f7b6..c107496 100755
--- a/integration/installed/pom.xml
+++ b/integration/installed/pom.xml
@@ -20,6 +20,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
diff --git a/integration/jaxrs-oauth-client/pom.xml b/integration/jaxrs-oauth-client/pom.xml
index 33630c8..90b00e2 100755
--- a/integration/jaxrs-oauth-client/pom.xml
+++ b/integration/jaxrs-oauth-client/pom.xml
@@ -33,6 +33,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
old mode 100644
new mode 100755
index f29935d..490d42d
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
@@ -11,13 +11,13 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.SecurityContext;
import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.util.HostUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class JaxrsHttpFacade implements HttpFacade {
+public class JaxrsHttpFacade implements OIDCHttpFacade {
protected final ContainerRequestContext requestContext;
protected final SecurityContext securityContext;
@@ -31,7 +31,12 @@ public class JaxrsHttpFacade implements HttpFacade {
this.securityContext = securityContext;
}
- protected class RequestFacade implements HttpFacade.Request {
+ protected class RequestFacade implements OIDCHttpFacade.Request {
+
+ @Override
+ public String getFirstParam(String param) {
+ throw new RuntimeException("NOT IMPLEMENTED");
+ }
@Override
public String getMethod() {
@@ -90,7 +95,7 @@ public class JaxrsHttpFacade implements HttpFacade {
}
}
- protected class ResponseFacade implements HttpFacade.Response {
+ protected class ResponseFacade implements OIDCHttpFacade.Response {
private javax.ws.rs.core.Response.ResponseBuilder responseBuilder = javax.ws.rs.core.Response.status(204);
diff --git a/integration/jboss-adapter-core/pom.xml b/integration/jboss-adapter-core/pom.xml
index 161a8af..559fd8b 100755
--- a/integration/jboss-adapter-core/pom.xml
+++ b/integration/jboss-adapter-core/pom.xml
@@ -26,6 +26,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
integration/jetty/jetty8.1/pom.xml 4(+4 -0)
diff --git a/integration/jetty/jetty8.1/pom.xml b/integration/jetty/jetty8.1/pom.xml
index 81625f4..8b3ac6c 100755
--- a/integration/jetty/jetty8.1/pom.xml
+++ b/integration/jetty/jetty8.1/pom.xml
@@ -40,6 +40,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
</dependency>
<dependency>
diff --git a/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index 243de1c..2bc1d8c 100755
--- a/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
+import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@@ -23,7 +24,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
- return new JettySessionTokenStore(request, resolvedDeployment);
+ return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
diff --git a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index 0d1bf3b..a319266 100755
--- a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
+import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@@ -23,7 +24,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
- return new JettySessionTokenStore(request, resolvedDeployment);
+ return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override
diff --git a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index 90b3c6a..2aec5ef 100755
--- a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
+import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@@ -38,6 +39,6 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
- return new JettySessionTokenStore(request, resolvedDeployment);
+ return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
}
integration/jetty/jetty-adapter-spi/pom.xml 115(+115 -0)
diff --git a/integration/jetty/jetty-adapter-spi/pom.xml b/integration/jetty/jetty-adapter-spi/pom.xml
new file mode 100755
index 0000000..714de2b
--- /dev/null
+++ b/integration/jetty/jetty-adapter-spi/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-jetty-adapter-spi</artifactId>
+ <name>Keycloak Jetty Adapter SPI</name>
+ <properties>
+ <jetty9.version>8.1.16.v20140903</jetty9.version>
+ <keycloak.osgi.export>
+ org.keycloak.adapters.jetty.core.*
+ </keycloak.osgi.export>
+ <keycloak.osgi.import>
+ org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
+ javax.servlet.*;version="[2.5,4)";resolution:=optional,
+ org.keycloak.*;version="${project.version}",
+ *;resolution:=optional
+ </keycloak.osgi.import>
+ </properties>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty9.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty9.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>${jetty9.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+
+ <!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <id>bundle-manifest</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <instructions>
+ <Bundle-ClassPath>.</Bundle-ClassPath>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>${keycloak.osgi.import}</Import-Package>
+ <Export-Package>${keycloak.osgi.export}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
integration/jetty/jetty-core/pom.xml 8(+8 -0)
diff --git a/integration/jetty/jetty-core/pom.xml b/integration/jetty/jetty-core/pom.xml
index 93504df..4628c6b 100755
--- a/integration/jetty/jetty-core/pom.xml
+++ b/integration/jetty/jetty-core/pom.xml
@@ -35,6 +35,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-jetty-adapter-spi</artifactId>
+ </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
index bde5ad0..c11f30a 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
@@ -92,7 +92,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getAttribute(AdapterDeploymentContext.class.getName());
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
- JettyHttpFacade facade = new JettyHttpFacade(request, null);
+ JettyHttpFacade facade = new OIDCJettyHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@@ -229,7 +229,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
log.trace("*** authenticate");
}
Request request = resolveRequest(req);
- JettyHttpFacade facade = new JettyHttpFacade(request, (HttpServletResponse) res);
+ OIDCJettyHttpFacade facade = new OIDCJettyHttpFacade(request, (HttpServletResponse) res);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
log.debug("*** deployment isn't configured return false");
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/OIDCJettyHttpFacade.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/OIDCJettyHttpFacade.java
new file mode 100755
index 0000000..3f6bd0b
--- /dev/null
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/OIDCJettyHttpFacade.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters.jetty.core;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCJettyHttpFacade extends JettyHttpFacade implements OIDCHttpFacade {
+
+ public OIDCJettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
+ super(request, response);
+ }
+
+ @Override
+ public KeycloakSecurityContext getSecurityContext() {
+ return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
+ }
+
+}
integration/jetty/pom.xml 1(+1 -0)
diff --git a/integration/jetty/pom.xml b/integration/jetty/pom.xml
index 8388996..3219087 100755
--- a/integration/jetty/pom.xml
+++ b/integration/jetty/pom.xml
@@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
+ <module>jetty-adapter-spi</module>
<module>jetty-core</module>
<module>jetty8.1</module>
<module>jetty9.2</module>
integration/pom.xml 2(+2 -0)
diff --git a/integration/pom.xml b/integration/pom.xml
index 3327b6d..e4b7f27 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
+ <module>adapter-spi</module>
<module>adapter-core</module>
<module>jaxrs-oauth-client</module>
<module>servlet-oauth-client</module>
@@ -21,6 +22,7 @@
<module>tomcat</module>
<module>as7-eap6</module>
<module>jetty</module>
+ <module>undertow-adapter-spi</module>
<module>undertow</module>
<module>wildfly</module>
<module>js</module>
diff --git a/integration/servlet-oauth-client/pom.xml b/integration/servlet-oauth-client/pom.xml
index b5f5d47..721c3bd 100755
--- a/integration/servlet-oauth-client/pom.xml
+++ b/integration/servlet-oauth-client/pom.xml
@@ -26,6 +26,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
index 72501ac..ed8744e 100755
--- a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
+++ b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java
@@ -3,15 +3,13 @@ package org.keycloak.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterDeploymentContext;
-import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.ServerRequest;
-import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.util.KeycloakUriBuilder;
-import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.Cookie;
@@ -167,7 +165,7 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
}
- public static class ServletFacade implements HttpFacade {
+ public static class ServletFacade implements OIDCHttpFacade {
private final HttpServletRequest servletRequest;
@@ -185,6 +183,11 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
return new Request() {
@Override
+ public String getFirstParam(String param) {
+ return servletRequest.getParameter(param);
+ }
+
+ @Override
public String getMethod() {
return servletRequest.getMethod();
}
diff --git a/integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java b/integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java
old mode 100644
new mode 100755
index c5834c2..6a37add
--- a/integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java
+++ b/integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java
@@ -1,8 +1,8 @@
package org.keycloak.adapters.springboot;
-import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.representations.adapters.config.AdapterConfig;
public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
@@ -12,7 +12,7 @@ public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.K
private static AdapterConfig adapterConfig;
@Override
- public KeycloakDeployment resolve(HttpFacade.Request request) {
+ public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) {
return keycloakDeployment;
}
integration/spring-security/pom.xml 4(+4 -0)
diff --git a/integration/spring-security/pom.xml b/integration/spring-security/pom.xml
index 718f342..17783fd 100755
--- a/integration/spring-security/pom.xml
+++ b/integration/spring-security/pom.xml
@@ -28,6 +28,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java
old mode 100644
new mode 100755
index 586dcba..50d0668
--- a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java
+++ b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java
@@ -1,4 +1,4 @@
/**
- * Provides an {@link org.keycloak.adapters.HttpFacade} implementation.
+ * Provides an {@link org.keycloak.adapters.OIDCHttpFacade} implementation.
*/
package org.keycloak.adapters.springsecurity.facade;
\ No newline at end of file
diff --git a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java
old mode 100644
new mode 100755
index 8c7a5b0..b8d7800
--- a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java
+++ b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java
@@ -1,9 +1,7 @@
package org.keycloak.adapters.springsecurity.facade;
import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.adapters.HttpFacade;
-import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletRequest;
-import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletResponse;
+import org.keycloak.adapters.OIDCHttpFacade;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
@@ -13,12 +11,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
- * Simple {@link HttpFacade} wrapping an {@link HttpServletRequest} and {@link HttpServletResponse}.
+ * Simple {@link org.keycloak.adapters.OIDCHttpFacade} wrapping an {@link HttpServletRequest} and {@link HttpServletResponse}.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
* @version $Revision: 1 $
*/
-public class SimpleHttpFacade implements HttpFacade {
+public class SimpleHttpFacade implements OIDCHttpFacade {
private final HttpServletRequest request;
private final HttpServletResponse response;
diff --git a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java
old mode 100644
new mode 100755
index 478f182..e2e5ba4
--- a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java
+++ b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/WrappedHttpServletRequest.java
@@ -33,6 +33,11 @@ class WrappedHttpServletRequest implements Request {
}
@Override
+ public String getFirstParam(String param) {
+ return request.getParameter(param);
+ }
+
+ @Override
public String getMethod() {
return request.getMethod();
}
diff --git a/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/token/SpringSecurityAdapterTokenStoreFactoryTest.java b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/token/SpringSecurityAdapterTokenStoreFactoryTest.java
old mode 100644
new mode 100755
index 602f8ca..a5f5b07
--- a/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/token/SpringSecurityAdapterTokenStoreFactoryTest.java
+++ b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/token/SpringSecurityAdapterTokenStoreFactoryTest.java
@@ -2,10 +2,9 @@ package org.keycloak.adapters.springsecurity.token;
import org.junit.Before;
import org.junit.Test;
-import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import javax.servlet.http.HttpServletRequest;
@@ -32,7 +31,7 @@ public class SpringSecurityAdapterTokenStoreFactoryTest {
@Test
public void testCreateAdapterTokenStore() throws Exception {
- AdapterTokenStore store = factory.createAdapterTokenStore(deployment, request);
+ AdapterSessionStore store = factory.createAdapterTokenStore(deployment, request);
assertNotNull(store);
assertTrue(store instanceof SpringSecurityTokenStore);
}
integration/tomcat/pom.xml 1(+1 -0)
diff --git a/integration/tomcat/pom.xml b/integration/tomcat/pom.xml
index c5808bf..23f457e 100755
--- a/integration/tomcat/pom.xml
+++ b/integration/tomcat/pom.xml
@@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
+ <module>tomcat-adapter-spi</module>
<module>tomcat-core</module>
<module>tomcat6</module>
<module>tomcat7</module>
diff --git a/integration/tomcat/tomcat-adapter-spi/pom.xml b/integration/tomcat/tomcat-adapter-spi/pom.xml
new file mode 100755
index 0000000..eb70711
--- /dev/null
+++ b/integration/tomcat/tomcat-adapter-spi/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-tomcat-adapter-spi</artifactId>
+ <name>Keycloak Tomcat Adapter SPI</name>
+ <properties>
+ <!-- <tomcat.version>8.0.14</tomcat.version> -->
+ <!-- <tomcat.version>7.0.52</tomcat.version> -->
+ <tomcat.version>6.0.41</tomcat.version>
+ </properties>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <!--
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-servlet-api</artifactId>
+ <version>${tomcat.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>catalina</artifactId>
+ <version>${tomcat.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/integration/tomcat/tomcat-core/pom.xml b/integration/tomcat/tomcat-core/pom.xml
index 4271b7f..25c7b6b 100755
--- a/integration/tomcat/tomcat-core/pom.xml
+++ b/integration/tomcat/tomcat-core/pom.xml
@@ -28,6 +28,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-tomcat-adapter-spi</artifactId>
+ </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
index 80663c8..3347978 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
@@ -5,13 +5,9 @@ import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
-import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
-import org.apache.catalina.authenticator.SavedRequest;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
-import org.apache.catalina.deploy.LoginConfig;
-import org.apache.tomcat.util.buf.ByteChunk;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.adapters.AdapterDeploymentContext;
@@ -28,15 +24,12 @@ import org.keycloak.enums.TokenStore;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Enumeration;
-import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
@@ -71,7 +64,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected void logoutInternal(Request request) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
- CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
+ CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@@ -164,7 +157,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
- CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
+ CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
Manager sessionManager = request.getContext().getManager();
CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(sessionManagementWrapper, deploymentContext, facade);
@@ -181,7 +174,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException;
protected boolean authenticateInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
- CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
+ CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
return false;
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AuthenticatedActionsValve.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AuthenticatedActionsValve.java
index 8728f36..6b42b85 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AuthenticatedActionsValve.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AuthenticatedActionsValve.java
@@ -39,10 +39,10 @@ public class AuthenticatedActionsValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
- CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
+ CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) {
- AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, new CatalinaHttpFacade(request, response));
+ AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, new OIDCCatalinaHttpFacade(request, response));
if (handler.handledRequest()) {
return;
}
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaAdapterSessionStore.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaAdapterSessionStore.java
new file mode 100755
index 0000000..f1ffbf0
--- /dev/null
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaAdapterSessionStore.java
@@ -0,0 +1,32 @@
+package org.keycloak.adapters.tomcat;
+
+import org.apache.catalina.connector.Request;
+import org.keycloak.adapters.AdapterSessionStore;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CatalinaAdapterSessionStore implements AdapterSessionStore {
+ protected Request request;
+ protected AbstractKeycloakAuthenticatorValve valve;
+
+ public CatalinaAdapterSessionStore(Request request, AbstractKeycloakAuthenticatorValve valve) {
+ this.request = request;
+ this.valve = valve;
+ }
+
+ public void saveRequest() {
+ try {
+ valve.keycloakSaveRequest(request);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean restoreRequest() {
+ return valve.keycloakRestoreRequest(request);
+ }
+}
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
index a91ede0..e0d3ca6 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
@@ -19,26 +19,23 @@ import java.util.logging.Logger;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class CatalinaSessionTokenStore implements AdapterTokenStore {
+public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore implements AdapterTokenStore {
private static final Logger log = Logger.getLogger("" + CatalinaSessionTokenStore.class);
- private Request request;
private KeycloakDeployment deployment;
private CatalinaUserSessionManagement sessionManagement;
protected GenericPrincipalFactory principalFactory;
- protected AbstractKeycloakAuthenticatorValve valve;
public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment,
CatalinaUserSessionManagement sessionManagement,
GenericPrincipalFactory principalFactory,
AbstractKeycloakAuthenticatorValve valve) {
- this.request = request;
+ super(request, valve);
this.deployment = deployment;
this.sessionManagement = sessionManagement;
this.principalFactory = principalFactory;
- this.valve = valve;
}
@Override
@@ -169,17 +166,4 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
// no-op
}
- @Override
- public void saveRequest() {
- try {
- valve.keycloakSaveRequest(request);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public boolean restoreRequest() {
- return valve.keycloakRestoreRequest(request);
- }
}
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/OIDCCatalinaHttpFacade.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/OIDCCatalinaHttpFacade.java
new file mode 100755
index 0000000..b77337e
--- /dev/null
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/OIDCCatalinaHttpFacade.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters.tomcat;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCCatalinaHttpFacade extends CatalinaHttpFacade implements OIDCHttpFacade{
+
+ public OIDCCatalinaHttpFacade(org.apache.catalina.connector.Request request, HttpServletResponse response) {
+ super(response, request);
+ }
+
+ @Override
+ public KeycloakSecurityContext getSecurityContext() {
+ return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
+ }
+
+}
integration/undertow/pom.xml 8(+8 -0)
diff --git a/integration/undertow/pom.xml b/integration/undertow/pom.xml
index 7a1d7b9..4dc9dfb 100755
--- a/integration/undertow/pom.xml
+++ b/integration/undertow/pom.xml
@@ -26,6 +26,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java
index 892343b..790078f 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java
@@ -21,18 +21,14 @@ import io.undertow.security.api.NotificationReceiver;
import io.undertow.security.api.SecurityContext;
import io.undertow.security.api.SecurityNotification;
import io.undertow.server.HttpServerExchange;
-import io.undertow.server.session.Session;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers;
-import io.undertow.util.Sessions;
import io.undertow.util.StatusCodes;
-import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.AuthOutcome;
-import org.keycloak.adapters.CookieTokenStore;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
@@ -93,9 +89,9 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
HttpServerExchange exchange = notification.getExchange();
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
- KeycloakSecurityContext ksc = exchange.getAttachment(UndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
+ KeycloakSecurityContext ksc = exchange.getAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
if (ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
index f0e467b..84c5fdd 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
@@ -48,7 +48,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
}
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
- exchange.putAttachment(UndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
+ exchange.putAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
}
@Override
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCUndertowHttpFacade.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCUndertowHttpFacade.java
new file mode 100755
index 0000000..9063399
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OIDCUndertowHttpFacade.java
@@ -0,0 +1,40 @@
+/*
+ * 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.undertow;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.AttachmentKey;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCUndertowHttpFacade extends UndertowHttpFacade implements OIDCHttpFacade {
+ public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
+
+ public OIDCUndertowHttpFacade(HttpServerExchange exchange) {
+ super(exchange);
+ }
+
+ @Override
+ public KeycloakSecurityContext getSecurityContext() {
+ return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
+ }
+
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
index f4b6ef3..bfa2cb3 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
@@ -16,7 +16,6 @@
*/
package org.keycloak.adapters.undertow;
-import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
@@ -25,7 +24,6 @@ import io.undertow.util.Headers;
import org.jboss.logging.Logger;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
-import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement;
@@ -81,7 +79,7 @@ public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
@Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPreAuthActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPreAuthActionsHandler.java
index 61e66c4..362cdb0 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPreAuthActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPreAuthActionsHandler.java
@@ -61,7 +61,7 @@ public class ServletPreAuthActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager());
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticatedActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticatedActionsHandler.java
index a21272e..4e7590f 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticatedActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticatedActionsHandler.java
@@ -57,7 +57,7 @@ public class UndertowAuthenticatedActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) {
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, facade);
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticationMechanism.java
index b55067b..85b7581 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticationMechanism.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowAuthenticationMechanism.java
@@ -25,7 +25,7 @@ public class UndertowAuthenticationMechanism extends AbstractUndertowKeycloakAut
@Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowPreAuthActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowPreAuthActionsHandler.java
index a68f39f..77194e1 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowPreAuthActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowPreAuthActionsHandler.java
@@ -47,7 +47,7 @@ public class UndertowPreAuthActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
- UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+ UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
if (handler.handleRequest()) return;
integration/undertow-adapter-spi/pom.xml 65(+65 -0)
diff --git a/integration/undertow-adapter-spi/pom.xml b/integration/undertow-adapter-spi/pom.xml
new file mode 100755
index 0000000..91a649d
--- /dev/null
+++ b/integration/undertow-adapter-spi/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ <name>Keycloak Undertow Integration</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/ServletHttpFacade.java b/integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/ServletHttpFacade.java
new file mode 100755
index 0000000..60bfaa4
--- /dev/null
+++ b/integration/undertow-adapter-spi/src/main/java/org/keycloak/adapters/undertow/ServletHttpFacade.java
@@ -0,0 +1,39 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import org.keycloak.adapters.HttpFacade;
+
+import javax.security.cert.X509Certificate;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletHttpFacade extends UndertowHttpFacade {
+ protected HttpServletRequest request;
+ protected HttpServletResponse response;
+
+ public ServletHttpFacade(HttpServerExchange exchange) {
+ super(exchange);
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ request = (HttpServletRequest)servletRequestContext.getServletRequest();
+ }
+
+ protected class RequestFacade extends UndertowHttpFacade.RequestFacade {
+ @Override
+ public String getFirstParam(String param) {
+ return request.getParameter(param);
+ }
+
+ }
+
+ @Override
+ public Request getRequest() {
+ return new RequestFacade();
+ }
+}
diff --git a/integration/wildfly/wildfly-adapter/pom.xml b/integration/wildfly/wildfly-adapter/pom.xml
index 3685b1b..e3803dc 100755
--- a/integration/wildfly/wildfly-adapter/pom.xml
+++ b/integration/wildfly/wildfly-adapter/pom.xml
@@ -26,10 +26,18 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
</dependency>
<dependency>
pom.xml 30(+30 -0)
diff --git a/pom.xml b/pom.xml
index bf0e5d6..1b1d323 100755
--- a/pom.xml
+++ b/pom.xml
@@ -756,6 +756,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
@@ -786,6 +791,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-jetty-adapter-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
<version>${project.version}</version>
</dependency>
@@ -856,6 +866,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-tomcat-adapter-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat-core-adapter</artifactId>
<version>${project.version}</version>
</dependency>
@@ -876,6 +891,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
<version>${project.version}</version>
</dependency>
@@ -956,6 +976,16 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-saml-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
</dependency>
saml/client-adapter/core/pom.xml 60(+60 -0)
diff --git a/saml/client-adapter/core/pom.xml b/saml/client-adapter/core/pom.xml
new file mode 100755
index 0000000..605d87f
--- /dev/null
+++ b/saml/client-adapter/core/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ <name>Keycloak SAML Client Adapter Core</name>
+ <description/>
+
+ <properties>
+ <timestamp>${maven.build.timestamp}</timestamp>
+ <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
new file mode 100755
index 0000000..84764e3
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
@@ -0,0 +1,172 @@
+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 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 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 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 getRequestBinding() {
+ return requestBinding;
+ }
+
+ public void setRequestBinding(String requestBinding) {
+ this.requestBinding = requestBinding;
+ }
+
+ public String getResponseBinding() {
+ return responseBinding;
+ }
+
+ public void setResponseBinding(String responseBinding) {
+ this.responseBinding = responseBinding;
+ }
+
+ public String getPostBindingUrl() {
+ return postBindingUrl;
+ }
+
+ public void setPostBindingUrl(String postBindingUrl) {
+ this.postBindingUrl = postBindingUrl;
+ }
+
+ public String getRedirectBindingUrl() {
+ return redirectBindingUrl;
+ }
+
+ public void setRedirectBindingUrl(String redirectBindingUrl) {
+ this.redirectBindingUrl = redirectBindingUrl;
+ }
+ }
+
+ private String entityID;
+ private SingleSignOnService singleSignOnService;
+ private SingleLogoutService singleLogoutService;
+ private List<Key> keys;
+
+ public String getEntityID() {
+ return entityID;
+ }
+
+ public void setEntityID(String entityID) {
+ this.entityID = entityID;
+ }
+
+ public SingleSignOnService getSingleSignOnService() {
+ return singleSignOnService;
+ }
+
+ public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
+ this.singleSignOnService = singleSignOnService;
+ }
+
+ public SingleLogoutService getSingleLogoutService() {
+ return singleLogoutService;
+ }
+
+ public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
+ this.singleLogoutService = singleLogoutService;
+ }
+
+ public List<Key> getKeys() {
+ return keys;
+ }
+
+ public void setKeys(List<Key> keys) {
+ this.keys = keys;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java
new file mode 100755
index 0000000..3d94f78
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/Key.java
@@ -0,0 +1,143 @@
+package org.keycloak.adapters.saml.config;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Key implements Serializable {
+
+ public static class KeyStoreConfig implements Serializable {
+ private String file;
+ private String resource;
+ private String password;
+ private String type;
+ private String alias;
+ private String privateKeyAlias;
+ private String privateKeyPassword;
+ private String certificateAlias;
+
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPrivateKeyAlias() {
+ return privateKeyAlias;
+ }
+
+ public void setPrivateKeyAlias(String privateKeyAlias) {
+ this.privateKeyAlias = privateKeyAlias;
+ }
+
+ public String getPrivateKeyPassword() {
+ return privateKeyPassword;
+ }
+
+ public void setPrivateKeyPassword(String privateKeyPassword) {
+ this.privateKeyPassword = privateKeyPassword;
+ }
+
+ public String getCertificateAlias() {
+ return certificateAlias;
+ }
+
+ public void setCertificateAlias(String certificateAlias) {
+ this.certificateAlias = certificateAlias;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+ }
+
+
+ private boolean signing;
+ private boolean encryption;
+ private KeyStoreConfig keystore;
+ private String privateKeyPem;
+ private String publicKeyPem;
+ private String certificatePem;
+
+
+ public boolean isSigning() {
+ return signing;
+ }
+
+ public void setSigning(boolean signing) {
+ this.signing = signing;
+ }
+
+ public boolean isEncryption() {
+ return encryption;
+ }
+
+ public void setEncryption(boolean encryption) {
+ this.encryption = encryption;
+ }
+
+ public KeyStoreConfig getKeystore() {
+ return keystore;
+ }
+
+ public void setKeystore(KeyStoreConfig keystore) {
+ this.keystore = keystore;
+ }
+
+ public String getPrivateKeyPem() {
+ return privateKeyPem;
+ }
+
+ public void setPrivateKeyPem(String privateKeyPem) {
+ this.privateKeyPem = privateKeyPem;
+ }
+
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+
+ public String getCertificatePem() {
+ return certificatePem;
+ }
+
+ public void setCertificatePem(String certificatePem) {
+ this.certificatePem = certificatePem;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java
new file mode 100755
index 0000000..9370dba
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java
@@ -0,0 +1,21 @@
+package org.keycloak.adapters.saml.config;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlAdapter implements Serializable {
+ private List<SP> sps = new LinkedList<>();
+
+ public List<SP> getSps() {
+ return sps;
+ }
+
+ public void setSps(List<SP> sps) {
+ this.sps = sps;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
new file mode 100755
index 0000000..0e0542c
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -0,0 +1,57 @@
+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 SIGNATURE_ALGORITHM_ATTR = "signatureAlgorithm";
+ public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
+ 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 REQUEST_BINDING_ATTR = "requestBinding";
+ public static final String RESPONSE_BINDING_ATTR = "responseBinding";
+ public static final String BINDING_URL_ATTR = "bindingUrl";
+ public static final String VALIDATE_RESPONSE_SIGNATURE_ATTR = "validateResponseSignature";
+ public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature";
+ public static final String POST_BINDING_URL_ATTR = "postBindingUrl";
+ public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl";
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
new file mode 100755
index 0000000..b72cb41
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -0,0 +1,206 @@
+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.SignatureAlgorithm;
+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());
+ deployment.setSignatureCanonicalizationMethod(sp.getSignatureCanonicalizationMethod());
+ deployment.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
+ if (sp.getSignatureAlgorithm() != null) {
+ deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(sp.getSignatureAlgorithm()));
+ }
+ 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);
+ }
+ if (sp.getKeys() != null) {
+ 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);
+
+ }
+ 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());
+ if (sp.getIdp().getSingleSignOnService().getResponseBinding() != null) {
+ sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
+ }
+ 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.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());
+ }
+ if (sp.getIdp().getKeys() != null) {
+ for (Key key : sp.getIdp().getKeys()) {
+ if (key.isSigning()) {
+ if (key.getKeystore() != null) {
+ KeyStore keyStore = loadKeystore(resourceLoader, key);
+ Certificate cert = null;
+ try {
+ cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
+ } catch (KeyStoreException e) {
+ throw new RuntimeException(e);
+ }
+ idp.setSignatureValidationKey(cert.getPublicKey());
+ } else {
+ if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
+ throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
+ }
+ try {
+ PublicKey publicKey = getPublicKeyFromPem(key);
+ idp.setSignatureValidationKey(publicKey);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+
+ return deployment;
+ }
+
+ protected static PublicKey getPublicKeyFromPem(Key key) throws Exception {
+ PublicKey publicKey;
+ if (key.getPublicKeyPem() != null) {
+ publicKey = PemUtils.decodePublicKey(key.getPublicKeyPem().trim());
+ } else {
+ Certificate cert = PemUtils.decodeCertificate(key.getCertificatePem().trim());
+ publicKey = cert.getPublicKey();
+ }
+ return publicKey;
+ }
+
+ protected static KeyStore loadKeystore(ResourceLoader resourceLoader, Key key) {
+ String type = key.getKeystore().getType();
+ if (type == null) type = "JKS";
+ KeyStore keyStore = null;
+ try {
+ keyStore = KeyStore.getInstance(type);
+ } catch (KeyStoreException e) {
+ throw new RuntimeException(e);
+ }
+ InputStream is = null;
+ if (key.getKeystore().getFile() != null) {
+ File fp = new File(key.getKeystore().getFile());
+ if (!fp.exists()) {
+ }
+ try {
+ is = new FileInputStream(fp);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("KeyStore " + key.getKeystore().getFile() + " does not exist");
+ }
+
+ } else {
+ is = resourceLoader.getResourceAsStream(key.getKeystore().getResource());
+ if (is == null) {
+ throw new RuntimeException("KeyStore " + key.getKeystore().getResource() + " does not exist");
+ }
+ }
+ try {
+ keyStore.load(is, key.getKeystore().getPassword().toCharArray());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return keyStore;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
new file mode 100755
index 0000000..96485d5
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -0,0 +1,98 @@
+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.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.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+ return sso;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java
new file mode 100755
index 0000000..d5b9ee6
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java
@@ -0,0 +1,46 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.StartElement;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlAdapterXMLParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ KeycloakSamlAdapter adapter = new KeycloakSamlAdapter();
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYCLOAK_SAML_ADAPTER);
+ while (xmlEventReader.hasNext()) {
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) {
+ SPXmlParser parser = new SPXmlParser();
+ SP sp = (SP)parser.parse(xmlEventReader);
+ if (sp != null) adapter.getSps().add(sp);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return adapter;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java
new file mode 100755
index 0000000..a63a6ac
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java
@@ -0,0 +1,60 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeysXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_ELEMENT);
+ List<Key> keys = new LinkedList<>();
+ while (xmlEventReader.hasNext()) {
+ XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+ if (xmlEvent == null)
+ break;
+ if (xmlEvent instanceof EndElement) {
+ EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+ String endElementName = StaxParserUtil.getEndElementName(endElement);
+ if (endElementName.equals(ConfigXmlConstants.KEYS_ELEMENT))
+ break;
+ else
+ throw logger.parserUnknownEndElement(endElementName);
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) {
+ KeyXmlParser parser = new KeyXmlParser();
+ Key key = (Key)parser.parse(xmlEventReader);
+ keys.add(key);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return keys;
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
new file mode 100755
index 0000000..787af69
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
@@ -0,0 +1,128 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeyXmlParser extends AbstractParser {
+
+ @Override
+ public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
+ Key key = new Key();
+ key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
+ key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
+ while (xmlEventReader.hasNext()) {
+ XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+ if (xmlEvent == null)
+ break;
+ if (xmlEvent instanceof EndElement) {
+ EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+ String endElementName = StaxParserUtil.getEndElementName(endElement);
+ if (endElementName.equals(ConfigXmlConstants.KEY_ELEMENT))
+ break;
+ else
+ throw logger.parserUnknownEndElement(endElementName);
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) {
+ key.setKeystore(parseKeyStore(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return key;
+ }
+
+ protected Key.KeyStoreConfig parseKeyStore(XMLEventReader xmlEventReader) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
+ Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
+ keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
+ keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
+ keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
+ if (keyStore.getFile() == null && keyStore.getResource() == null) {
+ throw new ParsingException("KeyStore element must have the url or classpath attribute set");
+ }
+ keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
+ if (keyStore.getPassword() == null) {
+ throw new ParsingException("KeyStore element must have the password attribute set");
+ }
+
+
+
+ while (xmlEventReader.hasNext()) {
+ XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+ if (xmlEvent == null)
+ break;
+ if (xmlEvent instanceof EndElement) {
+ EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+ String endElementName = StaxParserUtil.getEndElementName(endElement);
+ if (endElementName.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ if (keyStore.getCertificateAlias() == null) {
+ throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
+
+ }
+ } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ if (keyStore.getPrivateKeyAlias() == null) {
+ throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
+
+ }
+ keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
+ if (keyStore.getPrivateKeyPassword() == null) {
+ throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
+
+ }
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return keyStore;
+
+ }
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java
new file mode 100755
index 0000000..296f31e
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ResourceLoader.java
@@ -0,0 +1,11 @@
+package org.keycloak.adapters.saml.config.parsers;
+
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ResourceLoader {
+ InputStream getResourceAsStream(String resource);
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
new file mode 100755
index 0000000..089dc78
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -0,0 +1,141 @@
+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.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ sp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
+ sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
+ while (xmlEventReader.hasNext()) {
+ XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+ if (xmlEvent == null)
+ break;
+ if (xmlEvent instanceof EndElement) {
+ EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+ String endElementName = StaxParserUtil.getEndElementName(endElement);
+ if (endElementName.equals(ConfigXmlConstants.SP_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
+ KeysXmlParser parser = new KeysXmlParser();
+ List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
+ sp.setKeys(keys);
+ } else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
+ if (policy == null) {
+ throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
+
+ }
+ String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
+ SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
+ mapping.setPolicy(policy);
+ mapping.setAttributeName(attribute);
+ sp.setPrincipalNameMapping(mapping);
+
+ } else if (tag.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT)) {
+ parseRoleMapping(xmlEventReader, sp);
+ } else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
+ IDPXmlParser parser = new IDPXmlParser();
+ IDP idp = (IDP)parser.parse(xmlEventReader);
+ sp.setIdp(idp);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ return sp;
+ }
+
+ protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
+ StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
+ Set<String> roleAttributes = new HashSet<>();
+ Set<String> roleFriendlyAttributes = new HashSet<>();
+ while (xmlEventReader.hasNext()) {
+ XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
+ if (xmlEvent == null)
+ break;
+ if (xmlEvent instanceof EndElement) {
+ EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
+ String endElementName = StaxParserUtil.getEndElementName(endElement);
+ if (endElementName.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT))
+ break;
+ else
+ continue;
+ }
+ startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
+ if (startElement == null)
+ break;
+ String tag = StaxParserUtil.getStartElementName(startElement);
+ if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ if (attributeValue == null) {
+ throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
+
+ }
+ roleAttributes.add(attributeValue);
+ } else if (tag.equals(ConfigXmlConstants.FRIENDLY_ATTRIBUTE_ELEMENT)) {
+ StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
+ String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ if (attributeValue == null) {
+ throw new ParsingException("RoleMapping FriendlyAttribute element must have the name attribute set");
+
+ }
+ roleFriendlyAttributes.add(attributeValue);
+ } else {
+ StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
+ }
+
+ }
+ sp.setRoleAttributes(roleAttributes);
+ sp.setRoleFriendlyAttributes(roleFriendlyAttributes);
+ }
+
+
+ @Override
+ public boolean supports(QName qname) {
+ return false;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
new file mode 100755
index 0000000..bd48ba1
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
@@ -0,0 +1,144 @@
+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 String signatureAlgorithm;
+ private String signatureCanonicalizationMethod;
+ 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;
+ }
+
+ public String getSignatureAlgorithm() {
+ return signatureAlgorithm;
+ }
+
+ public void setSignatureAlgorithm(String signatureAlgorithm) {
+ this.signatureAlgorithm = signatureAlgorithm;
+ }
+
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
new file mode 100755
index 0000000..28a4ead
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -0,0 +1,364 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.enums.SslRequired;
+import org.keycloak.saml.SignatureAlgorithm;
+
+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 Binding requestBinding;
+ private Binding responseBinding;
+ private String requestBindingUrl;
+
+ @Override
+ public boolean signRequest() {
+ return signRequest;
+ }
+
+ @Override
+ public boolean validateResponseSignature() {
+ return validateResponseSignature;
+ }
+
+ @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 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 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 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 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;
+ private SignatureAlgorithm signatureAlgorithm;
+ private String signatureCanonicalizationMethod;
+
+
+ @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;
+ }
+
+ @Override
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
+ @Override
+ public SignatureAlgorithm getSignatureAlgorithm() {
+ return signatureAlgorithm;
+ }
+
+ public void setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
+ this.signatureAlgorithm = signatureAlgorithm;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
new file mode 100755
index 0000000..1cef2b1
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
@@ -0,0 +1,91 @@
+package org.keycloak.adapters.saml;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2AuthnRequestBuilder;
+import org.keycloak.saml.SAML2NameIDPolicyBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.w3c.dom.Document;
+
+import java.security.KeyPair;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class InitiateLogin implements AuthChallenge {
+ protected static Logger log = Logger.getLogger(InitiateLogin.class);
+
+ protected SamlDeployment deployment;
+ protected SamlSessionStore sessionStore;
+
+ public InitiateLogin(SamlDeployment deployment, SamlSessionStore sessionStore) {
+ this.deployment = deployment;
+ this.sessionStore = sessionStore;
+ }
+
+ @Override
+ public boolean errorPage() {
+ return true;
+ }
+
+ @Override
+ public boolean challenge(HttpFacade httpFacade) {
+ try {
+ String issuerURL = deployment.getEntityID();
+ String actionUrl = deployment.getIDP().getSingleSignOnService().getRequestBindingUrl();
+ String destinationUrl = actionUrl;
+ String nameIDPolicyFormat = deployment.getNameIDPolicyFormat();
+
+ if (nameIDPolicyFormat == null) {
+ nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
+ }
+
+
+
+ SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
+ .destination(destinationUrl)
+ .issuer(issuerURL)
+ .forceAuthn(deployment.isForceAuthentication())
+ .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
+ if (deployment.getIDP().getSingleSignOnService().getResponseBinding() != null) {
+ String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
+ if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
+ protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
+ }
+ authnRequestBuilder.protocolBinding(protocolBinding);
+
+ }
+ if (deployment.getAssertionConsumerServiceUrl() != null) {
+ authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
+ }
+ BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
+
+ if (deployment.getIDP().getSingleSignOnService().signRequest()) {
+
+
+ KeyPair keypair = deployment.getSigningKeyPair();
+ if (keypair == null) {
+ throw new RuntimeException("Signing keys not configured");
+ }
+ if (deployment.getSignatureCanonicalizationMethod() != null) {
+ binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
+ }
+
+ binding.signWith(keypair);
+ binding.signDocument();
+ }
+ sessionStore.saveRequest();
+
+ Document document = authnRequestBuilder.toDocument();
+ SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
+ SamlUtil.sendSaml(true, httpFacade, actionUrl, binding, document, samlBinding);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create authentication request.", e);
+ }
+ return true;
+ }
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
new file mode 100755
index 0000000..ade3e47
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -0,0 +1,458 @@
+package org.keycloak.adapters.saml;
+
+import org.jboss.logging.Logger;
+import org.keycloak.VerificationException;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.HttpFacade;
+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.assertion.AuthnStatementType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
+import org.keycloak.dom.saml.v2.assertion.SubjectType;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2LogoutRequestBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.SAMLRequestParser;
+import org.keycloak.saml.SignatureAlgorithm;
+import org.keycloak.saml.common.constants.GeneralConstants;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.common.util.Base64;
+import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
+import org.keycloak.saml.processing.web.util.PostBindingUtil;
+import org.keycloak.util.KeycloakUriBuilder;
+import org.keycloak.util.MultivaluedHashMap;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class SamlAuthenticator {
+ protected static Logger log = Logger.getLogger(SamlAuthenticator.class);
+
+ protected HttpFacade facade;
+ protected AuthChallenge challenge;
+ protected SamlDeployment deployment;
+ protected SamlSessionStore sessionStore;
+
+ public SamlAuthenticator(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+ this.facade = facade;
+ this.deployment = deployment;
+ this.sessionStore = sessionStore;
+ }
+
+ public AuthChallenge getChallenge() {
+ return challenge;
+ }
+
+ public AuthOutcome authenticate() {
+
+
+ String samlRequest = facade.getRequest().getFirstParam(GeneralConstants.SAML_REQUEST_KEY);
+ String samlResponse = facade.getRequest().getFirstParam(GeneralConstants.SAML_RESPONSE_KEY);
+ String relayState = facade.getRequest().getFirstParam(GeneralConstants.RELAY_STATE);
+ boolean globalLogout = "true".equals(facade.getRequest().getQueryParamValue("GLO"));
+ if (samlRequest != null) {
+ return handleSamlRequest(samlRequest, relayState);
+ } else if (samlResponse != null) {
+ return handleSamlResponse(samlResponse, relayState);
+ } else if (sessionStore.isLoggedIn()) {
+ if (globalLogout) {
+ return globalLogout();
+ }
+ if (verifySSL()) return AuthOutcome.FAILED;
+ log.debug("AUTHENTICATED: was cached");
+ return AuthOutcome.AUTHENTICATED;
+ }
+ return initiateLogin();
+ }
+
+ protected AuthOutcome globalLogout() {
+ SamlSession account = sessionStore.getAccount();
+ SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
+ .assertionExpiration(30)
+ .issuer(deployment.getEntityID())
+ .sessionIndex(account.getSessionIndex())
+ .userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
+ .destination(deployment.getIDP().getSingleLogoutService().getRequestBindingUrl());
+ BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
+ if (deployment.getIDP().getSingleLogoutService().signRequest()) {
+ binding.signWith(deployment.getSigningKeyPair())
+ .signDocument();
+ }
+
+ binding.relayState("logout");
+
+ try {
+ SamlUtil.sendSaml(true, facade, deployment.getIDP().getSingleLogoutService().getRequestBindingUrl(), binding, logoutBuilder.buildDocument(), deployment.getIDP().getSingleLogoutService().getRequestBinding());
+ } catch (ProcessingException e) {
+ throw new RuntimeException(e);
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+
+ protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
+ SAMLDocumentHolder holder = null;
+ boolean postBinding = false;
+ String requestUri = facade.getRequest().getURI();
+ if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
+ // strip out query params
+ int index = requestUri.indexOf('?');
+ if (index > -1) {
+ requestUri = requestUri.substring(0, index);
+ }
+ holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
+ } else {
+ postBinding = true;
+ holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
+ }
+ RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
+ if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
+ log.error("expected destination '" + requestUri + "' got '" + requestAbstractType.getDestination() + "'");
+ throw new RuntimeException("destination not equal to request.");
+ }
+
+ if (requestAbstractType instanceof LogoutRequestType) {
+ if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
+ }
+ LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
+ return logoutRequest(logout, relayState);
+
+ } else {
+ throw new RuntimeException("unknown SAML request type");
+ }
+ }
+
+ protected AuthOutcome logoutRequest(LogoutRequestType request, String relayState) {
+ if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
+ sessionStore.logoutByPrincipal(request.getNameID().getValue());
+ } else {
+ sessionStore.logoutBySsoId(request.getSessionIndex());
+ }
+
+ String issuerURL = deployment.getEntityID();
+ SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
+ builder.logoutRequestID(request.getID());
+ builder.destination(deployment.getIDP().getSingleLogoutService().getResponseBindingUrl());
+ builder.issuer(issuerURL);
+ BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
+ if (deployment.getIDP().getSingleLogoutService().signResponse()) {
+ binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
+ .signWith(deployment.getSigningKeyPair())
+ .signDocument();
+ if (deployment.getSignatureCanonicalizationMethod() != null) binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
+ }
+
+
+ try {
+ SamlUtil.sendSaml(false, facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
+ deployment.getIDP().getSingleLogoutService().getResponseBinding());
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (ProcessingException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return AuthOutcome.NOT_ATTEMPTED;
+
+ }
+
+
+ protected AuthOutcome handleSamlResponse(String samlResponse, String relayState) {
+ SAMLDocumentHolder holder = null;
+ boolean postBinding = false;
+ String requestUri = facade.getRequest().getURI();
+ if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
+ int index = requestUri.indexOf('?');
+ if (index > -1) {
+ requestUri = requestUri.substring(0, index);
+ }
+ holder = extractRedirectBindingResponse(samlResponse);
+ } else {
+ postBinding = true;
+ holder = extractPostBindingResponse(samlResponse);
+ }
+ StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
+ // validate destination
+ if (!requestUri.equals(statusResponse.getDestination())) {
+ throw new RuntimeException("destination not equal to request");
+ }
+ if (statusResponse instanceof ResponseType) {
+ if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
+ }
+ return handleLoginResponse((ResponseType)statusResponse);
+
+ } else {
+ if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
+ validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
+ }
+ // todo need to check that it is actually a LogoutResponse
+ return handleLogoutResponse(holder, statusResponse, relayState);
+ }
+
+ }
+
+ private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) {
+ try {
+ if (postBinding) {
+ verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
+ } else {
+ verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
+ }
+ } catch (VerificationException e) {
+ log.error("validation failed", e);
+ throw new RuntimeException("invalid document signature");
+ }
+ }
+
+ protected AuthOutcome handleLoginResponse(ResponseType responseType) {
+ AssertionType assertion = null;
+ try {
+ assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
+ if (AssertionUtil.hasExpired(assertion)) {
+ return initiateLogin();
+ }
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ } catch (ProcessingException e) {
+ throw new RuntimeException(e);
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+
+ SubjectType subject = assertion.getSubject();
+ SubjectType.STSubType subType = subject.getSubType();
+ NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+ String principalName = subjectNameID.getValue();
+
+ final Set<String> roles = new HashSet<>();
+ MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
+ MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();
+
+ Set<StatementAbstractType> statements = assertion.getStatements();
+ for (StatementAbstractType statement : statements) {
+ if (statement instanceof AttributeStatementType) {
+ AttributeStatementType attributeStatement = (AttributeStatementType) statement;
+ List<AttributeStatementType.ASTChoiceType> attList = attributeStatement.getAttributes();
+ for (AttributeStatementType.ASTChoiceType obj : attList) {
+ AttributeType attr = obj.getAttribute();
+ if (isRole(attr)) {
+ List<Object> attributeValues = attr.getAttributeValue();
+ if (attributeValues != null) {
+ for (Object attrValue : attributeValues) {
+ String role = getAttributeValue(attrValue);
+ roles.add(role);
+ }
+ }
+ } else {
+ List<Object> attributeValues = attr.getAttributeValue();
+ if (attributeValues != null) {
+ for (Object attrValue : attributeValues) {
+ String value = getAttributeValue(attrValue);
+ if (attr.getName() != null) {
+ attributes.add(attr.getName(), value);
+ }
+ if (attr.getFriendlyName() != null) {
+ friendlyAttributes.add(attr.getFriendlyName(), value);
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE_NAME) {
+ if (deployment.getPrincipalAttributeName() != null) {
+ String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
+ if (attribute != null) principalName = attribute;
+ }
+ } else if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_FRIENDLY_ATTRIBUTE_NAME) {
+ if (deployment.getPrincipalAttributeName() != null) {
+ String attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
+ if (attribute != null) principalName = attribute;
+ }
+ }
+
+ AuthnStatementType authn = null;
+ for (Object statement : assertion.getStatements()) {
+ if (statement instanceof AuthnStatementType) {
+ authn = (AuthnStatementType)statement;
+ break;
+ }
+ }
+
+
+ final SamlPrincipal principal = new SamlPrincipal(principalName, principalName, subjectNameID.getFormat().toString(), attributes, friendlyAttributes);
+ String index = authn == null ? null : authn.getSessionIndex();
+ final String sessionIndex = index;
+ SamlSession account = new SamlSession() {
+ @Override
+ public SamlPrincipal getPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return roles;
+ }
+
+ @Override
+ public String getSessionIndex() {
+ return sessionIndex;
+ }
+ };
+ sessionStore.saveAccount(account);
+ completeAuthentication(account);
+
+
+ // redirect to original request, it will be restored
+ String redirectUri = sessionStore.getRedirectUri();
+ if (redirectUri != null) {
+ facade.getResponse().setHeader("Location", redirectUri);
+ facade.getResponse().setStatus(302);
+ facade.getResponse().end();
+ } else {
+ log.debug("IDP initiated invocation");
+ }
+
+
+ return AuthOutcome.AUTHENTICATED;
+ }
+
+ protected abstract void completeAuthentication(SamlSession account);
+
+ private String getAttributeValue(Object attrValue) {
+ String value;
+ if (attrValue instanceof String) {
+ value = (String)attrValue;
+ } else if (attrValue instanceof Node) {
+ Node roleNode = (Node) attrValue;
+ value = roleNode.getFirstChild().getNodeValue();
+ } else if (attrValue instanceof NameIDType) {
+ NameIDType nameIdType = (NameIDType) attrValue;
+ value = nameIdType.getValue();
+ } else
+ throw new RuntimeException("Unknown attribute value type: " + attrValue.getClass().getName());
+ return value;
+ }
+
+ protected boolean isRole(AttributeType attribute) {
+ return deployment.getRoleAttributeNames().contains(attribute.getName()) || deployment.getRoleAttributeFriendlyNames().contains(attribute.getFriendlyName());
+ }
+
+ protected AuthOutcome handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
+ boolean loggedIn = sessionStore.isLoggedIn();
+ if (!loggedIn || !"logout".equals(relayState)) {
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+ sessionStore.logoutAccount();
+ return AuthOutcome.LOGGED_OUT;
+ }
+
+ protected SAMLDocumentHolder extractRedirectBindingResponse(String response) {
+ return SAMLRequestParser.parseRequestRedirectBinding(response);
+ }
+ protected SAMLDocumentHolder extractPostBindingResponse(String response) {
+ byte[] samlBytes = PostBindingUtil.base64Decode(response);
+ String xml = new String(samlBytes);
+ return SAMLRequestParser.parseResponseDocument(samlBytes);
+ }
+
+
+
+ protected AuthOutcome initiateLogin() {
+ challenge = new InitiateLogin(deployment, sessionStore);
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+
+ protected boolean verifySSL() {
+ if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
+ log.warn("SSL is required to authenticate");
+ return true;
+ }
+ return false;
+ }
+
+ public void verifyPostBindingSignature(Document document, PublicKey publicKey) throws VerificationException {
+ SAML2Signature saml2Signature = new SAML2Signature();
+ try {
+ if (!saml2Signature.validate(document, publicKey)) {
+ throw new VerificationException("Invalid signature on document");
+ }
+ } catch (ProcessingException e) {
+ throw new VerificationException("Error validating signature", e);
+ }
+ }
+
+ public void verifyRedirectBindingSignature(PublicKey publicKey, String paramKey) throws VerificationException {
+ String request = facade.getRequest().getQueryParamValue(paramKey);
+ String algorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
+ String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
+ String decodedAlgorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
+
+ if (request == null) {
+ throw new VerificationException("SAML Request was null");
+ }
+ if (algorithm == null) throw new VerificationException("SigAlg was null");
+ if (signature == null) throw new VerificationException("Signature was null");
+
+ // Shibboleth doesn't sign the document for redirect binding.
+ // todo maybe a flag?
+
+ String relayState = facade.getRequest().getQueryParamValue(GeneralConstants.RELAY_STATE);
+ KeycloakUriBuilder builder = KeycloakUriBuilder.fromPath("/")
+ .queryParam(paramKey, request);
+ if (relayState != null) {
+ builder.queryParam(GeneralConstants.RELAY_STATE, relayState);
+ }
+ builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
+ String rawQuery = builder.build().getRawQuery();
+
+ try {
+ //byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
+ byte[] decodedSignature = Base64.decode(signature);
+
+ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
+ Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
+ validator.initVerify(publicKey);
+ validator.update(rawQuery.getBytes("UTF-8"));
+ if (!validator.verify(decodedSignature)) {
+ throw new VerificationException("Invalid query param signature");
+ }
+ } catch (Exception e) {
+ throw new VerificationException(e);
+ }
+ }
+
+
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java
new file mode 100755
index 0000000..2479a23
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlConfigResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.HttpFacade.Request;
+
+/**
+ * On multi-tenant scenarios, Keycloak will defer the resolution of a
+ * SamlDeployment to the target application at the request-phase.
+ *
+ * A Request object is passed to the resolver and callers expect a complete
+ * SamlDeployment. Based on this SamlDeployment, Keycloak will resume
+ * authenticating and authorizing the request.
+ *
+ * The easiest way to build a SamlDeployment is to use
+ * DeploymentBuilder , passing the InputStream of an existing
+ * keycloak-saml.xml to the build() method.
+ *
+ * @author Juraci Paixão Kröhling <juraci at kroehling.de>
+ */
+public interface SamlConfigResolver {
+
+ public SamlDeployment resolve(Request facade);
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
new file mode 100755
index 0000000..4540b25
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -0,0 +1,78 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.enums.SslRequired;
+import org.keycloak.saml.SignatureAlgorithm;
+
+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 interface SamlDeployment {
+ enum Binding {
+ POST,
+ 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();
+ Binding getRequestBinding();
+ Binding getResponseBinding();
+ String getRequestBindingUrl();
+ }
+ public interface SingleLogoutService {
+ boolean validateRequestSignature();
+ boolean validateResponseSignature();
+ boolean signRequest();
+ boolean signResponse();
+ Binding getRequestBinding();
+ Binding getResponseBinding();
+ String getRequestBindingUrl();
+ String getResponseBindingUrl();
+ }
+ }
+
+ public IDP getIDP();
+
+ public boolean isConfigured();
+ SslRequired getSslRequired();
+ String getEntityID();
+ String getNameIDPolicyFormat();
+ boolean isForceAuthentication();
+ PrivateKey getDecryptionKey();
+ KeyPair getSigningKeyPair();
+ String getSignatureCanonicalizationMethod();
+ SignatureAlgorithm getSignatureAlgorithm();
+ String getAssertionConsumerServiceUrl();
+ String getLogoutPage();
+
+ Set<String> getRoleAttributeNames();
+ Set<String> getRoleAttributeFriendlyNames();
+
+ enum PrincipalNamePolicy {
+ FROM_NAME_ID,
+ FROM_ATTRIBUTE_NAME,
+ FROM_FRIENDLY_ATTRIBUTE_NAME
+ }
+ PrincipalNamePolicy getPrincipalNamePolicy();
+ String getPrincipalAttributeName();
+
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java
new file mode 100755
index 0000000..cb8ea77
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java
@@ -0,0 +1,19 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.HttpFacade;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlDeploymentContext {
+ private SamlDeployment deployment = null;
+
+ public SamlDeploymentContext(SamlDeployment deployment) {
+ this.deployment = deployment;
+ }
+
+ public SamlDeployment resolveDeployment(HttpFacade facade) {
+ return deployment;
+ }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
new file mode 100755
index 0000000..68b23da
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
@@ -0,0 +1,84 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.util.MultivaluedHashMap;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlPrincipal implements Serializable, Principal {
+ private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
+ private MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();
+ private String name;
+ private String samlSubject;
+ private String nameIDFormat;
+
+ public SamlPrincipal(String name, String samlSubject, String nameIDFormat, MultivaluedHashMap<String, String> attributes, MultivaluedHashMap<String, String> friendlyAttributes) {
+ this.name = name;
+ this.attributes = attributes;
+ this.friendlyAttributes = friendlyAttributes;
+ this.samlSubject = samlSubject;
+ this.nameIDFormat = nameIDFormat;
+ }
+
+ public SamlPrincipal() {
+ }
+
+ public String getSamlSubject() {
+ return samlSubject;
+ }
+
+ public String getNameIDFormat() {
+ return nameIDFormat;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+
+ public List<String> getAttributes(String name) {
+ List<String> list = attributes.get(name);
+ if (list != null) {
+ return Collections.unmodifiableList(list);
+ } else {
+ return Collections.emptyList();
+ }
+
+ }
+ public List<String> getFriendlyAttributes(String friendlyName) {
+ List<String> list = friendlyAttributes.get(name);
+ if (list != null) {
+ return Collections.unmodifiableList(list);
+ } else {
+ return Collections.emptyList();
+ }
+
+ }
+
+ public String getAttribute(String name) {
+ return attributes.getFirst(name);
+ }
+
+ public String getFriendlyAttribute(String friendlyName) {
+ return friendlyAttributes.getFirst(friendlyName);
+ }
+
+ public Set<String> getAttributeNames() {
+ return Collections.unmodifiableSet(attributes.keySet());
+
+ }
+
+ public Set<String> getFriendlyNames() {
+ return Collections.unmodifiableSet(friendlyAttributes.keySet());
+
+ }
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSession.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSession.java
new file mode 100755
index 0000000..b3a1833
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSession.java
@@ -0,0 +1,15 @@
+package org.keycloak.adapters.saml;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface SamlSession extends Serializable {
+ SamlPrincipal getPrincipal();
+ Set<String> getRoles();
+ String getSessionIndex();
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java
new file mode 100755
index 0000000..6b6a83a
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java
@@ -0,0 +1,20 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.AdapterSessionStore;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface SamlSessionStore extends AdapterSessionStore {
+ boolean isLoggedIn();
+ SamlSession getAccount();
+ void saveAccount(SamlSession account);
+ String getRedirectUri();
+ void logoutAccount();
+ void logoutByPrincipal(String principal);
+ void logoutBySsoId(List<String> ssoIds);
+
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlUtil.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlUtil.java
new file mode 100755
index 0000000..e0ea5ec
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlUtil.java
@@ -0,0 +1,35 @@
+package org.keycloak.adapters.saml;
+
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.w3c.dom.Document;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlUtil {
+ public static void sendSaml(boolean asRequest, HttpFacade httpFacade, String actionUrl,
+ BaseSAML2BindingBuilder binding, Document document,
+ SamlDeployment.Binding samlBinding) throws ProcessingException, ConfigurationException, IOException {
+ if (samlBinding == SamlDeployment.Binding.POST) {
+ String html = asRequest ? binding.postBinding(document).getHtmlRequest(actionUrl) : binding.postBinding(document).getHtmlResponse(actionUrl) ;
+ httpFacade.getResponse().setStatus(200);
+ httpFacade.getResponse().setHeader("Content-Type", "text/html");
+ httpFacade.getResponse().setHeader("Pragma", "no-cache");
+ httpFacade.getResponse().setHeader("Cache-Control", "no-cache, no-store");
+ httpFacade.getResponse().getOutputStream().write(html.getBytes());
+ httpFacade.getResponse().end();
+ } else {
+ String uri = asRequest ? binding.redirectBinding(document).requestURI(actionUrl).toString() : binding.redirectBinding(document).responseURI(actionUrl).toString();
+ httpFacade.getResponse().setStatus(302);
+ httpFacade.getResponse().setHeader("Location", uri);
+ httpFacade.getResponse().end();
+ }
+ }
+
+}
diff --git a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
new file mode 100755
index 0000000..c49cdf1
--- /dev/null
+++ b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
@@ -0,0 +1,78 @@
+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("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("redirect", idp.getSingleLogoutService().getRequestBinding());
+ Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
+ Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
+ Assert.assertEquals("redirecturl", idp.getSingleLogoutService().getRedirectBindingUrl());
+
+ Assert.assertTrue(idp.getKeys().size() == 1);
+ Assert.assertTrue(idp.getKeys().get(0).isSigning());
+ Assert.assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
+
+
+
+
+ }
+}
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
new file mode 100755
index 0000000..242dc8e
--- /dev/null
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -0,0 +1,55 @@
+<keycloak-saml-adapter>
+ <SP entityID="sp"
+ sslPolicy="ssl"
+ nameIDPolicyFormat="format"
+ signatureAlgorithm=""
+ sgnatureCanonicalizationMethod=""
+ forceAuthentication="true">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore file="file" resource="cp" password="pw">
+ <PrivateKey alias="private alias" password="private pw"/>
+ <Certificate alias="cert alias"/>
+ </KeyStore>
+ </Key>
+ <Key encryption="true">
+ <PrivateKeyPem>
+ private pem
+ </PrivateKeyPem>
+ <PublicKeyPem>
+ public pem
+ </PublicKeyPem>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="policy" attribute="attribute"/>
+ <RoleMapping>
+ <Attribute name="member"/>
+ <FriendlyAttribute name="memberOf"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="post"
+ bindingUrl="url"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="redirect"
+ responseBinding="post"
+ postBindingUrl="posturl"
+ redirectBindingUrl="redirecturl"
+ />
+ <Keys>
+ <Key signing="true">
+ <CertificatePem>
+ cert pem
+ </CertificatePem>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
saml/client-adapter/pom.xml 20(+20 -0)
diff --git a/saml/client-adapter/pom.xml b/saml/client-adapter/pom.xml
new file mode 100755
index 0000000..e1c0b71
--- /dev/null
+++ b/saml/client-adapter/pom.xml
@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <name>Keycloak SAML Client Adapter Modules</name>
+ <description/>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-client-adapter-pom</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>core</module>
+ <module>undertow</module>
+ </modules>
+</project>
saml/client-adapter/undertow/pom.xml 87(+87 -0)
diff --git a/saml/client-adapter/undertow/pom.xml b/saml/client-adapter/undertow/pom.xml
new file mode 100755
index 0000000..c7401b9
--- /dev/null
+++ b/saml/client-adapter/undertow/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-undertow-saml-adapter</artifactId>
+ <name>Keycloak Undertow SAML Adapter</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-adapter-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
new file mode 100755
index 0000000..9b29618
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
@@ -0,0 +1,148 @@
+/*
+ * 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.NotificationReceiver;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.security.api.SecurityNotification;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.AttachmentKey;
+import io.undertow.util.Headers;
+import io.undertow.util.StatusCodes;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.undertow.UndertowHttpFacade;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+
+/**
+ * Abstract base class for a Keycloak-enabled Undertow AuthenticationMechanism.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
+ public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
+ protected SamlDeploymentContext deploymentContext;
+ protected UndertowUserSessionManagement sessionManagement;
+ protected String errorPage;
+
+ public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
+ String errorPage) {
+ this.deploymentContext = deploymentContext;
+ this.sessionManagement = sessionManagement;
+ this.errorPage = errorPage;
+ }
+
+ @Override
+ public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
+ AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
+ if (challenge != null) {
+ if (challenge.errorPage() && errorPage != null) {
+ Integer code = servePage(exchange, errorPage);
+ return new ChallengeResult(true, code);
+ }
+ UndertowHttpFacade facade = createFacade(exchange);
+ if (challenge.challenge(facade)) {
+ return new ChallengeResult(true, exchange.getResponseCode());
+ }
+ }
+ return new ChallengeResult(false);
+ }
+
+ protected Integer servePage(final HttpServerExchange exchange, final String location) {
+ sendRedirect(exchange, location);
+ return StatusCodes.TEMPORARY_REDIRECT;
+ }
+
+ static void sendRedirect(final HttpServerExchange exchange, final String location) {
+ // TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better handle this.
+ String loc = exchange.getRequestScheme() + "://" + exchange.getHostAndPort() + location;
+ exchange.getResponseHeaders().put(Headers.LOCATION, loc);
+ }
+
+
+
+ protected void registerNotifications(final SecurityContext securityContext) {
+
+ final NotificationReceiver logoutReceiver = new NotificationReceiver() {
+ @Override
+ public void handleNotification(SecurityNotification notification) {
+ if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
+
+ HttpServerExchange exchange = notification.getExchange();
+ UndertowHttpFacade facade = createFacade(exchange);
+ SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
+ SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
+ sessionStore.logoutAccount();
+ }
+ };
+
+ securityContext.registerNotificationReceiver(logoutReceiver);
+ }
+
+ /**
+ * Call this inside your authenticate method.
+ */
+ public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
+ UndertowHttpFacade facade = createFacade(exchange);
+ SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
+ if (!deployment.isConfigured()) {
+ return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+ }
+ SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
+ UndertowSamlAuthenticator authenticator = new UndertowSamlAuthenticator(securityContext, facade,
+ deploymentContext.resolveDeployment(facade), sessionStore);
+ AuthOutcome outcome = authenticator.authenticate();
+ if (outcome == AuthOutcome.AUTHENTICATED) {
+ registerNotifications(securityContext);
+ return AuthenticationMechanismOutcome.AUTHENTICATED;
+ }
+ if (outcome == AuthOutcome.LOGGED_OUT) {
+ securityContext.logout();
+ if (deployment.getLogoutPage() != null) {
+ redirectLogout(deployment, exchange);
+ }
+ return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+ }
+ AuthChallenge challenge = authenticator.getChallenge();
+ if (challenge != null) {
+ exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, challenge);
+ }
+
+ if (outcome == AuthOutcome.FAILED) {
+ return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+ }
+ return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+ }
+
+ protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
+ String page = deployment.getLogoutPage();
+ sendRedirect(exchange, page);
+ exchange.setResponseCode(302);
+ exchange.endExchange();
+ }
+
+ protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
+ return new UndertowHttpFacade(exchange);
+ }
+
+ protected abstract SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext);
+}
\ No newline at end of file
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java
new file mode 100755
index 0000000..ac3da61
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.adapters.saml.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.AuthenticationMechanismFactory;
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.server.handlers.form.FormParserFactory;
+import io.undertow.servlet.ServletExtension;
+import io.undertow.servlet.api.AuthMethodConfig;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.LoginConfig;
+import io.undertow.servlet.api.ServletSessionConfig;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlConfigResolver;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
+import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+import javax.servlet.ServletContext;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlServletExtension implements ServletExtension {
+
+ protected static Logger log = Logger.getLogger(SamlServletExtension.class);
+
+ // todo when this DeploymentInfo method of the same name is fixed.
+ public boolean isAuthenticationMechanismPresent(DeploymentInfo deploymentInfo, final String mechanismName) {
+ LoginConfig loginConfig = deploymentInfo.getLoginConfig();
+ if (loginConfig != null) {
+ for (AuthMethodConfig method : loginConfig.getAuthMethods()) {
+ if (method.getName().equalsIgnoreCase(mechanismName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static InputStream getConfigInputStream(ServletContext context) {
+ InputStream is = null;
+ if (is == null) {
+ String path = context.getInitParameter("keycloak.config.file");
+ if (path == null) {
+ log.debug("using /WEB-INF/keycloak-saml.xml");
+ is = context.getResourceAsStream("/WEB-INF/keycloak-saml.xml");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return is;
+ }
+
+
+ @Override
+ @SuppressWarnings("UseSpecificCatch")
+ public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
+ if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK-SAML")) {
+ log.debug("auth-method is not keycloak saml!");
+ return;
+ }
+ log.debug("SamlServletException initialization");
+
+ // Possible scenarios:
+ // 1) The deployment has a keycloak.config.resolver specified and it exists:
+ // Outcome: adapter uses the resolver
+ // 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exists, isn't a resolver, ...) :
+ // Outcome: adapter is left unconfigured
+ // 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent)
+ // Outcome: adapter uses it
+ // 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
+ // Outcome: adapter is left unconfigured
+
+ SamlConfigResolver configResolver;
+ String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
+ SamlDeploymentContext deploymentContext = null;
+ if (configResolverClass != null) {
+ try {
+ throw new RuntimeException("Not implemented yet");
+ //configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
+ //deploymentContext = new AdapterDeploymentContext(configResolver);
+ //log.info("Using " + configResolverClass + " to resolve Keycloak configuration on a per-request basis.");
+ } catch (Exception ex) {
+ log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
+ //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ InputStream is = getConfigInputStream(servletContext);
+ final SamlDeployment deployment;
+ if (is == null) {
+ log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ deployment = new DefaultSamlDeployment();
+ } else {
+ try {
+ ResourceLoader loader = new ResourceLoader() {
+ @Override
+ public InputStream getResourceAsStream(String resource) {
+ return servletContext.getResourceAsStream(resource);
+ }
+ };
+ deployment = new DeploymentBuilder().build(is, loader);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ deploymentContext = new SamlDeploymentContext(deployment);
+ log.debug("Keycloak is using a per-deployment configuration.");
+ }
+
+ servletContext.setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
+ UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
+ final ServletSamlAuthMech mech = new ServletSamlAuthMech(deploymentContext, userSessionManagement, getErrorPage(deploymentInfo));
+
+
+ // setup handlers
+
+ deploymentInfo.addAuthenticationMechanism("KEYCLOAK-SAML", new AuthenticationMechanismFactory() {
+ @Override
+ public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
+ return mech;
+ }
+ }); // authentication
+
+ deploymentInfo.setIdentityManager(new IdentityManager() {
+ @Override
+ public Account verify(Account account) {
+ return account;
+ }
+
+ @Override
+ public Account verify(String id, Credential credential) {
+ throw new IllegalStateException("Should never be called in Keycloak flow");
+ }
+
+ @Override
+ public Account verify(Credential credential) {
+ throw new IllegalStateException("Should never be called in Keycloak flow");
+ }
+ });
+
+ log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
+ ServletSessionConfig cookieConfig = new ServletSessionConfig();
+ cookieConfig.setPath(deploymentInfo.getContextPath());
+ deploymentInfo.setServletSessionConfig(cookieConfig);
+
+ }
+
+ protected String getErrorPage(DeploymentInfo deploymentInfo) {
+ LoginConfig loginConfig = deploymentInfo.getLoginConfig();
+ String errorPage = null;
+ if (loginConfig != null) {
+ errorPage = loginConfig.getErrorPage();
+ }
+ return errorPage;
+ }
+}
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java
new file mode 100755
index 0000000..a5a0bd4
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlAuthMech.java
@@ -0,0 +1,71 @@
+package org.keycloak.adapters.saml.undertow;
+
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import io.undertow.util.Headers;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.InMemorySessionIdMapper;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.undertow.ServletHttpFacade;
+import org.keycloak.adapters.undertow.UndertowHttpFacade;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletSamlAuthMech extends AbstractSamlAuthMech {
+ private SessionIdMapper idMapper = new InMemorySessionIdMapper();
+ public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
+ super(deploymentContext, sessionManagement, errorPage);
+ }
+
+ @Override
+ protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
+ return new ServletSamlSessionStore(exchange, sessionManagement, securityContext, idMapper);
+ }
+
+ @Override
+ protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
+ return new ServletHttpFacade(exchange);
+ }
+
+ @Override
+ protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
+ servePage(exchange, deployment.getLogoutPage());
+ }
+
+ @Override
+ protected Integer servePage(HttpServerExchange exchange, String location) {
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ ServletRequest req = servletRequestContext.getServletRequest();
+ ServletResponse resp = servletRequestContext.getServletResponse();
+ RequestDispatcher disp = req.getRequestDispatcher(location);
+ //make sure the login page is never cached
+ exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
+ exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
+ exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
+
+
+ try {
+ disp.forward(req, resp);
+ } catch (ServletException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+
+}
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java
new file mode 100755
index 0000000..1f32666
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java
@@ -0,0 +1,180 @@
+package org.keycloak.adapters.saml.undertow;
+
+import io.undertow.security.api.SecurityContext;
+import io.undertow.security.idm.Account;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.session.Session;
+import io.undertow.server.session.SessionManager;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import io.undertow.servlet.spec.HttpSessionImpl;
+import io.undertow.servlet.util.SavedRequest;
+import io.undertow.util.Sessions;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+import org.keycloak.util.KeycloakUriBuilder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.security.Principal;
+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 ServletSamlSessionStore implements SamlSessionStore {
+ protected static Logger log = Logger.getLogger(SamlSessionStore.class);
+ public static final String SAML_REDIRECT_URI = "SAML_REDIRECT_URI";
+
+ private final HttpServerExchange exchange;
+ private final UndertowUserSessionManagement sessionManagement;
+ private final SecurityContext securityContext;
+ private final SessionIdMapper idMapper;
+
+ public ServletSamlSessionStore(HttpServerExchange exchange, UndertowUserSessionManagement sessionManagement,
+ SecurityContext securityContext,
+ SessionIdMapper idMapper) {
+ this.exchange = exchange;
+ this.sessionManagement = sessionManagement;
+ this.securityContext = securityContext;
+ this.idMapper = idMapper;
+ }
+
+ @Override
+ public void logoutAccount() {
+ HttpSession session = getSession(false);
+ if (session != null) {
+ SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
+ if (samlSession != null) {
+ if (samlSession.getSessionIndex() != null) {
+ idMapper.removeSession(session.getId());
+ }
+ session.removeAttribute(SamlSession.class.getName());
+ }
+ session.removeAttribute(SAML_REDIRECT_URI);
+ }
+ }
+
+ @Override
+ public void logoutByPrincipal(String principal) {
+ Set<String> sessions = idMapper.getUserSessions(principal);
+ if (sessions != null) {
+ List<String> ids = new LinkedList<>();
+ ids.addAll(sessions);
+ logoutSessionIds(ids);
+ for (String id : ids) {
+ idMapper.removeSession(id);
+ }
+ }
+
+ }
+
+ @Override
+ public void logoutBySsoId(List<String> ssoIds) {
+ if (ssoIds == null) return;
+ List<String> sessionIds = new LinkedList<>();
+ for (String id : ssoIds) {
+ String sessionId = idMapper.getSessionFromSSO(id);
+ if (sessionId != null) {
+ sessionIds.add(sessionId);
+ idMapper.removeSession(sessionId);
+ }
+
+ }
+ logoutSessionIds(sessionIds);
+ }
+
+ protected void logoutSessionIds(List<String> sessionIds) {
+ if (sessionIds == null || sessionIds.isEmpty()) return;
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ SessionManager sessionManager = servletRequestContext.getDeployment().getSessionManager();
+ sessionManagement.logoutHttpSessions(sessionManager, sessionIds);
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ HttpSession session = getSession(false);
+ if (session == null) {
+ log.debug("session was null, returning null");
+ return false;
+ }
+ final SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
+ if (samlSession == null) {
+ log.debug("SamlSession was not in session, returning null");
+ return false;
+ }
+
+ Account undertowAccount = new Account() {
+ @Override
+ public Principal getPrincipal() {
+ return samlSession.getPrincipal();
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return samlSession.getRoles();
+ }
+ };
+ securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false);
+ restoreRequest();
+ return true;
+ }
+
+ @Override
+ public void saveAccount(SamlSession account) {
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ HttpSession session = getSession(true);
+ session.setAttribute(SamlSession.class.getName(), account);
+ sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
+ idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), session.getId());
+
+ }
+
+ @Override
+ public SamlSession getAccount() {
+ HttpSession session = getSession(true);
+ return (SamlSession)session.getAttribute(SamlSession.class.getName());
+ }
+
+ @Override
+ public String getRedirectUri() {
+ final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
+ return (String)session.getAttribute(SAML_REDIRECT_URI);
+ }
+
+ @Override
+ public void saveRequest() {
+ SavedRequest.trySaveRequest(exchange);
+ final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
+ KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
+ .replaceQuery(exchange.getQueryString());
+ if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
+ String uri = uriBuilder.build().toString();
+
+ session.setAttribute(SAML_REDIRECT_URI, uri);
+
+ }
+
+ @Override
+ public boolean restoreRequest() {
+ HttpSession session = getSession(false);
+ if (session == null) return false;
+ SavedRequest.tryRestoreRequest(exchange, session);
+ session.removeAttribute(SAML_REDIRECT_URI);
+ return false;
+ }
+
+ protected HttpSession getSession(boolean create) {
+ final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+ HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+ return req.getSession(create);
+ }
+}
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/UndertowSamlAuthenticator.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/UndertowSamlAuthenticator.java
new file mode 100755
index 0000000..dfb2843
--- /dev/null
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/UndertowSamlAuthenticator.java
@@ -0,0 +1,43 @@
+package org.keycloak.adapters.saml.undertow;
+
+import io.undertow.security.api.SecurityContext;
+import io.undertow.security.idm.Account;
+import io.undertow.server.HttpServerExchange;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+
+import java.security.Principal;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UndertowSamlAuthenticator extends SamlAuthenticator {
+ protected SecurityContext securityContext;
+
+ public UndertowSamlAuthenticator(SecurityContext securityContext, HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+ super(facade, deployment, sessionStore);
+ this.securityContext = securityContext;
+ }
+
+ @Override
+ protected void completeAuthentication(final SamlSession samlSession) {
+ Account undertowAccount = new Account() {
+ @Override
+ public Principal getPrincipal() {
+ return samlSession.getPrincipal();
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return samlSession.getRoles();
+ }
+ };
+ securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false);
+
+ }
+}
saml/pom.xml 1(+1 -0)
diff --git a/saml/pom.xml b/saml/pom.xml
index 85d0080..2487e9e 100755
--- a/saml/pom.xml
+++ b/saml/pom.xml
@@ -16,5 +16,6 @@
<modules>
<module>saml-core</module>
<module>saml-protocol</module>
+ <module>client-adapter</module>
</modules>
</project>
saml/saml-core/pom.xml 5(+5 -0)
diff --git a/saml/saml-core/pom.xml b/saml/saml-core/pom.xml
index 7d08b3b..ec148f3 100755
--- a/saml/saml-core/pom.xml
+++ b/saml/saml-core/pom.xml
@@ -24,6 +24,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
</dependency>
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
index fb32fef..7941729 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
@@ -105,6 +105,23 @@ public class StaxParserUtil {
}
/**
+ * Get the Attribute value
+ *
+ * @param startElement
+ * @param tag localpart of the qname of the attribute
+ *
+ * @return false if attribute not set
+ */
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
+ String result = null;
+ Attribute attr = startElement.getAttributeByName(new QName(tag));
+ if (attr != null)
+ result = getAttributeValue(attr);
+ if (result == null) return false;
+ return Boolean.valueOf(result);
+ }
+
+ /**
* Given that the {@code XMLEventReader} is in {@code XMLStreamConstants.START_ELEMENT} mode, we parse into a DOM
* Element
*
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
index 0f6f5ed..af4d1ed 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
@@ -21,15 +21,22 @@
*/
package org.keycloak.saml.processing.core.saml.v2.util;
+import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.saml.common.ErrorCodes;
import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory;
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.common.exceptions.fed.IssueInstantMissingException;
import org.keycloak.saml.common.util.DocumentUtil;
+import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.common.util.StaxUtil;
+import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
+import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
import org.keycloak.dom.saml.v1.assertion.SAML11AssertionType;
import org.keycloak.dom.saml.v1.assertion.SAML11AttributeStatementType;
@@ -45,13 +52,17 @@ import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
+import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
+import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
@@ -510,4 +521,51 @@ public class AssertionUtil {
}
return roles;
}
+
+ public static AssertionType getAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
+ List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
+
+ if (assertions.isEmpty()) {
+ throw new ProcessingException("No assertion from response.");
+ }
+
+ ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
+ EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
+
+ if (encryptedAssertion != null) {
+ if (privateKey == null) {
+ throw new ProcessingException("Encryptd assertion and decrypt private key is null");
+ }
+ decryptAssertion(responseType, privateKey);
+
+ }
+ return responseType.getAssertions().get(0).getAssertion();
+ }
+
+ public static ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
+ SAML2Response saml2Response = new SAML2Response();
+
+ Document doc = saml2Response.convert(responseType);
+ Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
+
+ if (enc == null) {
+ throw new ProcessingException("No encrypted assertion found.");
+ }
+
+ String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
+ Document newDoc = DocumentUtil.createDocument();
+ Node importedNode = newDoc.importNode(enc, true);
+ newDoc.appendChild(importedNode);
+
+ Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
+ SAMLParser parser = new SAMLParser();
+
+ JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
+ AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
+ .getNodeAsStream(decryptedDocumentElement)));
+
+ responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
+
+ return responseType;
+ }
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/RandomSecret.java b/saml/saml-core/src/main/java/org/keycloak/saml/RandomSecret.java
new file mode 100755
index 0000000..67f4ba7
--- /dev/null
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/RandomSecret.java
@@ -0,0 +1,25 @@
+package org.keycloak.saml;
+
+import java.security.SecureRandom;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RandomSecret {
+ /**
+ * <p>
+ * Creates a random {@code byte[]} secret of the specified size.
+ * </p>
+ *
+ * @param size the size of the secret to be created, in bytes.
+ *
+ * @return a {@code byte[]} containing the generated secret.
+ */
+ public static byte[] createRandomSecret(final int size) {
+ SecureRandom random = new SecureRandom();
+ byte[] secret = new byte[size];
+ random.nextBytes(secret);
+ return secret;
+ }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
index 2b6a72c..8cdd302 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
@@ -5,6 +5,7 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/JaxrsSAML2BindingBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/JaxrsSAML2BindingBuilder.java
new file mode 100755
index 0000000..3afe813
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/JaxrsSAML2BindingBuilder.java
@@ -0,0 +1,76 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JaxrsSAML2BindingBuilder extends BaseSAML2BindingBuilder<JaxrsSAML2BindingBuilder> {
+ public static class PostBindingBuilder extends BasePostBindingBuilder {
+ public PostBindingBuilder(JaxrsSAML2BindingBuilder builder, Document document) throws ProcessingException {
+ super(builder, document);
+ }
+ public Response request(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
+ return buildResponse(document, actionUrl, true);
+ }
+ public Response response(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
+ return buildResponse(document, actionUrl, false);
+ }
+ protected Response buildResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
+ String str = builder.buildHtmlPostResponse(responseDoc, actionUrl, asRequest);
+
+ return Response.ok(str, MediaType.TEXT_HTML_TYPE)
+ .header("Pragma", "no-cache")
+ .header("Cache-Control", "no-cache, no-store").build();
+ }
+
+
+ }
+
+ public static class RedirectBindingBuilder extends BaseRedirectBindingBuilder {
+ public RedirectBindingBuilder(JaxrsSAML2BindingBuilder builder, Document document) throws ProcessingException {
+ super(builder, document);
+ }
+
+ public Response response(String redirectUri) throws ProcessingException, ConfigurationException, IOException {
+ return response(redirectUri, false);
+ }
+
+ public Response request(String redirect) throws ProcessingException, ConfigurationException, IOException {
+ return response(redirect, true);
+ }
+
+ private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
+ URI uri = generateURI(redirectUri, asRequest);
+ if (logger.isDebugEnabled()) logger.trace("redirect-binding uri: " + uri.toString());
+ CacheControl cacheControl = new CacheControl();
+ cacheControl.setNoCache(true);
+ return Response.status(302).location(uri)
+ .header("Pragma", "no-cache")
+ .header("Cache-Control", "no-cache, no-store").build();
+ }
+
+ }
+
+ public RedirectBindingBuilder redirectBinding(Document document) throws ProcessingException {
+ return new RedirectBindingBuilder(this, document);
+ }
+
+ public PostBindingBuilder postBinding(Document document) throws ProcessingException {
+ return new PostBindingBuilder(this, document);
+ }
+
+
+
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 54dff3a..d6b745b 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -26,6 +26,11 @@ import org.keycloak.protocol.RestartLoginCookie;
import org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper;
import org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper;
import org.keycloak.protocol.saml.mappers.SAMLRoleListMapper;
+import org.keycloak.saml.SAML2ErrorResponseBuilder;
+import org.keycloak.saml.SAML2LoginResponseBuilder;
+import org.keycloak.saml.SAML2LogoutRequestBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
@@ -166,15 +171,17 @@ public class SamlProtocol implements LoginProtocol {
protected Response getErrorResponse(ClientSessionModel clientSession, String status) {
SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
- .relayState(clientSession.getNote(GeneralConstants.RELAY_STATE))
.destination(clientSession.getRedirectUri())
.issuer(getResponseIssuer(realm))
.status(status);
try {
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
+ .relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
+ Document document = builder.buildDocument();
if (isPostBinding(clientSession)) {
- return builder.postBinding().response();
+ return binding.postBinding(document).response(clientSession.getRedirectUri());
} else {
- return builder.redirectBinding().response();
+ return binding.redirectBinding(document).response(clientSession.getRedirectUri());
}
} catch (Exception e) {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
@@ -336,7 +343,7 @@ public class SamlProtocol implements LoginProtocol {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
}
- SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2();
+ JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
bindingBuilder.relayState(relayState);
if (requiresRealmSignature(client)) {
@@ -490,12 +497,14 @@ public class SamlProtocol implements LoginProtocol {
if (isLogoutPostBindingForClient(clientSession)) {
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
- return logoutBuilder.postBinding().request(bindingUri);
+ JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+ return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
} else {
logger.debug("frontchannel redirect binding");
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
- return logoutBuilder.redirectBinding().request(bindingUri);
+ JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+ return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri);
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@@ -523,24 +532,25 @@ public class SamlProtocol implements LoginProtocol {
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
builder.destination(logoutBindingUri);
builder.issuer(getResponseIssuer(realm));
- builder.relayState(logoutRelayState);
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
+ binding.relayState(logoutRelayState);
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
if (signingAlgorithm != null) {
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
if (canonicalization != null) {
- builder.canonicalizationMethod(canonicalization);
+ binding.canonicalizationMethod(canonicalization);
}
- builder.signatureAlgorithm(algorithm)
+ binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (isLogoutPostBindingForInitiator(userSession)) {
- return builder.postBinding().response(logoutBindingUri);
+ return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else {
- return builder.redirectBinding().response(logoutBindingUri);
+ return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@@ -566,7 +576,8 @@ public class SamlProtocol implements LoginProtocol {
String logoutRequestString = null;
try {
- logoutRequestString = logoutBuilder.postBinding().encoded();
+ JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+ logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded();
} catch (Exception e) {
logger.warn("failed to send saml logout", e);
return;
@@ -614,26 +625,20 @@ public class SamlProtocol implements LoginProtocol {
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
.assertionExpiration(realm.getAccessCodeLifespan())
.issuer(getResponseIssuer(realm))
+ .sessionIndex(clientSession.getId())
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
.destination(logoutUrl);
+ return logoutBuilder;
+ }
+
+ private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) {
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
if (requiresRealmSignature(client)) {
- logoutBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
- .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
- .signDocument();
- }
- /*
- if (requiresEncryption(client)) {
- PublicKey publicKey = null;
- try {
- publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
- } catch (Exception e) {
- logger.error("failed", e);
- return;
- }
- logoutBuilder.encrypt(publicKey);
+ binding.signatureAlgorithm(getSignatureAlgorithm(client))
+ .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+ .signDocument();
}
- */
- return logoutBuilder;
+ return binding;
}
@Override
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
index d865b2c..d9d937e 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -2,6 +2,7 @@ package org.keycloak.protocol.saml;
import org.keycloak.VerificationException;
import org.keycloak.models.ClientModel;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
@@ -13,7 +14,6 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.security.PublicKey;
-import java.security.SecureRandom;
import java.security.Signature;
import java.security.cert.Certificate;
@@ -23,22 +23,6 @@ import java.security.cert.Certificate;
*/
public class SamlProtocolUtils {
- /**
- * <p>
- * Creates a random {@code byte[]} secret of the specified size.
- * </p>
- *
- * @param size the size of the secret to be created, in bytes.
- *
- * @return a {@code byte[]} containing the generated secret.
- */
- public static byte[] createRandomSecret(final int size) {
- SecureRandom random = new SecureRandom();
- byte[] secret = new byte[size];
- random.nextBytes(secret);
- return secret;
- }
-
public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
@@ -79,23 +63,23 @@ public class SamlProtocolUtils {
return cert.getPublicKey();
}
- public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation) throws VerificationException {
+ public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation, String paramKey) throws VerificationException {
MultivaluedMap<String, String> encodedParams = uriInformation.getQueryParameters(false);
- String request = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY);
+ String request = encodedParams.getFirst(paramKey);
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
String decodedAlgorithm = uriInformation.getQueryParameters(true).getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
- if (request == null) throw new VerificationException("SAMLRequest as null");
- if (algorithm == null) throw new VerificationException("SigAlg as null");
- if (signature == null) throw new VerificationException("Signature as null");
+ if (request == null) throw new VerificationException("SAM was null");
+ if (algorithm == null) throw new VerificationException("SigAlg was null");
+ if (signature == null) throw new VerificationException("Signature was null");
// Shibboleth doesn't sign the document for redirect binding.
// todo maybe a flag?
UriBuilder builder = UriBuilder.fromPath("/")
- .queryParam(GeneralConstants.SAML_REQUEST_KEY, request);
+ .queryParam(paramKey, request);
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
builder.queryParam(GeneralConstants.RELAY_STATE, encodedParams.getFirst(GeneralConstants.RELAY_STATE));
}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 38304e6..9521e0c 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -3,7 +3,6 @@ package org.keycloak.protocol.saml;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
-import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.ClientConnection;
import org.keycloak.VerificationException;
import org.keycloak.authentication.AuthenticationProcessor;
@@ -24,10 +23,12 @@ import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.RestartLoginCookie;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.SAMLRequestParser;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
@@ -389,19 +390,20 @@ public class SamlService {
builder.logoutRequestID(logoutRequest.getID());
builder.destination(logoutBindingUri);
builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
- builder.relayState(logoutRelayState);
+ JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
+ .relayState(logoutRelayState);
if (SamlProtocol.requiresRealmSignature(client)) {
SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client);
- builder.signatureAlgorithm(algorithm)
+ binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) {
- return builder.postBinding().response(logoutBindingUri);
+ return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else {
- return builder.redirectBinding().response(logoutBindingUri);
+ return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
}
} catch (Exception e) {
throw new RuntimeException(e);
@@ -458,7 +460,7 @@ public class SamlService {
return;
}
PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
- SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
+ SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, GeneralConstants.SAML_REQUEST_KEY);
}
testsuite/integration/pom.xml 8(+8 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 9772e45..599d0b2 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -103,6 +103,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-undertow-saml-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
</dependency>
<dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java
new file mode 100755
index 0000000..f51c15a
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlBindingTest.java
@@ -0,0 +1,450 @@
+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.adapters.saml.SamlPrincipal;
+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.processing.core.saml.v2.constants.X500SAMLProfileConstants;
+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.IOException;
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @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("/keycloak-saml/mappers", "/employee2", "employee2.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 "/keycloak-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 {
+ {
+ SamlKeycloakRule.SendUsernameServlet.sentPrincipal = null;
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = null;
+ driver.navigate().to("http://localhost:8081/employee2/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ List<String> requiredRoles = new LinkedList<>();
+ requiredRoles.add("manager");
+ requiredRoles.add("employee");
+ requiredRoles.add("user");
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = requiredRoles;
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee2/");
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = null;
+ SamlPrincipal principal = (SamlPrincipal)SamlKeycloakRule.SendUsernameServlet.sentPrincipal;
+ Assert.assertNotNull(principal);
+ Assert.assertEquals("bburke@redhat.com", principal.getAttribute(X500SAMLProfileConstants.EMAIL.get()));
+ Assert.assertEquals("bburke@redhat.com", principal.getFriendlyAttribute("email"));
+ Assert.assertEquals("617", principal.getAttribute("phone"));
+ Assert.assertNull(principal.getFriendlyAttribute("phone"));
+ driver.navigate().to("http://localhost:8081/employee2/?GLO=true");
+ checkLoggedOut("http://localhost:8081/employee2/");
+
+ }
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ ClientModel app = appRealm.getClientByClientId("http://localhost:8081/employee2/");
+ 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 <<<<<<<<");
+
+ {
+ SamlKeycloakRule.SendUsernameServlet.sentPrincipal = null;
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = null;
+ driver.navigate().to("http://localhost:8081/employee2/");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
+ List<String> requiredRoles = new LinkedList<>();
+ requiredRoles.add("el-jefe");
+ requiredRoles.add("user");
+ requiredRoles.add("hardcoded-role");
+ requiredRoles.add("pee-on");
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = requiredRoles;
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = requiredRoles;
+ loginPage.login("bburke", "password");
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee2/");
+ SamlKeycloakRule.SendUsernameServlet.checkRoles = null;
+ SamlPrincipal principal = (SamlPrincipal)SamlKeycloakRule.SendUsernameServlet.sentPrincipal;
+ Assert.assertNotNull(principal);
+ Assert.assertEquals("hard", principal.getAttribute("hardcoded-attribute"));
+
+
+ }
+ }
+
+ @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/");
+ String currentUrl = driver.getCurrentUrl();
+ Assert.assertTrue(currentUrl.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, true, 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("/keycloak-saml/sp-metadata.xml");
+ Assert.assertNotNull(is);
+ formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
+
+ WebTarget upload = adminRealms.path("demo/client-importers/saml2-entity-descriptor/upload");
+ System.out.println(upload.getUri());
+ Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
+ Assert.assertEquals(204, response.getStatus());
+ response.close();
+ client.close();
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java
new file mode 100755
index 0000000..e7889af
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlKeycloakRule.java
@@ -0,0 +1,190 @@
+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.junit.Assert;
+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;
+import java.util.List;
+
+/**
+ * @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 {
+
+ public static Principal sentPrincipal;
+ public static List<String> checkRoles;
+
+ @Override
+ protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+ if (checkRoles != null) {
+ for (String role : checkRoles) {
+ System.out.println("check role: " + role);
+ Assert.assertTrue(req.isUserInRole(role));
+ }
+
+ }
+ 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());
+ sentPrincipal = principal;
+
+ }
+ @Override
+ protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+ if (checkRoles != null) {
+ for (String role : checkRoles) {
+ Assert.assertTrue(req.isUserInRole(role));
+ }
+
+ }
+ 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());
+ sentPrincipal = principal;
+ }
+ }
+
+ 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");
+ constraint.addRoleAllowed("el-jefe");
+ LoginConfig loginConfig = new LoginConfig("KEYCLOAK-SAML", "Test Realm");
+
+ ResourceManager resourceManager = new TestResourceManager(warResourcePath);
+
+ DeploymentInfo deploymentInfo = new DeploymentInfo()
+ .setClassLoader(classLoader)
+ .setIdentityManager(new TestIdentityManager())
+ .setContextPath(contextPath)
+ .setDeploymentName(warDeploymentName)
+ .setLoginConfig(loginConfig)
+ .setResourceManager(resourceManager)
+ .addServlets(regularServletInfo)
+ .addSecurityConstraint(constraint)
+ .addServletExtension(new SamlServletExtension());
+ server.getServer().deploy(deploymentInfo);
+ }
+
+ public String getRealmJson() {
+ return "/keycloak-saml/testsaml.json";
+ }
+
+
+}
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..f5b05df
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/bad-client-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:8081/bad-client-sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8081/bad-client-sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keystore.jks
new file mode 100755
index 0000000..6a3e3ba
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..2f7c581
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/bad-realm-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:8081/bad-realm-sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8081/bad-realm-sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keystore.jks
new file mode 100755
index 0000000..215384c
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..c892ec7
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-enc/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" encryption="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-enc/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keystore.jks
new file mode 100755
index 0000000..822162c
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..caaff5c
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,25 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/employee2/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="memberOf"/>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..51c8e0c
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,44 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/employee-sig-front/"
+ sslPolicy="EXTERNAL"
+ logoutPage="/logout.jsp"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/employee-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/employee-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="REDIRECT"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="REDIRECT"
+ responseBinding="REDIRECT"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keystore.jks
new file mode 100755
index 0000000..4daad21
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..909216d
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,44 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/employee-sig/"
+ sslPolicy="EXTERNAL"
+ logoutPage="/logout.jsp"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/employee-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/employee-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="REDIRECT"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="REDIRECT"
+ responseBinding="REDIRECT"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keystore.jks
new file mode 100755
index 0000000..4daad21
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..9a57c85
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-metadata/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..0a907ce
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..ef9856a
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,44 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig-email/"
+ sslPolicy="EXTERNAL"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..7bc05f6
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig-persistent/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..5d614fa
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,45 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig-transient/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..501e8f5
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,24 @@
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp">
+ <SingleSignOnService requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/sp-metadata.xml b/testsuite/integration/src/test/resources/keycloak-saml/sp-metadata.xml
new file mode 100755
index 0000000..9b8b899
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/sp-metadata.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntitiesDescriptor Name="urn:mace:shibboleth:testshib:two"
+ xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ >
+ <EntityDescriptor entityID="http://localhost:8081/sales-metadata/">
+ <SPSSODescriptor AuthnRequestsSigned="true"
+ protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext">
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ </NameIDFormat>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/sales-metadata/"/>
+ <AssertionConsumerService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/sales-metadata/"
+ index="1" isDefault="true" />
+ <KeyDescriptor use="signing">
+ <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
+ <dsig:X509Data>
+ <dsig:X509Certificate>
+ MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw==
+ </dsig:X509Certificate>
+ </dsig:X509Data>
+ </dsig:KeyInfo>
+ </KeyDescriptor>
+ </SPSSODescriptor>
+ <Organization>
+ <OrganizationName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xml:lang="en">JBoss</OrganizationName>
+ <OrganizationDisplayName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xml:lang="en">JBoss by Red Hat</OrganizationDisplayName>
+ <OrganizationURL xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xml:lang="en">http://localhost:8080/sales-metadata/</OrganizationURL>
+ </Organization>
+ <ContactPerson contactType="technical">
+ <GivenName>The</GivenName>
+ <SurName>Admin</SurName>
+ <EmailAddress>admin@mycompany.com</EmailAddress>
+ </ContactPerson>
+ </EntityDescriptor>
+</EntitiesDescriptor>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
new file mode 100755
index 0000000..5fafa8e
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
@@ -0,0 +1,367 @@
+{
+ "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" ],
+ "http://localhost:8081/employee2/": [ "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_SHA256",
+ "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/employee2/",
+ "enabled": true,
+ "protocol": "saml",
+ "fullScopeAllowed": true,
+ "baseUrl": "http://localhost:8081/employee2/",
+ "redirectUris": [
+ "http://localhost:8081/employee2/*"
+ ],
+ "adminUrl": "http://localhost:8081/employee2/",
+ "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"
+ }
+ ],
+ "http://localhost:8081/employee2/" : [
+ {
+ "name": "employee",
+ "description": "Have Employee privileges"
+ }
+ ]
+ }
+ }
+}
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 4db2adf..e929c24 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -69,7 +69,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -92,7 +91,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -114,7 +112,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -139,7 +136,6 @@
"saml.signature.algorithm": "RSA_SHA256",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -157,7 +153,6 @@
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
}
},
@@ -175,7 +170,6 @@
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
}
},
@@ -198,9 +192,7 @@
"saml.client.signature": "true",
"saml.encrypt": "true",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
- "saml.encryption.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
"saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
}
},
@@ -219,7 +211,6 @@
"saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
},
@@ -292,7 +283,6 @@
"saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true",
- "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
}