KEYCLOAK-3124 Possibility test adapter on embedded undertow

This commit is contained in:
mposolda 2016-11-29 17:26:11 +01:00
parent c1d93e251d
commit 803fde6c1d
23 changed files with 782 additions and 38 deletions

View file

@ -16,6 +16,7 @@ env:
- TESTS=group1
- TESTS=group2
- TESTS=group3
- TESTS=group4
- TESTS=adapter
jdk:

View file

@ -66,6 +66,14 @@
<artifactId>keycloak-dependencies-server-all</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -18,10 +18,13 @@
package org.keycloak.testsuite.arquillian.undertow;
import io.undertow.Undertow;
import io.undertow.server.handlers.PathHandler;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DefaultServletConfig;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ServletContainer;
import io.undertow.servlet.api.ServletInfo;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
@ -34,14 +37,20 @@ import org.jboss.logging.Logger;
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.spec.WebArchive;
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
import org.jboss.shrinkwrap.undertow.api.UndertowWebArchive;
import org.keycloak.common.util.reflections.Reflections;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUndertowConfiguration> {
@ -52,6 +61,8 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
private KeycloakOnUndertowConfiguration configuration;
private KeycloakSessionFactory sessionFactory;
Map<String, String> deployedArchivesToContextPath = new HashMap<>();
private DeploymentInfo createAuthServerDeploymentInfo() {
ResteasyDeployment deployment = new ResteasyDeployment();
deployment.setApplicationClass(KeycloakApplication.class.getName());
@ -75,6 +86,8 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
public DeploymentInfo getDeplotymentInfoFromArchive(Archive<?> archive) {
if (archive instanceof UndertowWebArchive) {
return ((UndertowWebArchive) archive).getDeploymentInfo();
} else if (archive instanceof WebArchive) {
return new UndertowDeployerHelper().getDeploymentInfo(configuration, (WebArchive)archive);
} else {
throw new IllegalArgumentException("UndertowContainer only supports UndertowWebArchive or WebArchive.");
}
@ -93,7 +106,19 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
@Override
public ProtocolMetaData deploy(Archive<?> archive) throws DeploymentException {
DeploymentInfo di = getDeplotymentInfoFromArchive(archive);
ClassLoader parentCl = Thread.currentThread().getContextClassLoader();
UndertowWarClassLoader classLoader = new UndertowWarClassLoader(parentCl, archive);
Thread.currentThread().setContextClassLoader(classLoader);
try {
undertow.deploy(di);
} finally {
Thread.currentThread().setContextClassLoader(parentCl);
}
deployedArchivesToContextPath.put(archive.getName(), di.getContextPath());
return new ProtocolMetaData().addContext(
createHttpContextForDeploymentInfo(di));
}
@ -151,7 +176,29 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
@Override
public void undeploy(Archive<?> archive) throws DeploymentException {
throw new UnsupportedOperationException("Not implemented");
Field containerField = Reflections.findDeclaredField(UndertowJaxrsServer.class, "container");
Reflections.setAccessible(containerField);
ServletContainer container = (ServletContainer) Reflections.getFieldValue(containerField, undertow);
DeploymentManager deployment = container.getDeployment(archive.getName());
if (deployment != null) {
try {
deployment.stop();
} catch (ServletException se) {
throw new DeploymentException(se.getMessage(), se);
}
deployment.undeploy();
Field rootField = Reflections.findDeclaredField(UndertowJaxrsServer.class, "root");
Reflections.setAccessible(rootField);
PathHandler root = (PathHandler) Reflections.getFieldValue(rootField, undertow);
String path = deployedArchivesToContextPath.get(archive.getName());
root.removePrefixPath(path);
} else {
log.warnf("Deployment '%s' not found", archive.getName());
}
}
@Override

View file

@ -0,0 +1,254 @@
/*
* 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.
*/
package org.keycloak.testsuite.arquillian.undertow;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.SecurityInfo;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.WebResourceCollection;
import org.jboss.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Simple web.xml parser just to handle our test deployments
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class SimpleWebXmlParser {
private static final Logger log = Logger.getLogger(SimpleWebXmlParser.class);
void parseWebXml(Document webXml, DeploymentInfo di) {
try {
DocumentWrapper document = new DocumentWrapper(webXml);
// SERVLETS
Map<String, String> servletMappings = new HashMap<>();
List<ElementWrapper> sm = document.getElementsByTagName("servlet-mapping");
for (ElementWrapper mapping : sm) {
String servletName = mapping.getElementByTagName("servlet-name").getText();
String path = mapping.getElementByTagName("url-pattern").getText();
servletMappings.put(servletName, path);
}
List<ElementWrapper> servlets = document.getElementsByTagName("servlet");
for (ElementWrapper servlet : servlets) {
String servletName = servlet.getElementByTagName("servlet-name").getText();
String servletClass = servlet.getElementByTagName("servlet-class").getText();
Class<? extends Servlet> servletClazz = (Class<? extends Servlet>) Class.forName(servletClass);
ServletInfo undertowServlet = new ServletInfo(servletName, servletClazz);
if (servletMappings.containsKey(servletName)) {
undertowServlet.addMapping(servletMappings.get(servletName));
di.addServlet(undertowServlet);
} else {
log.warnf("Missing servlet-mapping for '%s'", servletName);
}
}
// FILTERS
Map<String, String> filterMappings = new HashMap<>();
List<ElementWrapper> fm = document.getElementsByTagName("filter-mapping");
for (ElementWrapper mapping : fm) {
String filterName = mapping.getElementByTagName("filter-name").getText();
String path = mapping.getElementByTagName("url-pattern").getText();
filterMappings.put(filterName, path);
}
List<ElementWrapper> filters = document.getElementsByTagName("filter");
for (ElementWrapper filter : filters) {
String filterName = filter.getElementByTagName("filter-name").getText();
String filterClass = filter.getElementByTagName("filter-class").getText();
Class<? extends Filter> filterClazz = (Class<? extends Filter>) Class.forName(filterClass);
FilterInfo undertowFilter = new FilterInfo(filterName, filterClazz);
di.addFilter(undertowFilter);
if (filterMappings.containsKey(filterName)) {
di.addFilterUrlMapping(filterName, filterMappings.get(filterName), DispatcherType.REQUEST);
} else {
log.warnf("Missing filter-mapping for '%s'", filterName);
}
}
// CONTEXT PARAMS
List<ElementWrapper> contextParams = document.getElementsByTagName("context-param");
for (ElementWrapper param : contextParams) {
String paramName = param.getElementByTagName("param-name").getText();
String paramValue = param.getElementByTagName("param-value").getText();
di.addInitParameter(paramName, paramValue);
}
// ROLES
List<ElementWrapper> securityRoles = document.getElementsByTagName("security-role");
for (ElementWrapper sr : securityRoles) {
String roleName = sr.getElementByTagName("role-name").getText();
di.addSecurityRole(roleName);
}
// SECURITY CONSTRAINTS
List<ElementWrapper> secConstraints = document.getElementsByTagName("security-constraint");
for (ElementWrapper constraint : secConstraints) {
String urlPattern = constraint.getElementByTagName("web-resource-collection")
.getElementByTagName("url-pattern")
.getText();
ElementWrapper authCsnt = constraint.getElementByTagName("auth-constraint");
String roleName = authCsnt==null ? null : authCsnt
.getElementByTagName("role-name")
.getText();
SecurityConstraint undertowConstraint = new SecurityConstraint();
WebResourceCollection collection = new WebResourceCollection();
collection.addUrlPattern(urlPattern);
undertowConstraint.addWebResourceCollection(collection);
if (roleName != null) {
undertowConstraint.addRoleAllowed(roleName);
} else {
undertowConstraint.setEmptyRoleSemantic(SecurityInfo.EmptyRoleSemantic.PERMIT);
}
di.addSecurityConstraint(undertowConstraint);
}
// LOGIN CONFIG
ElementWrapper loginCfg = document.getElementByTagName("login-config");
if (loginCfg != null) {
String mech = loginCfg.getElementByTagName("auth-method").getText();
String realmName = loginCfg.getElementByTagName("realm-name").getText();
ElementWrapper form = loginCfg.getElementByTagName("form-login-config");
if (form != null) {
String loginPage = form.getElementByTagName("form-login-page").getText();
String errorPage = form.getElementByTagName("form-error-page").getText();
di.setLoginConfig(new LoginConfig(mech, realmName, loginPage, errorPage));
} else {
di.setLoginConfig(new LoginConfig(realmName).addFirstAuthMethod(mech));
}
}
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
}
private static abstract class XmlWrapper {
abstract List<ElementWrapper> getElementsByTagName(String tagName);
abstract ElementWrapper getElementByTagName(String tagName);
List<ElementWrapper> getElementsFromNodeList(NodeList nl) {
List<ElementWrapper> result = new LinkedList<>();
for (int i=0; i<nl.getLength() ; i++) {
Node node = nl.item(i);
if (node instanceof Element) {
result.add(new ElementWrapper((Element) node));
}
}
return result;
}
ElementWrapper getElementFromNodeList(NodeList nl) {
if (nl.getLength() > 0) {
return new ElementWrapper((Element) nl.item(0));
} else {
return null;
}
}
}
private static class ElementWrapper extends XmlWrapper {
private final Element element;
public ElementWrapper(Element element) {
this.element = element;
}
@Override
public List<ElementWrapper> getElementsByTagName(String tagName) {
NodeList nl = element.getElementsByTagName(tagName);
return getElementsFromNodeList(nl);
}
@Override
public ElementWrapper getElementByTagName(String tagName) {
NodeList nl = element.getElementsByTagName(tagName);
return getElementFromNodeList(nl);
}
public String getText() {
return this.element.getTextContent();
}
}
private static class DocumentWrapper extends XmlWrapper {
private final Document document;
public DocumentWrapper(Document document) {
this.document = document;
}
@Override
List<ElementWrapper> getElementsByTagName(String tagName) {
NodeList nl = document.getElementsByTagName(tagName);
return getElementsFromNodeList(nl);
}
@Override
ElementWrapper getElementByTagName(String tagName) {
NodeList nl = document.getElementsByTagName(tagName);
return getElementFromNodeList(nl);
}
}
}

View file

@ -0,0 +1,194 @@
/*
* 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.
*/
package org.keycloak.testsuite.arquillian.undertow;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.annotation.WebServlet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import io.undertow.UndertowMessages;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.server.handlers.resource.ResourceChangeListener;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.server.handlers.resource.URLResource;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.ServletInfo;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.ClassAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class UndertowDeployerHelper {
private static final Logger log = Logger.getLogger(UndertowDeployerHelper.class);
DeploymentInfo getDeploymentInfo(KeycloakOnUndertowConfiguration config, WebArchive archive) {
String archiveName = archive.getName();
String contextPath = "/" + archive.getName().substring(0, archive.getName().lastIndexOf('.'));
String appContextUrl = "http://" + config.getBindAddress() + ":" + config.getBindHttpPort() + contextPath;
try {
DeploymentInfo di = new DeploymentInfo();
UndertowWarClassLoader classLoader = new UndertowWarClassLoader(UndertowDeployerHelper.class.getClassLoader(), archive);
di.setClassLoader(classLoader);
di.setDeploymentName(archiveName);
di.setContextPath(contextPath);
ResourceManager undertowResourcesWrapper = getResourceManager(appContextUrl, archive);
di.setResourceManager(undertowResourcesWrapper);
if (archive.contains("/WEB-INF/web.xml")) {
Document webXml = loadXML(archive.get("/WEB-INF/web.xml").getAsset().openStream());
new SimpleWebXmlParser().parseWebXml(webXml, di);
}
addAnnotatedServlets(di, archive);
return di;
} catch (Exception ioe) {
throw new RuntimeException("Error deploying " + archive.getName(), ioe);
}
}
private ResourceManager getResourceManager(final String appServerRoot, final WebArchive archive) throws IOException {
return new ResourceManager() {
@Override
public Resource getResource(String path) throws IOException {
if (path == null || path.isEmpty()) {
return null;
}
Node node = archive.get(path);
if (node == null) {
log.warnf("Application '%s' did not found resource on path %s", archive.getName(), path);
return null;
} else {
URL contextUrl = new URL(appServerRoot);
URL myResourceUrl = new URL(contextUrl.getProtocol(), contextUrl.getHost(), contextUrl.getPort(), path, new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
return node.getAsset().openStream();
}
};
}
});
return new URLResource(myResourceUrl, myResourceUrl.openConnection(), path);
}
}
@Override
public boolean isResourceChangeListenerSupported() {
return false;
}
@Override
public void registerResourceChangeListener(ResourceChangeListener listener) {
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
}
@Override
public void removeResourceChangeListener(ResourceChangeListener listener) {
throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
}
@Override
public void close() throws IOException {
// TODO: Should close open streams?
}
};
}
private Document loadXML(InputStream is) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
return dBuilder.parse(is);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new RuntimeException(e);
}
}
private void addAnnotatedServlets(DeploymentInfo di, Archive<?> archive) {
Map<ArchivePath, Node> classNodes = archive.getContent((ArchivePath path) -> {
String stringPath = path.get();
return (stringPath.startsWith("/WEB-INF/classes") && stringPath.endsWith("class"));
});
for (Map.Entry<ArchivePath, Node> entry : classNodes.entrySet()) {
Node n = entry.getValue();
ClassAsset classAsset = (ClassAsset) n.getAsset();
Class<?> clazz = classAsset.getSource();
WebServlet annotation = clazz.getAnnotation(WebServlet.class);
if (annotation != null) {
ServletInfo undertowServlet = new ServletInfo(clazz.getSimpleName(), (Class<? extends Servlet>) clazz);
String[] mappings = annotation.value();
if (mappings != null) {
for (String urlPattern : mappings) {
undertowServlet.addMapping(urlPattern);
}
}
di.addServlet(undertowServlet);
}
}
}
}

View file

@ -0,0 +1,61 @@
/*
* 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.
*/
package org.keycloak.testsuite.arquillian.undertow;
import java.io.InputStream;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.Node;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UndertowWarClassLoader extends ClassLoader {
private final Archive<?> archive;
public UndertowWarClassLoader(ClassLoader parent, Archive<?> archive) {
super(parent);
this.archive = archive;
}
@Override
public InputStream getResourceAsStream(String name) {
InputStream is = super.getResourceAsStream(name);
if (is == null) {
String resourcePath = "/WEB-INF/classes";
if (!name.startsWith("/")) {
resourcePath = resourcePath + "/";
}
resourcePath = resourcePath + name;
Node node = archive.get(resourcePath);
if (node == null) {
return null;
} else {
return node.getAsset().openStream();
}
} else {
return is;
}
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak.testsuite.adapter.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.common.util.UriUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@ -50,13 +51,7 @@ public class CustomerServlet extends HttpServlet {
//Clear principal form database-service by calling logout
StringBuilder result = new StringBuilder();
String urlBase;
if (System.getProperty("app.server.ssl.required", "false").equals("true")) {
urlBase = System.getProperty("app.server.ssl.base.url", "https://localhost:8643");
} else {
urlBase = System.getProperty("app.server.base.url", "http://localhost:8280");
}
String urlBase = ServletTestUtils.getUrlBase(req);
URL url = new URL(urlBase + "/customer-db/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
@ -78,13 +73,7 @@ public class CustomerServlet extends HttpServlet {
//try {
StringBuilder result = new StringBuilder();
String urlBase;
if (System.getProperty("app.server.ssl.required", "false").equals("true")) {
urlBase = System.getProperty("app.server.ssl.base.url", "https://localhost:8643");
} else {
urlBase = System.getProperty("app.server.base.url", "http://localhost:8280");
}
String urlBase = ServletTestUtils.getUrlBase(req);
URL url = new URL(urlBase + "/customer-db/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
@ -114,4 +103,5 @@ public class CustomerServlet extends HttpServlet {
// client.close();
// }
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak.testsuite.adapter.servlet;
import org.junit.Assert;
import org.keycloak.common.util.UriUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@ -37,12 +38,7 @@ public class InputServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String appBase;
if (System.getProperty("app.server.ssl.required", "false").equals("true")) {
appBase = System.getProperty("app.server.ssl.base.url", "https://localhost:8643");
} else {
appBase = System.getProperty("app.server.base.url", "http://localhost:8280");
}
String appBase = ServletTestUtils.getUrlBase(req);
String actionUrl = appBase + "/input-portal/secured/post";
if (req.getRequestURI().endsWith("insecure")) {
@ -82,7 +78,7 @@ public class InputServlet extends HttpServlet {
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.printf("parameter="+req.getParameter("parameter"));
pw.printf("parameter=" + req.getParameter("parameter"));
pw.flush();
}

View file

@ -13,9 +13,6 @@ import java.io.IOException;
*/
public class OfflineTokenServlet extends AbstractShowTokensServlet {
private static final String OFFLINE_CLIENT_APP_URI = (System.getProperty("app.server.ssl.required", "false").equals("true")) ?
System.getProperty("app.server.ssl.base.url", "https://localhost:8643") + "/offline-client" :
System.getProperty("app.server.base.url", "http://localhost:8280") + "/offline-client";
private static final String ADAPTER_ROOT_URL = (System.getProperty("auth.server.ssl.required", "false").equals("true")) ?
System.getProperty("auth.server.ssl.base.url", "https://localhost:8543") :
System.getProperty("auth.server.base.url", "http://localhost:8180");
@ -25,7 +22,7 @@ public class OfflineTokenServlet extends AbstractShowTokensServlet {
if (req.getRequestURI().endsWith("logout")) {
UriBuilder redirectUriBuilder = UriBuilder.fromUri(OFFLINE_CLIENT_APP_URI);
UriBuilder redirectUriBuilder = UriBuilder.fromUri(ServletTestUtils.getUrlBase(req) + "/offline-client");
if (req.getParameter(OAuth2Constants.SCOPE) != null) {
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, req.getParameter(OAuth2Constants.SCOPE));
}

View file

@ -0,0 +1,48 @@
/*
* 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.
*/
package org.keycloak.testsuite.adapter.servlet;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.common.util.UriUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ServletTestUtils {
// TODO: Couldn't just always read urlBase from req.getRequestURI() ?
public static String getUrlBase(HttpServletRequest req) {
if (System.getProperty("app.server.ssl.required", "false").equals("true")) {
return System.getProperty("app.server.ssl.base.url", "https://localhost:8643");
}
String urlBase = System.getProperty("app.server.base.url");
if (urlBase == null) {
String authServer = System.getProperty("auth.server.container", "auth-server-undertow");
if (authServer.contains("undertow")) {
urlBase = UriUtils.getOrigin(req.getRequestURL().toString());
} else {
urlBase = "http://localhost:8280";
}
}
return urlBase;
}
}

View file

@ -40,6 +40,9 @@
<exclude.client>-</exclude.client>
<!--exclude cluster tests by default, enabled by 'auth-server-*-cluster' profiles in tests/pom.xml-->
<exclude.cluster>**/cluster/**/*Test.java</exclude.cluster>
<!-- exclude undertow adapter tests. They can be added by -Dtest=org.keycloak.testsuite.adapter.undertow.**.*Test -->
<exclude.undertow.adapter>**/adapter/undertow/**/*Test.java</exclude.undertow.adapter>
</properties>
<dependencies>
@ -103,6 +106,7 @@
<exclude>${exclude.account}</exclude>
<exclude>${exclude.client}</exclude>
<exclude>${exclude.cluster}</exclude>
<exclude>${exclude.undertow.adapter}</exclude>
</excludes>
</configuration>
</plugin>

View file

@ -43,6 +43,10 @@ public class ContainerInfo {
this.contextRoot = contextRoot;
}
public boolean isUndertow() {
return getQualifier().toLowerCase().contains("undertow");
}
public boolean isAS7() {
return getQualifier().toLowerCase().contains("as7");
}

View file

@ -19,7 +19,10 @@ package org.keycloak.testsuite.arquillian;
import org.apache.tools.ant.DirectoryScanner;
import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
import org.jboss.arquillian.core.api.InstanceProducer;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.test.spi.TestClass;
import org.jboss.arquillian.test.spi.annotation.ClassScoped;
import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
import org.jboss.shrinkwrap.api.Archive;
@ -62,6 +65,10 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
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";
@Inject
@ClassScoped
private InstanceProducer<TestContext> testContextProducer;
@Override
public void process(Archive<?> archive, TestClass testClass) {
log.info("Processing archive " + archive.getName());
@ -158,6 +165,12 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
public void addFilterDependencies(Archive<?> archive, TestClass testClass) {
log.info("Adding filter dependencies to " + archive.getName());
TestContext testContext = testContextProducer.get();
if (testContext.getAppServerInfo().isUndertow()) {
return;
}
String dependency = testClass.getAnnotation(UseServletFilter.class).filterDependency();
((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies((dependency + ":" + System.getProperty("project.version"))));

View file

@ -126,7 +126,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
@Deployment(name = CustomerPortal.DEPLOYMENT_NAME)
protected static WebArchive customerPortal() {
return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class);
return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class, ServletTestUtils.class);
}
@Deployment(name = CustomerPortalNoConf.DEPLOYMENT_NAME)
@ -156,7 +156,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
@Deployment(name = InputPortal.DEPLOYMENT_NAME)
protected static WebArchive inputPortal() {
return servletDeployment(InputPortal.DEPLOYMENT_NAME, "keycloak.json", InputServlet.class);
return servletDeployment(InputPortal.DEPLOYMENT_NAME, "keycloak.json", InputServlet.class, ServletTestUtils.class);
}
@Deployment(name = TokenMinTTLPage.DEPLOYMENT_NAME)
@ -194,7 +194,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertEquals(driver.getCurrentUrl(), inputPortal + "/secured/post");
assertCurrentUrlEquals(driver, inputPortal + "/secured/post");
String pageSource = driver.getPageSource();
assertTrue(pageSource.contains("parameter=hello"));
@ -641,6 +641,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
String value = "hello";
Client client = ClientBuilder.newClient();
//pause(1000000);
Response response = client.target(basicAuthPage
.setTemplateValues("mposolda", "password", value).buildUri()).request().get();
@ -797,7 +799,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
String serverLogPath = null;
if (System.getProperty("app.server").equals("wildfly") || System.getProperty("app.server").equals("eap6") || System.getProperty("app.server").equals("eap")) {
String appServer = System.getProperty("app.server");
if (appServer != null && (appServer.equals("wildfly") || appServer.equals("eap6") || appServer.equals("eap"))) {
serverLogPath = System.getProperty("app.server.home") + "/standalone/log/server.log";
}

View file

@ -27,6 +27,7 @@ import java.util.List;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
import static org.keycloak.testsuite.util.WaitUtils.pause;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
@ -49,7 +50,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
@Deployment(name = OfflineToken.DEPLOYMENT_NAME)
protected static WebArchive offlineClient() {
return servletDeployment(OfflineToken.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, OfflineTokenServlet.class, ErrorServlet.class);
return servletDeployment(OfflineToken.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, OfflineTokenServlet.class, ErrorServlet.class, ServletTestUtils.class);
}
@Override
@ -92,8 +93,10 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
// Ensure that logout works for webapp (even if offline token will be still valid in Keycloak DB)
offlineTokenPage.logout();
assertCurrentUrlDoesntStartWith(offlineTokenPage);
loginPage.assertCurrent();
offlineTokenPage.navigateTo();
assertCurrentUrlDoesntStartWith(offlineTokenPage);
loginPage.assertCurrent();
setAdapterAndServerTimeOffset(0);
@ -177,6 +180,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
//This was necessary to be introduced, otherwise other testcases will fail
offlineTokenPage.logout();
assertCurrentUrlDoesntStartWith(offlineTokenPage);
loginPage.assertCurrent();
events.clear();

View file

@ -144,7 +144,7 @@ public abstract class AbstractSessionServletAdapterTest extends AbstractServlets
sessionPortalPage.navigateTo();
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
testRealmLoginPage.form().login("bburke@redhat.com", "password");
assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
assertCurrentUrlEquals(sessionPortalPage);
String pageSource = driver.getPageSource();
assertTrue(pageSource.contains("Counter=1"));
@ -164,7 +164,7 @@ public abstract class AbstractSessionServletAdapterTest extends AbstractServlets
// bburke should be still logged with original httpSession in our browser window
sessionPortalPage.navigateTo();
assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
assertCurrentUrlEquals(sessionPortalPage);
String pageSource = driver.getPageSource();
assertTrue(pageSource.contains("Counter=3"));
String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
@ -186,7 +186,7 @@ public abstract class AbstractSessionServletAdapterTest extends AbstractServlets
sessionPortalPage.navigateTo();
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
login.form().login("bburke@redhat.com", "password");
assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
assertCurrentUrlEquals(sessionPortalPage);
String pageSource = driver.getPageSource();
assertTrue(pageSource.contains("Counter=1"));

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package org.keycloak.testsuite.adapter.undertow.servlet;
import org.keycloak.testsuite.adapter.servlet.AbstractDemoFilterServletAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@AppServerContainer("auth-server-undertow")
public class UndertowDemoFilterServletAdapterTest extends AbstractDemoFilterServletAdapterTest {
}

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package org.keycloak.testsuite.adapter.undertow.servlet;
import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@AppServerContainer("auth-server-undertow")
public class UndertowDemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
}

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package org.keycloak.testsuite.adapter.undertow.servlet;
import org.keycloak.testsuite.adapter.servlet.AbstractOfflineServletsAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@AppServerContainer("auth-server-undertow")
public class UndertowOfflineServletsAdapterTest extends AbstractOfflineServletsAdapterTest {
}

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package org.keycloak.testsuite.adapter.undertow.servlet;
import org.keycloak.testsuite.adapter.servlet.AbstractSessionServletAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@AppServerContainer("auth-server-undertow")
public class UndertowSessionServletAdapterTest extends AbstractSessionServletAdapterTest {
}

View file

@ -56,6 +56,11 @@ public class URLAssert {
currentUrlEqual(driver, page.toString()));
}
public static void assertCurrentUrlEquals(WebDriver driver, final String url) {
assertTrue("Expected URL: " + url + "; actual: " + driver.getCurrentUrl(),
currentUrlEqual(driver, url));
}
public static void assertCurrentUrlStartsWith(AbstractPage page) {
assertCurrentUrlStartsWith(page.getDriver(), page.toString());
}

View file

@ -6,19 +6,22 @@ fi
if [ $1 == "group1" ]; then
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.a**.*Test
fi
if [ $1 == "group2" ]; then
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.b**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.cli**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.com**.*Test
fi
if [ $1 == "group2" ]; then
if [ $1 == "group3" ]; then
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.d**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.e**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.f**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.i**.*Test
fi
if [ $1 == "group3" ]; then
if [ $1 == "group4" ]; then
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.k**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.m**.*Test
mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.o**.*Test