diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java index b59339cc91..d145efeb28 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeploymentContext.java @@ -25,12 +25,21 @@ import org.keycloak.adapters.spi.HttpFacade; */ public class SamlDeploymentContext { private SamlDeployment deployment = null; + private SamlConfigResolver resolver = null; public SamlDeploymentContext(SamlDeployment deployment) { this.deployment = deployment; } + public SamlDeploymentContext(SamlConfigResolver resolver) { + this.resolver = resolver; + } + public SamlDeployment resolveDeployment(HttpFacade facade) { - return deployment; + if (deployment != null) { + return deployment; + } else { + return resolver.resolve(facade.getRequest()); + } } } diff --git a/adapters/saml/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java b/adapters/saml/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java index ab6317a784..a06f1036e1 100755 --- a/adapters/saml/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java +++ b/adapters/saml/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java @@ -19,6 +19,7 @@ package org.keycloak.adapters.saml.servlet; import org.keycloak.adapters.saml.DefaultSamlDeployment; import org.keycloak.adapters.saml.SamlAuthenticator; +import org.keycloak.adapters.saml.SamlConfigResolver; import org.keycloak.adapters.saml.SamlDeployment; import org.keycloak.adapters.saml.SamlDeploymentContext; import org.keycloak.adapters.saml.SamlSession; @@ -74,15 +75,12 @@ public class SamlFilter implements Filter { String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver"); if (configResolverClass != null) { try { - throw new RuntimeException("Not implemented yet"); - // KeycloakConfigResolver configResolver = (KeycloakConfigResolver) - // context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance(); - // deploymentContext = new SamlDeploymentContext(configResolver); - // log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", - // configResolverClass); + SamlConfigResolver configResolver = (SamlConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance(); + deploymentContext = new SamlDeploymentContext(configResolver); + log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); } catch (Exception ex) { - log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[] { configResolverClass, ex.getMessage() }); - // deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment()); + log.log(Level.WARNING, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[] { configResolverClass, ex.getMessage() }); + deploymentContext = new SamlDeploymentContext(new DefaultSamlDeployment()); } } else { String fp = filterConfig.getInitParameter("keycloak.config.file"); diff --git a/adapters/saml/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java b/adapters/saml/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java index cc3abc2a17..3f57bff2d9 100755 --- a/adapters/saml/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java +++ b/adapters/saml/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java @@ -97,13 +97,12 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i String configResolverClass = context.getServletContext().getInitParameter("keycloak.config.resolver"); if (configResolverClass != null) { try { - throw new RuntimeException("Not implemented yet"); - //KeycloakConfigResolver configResolver = (KeycloakConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance(); - //deploymentContext = new SamlDeploymentContext(configResolver); - //log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); + SamlConfigResolver configResolver = (SamlConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance(); + deploymentContext = new SamlDeploymentContext(configResolver); + log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); } catch (Exception ex) { log.errorv("The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", configResolverClass, ex.getMessage()); - //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment()); + deploymentContext = new SamlDeploymentContext(new DefaultSamlDeployment()); } } else { InputStream is = getConfigInputStream(context); diff --git a/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java b/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java index 4985dfb4ca..1ffa287cfe 100755 --- a/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java +++ b/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java @@ -120,13 +120,12 @@ public class SamlServletExtension implements ServletExtension { 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."); + configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance(); + deploymentContext = new SamlDeploymentContext(configResolver); + log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); } 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()); + deploymentContext = new SamlDeploymentContext(new DefaultSamlDeployment()); } } else { InputStream is = getConfigInputStream(servletContext); diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java index f8eb110356..41762870aa 100644 --- a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java +++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java @@ -30,6 +30,7 @@ import org.jboss.logging.Logger; import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.saml.AdapterConstants; 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; @@ -61,13 +62,12 @@ public class KeycloakConfigurationServletListener implements ServletContextListe if (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."); + SamlConfigResolver configResolver = (SamlConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance(); + deploymentContext = new SamlDeploymentContext(configResolver); + log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); } 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()); + log.errorv("The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[] { configResolverClass, ex.getMessage() }); + deploymentContext = new SamlDeploymentContext(new DefaultSamlDeployment()); } } else { InputStream is = getConfigInputStream(servletContext); diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAPDeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/jboss/eap/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAPDeploymentArchiveProcessor.java index 28daaeba5f..71fe996866 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAPDeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAPDeploymentArchiveProcessor.java @@ -40,7 +40,9 @@ public class EAPDeploymentArchiveProcessor implements ApplicationArchiveProcesso modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH); modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS); - modifySAMLAdapterConfig(archive); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2); } private void modifyWebXML(Archive archive, TestClass testClass) { @@ -61,10 +63,10 @@ public class EAPDeploymentArchiveProcessor implements ApplicationArchiveProcesso DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath); } - private void modifySAMLAdapterConfig(Archive archive) { - if (!archive.contains(DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH)) return; + private void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + if (!archive.contains(adapterConfigPath)) return; - log.debug("Modifying adapter config " + DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH + " in " + archive.getName()); - DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive); + log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); + DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath); } } diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAP6DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAP6DeploymentArchiveProcessor.java index edcbea02b7..0a2618637a 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAP6DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAP6DeploymentArchiveProcessor.java @@ -42,7 +42,9 @@ public class EAP6DeploymentArchiveProcessor implements ApplicationArchiveProcess modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH); modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS); - modifySAMLAdapterConfig(archive); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2); } private void modifyWebXML(Archive archive, TestClass testClass) { @@ -72,10 +74,10 @@ public class EAP6DeploymentArchiveProcessor implements ApplicationArchiveProcess DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath); } - private void modifySAMLAdapterConfig(Archive archive) { - if (!archive.contains(DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH)) return; + private void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + if (!archive.contains(adapterConfigPath)) return; - log.debug("Modifying adapter config " + DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH + " in " + archive.getName()); - DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive); + log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); + DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath); } } diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/WildflyDeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/WildflyDeploymentArchiveProcessor.java index a5ee439ec6..644afcbb67 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/WildflyDeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/WildflyDeploymentArchiveProcessor.java @@ -40,7 +40,9 @@ public class WildflyDeploymentArchiveProcessor implements ApplicationArchiveProc modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH); modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS); - modifySAMLAdapterConfig(archive); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2); } private void modifyWebXML(Archive archive, TestClass testClass) { @@ -61,10 +63,10 @@ public class WildflyDeploymentArchiveProcessor implements ApplicationArchiveProc DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath); } - private void modifySAMLAdapterConfig(Archive archive) { - if (!archive.contains(DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH)) return; + private void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + if (!archive.contains(adapterConfigPath)) return; - log.debug("Modifying adapter config " + DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH + " in " + archive.getName()); - DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive); + log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); + DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath); } } diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly10/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly10DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly10/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly10DeploymentArchiveProcessor.java index 5c61ab5ee2..4d57817820 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly10/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly10DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly10/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly10DeploymentArchiveProcessor.java @@ -41,7 +41,9 @@ public class Wildfly10DeploymentArchiveProcessor implements ApplicationArchivePr modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH); modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS); - modifySAMLAdapterConfig(archive); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2); } private void modifyWebXML(Archive archive, TestClass testClass) { @@ -62,10 +64,10 @@ public class Wildfly10DeploymentArchiveProcessor implements ApplicationArchivePr DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath); } - private void modifySAMLAdapterConfig(Archive archive) { - if (!archive.contains(DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH)) return; + private void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + if (!archive.contains(adapterConfigPath)) return; - log.debug("Modifying adapter config " + DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH + " in " + archive.getName()); - DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive); + log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); + DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath); } } diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly9/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly9DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly9/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly9DeploymentArchiveProcessor.java index ef2294762f..b659b888da 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly9/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly9DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly9/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly9DeploymentArchiveProcessor.java @@ -41,7 +41,9 @@ public class Wildfly9DeploymentArchiveProcessor implements ApplicationArchivePro modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH); modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH_JS); - modifySAMLAdapterConfig(archive); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT1); + modifySAMLAdapterConfig(archive, DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH_TENANT2); } private void modifyWebXML(Archive archive, TestClass testClass) { @@ -62,10 +64,10 @@ public class Wildfly9DeploymentArchiveProcessor implements ApplicationArchivePro DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath); } - private void modifySAMLAdapterConfig(Archive archive) { - if (!archive.contains(DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH)) return; + private void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + if (!archive.contains(adapterConfigPath)) return; - log.debug("Modifying adapter config " + DeploymentArchiveProcessorUtils.SAML_ADAPTER_CONFIG_PATH + " in " + archive.getName()); - DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive); + log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); + DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath); } } diff --git a/testsuite/integration-arquillian/test-apps/servlets/pom.xml b/testsuite/integration-arquillian/test-apps/servlets/pom.xml index 8ed980f196..ac6fc5b060 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml @@ -50,6 +50,14 @@ org.keycloak keycloak-saml-core + + org.keycloak + keycloak-saml-adapter-core + + + org.keycloak + keycloak-saml-core-public + - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlMultiTenantResolver.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlMultiTenantResolver.java new file mode 100644 index 0000000000..559a4ef3a8 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlMultiTenantResolver.java @@ -0,0 +1,60 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.testsuite.adapter.servlet; + +import java.io.InputStream; +import org.keycloak.adapters.saml.SamlConfigResolver; +import org.keycloak.adapters.saml.SamlDeployment; +import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder; +import org.keycloak.adapters.saml.config.parsers.ResourceLoader; +import org.keycloak.adapters.spi.HttpFacade; +import org.keycloak.saml.common.exceptions.ParsingException; + +/** + * + * @author rmartinc + */ +public class SamlMultiTenantResolver implements SamlConfigResolver { + + @Override + public SamlDeployment resolve(HttpFacade.Request request) { + String realm = request.getQueryParamValue("realm"); + if (realm == null) { + throw new IllegalStateException("Not able to resolve realm from the request path!"); + } + + InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml"); + if (is == null) { + throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml"); + } + + ResourceLoader loader = new ResourceLoader() { + @Override + public InputStream getResourceAsStream(String path) { + return getClass().getResourceAsStream(path); + } + }; + + try { + return new DeploymentBuilder().build(is, loader); + } catch (ParsingException e) { + throw new IllegalStateException("Cannot load SAML deployment", e); + } + } + +} diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java index 2c0b17d1d2..e7955266b6 100755 --- a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java +++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java @@ -205,4 +205,14 @@ public class SendUsernameServlet extends HttpServlet { return output; } + + @GET + @Path("getAssertionIssuer") + public Response getAssertionIssuer() throws IOException { + sentPrincipal = httpServletRequest.getUserPrincipal(); + SamlPrincipal principal = (SamlPrincipal) sentPrincipal; + return Response.ok(principal.getAssertion().getIssuer().getValue()) + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_TYPE + ";charset=UTF-8").build(); + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant1Saml.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant1Saml.java new file mode 100644 index 0000000000..bb08f88284 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant1Saml.java @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.testsuite.adapter.page; + +import java.net.MalformedURLException; +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; + +import java.net.URL; +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author rmartinc + */ +public class MultiTenant1Saml extends SAMLServlet { + public static final String DEPLOYMENT_NAME = "multi-tenant-saml"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + try { + return new URL(url + "/?realm=tenant1"); + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void logout() { + driver.navigate().to(getUriBuilder().queryParam("GLO", "true").queryParam("realm", "tenant1").build().toASCIIString()); + getUriBuilder().replaceQueryParam("GLO"); + pause(300); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant2Saml.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant2Saml.java new file mode 100644 index 0000000000..e2623d1d21 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant2Saml.java @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.testsuite.adapter.page; + +import java.net.MalformedURLException; +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; + +import java.net.URL; +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author rmartinc + */ +public class MultiTenant2Saml extends SAMLServlet { + public static final String DEPLOYMENT_NAME = "multi-tenant-saml"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + try { + return new URL(url + "/?realm=tenant2"); + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void logout() { + driver.navigate().to(getUriBuilder().queryParam("GLO", "true").queryParam("realm", "tenant2").build().toASCIIString()); + getUriBuilder().replaceQueryParam("GLO"); + pause(300); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java index 90a9850a34..2ca18808e4 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java @@ -37,9 +37,16 @@ import org.keycloak.testsuite.utils.io.IOUtil; import org.keycloak.util.JsonSerialization; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.DOMException; +import org.w3c.dom.NodeList; import java.io.File; import java.io.IOException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.jboss.logging.Logger; import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.isEAP6AppServer; import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.isEAPAppServer; @@ -81,6 +88,8 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { public static final String ADAPTER_CONFIG_PATH_JS = "/keycloak.json"; public static final String SAML_ADAPTER_CONFIG_PATH = "/WEB-INF/keycloak-saml.xml"; public static final String JBOSS_DEPLOYMENT_XML_PATH = "/WEB-INF/jboss-deployment-structure.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT1 = "/WEB-INF/classes/tenant1-keycloak-saml.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT2 = "/WEB-INF/classes/tenant2-keycloak-saml.xml"; @Inject @ClassScoped @@ -131,15 +140,17 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_TENANT2, relative); modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_JS, relative); modifyAdapterConfig(archive, SAML_ADAPTER_CONFIG_PATH, relative); + modifyAdapterConfig(archive, SAML_ADAPTER_CONFIG_PATH_TENANT1, relative); + modifyAdapterConfig(archive, SAML_ADAPTER_CONFIG_PATH_TENANT2, relative); } protected void modifyAdapterConfig(Archive archive, String adapterConfigPath, boolean relative) { if (archive.contains(adapterConfigPath)) { log.info("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); - if (adapterConfigPath.equals(SAML_ADAPTER_CONFIG_PATH)) { // SAML adapter config + if (adapterConfigPath.endsWith(".xml")) { // SAML adapter config log.info("Modifying saml adapter config in " + archive.getName()); - Document doc = loadXML(archive.get("WEB-INF/keycloak-saml.xml").getAsset().openStream()); + Document doc = loadXML(archive.get(adapterConfigPath).getAsset().openStream()); if (AUTH_SERVER_SSL_REQUIRED) { modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "8080", System.getProperty("auth.server.https.port")); modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "http", "https"); @@ -225,7 +236,6 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { } if (testClass.getJavaClass().isAnnotationPresent(UseServletFilter.class) && archive.contains(JBOSS_DEPLOYMENT_XML_PATH)) { - addFilterDependencies(archive, testClass); //We need to add filter declaration to web.xml @@ -240,6 +250,20 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { filter.appendChild(filterName); filter.appendChild(filterClass); + + // check if there was a resolver for OIDC and set as a filter param + String keycloakResolverClass = getKeycloakResolverClass(webXmlDoc); + if (keycloakResolverClass != null) { + Element initParam = webXmlDoc.createElement("init-param"); + Element paramName = webXmlDoc.createElement("param-name"); + paramName.setTextContent("keycloak.config.resolver"); + Element paramValue = webXmlDoc.createElement("param-value"); + paramValue.setTextContent(keycloakResolverClass); + initParam.appendChild(paramName); + initParam.appendChild(paramValue); + filter.appendChild(initParam); + } + appendChildInDocument(webXmlDoc, "web-app", filter); filter.appendChild(filterName); @@ -292,4 +316,21 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { archive.add(new StringAsset((documentToString(webXmlDoc))), WEBXML_PATH); } + + private String getKeycloakResolverClass(Document doc) { + try { + XPathFactory factory = XPathFactory.newInstance(); + XPath xpath = factory.newXPath(); + XPathExpression expr = xpath.compile("//web-app/context-param[param-name='keycloak.config.resolver']/param-value/text()"); + NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + if (nodes != null && nodes.getLength() > 0) { + return nodes.item(0).getNodeValue(); + } + } catch(DOMException e) { + throw new IllegalStateException(e); + } catch (XPathExpressionException e) { + throw new IllegalStateException(e); + } + return null; + } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant1.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant1.java new file mode 100644 index 0000000000..274937daf2 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant1.java @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.testsuite.auth.page.login; + +/** + * @author rmartinc + */ +public class SAMLPostLoginTenant1 extends Login { + SAMLPostLoginTenant1() { + setProtocol(LOGIN_ACTION); + setAuthRealm("tenant1"); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant2.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant2.java new file mode 100644 index 0000000000..a933d6073d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant2.java @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.testsuite.auth.page.login; + +/** + * @author rmartinc + */ +public class SAMLPostLoginTenant2 extends Login { + SAMLPostLoginTenant2() { + setProtocol(LOGIN_ACTION); + setAuthRealm("tenant2"); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java index 622011026e..ba694bb14d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; import java.util.List; +import org.jboss.shrinkwrap.api.asset.UrlAsset; import org.junit.Assert; import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; @@ -110,6 +111,49 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest { return deployment; } + public static WebArchive samlServletDeploymentMultiTenant(String name, String webXMLPath, + String config1, String config2, + String keystore1, String keystore2, Class... servletClasses) { + String baseSAMLPath = "/adapter-test/keycloak-saml/"; + String webInfPath = baseSAMLPath + name + "/WEB-INF/"; + + URL webXML = AbstractServletsAdapterTest.class.getResource(baseSAMLPath + webXMLPath); + Assert.assertNotNull("web.xml should be in " + baseSAMLPath + webXMLPath, webXML); + + WebArchive deployment = ShrinkWrap.create(WebArchive.class, name + ".war") + .addClasses(servletClasses) + .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML); + + String webXMLContent; + try { + webXMLContent = IOUtils.toString(webXML.openStream(), Charset.forName("UTF-8")) + .replace("%CONTEXT_PATH%", name); + } catch (IOException e) { + throw new RuntimeException(e); + } + deployment.add(new StringAsset(webXMLContent), "/WEB-INF/web.xml"); + + // add the xml for each tenant in classes + URL config1Url = AbstractServletsAdapterTest.class.getResource(webInfPath + config1); + Assert.assertNotNull("config1Url should be in " + webInfPath + config1, config1Url); + deployment.add(new UrlAsset(config1Url), "/WEB-INF/classes/" + config1); + URL config2Url = AbstractServletsAdapterTest.class.getResource(webInfPath + config2); + Assert.assertNotNull("config2Url should be in " + webInfPath + config2, config2Url); + deployment.add(new UrlAsset(config2Url), "/WEB-INF/classes/" + config2); + + // add the keystores for each tenant in classes + URL keystore1Url = AbstractServletsAdapterTest.class.getResource(webInfPath + keystore1); + Assert.assertNotNull("keystore1Url should be in " + webInfPath + keystore1, keystore1Url); + deployment.add(new UrlAsset(keystore1Url), "/WEB-INF/classes/" + keystore1); + URL keystore2Url = AbstractServletsAdapterTest.class.getResource(webInfPath + keystore2); + Assert.assertNotNull("keystore2Url should be in " + webInfPath + keystore2, keystore2Url); + deployment.add(new UrlAsset(keystore2Url), "/WEB-INF/classes/" + keystore2); + + addContextXml(deployment, name); + + return deployment; + } + @Override public void addAdapterTestRealms(List testRealms) { testRealms.add(IOUtil.loadRealm("/adapter-test/demorealm.json")); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java index 18df393ce6..0f0e79b249 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java @@ -135,6 +135,8 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; import org.keycloak.testsuite.arquillian.containers.ContainerConstants; import org.keycloak.testsuite.auth.page.login.Login; import org.keycloak.testsuite.auth.page.login.SAMLIDPInitiatedLogin; +import org.keycloak.testsuite.auth.page.login.SAMLPostLoginTenant1; +import org.keycloak.testsuite.auth.page.login.SAMLPostLoginTenant2; import org.keycloak.testsuite.page.AbstractPage; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.SamlClient; @@ -255,6 +257,18 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest { @Page protected EcpSP ecpSPPage; + @Page + protected MultiTenant1Saml mutiTenant1SamlPage; + + @Page + protected MultiTenant2Saml mutiTenant2SamlPage; + + @Page + protected SAMLPostLoginTenant1 tenant1RealmSAMLPostLoginPage; + + @Page + protected SAMLPostLoginTenant2 tenant2RealmSAMLPostLoginPage; + public static final String FORBIDDEN_TEXT = "HTTP status code: 403"; public static final String WEBSPHERE_FORBIDDEN_TEXT = "Error reported: 403"; @@ -399,9 +413,19 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest { return samlServletDeployment(EcpSP.DEPLOYMENT_NAME, SendUsernameServlet.class); } + @Deployment(name = MultiTenant1Saml.DEPLOYMENT_NAME) + protected static WebArchive multiTenant() { + return samlServletDeploymentMultiTenant(MultiTenant1Saml.DEPLOYMENT_NAME, "multi-tenant-saml/WEB-INF/web.xml", + "tenant1-keycloak-saml.xml", "tenant2-keycloak-saml.xml", + "keystore-tenant1.jks", "keystore-tenant2.jks", + SendUsernameServlet.class, SamlMultiTenantResolver.class); + } + @Override public void addAdapterTestRealms(List testRealms) { testRealms.add(IOUtil.loadRealm("/adapter-test/keycloak-saml/testsaml.json")); + testRealms.add(IOUtil.loadRealm("/adapter-test/keycloak-saml/tenant1-realm.json")); + testRealms.add(IOUtil.loadRealm("/adapter-test/keycloak-saml/tenant2-realm.json")); } @Override @@ -436,6 +460,14 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest { || driver.getPageSource().contains(FORBIDDEN_TEXT) || driver.getPageSource().contains(WEBSPHERE_FORBIDDEN_TEXT)); // WebSphere } + + private void assertFailedLogin(AbstractPage page, UserRepresentation user, Login loginPage) { + page.navigateTo(); + assertCurrentUrlStartsWith(loginPage); + loginPage.form().login(user); + // we remain in login + assertCurrentUrlStartsWith(loginPage); + } private void assertSuccessfulLogin(AbstractPage page, UserRepresentation user, Login loginPage, String expectedString) { page.navigateTo(); @@ -554,6 +586,42 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest { assertSuccessfulLogin(employeeAcsServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke"); } + @Test + public void multiTenant1SamlTest() throws Exception { + UserRepresentation user1 = createUserRepresentation("user-tenant1", "user-tenant1@redhat.com", "Bill", "Burke", true); + setPasswordFor(user1, "user-tenant1"); + // check the user in the tenant logs in ok + assertSuccessfulLogin(mutiTenant1SamlPage, user1, tenant1RealmSAMLPostLoginPage, "principal=user-tenant1"); + // check the issuer is the correct tenant + driver.navigate().to(mutiTenant1SamlPage.getUriBuilder().path("getAssertionIssuer").build().toASCIIString()); + waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant1"); + // check logout + mutiTenant1SamlPage.logout(); + checkLoggedOut(mutiTenant1SamlPage, tenant1RealmSAMLPostLoginPage); + // check a user in the other tenant doesn't login + UserRepresentation user2 = createUserRepresentation("user-tenant2", "user-tenant2@redhat.com", "Bill", "Burke", true); + setPasswordFor(user2, "user-tenant2"); + assertFailedLogin(mutiTenant1SamlPage, user2, tenant1RealmSAMLPostLoginPage); + } + + @Test + public void multiTenant2SamlTest() throws Exception { + UserRepresentation user2 = createUserRepresentation("user-tenant2", "user-tenant2@redhat.com", "Bill", "Burke", true); + setPasswordFor(user2, "user-tenant2"); + // check the user in the tenant logs in ok + assertSuccessfulLogin(mutiTenant2SamlPage, user2, tenant2RealmSAMLPostLoginPage, "principal=user-tenant2"); + // check the issuer is the correct tenant + driver.navigate().to(mutiTenant2SamlPage.getUriBuilder().path("getAssertionIssuer").build().toASCIIString()); + waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant2"); + // check logout + mutiTenant2SamlPage.logout(); + checkLoggedOut(mutiTenant2SamlPage, tenant2RealmSAMLPostLoginPage); + // check a user in the other tenant doesn't login + UserRepresentation user1 = createUserRepresentation("user-tenant1", "user-tenant1@redhat.com", "Bill", "Burke", true); + setPasswordFor(user1, "user-tenant1"); + assertFailedLogin(mutiTenant2SamlPage, user1, tenant2RealmSAMLPostLoginPage); + } + private static final KeyPair NEW_KEY_PAIR = KeyUtils.generateRsaKeyPair(1024); private static final String NEW_KEY_PRIVATE_KEY_PEM = PemUtils.encodeKey(NEW_KEY_PAIR.getPrivate()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml index 93562268d8..1edfc3ebad 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml @@ -26,7 +26,8 @@ + - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant1.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant1.jks new file mode 100644 index 0000000000..c3ad85b4fb Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant1.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant2.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant2.jks new file mode 100644 index 0000000000..d2b3d9bfb2 Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant2.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant1-keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant1-keycloak-saml.xml new file mode 100644 index 0000000000..cd3ae8b96f --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant1-keycloak-saml.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant2-keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant2-keycloak-saml.xml new file mode 100644 index 0000000000..4058f4df0a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant2-keycloak-saml.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/web.xml new file mode 100644 index 0000000000..1720657cb3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/web.xml @@ -0,0 +1,62 @@ + + + + + + %CONTEXT_PATH% + + + javax.ws.rs.core.Application + 1 + + + javax.ws.rs.core.Application + /* + + + + /error.html + + + + + Application + /* + + + user + + + + + KEYCLOAK-SAML + demo + + + + manager + + + + keycloak.config.resolver + org.keycloak.testsuite.adapter.servlet.SamlMultiTenantResolver + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant1-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant1-realm.json new file mode 100644 index 0000000000..99c13cfbc8 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant1-realm.json @@ -0,0 +1,73 @@ +{ + "id": "tenant1", + "realm": "tenant1", + "enabled": true, + "accessTokenLifespan": 3000, + "accessCodeLifespan": 10, + "accessCodeLifespanUserAction": 6000, + "sslRequired": "external", + "registrationAllowed": false, + "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" ], + "users" : [ + { + "username" : "bburke@redhat.com", + "enabled": true, + "email" : "bburke@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": [ "user" ] + }, + { + "username" : "user-tenant1", + "enabled": true, + "email" : "user-tenant1@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "user-tenant1" } + ], + "realmRoles": [ "user" ] + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + } + ] + }, + "clients": [ + { + "clientId": "multi-tenant", + "name": "multi-tenant", + "enabled": true, + "protocol": "saml", + "fullScopeAllowed": true, + "frontchannelLogout": true, + "baseUrl": "http://localhost:8080/multi-tenant-saml/", + "redirectUris": [ + "http://localhost:8080/multi-tenant-saml/*" + ], + "attributes": { + "saml_assertion_consumer_url_post": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant1", + "saml_assertion_consumer_url_redirect": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant1", + "saml_single_logout_service_url_post": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant1", + "saml_single_logout_service_url_redirect": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant1", + "saml.server.signature": "true", + "saml.client.signature": "true", + "saml.signature.algorithm": "RSA_SHA256", + "saml.authnstatement": "true", + "saml.signing.certificate": "MIICwTCCAakCBgFjh8UCJDANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBltdWx0aS10ZW5hbnQtc2FtbC10ZW5hbnQxMB4XDTE4MDUyMjEyMTIwNVoXDTI4MDUyMjEyMTM0NVowJDEiMCAGA1UEAwwZbXVsdGktdGVuYW50LXNhbWwtdGVuYW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJFoRVHp55NFqAbzQ9WRWKNP3yUrAT5HT0klZg8ttuMq5JhuGvl5aADnoOUhD/SbJST73ObF3JMqRSW8899yKByYxG5HH03KEpbGbB+gT2dYwzHSqN7E2G0h+VSpfQvjOyMFdRQORbBicnTVN+a828JlTf7uxmQ2ifgKuuZgUtUydLRh9vbcbYMP0sqKBNXAJKzJuqv6yQPmn78OGvtDZz+oThcJ44QQ19A/PaNrDNPKvjQsibfBfyV/tCaRs6UKIdROy272ZDL8aqVrZqnFzF5mTNOa+Ko91UTLaWKsSxBPk3Tv2tZJkeoWztFBOjVEV3Uz76BraUSZeg78oNfuuJMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABVma/QDyE2ZyNZuFjoJ9+FuUlTElcoHsId1W6UY1tfpi4kqBneLtxJfOUtCHveogNgFaJJHiAfJDtpj3iNuEtziyRQuSlg7NiHHhKcxAUpBBs6So2zlTfTrwC647IOQWPFQRTQruJJnSMQHQ0PRBG6HBQKpVtltk5PEMOi0YK/y0XxMMqi2/0TCyVEuQlLbu+gN95xIkXTh90dte1Vh7PJk54Mby9Yk3Q0Qq0aVF805/GimA9o/rGYSruH6Tj8qGp6rvVN6StM4o9sDnZIEcZMEWTvaeLZxQ0A8TD4NWgp+M2MmRH/hahyOkiYL3nv/S/MTe0VmC908h6+R0QqGTYQ==", + "saml.signing.private.key": "MIIEowIBAAKCAQEAkWhFUennk0WoBvND1ZFYo0/fJSsBPkdPSSVmDy224yrkmG4a+XloAOeg5SEP9JslJPvc5sXckypFJbzz33IoHJjEbkcfTcoSlsZsH6BPZ1jDMdKo3sTYbSH5VKl9C+M7IwV1FA5FsGJydNU35rzbwmVN/u7GZDaJ+Aq65mBS1TJ0tGH29txtgw/SyooE1cAkrMm6q/rJA+afvw4a+0NnP6hOFwnjhBDX0D89o2sM08q+NCyJt8F/JX+0JpGzpQoh1E7LbvZkMvxqpWtmqcXMXmZM05r4qj3VRMtpYqxLEE+TdO/a1kmR6hbO0UE6NURXdTPvoGtpRJl6Dvyg1+64kwIDAQABAoIBAA3Xrlm4+cnEZNWcjQWk25pYfTbNnEWwhjTBcbDaOkHwEGkOelTroOINKv0FI762klet/n6dsXz1FjYcgd7wwC7QwEp7TNib9x8RbrOoEEcXZSW2F0t10+C3zkOoCvZ5wGR6HYY2QZ4kER9cOQEnU4hzGnS9iHd71bCeXOKXousWyJGMg3Bl5MSIqNabSIMNTqOHVR8TEMOSGjkYQO4oTP+YcySbIatAVj95aMGTUWJZxMVDBHt2CyjvKIRq1nJV8nhuUH3ui2wZJIscjPuSWFHAuB/dDli2XK8aZUyMGrbD2AjFhegrG73/w71QvCqZ14hOiaX3SCRKJFqwsZ+iW/kCgYEA2emTez8HDvxW5dhbV+qIl+PSBhGBz7w0Wr6dc7Nakriue9ad3lBr2LjrI2hLUHcrS/TKhnyLfe67lODysYPd9HHwFDMxgXQLTrZudQkUt8ebNi//1MGRuZ1Z6W/7MW9oN/JwoqWcYkXN+4bIQyTkKQA4lvkaBOuKEIFUn059I50CgYEAqtJ56pc9gJDB+nnJ778dYlLLxjsGIZawWXf9BgxnPnoIRn5ejOusfB/BExeLxngmMOxNI0IAL59NmkNPCse/SSXGM8GS2llSqEtZBCtTfYuhhyAhoqA4qwrm9WJybuYaKeBllmmrbOX/TmzhC0dwy0anUG0tm4NFTV8AK95hje8CgYEAm3woeWYteSngLzxDYOW99PLfpujTARC/Ioij/CxbUhloloA6QKiNayP201rVcmK1iArwfylats6jFcW0Jal7s7GgpikpB79vWgido/CI0eEhBHcXSg2cFx8JSqFWUJ23dUQNzl/wx8YbBX/UYORv0DmSJ1cyk5Qk/UXqxYjRjZkCgYACwfcZ5Gsnwi5/fqvV5P3ycme7wYQt0qLyLs+040pfZdTwXmXkXIGiV1jkmAK3p4TmUUpFgXFDU40LKn8CK4tZAPUcLMnUIJEHCoBbYt+sLS7kYY5pc7C2giyMVZSHWcueVXMOZJJR5byjZXqUlgiqH2/gCoMr+YiK4Te9fY+RnQKBgGekolH+YH4HiUtYKn5qGKveiSpOy/X90zMLLKFWVjngsYEqDj7Bi8S8t1MReaMPMMo/sEBwYE+jomnCH3Dj+HlKf00WjFa/5pbFWqbiIcGGdXslfJSNSDeQ5bpdc6MqFnumZHumExqUGGGXUxVXnhf2NyhfGixNiukWXAIhzlW2" + } + } + ] +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant2-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant2-realm.json new file mode 100644 index 0000000000..97deab5217 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant2-realm.json @@ -0,0 +1,73 @@ +{ + "id": "tenant2", + "realm": "tenant2", + "enabled": true, + "accessTokenLifespan": 3000, + "accessCodeLifespan": 10, + "accessCodeLifespanUserAction": 6000, + "sslRequired": "external", + "registrationAllowed": false, + "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" ], + "users" : [ + { + "username" : "bburke@redhat.com", + "enabled": true, + "email" : "bburke@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": [ "user" ] + }, + { + "username" : "user-tenant2", + "enabled": true, + "email" : "user-tenant2@redhat.com", + "firstName": "Bill", + "lastName": "Burke", + "credentials" : [ + { "type" : "password", + "value" : "user-tenant2" } + ], + "realmRoles": [ "user" ] + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + } + ] + }, + "clients": [ + { + "clientId": "multi-tenant", + "name": "multi-tenant", + "enabled": true, + "protocol": "saml", + "fullScopeAllowed": true, + "frontchannelLogout": true, + "baseUrl": "http://localhost:8080/multi-tenant-saml/", + "redirectUris": [ + "http://localhost:8080/multi-tenant-saml/*" + ], + "attributes": { + "saml_assertion_consumer_url_post": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant2", + "saml_assertion_consumer_url_redirect": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant2", + "saml_single_logout_service_url_post": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant2", + "saml_single_logout_service_url_redirect": "http://localhost:8080/multi-tenant-saml/saml?realm=tenant2", + "saml.server.signature": "true", + "saml.client.signature": "true", + "saml.signature.algorithm": "RSA_SHA256", + "saml.authnstatement": "true", + "saml.signing.certificate": "MIICpzCCAY8CBgFjh/X1zDANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxtdWx0aS10ZW5hbnQwHhcNMTgwNTIyMTMwNTMzWhcNMjgwNTIyMTMwNzEzWjAXMRUwEwYDVQQDDAxtdWx0aS10ZW5hbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEIM1mJ9KE9OtVDl+6N9Cyg1byP8g8NARWIxstKR69eb9MlrXdzAZtv6S/DyLHuNpjX1DXisuHYHn4Si/2xa0XpRos151lOrk4YrJPokQWz+/Qej3vpJOeWC/EXeISs59t3zrUf/v2OtI57lln7ItABe6+nbTmGuLVCXYxHTaLxo4PRMh9IdsWIWjGi1F4uUR1P253ty9GgYFiRzNMxT9M4yOc5nXqih0kid7iZp+Idz1qLdxphgwUY5ElV4aBy1g5eudll8UFsGqaYUNC+V048BRoGyjstzoN8nYDN7yn89Vj5mjEzPH+FQwd1YlT2f7jH92UMBDdHpWrVOEF0DPVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACaadxu5ap+jr1wsXUX7KrrS5zc5yrNndEduFRLCetqeQ2hAmQkawqVDJnbjGH+uK5pNQ9Qk+ez77hbjMd8TlDwWD6wKdnIgZybxZ8WQi2/IWGXhrHwyITKSJukG1aDu1c9gwO9ho8J17bjpiaINx35s6Gf+iAttwv+Az7DytU42nhXxbSIgG4kb/RQkqOyIagCDJtExUgAs0mdG/Qm0uIMBQ3OZon06tAkj8H2M6PagRHDxIvCuu+MvOzv7WKOtOBP8p/cw5+1tagb9zO7U5yzT9iexYKfn5soU80sbUFgy5PZPq5MEF/xqzgwJdZVkRk0XAcIDTLjwquxd0R+u9tg=", + "saml.signing.private.key": "MIIEpAIBAAKCAQEAxCDNZifShPTrVQ5fujfQsoNW8j/IPDQEViMbLSkevXm/TJa13cwGbb+kvw8ix7jaY19Q14rLh2B5+Eov9sWtF6UaLNedZTq5OGKyT6JEFs/v0Ho976STnlgvxF3iErOfbd861H/79jrSOe5ZZ+yLQAXuvp205hri1Ql2MR02i8aOD0TIfSHbFiFoxotReLlEdT9ud7cvRoGBYkczTMU/TOMjnOZ16oodJIne4mafiHc9ai3caYYMFGORJVeGgctYOXrnZZfFBbBqmmFDQvldOPAUaBso7Lc6DfJ2Aze8p/PVY+ZoxMzx/hUMHdWJU9n+4x/dlDAQ3R6Vq1ThBdAz1QIDAQABAoIBACKrbcOuLG+mX+dUOCXR8glsYDVIgxvpUg7r+8Ta7P0vhVqDlbiUdVp3Myc3BL3rdmd0lPTVKy9OJaF3c80amoOAgwUERGV9oPpPsBeVppWlwk3HHiW7oQCvtBnxQqJtsDQa7upbiW24bishcBqH3QG/SrnVZQH8JLbmCkeaU2cXrbqZchGTj4nMVlHaFgOqb9A6y911Jlbyh9F2lP5aNRPekcNUV17+JhxKxCeyPesrvwey4DcbTWC0Li+BtnO2YwGADRteiz23g8xz+miQO4vTcOy/rctgNkmlo6U9vSwfuvRmMlVy1q1JnAiyATr/nzAz5zYPSHuQjeA/i9vZU00CgYEA9fSKyd3l+AZDPivVb+uqOu6pWAs3UoVZwE7SeEvvt93o+eOMWn+e9K5XimCfT/EM+B09L186hx5RL46ZNuUGWDtCSZXWpD5MRIt7e3gLSmVUyg9BM5UJk7zRwi933A5+VqwpTw/ivT0XSVbErRPSirmbXLO2n+i00a7J9BmsP/cCgYEAzCNRkaiW4dEMGsdACa+FW+QATvGXtcW10xBD3NuvBo8XvNRf/d2o3Pd99cljTlKiBLMZYap3+AuJzHtHRb9bEhJDKmitVlZpcNCcC0RA3t+9CfNdvHCrpW+lZEdVUFmIbkvgkqeFG2e7o03Q2G1avDnjzQxCworO5XJQMBcYD5MCgYEA4RXSjbsM4laY4ySqR6qcNyKCx5g8IMD4yg1Yf86+qr3ioA2mPIvepH2Ij5KtOTOYctgPTnMP1Ofh1Gvju2EM1WIl38HIlLaOhYxAjVXmv0bMub4MJXCXOyTpsZRPVIvPAvK7OyeGkTh/PxaxFtO1Mk955vRwhRcpo1saZtG32TECgYEAtyWg0xv8cpEJWSUWkRoGfdDrbehXAmBlpv1axVXbi/jphSLNFIjALa9mNRP/oo+EiM7eoL8+by567RhVc4AhBu+Xjv7nNSTF6M9gkMMlqE/33GuZ160GcqDeND/DjRkmzD4LN8hQJaxFrlfsXaCO3XzaomazprK+uSB8TQkLLz0CgYAWja2b42bn9vqXWnhfOorgD+HaBpVhhOAzyJu+U2YJViOSdtZpUgQCmz8XUOm5en2CwAIFOG0NvKtpLMmqNKnC7Gigg6ow2hw0v+VaH2trU295Z/lkRaX6fUT//My04aXPHg5bR3DXyd3YKOUV1MfvpTQelG1MKLX3YaMGxcmZ5w==" + } + } + ] +} diff --git a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java index c5dece7818..45933f4358 100644 --- a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java +++ b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/arquillian/DeploymentArchiveProcessorUtils.java @@ -18,6 +18,11 @@ package org.keycloak.testsuite.utils.arquillian; import java.io.File; import java.io.IOException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.jboss.arquillian.test.spi.TestClass; import org.jboss.logging.Logger; import org.jboss.shrinkwrap.api.Archive; @@ -28,8 +33,10 @@ import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.testsuite.utils.annotation.UseServletFilter; import org.keycloak.testsuite.utils.io.IOUtil; import org.keycloak.util.JsonSerialization; +import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; /** * @@ -49,6 +56,8 @@ public class DeploymentArchiveProcessorUtils { public static final String ADAPTER_CONFIG_PATH_JS = "/keycloak.json"; public static final String SAML_ADAPTER_CONFIG_PATH = "/WEB-INF/keycloak-saml.xml"; public static final String JBOSS_DEPLOYMENT_XML_PATH = "/WEB-INF/jboss-deployment-structure.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT1 = "/WEB-INF/classes/tenant1-keycloak-saml.xml"; + public static final String SAML_ADAPTER_CONFIG_PATH_TENANT2 = "/WEB-INF/classes/tenant2-keycloak-saml.xml"; /** * @return true iff archive's name equals run-on-server-classes.war @@ -85,6 +94,19 @@ public class DeploymentArchiveProcessorUtils { filter.appendChild(filterName); filter.appendChild(filterClass); + // check if there was a resolver for OIDC and set as a filter param + String keycloakResolverClass = getKeycloakResolverClass(webXmlDoc); + if (keycloakResolverClass != null) { + Element initParam = webXmlDoc.createElement("init-param"); + Element paramName = webXmlDoc.createElement("param-name"); + paramName.setTextContent("keycloak.config.resolver"); + Element paramValue = webXmlDoc.createElement("param-value"); + paramValue.setTextContent(keycloakResolverClass); + initParam.appendChild(paramName); + initParam.appendChild(paramValue); + filter.appendChild(initParam); + } + // Limitation that all deployments of annotated class use same skipPattern. Refactor if // something more flexible is needed (would require more tricky web.xml parsing though...) String skipPattern = testClass.getAnnotation(UseServletFilter.class).skipPattern(); @@ -131,6 +153,23 @@ public class DeploymentArchiveProcessorUtils { archive.add(new StringAsset((IOUtil.documentToString(webXmlDoc))), WEBXML_PATH); } + + public static String getKeycloakResolverClass(Document doc) { + try { + XPathFactory factory = XPathFactory.newInstance(); + XPath xpath = factory.newXPath(); + XPathExpression expr = xpath.compile("//web-app/context-param[param-name='keycloak.config.resolver']/param-value/text()"); + NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + if (nodes != null && nodes.getLength() > 0) { + return nodes.item(0).getNodeValue(); + } + } catch(DOMException e) { + throw new IllegalStateException(e); + } catch (XPathExpressionException e) { + throw new IllegalStateException(e); + } + return null; + } public static void addFilterDependencies(Archive archive, TestClass testClass) { log.info("Adding filter dependencies to " + archive.getName()); @@ -163,8 +202,8 @@ public class DeploymentArchiveProcessorUtils { } } - public static void modifySAMLAdapterConfig(Archive archive) { - Document doc = IOUtil.loadXML(archive.get(SAML_ADAPTER_CONFIG_PATH).getAsset().openStream()); + public static void modifySAMLAdapterConfig(Archive archive, String adapterConfigPath) { + Document doc = IOUtil.loadXML(archive.get(adapterConfigPath).getAsset().openStream()); if (AUTH_SERVER_SSL_REQUIRED) { IOUtil.modifyDocElementAttribute(doc, "SingleSignOnService", "bindingUrl", "8080", System.getProperty("auth.server.https.port")); @@ -185,7 +224,7 @@ public class DeploymentArchiveProcessorUtils { IOUtil.modifyDocElementAttribute(doc, "SP", "logoutPage", "8081", System.getProperty("app.server.http.port")); } - archive.add(new StringAsset(IOUtil.documentToString(doc)), SAML_ADAPTER_CONFIG_PATH); + archive.add(new StringAsset(IOUtil.documentToString(doc)), adapterConfigPath); ((WebArchive) archive).addAsResource(new File(DeploymentArchiveProcessorUtils.class.getResource("/keystore/keycloak.truststore").getFile())); }