keycloak-aplcache
Changes
adapters/saml/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java 14(+6 -8)
adapters/saml/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java 9(+4 -5)
adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java 9(+4 -5)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java 12(+6 -6)
testsuite/integration-arquillian/servers/app-server/jboss/eap/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAPDeploymentArchiveProcessor.java 12(+7 -5)
testsuite/integration-arquillian/servers/app-server/jboss/eap6/src/main/java/org/keycloak/testsuite/arquillian/eap/container/EAP6DeploymentArchiveProcessor.java 12(+7 -5)
testsuite/integration-arquillian/servers/app-server/jboss/wildfly/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/WildflyDeploymentArchiveProcessor.java 12(+7 -5)
testsuite/integration-arquillian/servers/app-server/jboss/wildfly10/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly10DeploymentArchiveProcessor.java 12(+7 -5)
testsuite/integration-arquillian/servers/app-server/jboss/wildfly9/src/main/java/org/keycloak/testsuite/arquillian/wildfly/container/Wildfly9DeploymentArchiveProcessor.java 12(+7 -5)
testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlMultiTenantResolver.java 60(+60 -0)
testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant1Saml.java 52(+52 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant2Saml.java 52(+52 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java 47(+44 -3)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant1.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/SAMLPostLoginTenant2.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java 44(+44 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java 68(+68 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml 3(+2 -1)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant1.jks 0(+0 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/keystore-tenant2.jks 0(+0 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant1-keycloak-saml.xml 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/tenant2-keycloak-saml.xml 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/multi-tenant-saml/WEB-INF/web.xml 62(+62 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/tenant1-realm.json 73(+73 -0)
Details
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 b59339c..d145efe 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 ab6317a..a06f103 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 cc3abc2..3f57bff 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 4985dfb..1ffa287 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 f8eb110..4176287 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 28daaeb..71fe996 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 edcbea0..0a26186 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 a5ee439..644afcb 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 5c61ab5..4d57817 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 ef22947..b659b88 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 8ed980f..ac6fc5b 100644
--- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml
@@ -50,6 +50,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core-public</artifactId>
+ </dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
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 0000000..559a4ef
--- /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 2c0b17d..e795526 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 0000000..bb08f88
--- /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 0000000..e2623d1
--- /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 90a9850..2ca1880 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 0000000..274937d
--- /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 0000000..a933d60
--- /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 6220110..ba694bb 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<RealmRepresentation> 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 18df393..0f0e79b 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<RealmRepresentation> 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 9356226..1edfc3e 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 @@
<!--required by SAML test servlets-->
<module name="org.keycloak.keycloak-adapter-spi" />
<module name="org.keycloak.keycloak-saml-core" />
+ <module name="org.keycloak.keycloak-saml-core-public" />
</dependencies>
</deployment>
-</jboss-deployment-structure>
\ No newline at end of file
+</jboss-deployment-structure>
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 0000000..c3ad85b
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 0000000..d2b3d9b
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 0000000..cd3ae8b
--- /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 @@
+<keycloak-saml-adapter>
+ <SP entityID="multi-tenant"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp?realm=tenant1"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/keystore-tenant1.jks" password="changeit">
+ <PrivateKey alias="multi-tenant" password="changeit"/>
+ <Certificate alias="multi-tenant"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleIdentifiers>
+ <Attribute name="Role"/>
+ </RoleIdentifiers>
+ <IDP entityID="idp"
+ signatureAlgorithm="RSA_SHA256">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ validateAssertionSignature="false"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8080/auth/realms/tenant1/protocol/saml"/>
+ <SingleLogoutService signRequest="true"
+ signResponse="true"
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8080/auth/realms/tenant1/protocol/saml"
+ redirectBindingUrl="http://localhost:8080/auth/realms/tenant1/protocol/saml"/>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
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 0000000..4058f4d
--- /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 @@
+<keycloak-saml-adapter>
+ <SP entityID="multi-tenant"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp?realm=tenant2"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/keystore-tenant2.jks" password="changeit">
+ <PrivateKey alias="multi-tenant" password="changeit"/>
+ <Certificate alias="multi-tenant"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleIdentifiers>
+ <Attribute name="Role"/>
+ </RoleIdentifiers>
+ <IDP entityID="idp"
+ signatureAlgorithm="RSA_SHA256">
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ validateAssertionSignature="false"
+ requestBinding="POST"
+ bindingUrl="http://localhost:8080/auth/realms/tenant2/protocol/saml"/>
+ <SingleLogoutService signRequest="true"
+ signResponse="true"
+ validateRequestSignature="true"
+ validateResponseSignature="true"
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8080/auth/realms/tenant2/protocol/saml"
+ redirectBindingUrl="http://localhost:8080/auth/realms/tenant2/protocol/saml"/>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
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 0000000..1720657
--- /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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016 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.
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>%CONTEXT_PATH%</module-name>
+
+ <servlet>
+ <servlet-name>javax.ws.rs.core.Application</servlet-name>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>javax.ws.rs.core.Application</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <error-page>
+ <location>/error.html</location>
+ </error-page>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Application</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK-SAML</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>manager</role-name>
+ </security-role>
+
+ <context-param>
+ <param-name>keycloak.config.resolver</param-name>
+ <param-value>org.keycloak.testsuite.adapter.servlet.SamlMultiTenantResolver</param-value>
+ </context-param>
+</web-app>
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 0000000..99c13cf
--- /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 0000000..97deab5
--- /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 c5dece7..45933f4 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()));
}