KEYCLOAK-1925: SAML adapter multitenant support
This commit is contained in:
parent
3918dbed59
commit
4a82979792
29 changed files with 784 additions and 58 deletions
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
</project>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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>
|
||||
</jboss-deployment-structure>
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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=="
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue