KEYCLOAK-6748 undertow saml adapter tests
This commit is contained in:
parent
bce2aee144
commit
560d76b7ee
18 changed files with 294 additions and 80 deletions
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.adapters.saml.undertow.SamlServletExtension
|
|
@ -1086,7 +1086,7 @@ public class RepresentationToModel {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles) {
|
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles) {
|
||||||
logger.debug("Create client: {0}" + resourceRep.getClientId());
|
logger.debugv("Create client: {0}", resourceRep.getClientId());
|
||||||
|
|
||||||
ClientModel client = resourceRep.getId() != null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId());
|
ClientModel client = resourceRep.getId() != null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId());
|
||||||
if (resourceRep.getName() != null) client.setName(resourceRep.getName());
|
if (resourceRep.getName() != null) client.setName(resourceRep.getName());
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
<groupId>org.keycloak.testsuite</groupId>
|
||||||
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
|
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
|
||||||
|
|
|
@ -27,12 +27,18 @@ import io.undertow.servlet.api.ServletInfo;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.core.Application;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.arquillian.undertow.UndertowContainerConfiguration;
|
import org.arquillian.undertow.UndertowContainerConfiguration;
|
||||||
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
|
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
|
||||||
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
|
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
|
||||||
|
@ -43,11 +49,14 @@ import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaD
|
||||||
import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet;
|
import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
|
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
|
||||||
|
import org.jboss.resteasy.spi.ResteasyDeployment;
|
||||||
import org.jboss.shrinkwrap.api.Archive;
|
import org.jboss.shrinkwrap.api.Archive;
|
||||||
|
import org.jboss.shrinkwrap.api.asset.ClassAsset;
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
|
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
|
||||||
import org.jboss.shrinkwrap.undertow.api.UndertowWebArchive;
|
import org.jboss.shrinkwrap.undertow.api.UndertowWebArchive;
|
||||||
import org.keycloak.common.util.reflections.Reflections;
|
import org.keycloak.common.util.reflections.Reflections;
|
||||||
|
import org.keycloak.testsuite.arquillian.undertow.saml.util.RestSamlApplicationConfig;
|
||||||
import org.keycloak.testsuite.utils.undertow.UndertowDeployerHelper;
|
import org.keycloak.testsuite.utils.undertow.UndertowDeployerHelper;
|
||||||
import org.keycloak.testsuite.utils.undertow.UndertowWarClassLoader;
|
import org.keycloak.testsuite.utils.undertow.UndertowWarClassLoader;
|
||||||
|
|
||||||
|
@ -115,7 +124,13 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
|
||||||
if (archive instanceof UndertowWebArchive) {
|
if (archive instanceof UndertowWebArchive) {
|
||||||
di = ((UndertowWebArchive) archive).getDeploymentInfo();
|
di = ((UndertowWebArchive) archive).getDeploymentInfo();
|
||||||
} else if (archive instanceof WebArchive) {
|
} else if (archive instanceof WebArchive) {
|
||||||
di = new UndertowDeployerHelper().getDeploymentInfo(configuration, (WebArchive)archive);
|
WebArchive webArchive = (WebArchive)archive;
|
||||||
|
if (isJaxrsApp(webArchive)) {
|
||||||
|
di = new UndertowDeployerHelper().getDeploymentInfo(configuration, webArchive,
|
||||||
|
undertow.undertowDeployment(getCustomResteasyDeployment(webArchive)));
|
||||||
|
} else {
|
||||||
|
di = new UndertowDeployerHelper().getDeploymentInfo(configuration, webArchive);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("UndertowContainer only supports UndertowWebArchive or WebArchive.");
|
throw new IllegalArgumentException("UndertowContainer only supports UndertowWebArchive or WebArchive.");
|
||||||
}
|
}
|
||||||
|
@ -191,4 +206,29 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
|
||||||
}
|
}
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isJaxrsApp(WebArchive archive) throws DeploymentException {
|
||||||
|
try {
|
||||||
|
return IOUtils.toString(archive.get("/WEB-INF/web.xml").getAsset().openStream(), Charset.forName("UTF-8"))
|
||||||
|
.contains(Application.class.getName());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new DeploymentException("Unable to read archive.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResteasyDeployment getCustomResteasyDeployment(WebArchive webArchive) {
|
||||||
|
//take all classes from war and add those with @Path annotation to RestSamlApplicationConfig
|
||||||
|
Set<Class<?>> classes = webArchive.getContent(archivePath ->
|
||||||
|
archivePath.get().startsWith("/WEB-INF/classes/") &&
|
||||||
|
archivePath.get().endsWith(".class")
|
||||||
|
).values().stream()
|
||||||
|
.filter(node -> node.getAsset() instanceof ClassAsset)
|
||||||
|
.map(node -> ((ClassAsset)node.getAsset()).getSource())
|
||||||
|
.filter(clazz -> clazz.isAnnotationPresent(Path.class))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
ResteasyDeployment deployment = new ResteasyDeployment();
|
||||||
|
deployment.setApplication(new RestSamlApplicationConfig(classes));
|
||||||
|
return deployment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ public class UndertowDeploymentArchiveProcessor implements ApplicationArchivePro
|
||||||
modifyWebXML(archive, testClass);
|
modifyWebXML(archive, testClass);
|
||||||
|
|
||||||
modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH);
|
modifyOIDCAdapterConfig(archive, DeploymentArchiveProcessorUtils.ADAPTER_CONFIG_PATH);
|
||||||
|
|
||||||
|
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) {
|
private void modifyWebXML(Archive<?> archive, TestClass testClass) {
|
||||||
|
@ -56,4 +60,11 @@ public class UndertowDeploymentArchiveProcessor implements ApplicationArchivePro
|
||||||
|
|
||||||
DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath);
|
DeploymentArchiveProcessorUtils.modifyOIDCAdapterConfig(archive, adapterConfigPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void modifySAMLAdapterConfig(Archive<?> archive, String adapterConfigPath) {
|
||||||
|
if (!archive.contains(adapterConfigPath)) return;
|
||||||
|
|
||||||
|
log.debug("Modifying adapter config " + adapterConfigPath + " in " + archive.getName());
|
||||||
|
DeploymentArchiveProcessorUtils.modifySAMLAdapterConfig(archive, adapterConfigPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.arquillian.undertow.saml.util;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.ws.rs.core.Application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wildfly JAX-RS Integration has support for scanning deployment for annotations.
|
||||||
|
*
|
||||||
|
* https://github.com/wildfly/wildfly/blob/14.0.1.Final/jaxrs/src/main/java/org/jboss/as/jaxrs/deployment/JaxrsAnnotationProcessor.java
|
||||||
|
*
|
||||||
|
* On undertow we have to set Application Class manually:
|
||||||
|
*
|
||||||
|
* ResteasyDeployment deployment = new ResteasyDeployment();
|
||||||
|
* deployment.setApplication(application);
|
||||||
|
*
|
||||||
|
* @author vramik
|
||||||
|
*/
|
||||||
|
public class RestSamlApplicationConfig extends Application {
|
||||||
|
|
||||||
|
private final Set<Class<?>> classes;
|
||||||
|
|
||||||
|
public RestSamlApplicationConfig(Set<Class<?>> classes) {
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Class<?>> getClasses() {
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ public class SamlMultiTenantResolver implements SamlConfigResolver {
|
||||||
throw new IllegalStateException("Not able to resolve realm from the request path!");
|
throw new IllegalStateException("Not able to resolve realm from the request path!");
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml");
|
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/" + realm + "-keycloak-saml.xml");
|
||||||
if (is == null) {
|
if (is == null) {
|
||||||
throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml");
|
throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml");
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,10 @@ public class SamlMultiTenantResolver implements SamlConfigResolver {
|
||||||
ResourceLoader loader = new ResourceLoader() {
|
ResourceLoader loader = new ResourceLoader() {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceAsStream(String path) {
|
public InputStream getResourceAsStream(String path) {
|
||||||
return getClass().getResourceAsStream(path);
|
return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new DeploymentBuilder().build(is, loader);
|
return new DeploymentBuilder().build(is, loader);
|
||||||
} catch (ParsingException e) {
|
} catch (ParsingException e) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class MultiTenant1Saml extends SAMLServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout() {
|
public void logout() {
|
||||||
driver.navigate().to(getUriBuilder().queryParam("GLO", "true").queryParam("realm", "tenant1").build().toASCIIString());
|
driver.navigate().to(getUriBuilder().clone().queryParam("GLO", "true").queryParam("realm", "tenant1").build().toASCIIString());
|
||||||
getUriBuilder().replaceQueryParam("GLO");
|
getUriBuilder().replaceQueryParam("GLO");
|
||||||
pause(300);
|
pause(300);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class MultiTenant2Saml extends SAMLServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout() {
|
public void logout() {
|
||||||
driver.navigate().to(getUriBuilder().queryParam("GLO", "true").queryParam("realm", "tenant2").build().toASCIIString());
|
driver.navigate().to(getUriBuilder().clone().queryParam("GLO", "true").queryParam("realm", "tenant2").build().toASCIIString());
|
||||||
getUriBuilder().replaceQueryParam("GLO");
|
getUriBuilder().replaceQueryParam("GLO");
|
||||||
pause(300);
|
pause(300);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.adapter.page;
|
package org.keycloak.testsuite.adapter.page;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
||||||
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author mhajas
|
* @author mhajas
|
||||||
|
@ -27,8 +30,7 @@ import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||||
public abstract class SAMLServlet extends AbstractPageWithInjectedUrl {
|
public abstract class SAMLServlet extends AbstractPageWithInjectedUrl {
|
||||||
|
|
||||||
public void logout() {
|
public void logout() {
|
||||||
driver.navigate().to(getUriBuilder().queryParam("GLO", "true").build().toASCIIString());
|
driver.navigate().to(getUriBuilder().clone().queryParam("GLO", "true").build().toASCIIString());
|
||||||
getUriBuilder().replaceQueryParam("GLO");
|
|
||||||
pause(300);
|
pause(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +43,14 @@ public abstract class SAMLServlet extends AbstractPageWithInjectedUrl {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkRolesEndPoint(boolean value) {
|
public void checkRolesEndPoint(boolean value) {
|
||||||
driver.navigate().to(getUriBuilder().build().toASCIIString() + "/" + (value ? "" : "un") + "checkRoles");
|
driver.navigate().to(getUriBuilder().clone().path((value ? "" : "un") + "checkRoles").build().toASCIIString());
|
||||||
pause(300);
|
pause(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRolesToCheck(String roles) {
|
||||||
|
UriBuilder uriBuilder = getUriBuilder().clone();
|
||||||
|
String toASCIIString = uriBuilder.path("setCheckRoles").queryParam("roles", roles).build().toASCIIString();
|
||||||
|
driver.navigate().to(toASCIIString);
|
||||||
|
WaitUtils.waitUntilElement(By.tagName("body")).text().contains("These roles will be checked:");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,11 @@ public class AdapterTestExecutionDecider implements TestExecutionDecider {
|
||||||
if (testContext.isAdapterContainerEnabled() || testContext.isAdapterContainerEnabledCluster()) {
|
if (testContext.isAdapterContainerEnabled() || testContext.isAdapterContainerEnabledCluster()) {
|
||||||
|
|
||||||
if (method.isAnnotationPresent(AppServerContainer.class)) { // taking method level annotation first as it has higher priority
|
if (method.isAnnotationPresent(AppServerContainer.class)) { // taking method level annotation first as it has higher priority
|
||||||
if (getCorrespondingAnnotation(method).skip()) {
|
if (getCorrespondingAnnotation(method) == null) { //no corresponding annotation - taking class level annotation
|
||||||
|
if (getCorrespondingAnnotation(testContext.getTestClass()).skip()) {
|
||||||
|
return execute(method, Boolean.FALSE, "Skipped by @AppServerContainer class level annotation.");
|
||||||
|
}
|
||||||
|
} else if (getCorrespondingAnnotation(method).skip()) { //corresponding annotation
|
||||||
return execute(method, Boolean.FALSE, "Skipped by @AppServerContainer method level annotation.");
|
return execute(method, Boolean.FALSE, "Skipped by @AppServerContainer method level annotation.");
|
||||||
}
|
}
|
||||||
} else { //taking class level annotation
|
} else { //taking class level annotation
|
||||||
|
@ -88,7 +92,7 @@ public class AdapterTestExecutionDecider implements TestExecutionDecider {
|
||||||
return appServerContainers.stream()
|
return appServerContainers.stream()
|
||||||
.filter(annotation -> annotation.value().equals(testContextInstance.get().getAppServerContainerName()))
|
.filter(annotation -> annotation.value().equals(testContextInstance.get().getAppServerContainerName()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("Not found the @AppServerContainer annotation with current app server."));
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AppServerContainer getCorrespondingAnnotation(Class testClass) {
|
private AppServerContainer getCorrespondingAnnotation(Class testClass) {
|
||||||
|
@ -107,7 +111,7 @@ public class AdapterTestExecutionDecider implements TestExecutionDecider {
|
||||||
return appServerContainers.stream()
|
return appServerContainers.stream()
|
||||||
.filter(annotation -> annotation.value().equals(testContextInstance.get().getAppServerContainerName()))
|
.filter(annotation -> annotation.value().equals(testContextInstance.get().getAppServerContainerName()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("Not found the @AppServerContainer annotation with current app server."));
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExecutionDecision execute(Method method, Boolean execute, String message) {
|
private ExecutionDecision execute(Method method, Boolean execute, String message) {
|
||||||
|
@ -116,7 +120,7 @@ public class AdapterTestExecutionDecider implements TestExecutionDecider {
|
||||||
return ExecutionDecision.execute();
|
return ExecutionDecision.execute();
|
||||||
} else {
|
} else {
|
||||||
cache.put(method, new CachedRecord(Boolean.FALSE, message));
|
cache.put(method, new CachedRecord(Boolean.FALSE, message));
|
||||||
log.debug(message);
|
log.debug(method.getName() + " " + message);
|
||||||
return ExecutionDecision.dontExecute(message);
|
return ExecutionDecision.dontExecute(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.keycloak.testsuite.arquillian.containers.ContainerConstants;
|
||||||
/**
|
/**
|
||||||
* @author mhajas
|
* @author mhajas
|
||||||
*/
|
*/
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
||||||
|
@ -94,4 +95,18 @@ public class SAMLFilterServletAdapterTest extends SAMLServletAdapterTest {
|
||||||
public void testErrorHandlingSigned() {
|
public void testErrorHandlingSigned() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Override
|
||||||
|
@Ignore
|
||||||
|
public void multiTenant1SamlTest() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Override
|
||||||
|
@Ignore
|
||||||
|
public void multiTenant2SamlTest() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ import org.xml.sax.SAXException;
|
||||||
/**
|
/**
|
||||||
* @author mhajas
|
* @author mhajas
|
||||||
*/
|
*/
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
||||||
|
@ -265,10 +266,10 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest {
|
||||||
protected EcpSP ecpSPPage;
|
protected EcpSP ecpSPPage;
|
||||||
|
|
||||||
@Page
|
@Page
|
||||||
protected MultiTenant1Saml mutiTenant1SamlPage;
|
protected MultiTenant1Saml multiTenant1SamlPage;
|
||||||
|
|
||||||
@Page
|
@Page
|
||||||
protected MultiTenant2Saml mutiTenant2SamlPage;
|
protected MultiTenant2Saml multiTenant2SamlPage;
|
||||||
|
|
||||||
@Page
|
@Page
|
||||||
protected SAMLPostLoginTenant1 tenant1RealmSAMLPostLoginPage;
|
protected SAMLPostLoginTenant1 tenant1RealmSAMLPostLoginPage;
|
||||||
|
@ -595,38 +596,50 @@ public class SAMLServletAdapterTest extends AbstractServletsAdapterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multiTenant1SamlTest() throws Exception {
|
public void multiTenant1SamlTest() throws Exception {
|
||||||
UserRepresentation user1 = createUserRepresentation("user-tenant1", "user-tenant1@redhat.com", "Bill", "Burke", true);
|
multiTenant1SamlPage.setRolesToCheck("user");
|
||||||
setPasswordFor(user1, "user-tenant1");
|
|
||||||
// check the user in the tenant logs in ok
|
try {
|
||||||
assertSuccessfulLogin(mutiTenant1SamlPage, user1, tenant1RealmSAMLPostLoginPage, "principal=user-tenant1");
|
UserRepresentation user1 = createUserRepresentation("user-tenant1", "user-tenant1@redhat.com", "Bill", "Burke", true);
|
||||||
// check the issuer is the correct tenant
|
setPasswordFor(user1, "user-tenant1");
|
||||||
driver.navigate().to(mutiTenant1SamlPage.getUriBuilder().path("getAssertionIssuer").build().toASCIIString());
|
// check the user in the tenant logs in ok
|
||||||
waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant1");
|
assertSuccessfulLogin(multiTenant1SamlPage, user1, tenant1RealmSAMLPostLoginPage, "principal=user-tenant1");
|
||||||
// check logout
|
// check the issuer is the correct tenant
|
||||||
mutiTenant1SamlPage.logout();
|
driver.navigate().to(multiTenant1SamlPage.getUriBuilder().clone().path("getAssertionIssuer").build().toASCIIString());
|
||||||
checkLoggedOut(mutiTenant1SamlPage, tenant1RealmSAMLPostLoginPage);
|
waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant1");
|
||||||
// check a user in the other tenant doesn't login
|
// check logout
|
||||||
UserRepresentation user2 = createUserRepresentation("user-tenant2", "user-tenant2@redhat.com", "Bill", "Burke", true);
|
multiTenant1SamlPage.logout();
|
||||||
setPasswordFor(user2, "user-tenant2");
|
checkLoggedOut(multiTenant1SamlPage, tenant1RealmSAMLPostLoginPage);
|
||||||
assertFailedLogin(mutiTenant1SamlPage, user2, 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(multiTenant1SamlPage, user2, tenant1RealmSAMLPostLoginPage);
|
||||||
|
} finally {
|
||||||
|
multiTenant1SamlPage.checkRolesEndPoint(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multiTenant2SamlTest() throws Exception {
|
public void multiTenant2SamlTest() throws Exception {
|
||||||
UserRepresentation user2 = createUserRepresentation("user-tenant2", "user-tenant2@redhat.com", "Bill", "Burke", true);
|
multiTenant2SamlPage.setRolesToCheck("user");
|
||||||
setPasswordFor(user2, "user-tenant2");
|
|
||||||
// check the user in the tenant logs in ok
|
try {
|
||||||
assertSuccessfulLogin(mutiTenant2SamlPage, user2, tenant2RealmSAMLPostLoginPage, "principal=user-tenant2");
|
UserRepresentation user2 = createUserRepresentation("user-tenant2", "user-tenant2@redhat.com", "Bill", "Burke", true);
|
||||||
// check the issuer is the correct tenant
|
setPasswordFor(user2, "user-tenant2");
|
||||||
driver.navigate().to(mutiTenant2SamlPage.getUriBuilder().path("getAssertionIssuer").build().toASCIIString());
|
// check the user in the tenant logs in ok
|
||||||
waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant2");
|
assertSuccessfulLogin(multiTenant2SamlPage, user2, tenant2RealmSAMLPostLoginPage, "principal=user-tenant2");
|
||||||
// check logout
|
// check the issuer is the correct tenant
|
||||||
mutiTenant2SamlPage.logout();
|
driver.navigate().to(multiTenant2SamlPage.getUriBuilder().clone().path("getAssertionIssuer").build().toASCIIString());
|
||||||
checkLoggedOut(mutiTenant2SamlPage, tenant2RealmSAMLPostLoginPage);
|
waitUntilElement(By.xpath("//body")).text().contains("/auth/realms/tenant2");
|
||||||
// check a user in the other tenant doesn't login
|
// check logout
|
||||||
UserRepresentation user1 = createUserRepresentation("user-tenant1", "user-tenant1@redhat.com", "Bill", "Burke", true);
|
multiTenant2SamlPage.logout();
|
||||||
setPasswordFor(user1, "user-tenant1");
|
checkLoggedOut(multiTenant2SamlPage, tenant2RealmSAMLPostLoginPage);
|
||||||
assertFailedLogin(mutiTenant2SamlPage, user1, 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(multiTenant2SamlPage, user1, tenant2RealmSAMLPostLoginPage);
|
||||||
|
} finally {
|
||||||
|
multiTenant2SamlPage.checkRolesEndPoint(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final KeyPair NEW_KEY_PAIR = KeyUtils.generateRsaKeyPair(1024);
|
private static final KeyPair NEW_KEY_PAIR = KeyUtils.generateRsaKeyPair(1024);
|
||||||
|
|
|
@ -40,12 +40,27 @@ public class NonIDERunListener extends RunListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableKeycloakLogging() {
|
private void disableKeycloakLogging() {
|
||||||
String loggingLevel = System.getProperty(KEYCLOAK_LOGGING_LEVEL_NAME);
|
String loggingLevel = System.getProperty(KEYCLOAK_LOGGING_LEVEL_NAME, "").toLowerCase();
|
||||||
if (loggingLevel == null || loggingLevel.isEmpty()) {
|
if (loggingLevel.isEmpty()) {
|
||||||
|
|
||||||
log.infof("Setting %s to off. Keycloak server logging will be disabled", KEYCLOAK_LOGGING_LEVEL_NAME);
|
log.infof("Setting %s to off. Keycloak server logging will be disabled", KEYCLOAK_LOGGING_LEVEL_NAME);
|
||||||
System.setProperty(KEYCLOAK_LOGGING_LEVEL_NAME, "off");
|
System.setProperty(KEYCLOAK_LOGGING_LEVEL_NAME, "off");
|
||||||
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.OFF);
|
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.OFF);
|
||||||
|
} else {
|
||||||
|
switch (loggingLevel) {
|
||||||
|
case "debug":
|
||||||
|
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.DEBUG);
|
||||||
|
break;
|
||||||
|
case "trace":
|
||||||
|
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.TRACE);
|
||||||
|
break;
|
||||||
|
case "all":
|
||||||
|
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.ALL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
org.apache.log4j.Logger.getLogger("org.keycloak").setLevel(Level.INFO);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,18 @@
|
||||||
<role-name>user</role-name>
|
<role-name>user</role-name>
|
||||||
</auth-constraint>
|
</auth-constraint>
|
||||||
</security-constraint>
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Unsecured-setCheckRoles</web-resource-name>
|
||||||
|
<url-pattern>/setCheckRoles/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Unsecured-uncheckRoles</web-resource-name>
|
||||||
|
<url-pattern>/uncheckRoles/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
<login-config>
|
<login-config>
|
||||||
<auth-method>KEYCLOAK-SAML</auth-method>
|
<auth-method>KEYCLOAK-SAML</auth-method>
|
||||||
|
|
|
@ -45,6 +45,18 @@
|
||||||
<role-name>manager</role-name>
|
<role-name>manager</role-name>
|
||||||
</auth-constraint>
|
</auth-constraint>
|
||||||
</security-constraint>
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Unsecured-setCheckRoles</web-resource-name>
|
||||||
|
<url-pattern>/setCheckRoles/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Unsecured-uncheckRoles</web-resource-name>
|
||||||
|
<url-pattern>/uncheckRoles/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
<login-config>
|
<login-config>
|
||||||
<auth-method>KEYCLOAK-SAML</auth-method>
|
<auth-method>KEYCLOAK-SAML</auth-method>
|
||||||
|
|
|
@ -55,33 +55,35 @@ class SimpleWebXmlParser {
|
||||||
try {
|
try {
|
||||||
DocumentWrapper document = new DocumentWrapper(webXml);
|
DocumentWrapper document = new DocumentWrapper(webXml);
|
||||||
|
|
||||||
// SERVLETS
|
if (di.getServlets().get("ResteasyServlet") == null) {
|
||||||
|
|
||||||
Map<String, String> servletMappings = new HashMap<>();
|
// SERVLETS
|
||||||
List<ElementWrapper> sm = document.getElementsByTagName("servlet-mapping");
|
Map<String, String> servletMappings = new HashMap<>();
|
||||||
for (ElementWrapper mapping : sm) {
|
List<ElementWrapper> sm = document.getElementsByTagName("servlet-mapping");
|
||||||
String servletName = mapping.getElementByTagName("servlet-name").getText();
|
for (ElementWrapper mapping : sm) {
|
||||||
String path = mapping.getElementByTagName("url-pattern").getText();
|
String servletName = mapping.getElementByTagName("servlet-name").getText();
|
||||||
servletMappings.put(servletName, path);
|
String path = mapping.getElementByTagName("url-pattern").getText();
|
||||||
}
|
servletMappings.put(servletName, path);
|
||||||
|
}
|
||||||
|
|
||||||
List<ElementWrapper> servlets = document.getElementsByTagName("servlet");
|
List<ElementWrapper> servlets = document.getElementsByTagName("servlet");
|
||||||
for (ElementWrapper servlet : servlets) {
|
for (ElementWrapper servlet : servlets) {
|
||||||
String servletName = servlet.getElementByTagName("servlet-name").getText();
|
String servletName = servlet.getElementByTagName("servlet-name").getText();
|
||||||
ElementWrapper servletClassEw = servlet.getElementByTagName("servlet-class");
|
ElementWrapper servletClassEw = servlet.getElementByTagName("servlet-class");
|
||||||
String servletClass = servletClassEw == null ? servletName : servletClassEw.getText();
|
String servletClass = servletClassEw == null ? servletName : servletClassEw.getText();
|
||||||
ElementWrapper loadOnStartupEw = servlet.getElementByTagName("load-on-startup");
|
ElementWrapper loadOnStartupEw = servlet.getElementByTagName("load-on-startup");
|
||||||
Integer loadOnStartup = loadOnStartupEw == null ? null : Integer.valueOf(loadOnStartupEw.getText());
|
Integer loadOnStartup = loadOnStartupEw == null ? null : Integer.valueOf(loadOnStartupEw.getText());
|
||||||
|
|
||||||
Class<? extends Servlet> servletClazz = (Class<? extends Servlet>) Class.forName(servletClass);
|
Class<? extends Servlet> servletClazz = (Class<? extends Servlet>) Class.forName(servletClass);
|
||||||
ServletInfo undertowServlet = new ServletInfo(servletName, servletClazz);
|
ServletInfo undertowServlet = new ServletInfo(servletName, servletClazz);
|
||||||
|
|
||||||
if (servletMappings.containsKey(servletName)) {
|
if (servletMappings.containsKey(servletName)) {
|
||||||
undertowServlet.addMapping(servletMappings.get(servletName));
|
undertowServlet.addMapping(servletMappings.get(servletName));
|
||||||
undertowServlet.setLoadOnStartup(loadOnStartup);
|
undertowServlet.setLoadOnStartup(loadOnStartup);
|
||||||
di.addServlet(undertowServlet);
|
di.addServlet(undertowServlet);
|
||||||
} else {
|
} else {
|
||||||
log.warnf("Missing servlet-mapping for '%s'", servletName);
|
log.warnf("Missing servlet-mapping for '%s'", servletName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,21 +182,29 @@ class SimpleWebXmlParser {
|
||||||
ElementWrapper sessionCfg = document.getElementByTagName("session-config");
|
ElementWrapper sessionCfg = document.getElementByTagName("session-config");
|
||||||
if (sessionCfg != null) {
|
if (sessionCfg != null) {
|
||||||
ElementWrapper cookieConfig = sessionCfg.getElementByTagName("cookie-config");
|
ElementWrapper cookieConfig = sessionCfg.getElementByTagName("cookie-config");
|
||||||
String httpOnly = cookieConfig.getElementByTagName("http-only").getText();
|
|
||||||
String cookieName = cookieConfig.getElementByTagName("name").getText();
|
String cookieName = cookieConfig.getElementByTagName("name").getText();
|
||||||
|
|
||||||
ServletSessionConfig cfg = new ServletSessionConfig();
|
ServletSessionConfig cfg = new ServletSessionConfig();
|
||||||
cfg.setHttpOnly(Boolean.parseBoolean(httpOnly));
|
if (cookieConfig.getElementByTagName("http-only") != null) {
|
||||||
|
cfg.setHttpOnly(Boolean.parseBoolean(cookieConfig.getElementByTagName("http-only").getText()));
|
||||||
|
}
|
||||||
cfg.setName(cookieName);
|
cfg.setName(cookieName);
|
||||||
di.setServletSessionConfig(cfg);
|
di.setServletSessionConfig(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ERROR PAGES
|
// ERROR PAGES
|
||||||
List<ElementWrapper> errorPages = document.getElementsByTagName("error-page");
|
List<ElementWrapper> errorPages = document.getElementsByTagName("error-page");
|
||||||
for (ElementWrapper errorPage : errorPages) {
|
for (ElementWrapper errorPageWrapper : errorPages) {
|
||||||
int errorCode = Integer.parseInt(errorPage.getElementByTagName("error-code").getText());
|
String location = errorPageWrapper.getElementByTagName("location").getText();
|
||||||
String location = errorPage.getElementByTagName("location").getText();
|
|
||||||
di.addErrorPage(new ErrorPage(location, errorCode));
|
ErrorPage errorPage;
|
||||||
|
if (errorPageWrapper.getElementByTagName("error-code") != null) {
|
||||||
|
errorPage = new ErrorPage(location, Integer.parseInt(errorPageWrapper.getElementByTagName("error-code").getText()));
|
||||||
|
} else {
|
||||||
|
errorPage = new ErrorPage(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
di.addErrorPage(errorPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
|
|
@ -25,8 +25,6 @@ import io.undertow.server.handlers.resource.ResourceManager;
|
||||||
import io.undertow.server.handlers.resource.URLResource;
|
import io.undertow.server.handlers.resource.URLResource;
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
import io.undertow.servlet.api.ServletInfo;
|
import io.undertow.servlet.api.ServletInfo;
|
||||||
import org.apache.jasper.deploy.JspPropertyGroup;
|
|
||||||
import org.apache.jasper.deploy.TagLibraryInfo;
|
|
||||||
import org.arquillian.undertow.UndertowContainerConfiguration;
|
import org.arquillian.undertow.UndertowContainerConfiguration;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.shrinkwrap.api.Archive;
|
import org.jboss.shrinkwrap.api.Archive;
|
||||||
|
@ -58,6 +56,10 @@ public class UndertowDeployerHelper {
|
||||||
private static final Logger log = Logger.getLogger(UndertowDeployerHelper.class);
|
private static final Logger log = Logger.getLogger(UndertowDeployerHelper.class);
|
||||||
|
|
||||||
public DeploymentInfo getDeploymentInfo(UndertowContainerConfiguration config, WebArchive archive) {
|
public DeploymentInfo getDeploymentInfo(UndertowContainerConfiguration config, WebArchive archive) {
|
||||||
|
return getDeploymentInfo(config, archive, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeploymentInfo getDeploymentInfo(UndertowContainerConfiguration config, WebArchive archive, DeploymentInfo di) {
|
||||||
String archiveName = archive.getName();
|
String archiveName = archive.getName();
|
||||||
|
|
||||||
String appName = archive.getName().substring(0, archive.getName().lastIndexOf('.'));
|
String appName = archive.getName().substring(0, archive.getName().lastIndexOf('.'));
|
||||||
|
@ -69,7 +71,9 @@ public class UndertowDeployerHelper {
|
||||||
String appContextUrl = "http://" + config.getBindAddress() + ":" + config.getBindHttpPort() + contextPath;
|
String appContextUrl = "http://" + config.getBindAddress() + ":" + config.getBindHttpPort() + contextPath;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DeploymentInfo di = new DeploymentInfo();
|
if (di == null) {
|
||||||
|
di = new DeploymentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
UndertowWarClassLoader classLoader = new UndertowWarClassLoader(UndertowDeployerHelper.class.getClassLoader(), archive);
|
UndertowWarClassLoader classLoader = new UndertowWarClassLoader(UndertowDeployerHelper.class.getClassLoader(), archive);
|
||||||
di.setClassLoader(classLoader);
|
di.setClassLoader(classLoader);
|
||||||
|
@ -89,7 +93,7 @@ public class UndertowDeployerHelper {
|
||||||
|
|
||||||
di.addWelcomePages("index.html", "index.jsp");
|
di.addWelcomePages("index.html", "index.jsp");
|
||||||
|
|
||||||
JspServletBuilder.setupDeployment(di, new HashMap<String, JspPropertyGroup>(), new HashMap<String, TagLibraryInfo>(), new HackInstanceManager());
|
JspServletBuilder.setupDeployment(di, new HashMap<>(), new HashMap<>(), new HackInstanceManager());
|
||||||
|
|
||||||
addAnnotatedServlets(di, archive);
|
addAnnotatedServlets(di, archive);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue