diff --git a/distribution/appliance-dist/assembly.xml b/distribution/appliance-dist/assembly.xml index 53de6bca3c..b3307b022c 100755 --- a/distribution/appliance-dist/assembly.xml +++ b/distribution/appliance-dist/assembly.xml @@ -81,6 +81,8 @@ org.keycloak:keycloak-as7-adapter-dist:zip org.keycloak:keycloak-eap6-adapter-dist:zip org.keycloak:keycloak-tomcat7-adapter-dist:zip + org.keycloak:keycloak-jetty91-adapter-dist:zip + org.keycloak:keycloak-jetty92-adapter-dist:zip adapters diff --git a/distribution/appliance-dist/pom.xml b/distribution/appliance-dist/pom.xml index 91707b7c77..cfd8497d40 100755 --- a/distribution/appliance-dist/pom.xml +++ b/distribution/appliance-dist/pom.xml @@ -32,6 +32,18 @@ ${project.version} zip + + org.keycloak + keycloak-jetty91-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-jetty92-adapter-dist + ${project.version} + zip + org.keycloak keycloak-eap6-adapter-dist diff --git a/distribution/jetty91-adapter-zip/assembly.xml b/distribution/jetty91-adapter-zip/assembly.xml new file mode 100755 index 0000000000..7453fa0610 --- /dev/null +++ b/distribution/jetty91-adapter-zip/assembly.xml @@ -0,0 +1,38 @@ + + war-dist + + + zip + + false + + + + + + keycloak.mod + + modules + + + ${project.build.directory}/modules + + + + + + false + true + true + + org.keycloak:keycloak-jetty91-adapter + + + org.eclipse.jetty:jetty-server + org.eclipse.jetty:jetty-util + org.eclipse.jetty:jetty-security + + lib/keycloak + + + diff --git a/distribution/jetty91-adapter-zip/keycloak.mod b/distribution/jetty91-adapter-zip/keycloak.mod new file mode 100755 index 0000000000..130f4e9efa --- /dev/null +++ b/distribution/jetty91-adapter-zip/keycloak.mod @@ -0,0 +1,11 @@ +# +# Keycloak Jetty Adapter +# + +[depend] +server +security + +[lib] +lib/keycloak/*.jar + diff --git a/distribution/jetty91-adapter-zip/pom.xml b/distribution/jetty91-adapter-zip/pom.xml new file mode 100755 index 0000000000..1767809fe5 --- /dev/null +++ b/distribution/jetty91-adapter-zip/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + + keycloak-parent + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../pom.xml + + + keycloak-jetty91-adapter-dist + pom + Keycloak Jetty 9.1.x Adapter Distro + + + + + org.keycloak + keycloak-jetty91-adapter + ${project.version} + + + + + + maven-assembly-plugin + 2.4 + + + assemble + package + + single + + + + assembly.xml + + + target + + + target/assembly/work + + false + + + + + + + + diff --git a/distribution/jetty92-adapter-zip/assembly.xml b/distribution/jetty92-adapter-zip/assembly.xml new file mode 100755 index 0000000000..5622c073dd --- /dev/null +++ b/distribution/jetty92-adapter-zip/assembly.xml @@ -0,0 +1,38 @@ + + war-dist + + + zip + + false + + + + + + keycloak.mod + + modules + + + ${project.build.directory}/modules + + + + + + false + true + true + + org.keycloak:keycloak-jetty92-adapter + + + org.eclipse.jetty:jetty-server + org.eclipse.jetty:jetty-util + org.eclipse.jetty:jetty-security + + lib/keycloak + + + diff --git a/distribution/jetty92-adapter-zip/keycloak.mod b/distribution/jetty92-adapter-zip/keycloak.mod new file mode 100755 index 0000000000..130f4e9efa --- /dev/null +++ b/distribution/jetty92-adapter-zip/keycloak.mod @@ -0,0 +1,11 @@ +# +# Keycloak Jetty Adapter +# + +[depend] +server +security + +[lib] +lib/keycloak/*.jar + diff --git a/distribution/jetty92-adapter-zip/pom.xml b/distribution/jetty92-adapter-zip/pom.xml new file mode 100755 index 0000000000..cd61075de0 --- /dev/null +++ b/distribution/jetty92-adapter-zip/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + + keycloak-parent + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../pom.xml + + + keycloak-jetty92-adapter-dist + pom + Keycloak Jetty 9.2.x Adapter Distro + + + + + org.keycloak + keycloak-jetty92-adapter + ${project.version} + + + + + + maven-assembly-plugin + 2.4 + + + assemble + package + + single + + + + assembly.xml + + + target + + + target/assembly/work + + false + + + + + + + + diff --git a/distribution/war-dist/assembly.xml b/distribution/war-dist/assembly.xml index adf071bd61..e4550d721e 100755 --- a/distribution/war-dist/assembly.xml +++ b/distribution/war-dist/assembly.xml @@ -32,6 +32,8 @@ org.keycloak:keycloak-as7-adapter-dist:zip org.keycloak:keycloak-eap6-adapter-dist:zip org.keycloak:keycloak-tomcat7-adapter-dist:zip + org.keycloak:keycloak-jetty91-adapter-dist:zip + org.keycloak:keycloak-jetty92-adapter-dist:zip adapters diff --git a/distribution/war-dist/pom.xml b/distribution/war-dist/pom.xml index 06c3e70fcc..00491c08f4 100755 --- a/distribution/war-dist/pom.xml +++ b/distribution/war-dist/pom.xml @@ -38,6 +38,18 @@ ${project.version} zip + + org.keycloak + keycloak-jetty91-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-jetty92-adapter-dist + ${project.version} + zip + org.keycloak keycloak-example-themes-dist diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml index db6dead9ce..0aa171171b 100755 --- a/docbook/reference/en/en-US/master.xml +++ b/docbook/reference/en/en-US/master.xml @@ -12,6 +12,7 @@ + @@ -86,6 +87,7 @@ This one is short &AdapterConfig; &JBossAdapter; &TomcatAdapter; + &Jetty9Adapter; &JavascriptAdapter; &InstalledApplications; &Logout; diff --git a/docbook/reference/en/en-US/modules/Overview.xml b/docbook/reference/en/en-US/modules/Overview.xml index 9ef7451c14..72ade64ec5 100755 --- a/docbook/reference/en/en-US/modules/Overview.xml +++ b/docbook/reference/en/en-US/modules/Overview.xml @@ -98,7 +98,7 @@ Multitenancy support. You can host and manage multiple realms for multiple organizations. - Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat 7 and Pure JavaScript applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments + Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat 7, Jetty 9.1.x, Jetty 9.2.x, and Pure JavaScript applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments diff --git a/docbook/reference/en/en-US/modules/jetty9-adapter.xml b/docbook/reference/en/en-US/modules/jetty9-adapter.xml new file mode 100755 index 0000000000..47f1dda4b5 --- /dev/null +++ b/docbook/reference/en/en-US/modules/jetty9-adapter.xml @@ -0,0 +1,159 @@ +
+ Jetty 9.x Adapters + + Keycloak has a separate adapter for Jetty 9.1.x and Jetty 9.2.x that you will have to install into your Jetty + installation. You then have to provide some extra configuration in each WAR you deploy to + Jetty. Let's go over these steps. + +
+ Adapter Installation + + There is a adapter zip file for Jetty 9.x in the adapters/ directory in the Keycloak appliance + or war distribution. Depending on your version of Jetty, you must unzip this file into Jetty's root directory. Including + adapter's jars within your WEB-INF/lib directory will not work! + + + +$ cd $JETTY_HOME +$ unzip keycloak-jetty92-adapter-dist.zip + + + + Next, you will have to enable the keycloak module for your jetty.base. + + + +$ cd your-base +$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak + + + +
+ +
+ Required Per WAR Configuration + + This section describes how to secure a WAR directly by adding config and editing files within your WAR package. + + + The first thing you must do is create a WEB-INF/jetty-web.xml file in your WAR package. This is + a Jetty specific config file and you must define a Keycloak specific authenticator within it. + + + + + + + + + + + +]]> + + + Next you must create + a keycloak.json adapter config file within the WEB-INF directory + of your WAR. The format of this config file is describe in the general adapter configuration + section. + + + + The Jetty 9.1.x adapter will not be able to find the keycloak.json file. You will have to define + all adapter settings within the jetty-web.xml file as described below. + + + + Instead of using keycloak.json, you can define everything within the jetty-web.xml. You'll + just have to figure out how the json settings match to the org.keycloak.representations.adapters.config.AdapterConfig + class. + + + + + + + + + + + + tomcat + customer-portal + http://localhost:8081/auth + external + + + + secret + password + + + + MIGfMA0GCSqGSIb3DQEBAQUAA4 + + + + + + +]]> + + + + You do not have to crack open your WAR to secure it with keycloak. Instead create the jetty-web.xml file + in your webapps directory with the name of yourwar.xml. Jetty should pick it up. In this mode, you'll have + to declare keycloak.json configuration directly within the xml file. + + + Finally you must specify both a login-config and use standard servlet security to specify + role-base constraints on your URLs. Here's an example: + + + + + + customer-portal + + + + Customers + /* + + + user + + + + + + /* + + + CONFIDENTIAL + + + + + BASIC + this is ignored currently/realm-name> + + + + admin + + + user + + +]]> + + +
+
\ No newline at end of file diff --git a/integration/jetty9/adapter/pom.xml b/integration/jetty/jetty9-core/pom.xml similarity index 69% rename from integration/jetty9/adapter/pom.xml rename to integration/jetty/jetty9-core/pom.xml index ba8ac3b7ae..dc28ab39c6 100755 --- a/integration/jetty9/adapter/pom.xml +++ b/integration/jetty/jetty9-core/pom.xml @@ -9,10 +9,10 @@ 4.0.0 - keycloak-jetty9-adapter - Keycloak Jetty 9 Integration + keycloak-jetty9-core + Keycloak Jetty 9.x Core Integration - 9.1.0.v20131115 + 9.1.5.v20140505 @@ -32,11 +32,6 @@ keycloak-adapter-core ${project.version}
- - org.keycloak - keycloak-jboss-adapter-core - ${project.version} - org.apache.httpcomponents httpclient @@ -66,42 +61,21 @@ org.eclipse.jetty jetty-server ${jetty9.version} - provided - - - - org.eclipse.jetty - jetty-jaas - ${jetty9.version} - provided + compile org.eclipse.jetty jetty-util ${jetty9.version} - provided - - - - org.eclipse.jetty - jetty-webapp - ${jetty9.version} - provided + compile org.eclipse.jetty jetty-security ${jetty9.version} - provided - - - - org.eclipse.jetty - jetty-servlet - ${jetty9.version} - provided + compile diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java similarity index 84% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java index 64ce3a6503..4edb4a34a8 100755 --- a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java +++ b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java @@ -11,6 +11,7 @@ import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.AdapterTokenStore; import org.keycloak.adapters.AdapterUtils; +import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.KeycloakAccount; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.OAuthRequestAuthenticator; @@ -20,7 +21,6 @@ import org.keycloak.enums.TokenStore; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import java.io.IOException; import java.security.Principal; import java.util.Set; @@ -28,18 +28,14 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class JettyRequestAuthenticator extends RequestAuthenticator { - - private static final Logger log = Logger.getLogger(JettyRequestAuthenticator.class); - protected KeycloakJettyAuthenticator valve; +public abstract class AbstractJettyRequestAuthenticator extends RequestAuthenticator { + protected static final Logger log = Logger.getLogger(AbstractJettyRequestAuthenticator.class); + protected AbstractKeycloakJettyAuthenticator valve; protected Request request; protected KeycloakPrincipal principal; - public JettyRequestAuthenticator(KeycloakDeployment deployment, - KeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore, - JettyHttpFacade facade, - Request request) { - super(facade, deployment, tokenStore, -1); + public AbstractJettyRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort, AbstractKeycloakJettyAuthenticator valve, Request request) { + super(facade, deployment, tokenStore, sslRedirectPort); this.valve = valve; this.request = request; } @@ -56,28 +52,6 @@ public class JettyRequestAuthenticator extends RequestAuthenticator { }; } - protected void saveServletRequest(HttpServletRequest request, HttpSession session) { - // remember the current URI - synchronized (session) { - // But only if it is not set already, or we save every uri that leads to a login form redirect - if (session.getAttribute(FormAuthenticator.__J_URI) == null) { - StringBuffer buf = request.getRequestURL(); - if (request.getQueryString() != null) - buf.append("?").append(request.getQueryString()); - session.setAttribute(FormAuthenticator.__J_URI, buf.toString()); - session.setAttribute(FormAuthenticator.__J_METHOD, request.getMethod()); - - if (MimeTypes.Type.FORM_ENCODED.is(request.getContentType()) && HttpMethod.POST.is(request.getMethod())) { - Request base_request = (request instanceof Request) ? (Request) request : HttpChannel - .getCurrentHttpChannel().getRequest(); - base_request.extractParameters(); - session.setAttribute(FormAuthenticator.__J_POST, new MultiMap(base_request.getParameters())); - } - } - } - } - - @Override protected void completeOAuthAuthentication(final KeycloakPrincipal skp) { principal = skp; @@ -134,7 +108,7 @@ public class JettyRequestAuthenticator extends RequestAuthenticator { MultiMap j_post = (MultiMap) session.getAttribute(FormAuthenticator.__J_POST); if (j_post != null) { Request base_request = HttpChannel.getCurrentHttpChannel().getRequest(); - base_request.setParameters(j_post); + restoreFormParameters(j_post, base_request); } session.removeAttribute(FormAuthenticator.__J_URI); session.removeAttribute(FormAuthenticator.__J_METHOD); @@ -149,4 +123,29 @@ public class JettyRequestAuthenticator extends RequestAuthenticator { HttpSession session = request.getSession(create); return session != null ? session.getId() : null; } + + protected void saveServletRequest(HttpServletRequest request, HttpSession session) { + // remember the current URI + synchronized (session) { + // But only if it is not set already, or we save every uri that leads to a login form redirect + if (session.getAttribute(FormAuthenticator.__J_URI) == null) { + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) + buf.append("?").append(request.getQueryString()); + session.setAttribute(FormAuthenticator.__J_URI, buf.toString()); + session.setAttribute(FormAuthenticator.__J_METHOD, request.getMethod()); + + if (MimeTypes.Type.FORM_ENCODED.is(request.getContentType()) && HttpMethod.POST.is(request.getMethod())) { + Request base_request = (request instanceof Request) ? (Request) request : HttpChannel + .getCurrentHttpChannel().getRequest(); + MultiMap formParameters = extractFormParameters(base_request); + session.setAttribute(FormAuthenticator.__J_POST, formParameters); + } + } + } + } + + protected abstract MultiMap extractFormParameters(Request base_request); + + protected abstract void restoreFormParameters(MultiMap j_post, Request base_request); } diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java similarity index 53% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java index 9f29340412..69f025aea0 100755 --- a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java +++ b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java @@ -1,20 +1,18 @@ package org.keycloak.adapters.jetty; -import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.http.MimeTypes; -import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.DefaultUserIdentity; import org.eclipse.jetty.security.ServerAuthException; import org.eclipse.jetty.security.UserAuthentication; -import org.eclipse.jetty.security.authentication.FormAuthenticator; +import org.eclipse.jetty.security.authentication.DeferredAuthentication; +import org.eclipse.jetty.security.authentication.LoginAuthenticator; import org.eclipse.jetty.server.Authentication; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.MultiMap; import org.jboss.logging.Logger; import org.keycloak.KeycloakPrincipal; +import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.AdapterConstants; import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterTokenStore; @@ -30,91 +28,36 @@ import org.keycloak.adapters.NodesRegistrationManagement; import org.keycloak.adapters.PreAuthActionsHandler; import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.enums.TokenStore; +import org.keycloak.representations.adapters.config.AdapterConfig; import javax.security.auth.Subject; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; -import java.security.Principal; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class KeycloakJettyAuthenticator extends FormAuthenticator { - private static final org.jboss.logging.Logger log = Logger.getLogger(KeycloakJettyAuthenticator.class); +public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthenticator { + public static final String TOKEN_STORE_NOTE = "TOKEN_STORE_NOTE"; + protected static final org.jboss.logging.Logger log = Logger.getLogger(AbstractKeycloakJettyAuthenticator.class); protected AdapterDeploymentContext deploymentContext; protected NodesRegistrationManagement nodesRegistrationManagement; + protected AdapterConfig adapterConfig; + protected KeycloakConfigResolver configResolver; - public KeycloakJettyAuthenticator() { + public AbstractKeycloakJettyAuthenticator() { super(); } - public KeycloakJettyAuthenticator(String login, String error, boolean dispatch) { - super(login, error, dispatch); - } - - @Override - public void setConfiguration(AuthConfiguration configuration) { - super.setConfiguration(configuration); - initializeKeycloak(); - } - - @SuppressWarnings("UseSpecificCatch") - public void initializeKeycloak() { - String contextPath = ContextHandler.getCurrentContext().getContextPath(); - ServletContext theServletContext = ContextHandler.getCurrentContext().getContext(contextPath); - // Possible scenarios: - // 1) The deployment has a keycloak.config.resolver specified and it exists: - // Outcome: adapter uses the resolver - // 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exists, isn't a resolver, ...) : - // Outcome: adapter is left unconfigured - // 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent) - // Outcome: adapter uses it - // 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent) - // Outcome: adapter is left unconfigured - - String configResolverClass = theServletContext.getInitParameter("keycloak.config.resolver"); - if (configResolverClass != null) { - try { - KeycloakConfigResolver configResolver = (KeycloakConfigResolver) ContextHandler.getCurrentContext().getClassLoader().loadClass(configResolverClass).newInstance(); - deploymentContext = new AdapterDeploymentContext(configResolver); - log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); - } catch (Exception ex) { - log.infov("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()); - } - } else { - InputStream configInputStream = getConfigInputStream(theServletContext); - KeycloakDeployment kd; - if (configInputStream == null) { - log.debug("No adapter configuration. Keycloak is unconfigured and will deny all requests."); - kd = new KeycloakDeployment(); - } else { - kd = KeycloakDeploymentBuilder.build(configInputStream); - } - deploymentContext = new AdapterDeploymentContext(kd); - log.debug("Keycloak is using a per-deployment configuration."); - } - - theServletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); - //AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer()); - //setNext(actions); - - nodesRegistrationManagement = new NodesRegistrationManagement(); - } - private static InputStream getJSONFromServletContext(ServletContext servletContext) { String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME); if (json == null) { @@ -123,6 +66,113 @@ public class KeycloakJettyAuthenticator extends FormAuthenticator { return new ByteArrayInputStream(json.getBytes()); } + public static AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) { + AdapterTokenStore store = (AdapterTokenStore)request.getAttribute(TOKEN_STORE_NOTE); + if (store != null) { + return store; + } + + if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) { + store = new JettySessionTokenStore(request, resolvedDeployment); + } else { + store = new JettyCookieTokenStore(request, facade, resolvedDeployment); + } + + request.setAttribute(TOKEN_STORE_NOTE, store); + return store; + } + + public static void logoutCurrent(Request request) { + AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext)request.getAttribute(AdapterDeploymentContext.class.getName()); + KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName()); + if (ksc != null) { + JettyHttpFacade facade = new JettyHttpFacade(request, null); + KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); + if (ksc instanceof RefreshableKeycloakSecurityContext) { + ((RefreshableKeycloakSecurityContext) ksc).logout(deployment); + } + + AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment); + tokenStore.logout(); + request.removeAttribute(KeycloakSecurityContext.class.getName()); + } + } + + public static UserIdentity createIdentity(KeycloakPrincipal principal) { + Set roles = AdapterUtils.getRolesFromSecurityContext(principal.getKeycloakSecurityContext()); + if (roles == null) { + roles = new HashSet(); + } + Subject theSubject = new Subject(); + String[] theRoles = new String[roles.size()]; + roles.toArray(theRoles); + + return new DefaultUserIdentity(theSubject, principal, theRoles); + } + + @Override + public void setConfiguration(AuthConfiguration configuration) { + //super.setConfiguration(configuration); + initializeKeycloak(); + } + + @Override + public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication.User validatedUser) throws ServerAuthException + { + return true; + } + + public AdapterConfig getAdapterConfig() { + return adapterConfig; + } + + public void setAdapterConfig(AdapterConfig adapterConfig) { + this.adapterConfig = adapterConfig; + } + + public KeycloakConfigResolver getConfigResolver() { + return configResolver; + } + + public void setConfigResolver(KeycloakConfigResolver configResolver) { + this.configResolver = configResolver; + } + + @SuppressWarnings("UseSpecificCatch") + public void initializeKeycloak() { + nodesRegistrationManagement = new NodesRegistrationManagement(); + String contextPath = ContextHandler.getCurrentContext().getContextPath(); + ServletContext theServletContext = ContextHandler.getCurrentContext().getContext(contextPath); + + // Jetty 9.1.x servlet context will be null :( + if (configResolver == null && theServletContext != null) { + String configResolverClass = theServletContext.getInitParameter("keycloak.config.resolver"); + if (configResolverClass != null) { + try { + configResolver = (KeycloakConfigResolver) ContextHandler.getCurrentContext().getClassLoader().loadClass(configResolverClass).newInstance(); + log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass); + } catch (Exception ex) { + log.infov("The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()}); + } + } + } + + if (configResolver != null) { + deploymentContext = new AdapterDeploymentContext(configResolver); + } else if (adapterConfig != null) { + KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig); + deploymentContext = new AdapterDeploymentContext(kd); + } else if (theServletContext != null) { + InputStream configInputStream = getConfigInputStream(theServletContext); + if (configInputStream != null) { + deploymentContext = new AdapterDeploymentContext(KeycloakDeploymentBuilder.build(configInputStream)); + } + } + if (deploymentContext == null) { + deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment()); + } + if (theServletContext != null) theServletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); + } private InputStream getConfigInputStream(ServletContext servletContext) { InputStream is = getJSONFromServletContext(servletContext); @@ -157,11 +207,13 @@ public class KeycloakJettyAuthenticator extends FormAuthenticator { if (handler.handleRequest()) { return Authentication.SEND_SUCCESS; } + if (!mandatory) + return new DeferredAuthentication(this); AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment); nodesRegistrationManagement.tryRegister(deployment); - JettyRequestAuthenticator authenticator = new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request); + AbstractJettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore); AuthOutcome outcome = authenticator.authenticate(); if (outcome == AuthOutcome.AUTHENTICATED) { if (facade.isEnded()) { @@ -183,46 +235,37 @@ public class KeycloakJettyAuthenticator extends FormAuthenticator { return Authentication.SEND_CONTINUE; } + protected abstract AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore); + @Override public String getAuthMethod() { return "KEYCLOAK"; } - public static final String TOKEN_STORE_NOTE = "TOKEN_STORE_NOTE"; - protected AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) { - AdapterTokenStore store = (AdapterTokenStore)request.getAttribute(TOKEN_STORE_NOTE); - if (store != null) { - return store; - } - - if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) { - store = new JettySessionTokenStore(request, resolvedDeployment); - } else { - store = new JettyCookieTokenStore(request, facade, resolvedDeployment); - } - - request.setAttribute(TOKEN_STORE_NOTE, store); - return store; - } - - protected Authentication register(HttpServletRequest httpServletRequest, KeycloakPrincipal principal) { - Set roles = AdapterUtils.getRolesFromSecurityContext(principal.getKeycloakSecurityContext()); - if (roles == null) { - roles = new HashSet(); - } - Request request = (Request) httpServletRequest; + protected Authentication register(Request request, KeycloakPrincipal principal) { + request.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext); Authentication authentication = request.getAuthentication(); - if (!(authentication instanceof UserAuthentication)) { - Subject theSubject = new Subject(); - String[] theRoles = new String[roles.size()]; - roles.toArray(theRoles); - - UserIdentity userIdentity = new DefaultUserIdentity(theSubject, principal, theRoles); - authentication = new UserAuthentication(getAuthMethod(), userIdentity); + if (!(authentication instanceof KeycloakAuthentication)) { + UserIdentity userIdentity = createIdentity(principal); + authentication = new KeycloakAuthentication(getAuthMethod(), userIdentity); request.setAuthentication(authentication); } return authentication; } + public static class KeycloakAuthentication extends UserAuthentication + { + public KeycloakAuthentication(String method, UserIdentity userIdentity) { + super(method, userIdentity); + } + @Override + public void logout() { + Request request = HttpChannel.getCurrentHttpChannel().getRequest(); + logoutCurrent(request); + } + + + + } } diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java similarity index 98% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java index aa685b33a8..c0b1685e3d 100755 --- a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java +++ b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java @@ -5,7 +5,6 @@ import org.jboss.logging.Logger; import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.AdapterTokenStore; -import org.keycloak.adapters.AdapterUtils; import org.keycloak.adapters.CookieTokenStore; import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.KeycloakAccount; @@ -13,8 +12,6 @@ import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RequestAuthenticator; -import java.util.Set; - /** * Handle storage of token info in cookie. Per-request object. * diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java similarity index 100% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java similarity index 96% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java index 8859e22b69..f955633de0 100755 --- a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java +++ b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java @@ -12,7 +12,6 @@ import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RequestAuthenticator; import javax.servlet.http.HttpSession; -import java.util.Set; /** * Handle storage of token info in HTTP Session. Per-request object @@ -66,7 +65,7 @@ public class JettySessionTokenStore implements AdapterTokenStore { securityContext.setCurrentRequestInfo(deployment, this); request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext); - JettyRequestAuthenticator jettyAuthenticator = (JettyRequestAuthenticator) authenticator; + AbstractJettyRequestAuthenticator jettyAuthenticator = (AbstractJettyRequestAuthenticator) authenticator; KeycloakPrincipal principal = AdapterUtils.createPrincipal(deployment, securityContext); jettyAuthenticator.principal = principal; jettyAuthenticator.restoreRequest(); diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java similarity index 79% rename from integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java rename to integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java index 4caa6e7aac..40fc72f440 100755 --- a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java +++ b/integration/jetty/jetty9-core/src/main/java/org/keycloak/adapters/jetty/JettyUserSessionManagement.java @@ -1,6 +1,7 @@ package org.keycloak.adapters.jetty; import org.eclipse.jetty.server.SessionManager; +import org.jboss.logging.Logger; import org.keycloak.adapters.UserSessionManagement; import javax.servlet.http.HttpSession; @@ -11,6 +12,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class JettyUserSessionManagement implements UserSessionManagement { + private static final org.jboss.logging.Logger log = Logger.getLogger(JettyUserSessionManagement.class); protected SessionManager sessionManager; public JettyUserSessionManagement(SessionManager sessionManager) { @@ -24,6 +26,7 @@ public class JettyUserSessionManagement implements UserSessionManagement { @Override public void logoutHttpSessions(List ids) { + log.trace("---> logoutHttpSessions"); for (String id : ids) { HttpSession httpSession = sessionManager.getHttpSession(id); if (httpSession != null) httpSession.invalidate(); diff --git a/integration/jetty/jetty9.1/pom.xml b/integration/jetty/jetty9.1/pom.xml new file mode 100755 index 0000000000..79ac91e2fd --- /dev/null +++ b/integration/jetty/jetty9.1/pom.xml @@ -0,0 +1,105 @@ + + + + keycloak-parent + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../../pom.xml + + 4.0.0 + + keycloak-jetty91-adapter + Keycloak Jetty 9.1.x Integration + + 9.1.5.v20140505 + + + + + + org.jboss.logging + jboss-logging + ${jboss.logging.version} + + + org.keycloak + keycloak-core + ${project.version} + + + org.keycloak + keycloak-adapter-core + ${project.version} + + + org.keycloak + keycloak-jetty9-core + ${project.version} + + + org.apache.httpcomponents + httpclient + ${keycloak.apache.httpcomponents.version} + + + net.iharder + base64 + + + org.bouncycastle + bcprov-jdk16 + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + org.codehaus.jackson + jackson-xc + + + org.eclipse.jetty + jetty-server + ${jetty9.version} + compile + + + + org.eclipse.jetty + jetty-util + ${jetty9.version} + compile + + + + org.eclipse.jetty + jetty-security + ${jetty9.version} + compile + + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + + diff --git a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java new file mode 100755 index 0000000000..60059f2b6f --- /dev/null +++ b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java @@ -0,0 +1,32 @@ +package org.keycloak.adapters.jetty; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.MultiMap; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JettyRequestAuthenticator extends AbstractJettyRequestAuthenticator { + + public JettyRequestAuthenticator(KeycloakDeployment deployment, + AbstractKeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore, + JettyHttpFacade facade, + Request request) { + super(facade, deployment, tokenStore, -1, valve, request); + } + + + @Override + protected MultiMap extractFormParameters(Request base_request) { + MultiMap formParameters = new MultiMap(); + base_request.extractParameters(); + return base_request.getParameters(); + } + @Override + protected void restoreFormParameters(MultiMap j_post, Request base_request) { + base_request.setParameters(j_post); + } +} diff --git a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java new file mode 100755 index 0000000000..7153370203 --- /dev/null +++ b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java @@ -0,0 +1,24 @@ +package org.keycloak.adapters.jetty; + +import org.eclipse.jetty.server.Request; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticator { + + public KeycloakJettyAuthenticator() { + super(); + } + + + @Override + protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) { + return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request); + } + + +} diff --git a/integration/jetty/jetty9.2/pom.xml b/integration/jetty/jetty9.2/pom.xml new file mode 100755 index 0000000000..42e549dc66 --- /dev/null +++ b/integration/jetty/jetty9.2/pom.xml @@ -0,0 +1,105 @@ + + + + keycloak-parent + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../../pom.xml + + 4.0.0 + + keycloak-jetty92-adapter + Keycloak Jetty 9.2.x Integration + + 9.2.4.v20141103 + + + + + + org.jboss.logging + jboss-logging + ${jboss.logging.version} + + + org.keycloak + keycloak-core + ${project.version} + + + org.keycloak + keycloak-adapter-core + ${project.version} + + + org.keycloak + keycloak-jetty9-core + ${project.version} + + + org.apache.httpcomponents + httpclient + ${keycloak.apache.httpcomponents.version} + + + net.iharder + base64 + + + org.bouncycastle + bcprov-jdk16 + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + org.codehaus.jackson + jackson-xc + + + org.eclipse.jetty + jetty-server + ${jetty9.version} + compile + + + + org.eclipse.jetty + jetty-util + ${jetty9.version} + compile + + + + org.eclipse.jetty + jetty-security + ${jetty9.version} + compile + + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + + diff --git a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java new file mode 100755 index 0000000000..9b08794b66 --- /dev/null +++ b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java @@ -0,0 +1,32 @@ +package org.keycloak.adapters.jetty; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.MultiMap; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class JettyRequestAuthenticator extends AbstractJettyRequestAuthenticator { + + public JettyRequestAuthenticator(KeycloakDeployment deployment, + AbstractKeycloakJettyAuthenticator valve, AdapterTokenStore tokenStore, + JettyHttpFacade facade, + Request request) { + super(facade, deployment, tokenStore, -1, valve, request); + } + + + @Override + protected MultiMap extractFormParameters(Request base_request) { + MultiMap formParameters = new MultiMap(); + base_request.extractFormParameters(formParameters); + return formParameters; + } + @Override + protected void restoreFormParameters(MultiMap j_post, Request base_request) { + base_request.setContentParameters(j_post); + } +} diff --git a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java new file mode 100755 index 0000000000..7153370203 --- /dev/null +++ b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java @@ -0,0 +1,24 @@ +package org.keycloak.adapters.jetty; + +import org.eclipse.jetty.server.Request; +import org.keycloak.adapters.AdapterTokenStore; +import org.keycloak.adapters.KeycloakDeployment; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticator { + + public KeycloakJettyAuthenticator() { + super(); + } + + + @Override + protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) { + return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request); + } + + +} diff --git a/integration/jetty/pom.xml b/integration/jetty/pom.xml new file mode 100755 index 0000000000..64d14b5d83 --- /dev/null +++ b/integration/jetty/pom.xml @@ -0,0 +1,21 @@ + + + keycloak-parent + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../pom.xml + + Keycloak Jetty Integration + + 4.0.0 + + keycloak-jetty-integration-pom + pom + + + jetty9-core + jetty9.2 + jetty9.1 + + diff --git a/integration/pom.xml b/integration/pom.xml index 034f21c144..bd21fd8f99 100755 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -20,7 +20,7 @@ jboss-adapter-core as7-eap6/adapter tomcat7/adapter - jetty9/adapter + jetty undertow wildfly-adapter wildfly-subsystem diff --git a/integration/tomcat7/adapter/pom.xml b/integration/tomcat7/adapter/pom.xml index 091a22bf92..f5cb8fef36 100755 --- a/integration/tomcat7/adapter/pom.xml +++ b/integration/tomcat7/adapter/pom.xml @@ -11,6 +11,9 @@ keycloak-tomcat7-adapter Keycloak Tomcat7 Integration + + 7.0.52 + @@ -29,11 +32,6 @@ keycloak-adapter-core ${project.version} - - org.keycloak - keycloak-jboss-adapter-core - ${project.version} - org.apache.httpcomponents httpclient @@ -62,13 +60,13 @@ org.apache.tomcat tomcat-servlet-api - 7.0.52 + ${tomcat.version} provided org.apache.tomcat tomcat-catalina - 7.0.54 + ${tomcat.version} provided diff --git a/testsuite/jetty/jetty91/pom.xml b/testsuite/jetty/jetty91/pom.xml new file mode 100755 index 0000000000..b59008e51f --- /dev/null +++ b/testsuite/jetty/jetty91/pom.xml @@ -0,0 +1,515 @@ + + + + keycloak-testsuite-pom + org.keycloak + 1.1.0.Beta2-SNAPSHOT + ../../pom.xml + + 4.0.0 + + keycloak-testsuite-jetty91 + Keycloak Jetty 9.1.x Integration TestSuite + + 9.1.5.v20140505 + + + + + + org.keycloak + keycloak-dependencies-server-all + ${project.version} + pom + + + org.keycloak + keycloak-admin-client + ${project.version} + + + log4j + log4j + + + org.slf4j + slf4j-api + 1.6.1 + + + org.slf4j + slf4j-log4j12 + 1.6.1 + + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + + + org.jboss.resteasy + jaxrs-api + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version.latest} + + + log4j + log4j + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-crypto + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version.latest} + + + org.jboss.resteasy + resteasy-undertow + ${resteasy.version.latest} + + + com.google.zxing + javase + + + org.bouncycastle + bcprov-jdk16 + + + org.apache.httpcomponents + httpclient + ${keycloak.apache.httpcomponents.version} + + + org.keycloak + keycloak-ldap-federation + ${project.version} + + + org.keycloak + keycloak-undertow-adapter + ${project.version} + + + org.keycloak + keycloak-jetty91-adapter + ${project.version} + + + org.jboss.logging + jboss-logging + + + io.undertow + undertow-servlet + + + io.undertow + undertow-core + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-mapper-asl + + + org.codehaus.jackson + jackson-xc + + + junit + junit + + + org.hamcrest + hamcrest-all + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + + + com.h2database + h2 + + + org.hibernate + hibernate-entitymanager + + + com.icegreen + greenmail + + + org.slf4j + slf4j-api + + + + + org.infinispan + infinispan-core + + + org.seleniumhq.selenium + selenium-java + + + xml-apis + xml-apis + + + org.seleniumhq.selenium + selenium-chrome-driver + + + org.wildfly + wildfly-undertow + ${wildfly.version} + test + + + org.keycloak + keycloak-testsuite-integration + ${project.version} + test + + + org.keycloak + keycloak-testsuite-integration + ${project.version} + test-jar + test + + + org.eclipse.jetty + jetty-util + ${jetty9.version} + provided + + + + org.eclipse.jetty + jetty-webapp + ${jetty9.version} + provided + + + + org.eclipse.jetty + jetty-security + ${jetty9.version} + provided + + + + org.eclipse.jetty + jetty-servlet + ${jetty9.version} + provided + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + test-jar + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.codehaus.mojo + exec-maven-plugin + + ${project.basedir} + + + + + + + + keycloak-server + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testutils.KeycloakServer + + + + + + + mail-server + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testutils.MailServer + + + + + + + totp + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testutils.TotpGenerator + + + + + + + + jpa + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + jpa + jpa + jpa + jpa + + + + + + + + + mongo + + + localhost + 27018 + keycloak + true + 127.0.0.1 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + test + integration-test + + test + + + + mongo + mongo + mongo + mongo + ${keycloak.connectionsMongo.host} + ${keycloak.connectionsMongo.port} + ${keycloak.connectionsMongo.db} + ${keycloak.connectionsMongo.clearOnStartup} + ${keycloak.connectionsMongo.bindIp} + + + + + default-test + + true + + + + + + + + com.github.joelittlejohn.embedmongo + embedmongo-maven-plugin + + + start-mongodb + pre-integration-test + + start + + + ${keycloak.connectionsMongo.port} + file + ${project.build.directory}/mongodb.log + ${keycloak.connectionsMongo.bindIp} + + + + stop-mongodb + post-integration-test + + stop + + + + + + + + + + + infinispan + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + infinispan + infinispan + infinispan + + + + + + + + + + + + keycloak.connectionsJpa.driver + com.mysql.jdbc.Driver + + + mysql + + + mysql + mysql-connector-java + ${mysql.version} + + + + + + + + + keycloak.connectionsJpa.driver + org.postgresql.Driver + + + postgresql + + + org.postgresql + postgresql + ${postgresql.version} + + + + + + clean-jpa + + + + org.liquibase + liquibase-maven-plugin + + META-INF/jpa-changelog-master.xml + + ${keycloak.connectionsJpa.url} + ${keycloak.connectionsJpa.driver} + ${keycloak.connectionsJpa.user} + ${keycloak.connectionsJpa.password} + + false + + + + clean-jpa + clean + + dropAll + + + + + + + + + diff --git a/testsuite/jetty9/src/test/java/org/keycloak/testsuite/Jetty9Test.java b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java similarity index 87% rename from testsuite/jetty9/src/test/java/org/keycloak/testsuite/Jetty9Test.java rename to testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java index abb019cd67..8995fa954f 100755 --- a/testsuite/jetty9/src/test/java/org/keycloak/testsuite/Jetty9Test.java +++ b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.security.Constraint; -import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.AfterClass; import org.junit.Assert; @@ -40,6 +39,7 @@ import org.junit.Rule; import org.junit.Test; import org.keycloak.KeycloakSecurityContext; import org.keycloak.OAuth2Constants; +import org.keycloak.adapters.jetty.AbstractKeycloakJettyAuthenticator; import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -58,12 +58,9 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.UriBuilder; -import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.net.URL; import java.security.Principal; -import java.util.regex.Matcher; /** * @author Stian Thorgersen @@ -84,6 +81,14 @@ public class Jetty9Test { public static class SendUsernameServlet extends HttpServlet { @Override protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { + if (req.getPathInfo().endsWith("logout")) { + req.logout(); + resp.setContentType("text/plain"); + OutputStream stream = resp.getOutputStream(); + stream.write("logout".getBytes()); + return; + + } resp.setContentType("text/plain"); OutputStream stream = resp.getOutputStream(); Principal principal = req.getUserPrincipal(); @@ -121,7 +126,7 @@ public class Jetty9Test { ConstraintSecurityHandler securityHandler = formHandler(); - KeycloakJettyAuthenticator authenticator = new KeycloakJettyAuthenticator(); + AbstractKeycloakJettyAuthenticator authenticator = new KeycloakJettyAuthenticator(); securityHandler.setAuthenticator(authenticator); appContext.setSecurityHandler(securityHandler); @@ -199,6 +204,20 @@ public class Jetty9Test { String currentUrl = driver.getCurrentUrl(); Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); + // test servletRequest.logout() + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/"); + pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke")); + driver.navigate().to("http://localhost:8080/customer-portal/logout"); + pageSource = driver.getPageSource(); + Assert.assertTrue(pageSource.contains("logout")); + driver.navigate().to("http://localhost:8080/customer-portal"); + currentUrl = driver.getCurrentUrl(); + Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); + } diff --git a/testsuite/jetty9/src/test/resources/jetty-test/demorealm.json b/testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json similarity index 100% rename from testsuite/jetty9/src/test/resources/jetty-test/demorealm.json rename to testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json diff --git a/testsuite/jetty9/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json b/testsuite/jetty/jetty91/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json similarity index 100% rename from testsuite/jetty9/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json rename to testsuite/jetty/jetty91/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json diff --git a/testsuite/jetty9/pom.xml b/testsuite/jetty/jetty92/pom.xml similarity index 96% rename from testsuite/jetty9/pom.xml rename to testsuite/jetty/jetty92/pom.xml index fd72e85d9c..04adf0e135 100755 --- a/testsuite/jetty9/pom.xml +++ b/testsuite/jetty/jetty92/pom.xml @@ -5,14 +5,14 @@ keycloak-testsuite-pom org.keycloak 1.1.0.Beta2-SNAPSHOT - ../pom.xml + ../../pom.xml 4.0.0 - keycloak-testsuite-jetty9 - Keycloak Jetty 9 Integration TestSuite + keycloak-testsuite-jetty92 + Keycloak Jetty 9.2.x Integration TestSuite - 9.1.0.v20131115 + 9.2.4.v20141103 @@ -120,7 +120,7 @@ org.keycloak - keycloak-jetty9-adapter + keycloak-jetty92-adapter ${project.version} diff --git a/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java new file mode 100755 index 0000000000..8995fa954f --- /dev/null +++ b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java @@ -0,0 +1,229 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite; + +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.KeycloakSecurityContext; +import org.keycloak.OAuth2Constants; +import org.keycloak.adapters.jetty.AbstractKeycloakJettyAuthenticator; +import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.protocol.oidc.OpenIDConnectService; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.rule.AbstractKeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.keycloak.testutils.KeycloakServer; +import org.openqa.selenium.WebDriver; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.io.OutputStream; +import java.security.Principal; + +/** + * @author Stian Thorgersen + */ +public class Jetty9Test { + static String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) + .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal").build("demo").toString(); + + @ClassRule + public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { + @Override + protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { + RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/jetty-test/demorealm.json"), RealmRepresentation.class); + RealmModel realm = manager.importRealm(representation); + } + }; + + public static class SendUsernameServlet extends HttpServlet { + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { + if (req.getPathInfo().endsWith("logout")) { + req.logout(); + resp.setContentType("text/plain"); + OutputStream stream = resp.getOutputStream(); + stream.write("logout".getBytes()); + return; + + } + resp.setContentType("text/plain"); + OutputStream stream = resp.getOutputStream(); + Principal principal = req.getUserPrincipal(); + if (principal == null) { + stream.write("null".getBytes()); + return; + } + String name = principal.getName(); + stream.write(name.getBytes()); + stream.write("\n".getBytes()); + KeycloakSecurityContext context = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName()); + stream.write(context.getIdToken().getName().getBytes()); + stream.write("\n".getBytes()); + stream.write(logoutUri.getBytes()); + + } + @Override + protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } + } + + public static Server server = null; + protected static WebAppContext appContext = null; + + + protected static void deploySP() throws Exception { + appContext = new WebAppContext(); + appContext.setResourceBase(Jetty9Test.class.getClassLoader().getResource("jetty-test/webapp").toExternalForm()); + appContext.setContextPath("/customer-portal"); + appContext.setParentLoaderPriority(true); + + appContext.addServlet(new ServletHolder(new SendUsernameServlet()), "/*"); + + + ConstraintSecurityHandler securityHandler = formHandler(); + + AbstractKeycloakJettyAuthenticator authenticator = new KeycloakJettyAuthenticator(); + securityHandler.setAuthenticator(authenticator); + + appContext.setSecurityHandler(securityHandler); + } + + private static ConstraintSecurityHandler formHandler() { + Constraint constraint = new Constraint(); + constraint.setName(Constraint.__FORM_AUTH); + ; + constraint.setRoles(new String[] { "user", "admin" }); + constraint.setAuthenticate(true); + + ConstraintMapping constraintMapping = new ConstraintMapping(); + constraintMapping.setConstraint(constraint); + constraintMapping.setPathSpec("/*"); + + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + securityHandler.setConstraintMappings(new ConstraintMapping[] { constraintMapping }); + + HashLoginService loginService = new HashLoginService(); + securityHandler.setLoginService(loginService); + return securityHandler; + } + + + + @BeforeClass + public static void initJetty() throws Exception { + server = new Server(8080); + + deploySP(); + + HandlerCollection handlers = new HandlerCollection(); + handlers.setHandlers(new Handler[] { appContext }); + server.setHandler(handlers); + + server.start(); + } + + + + @AfterClass + public static void shutdownJetty() throws Exception { + server.stop(); + server.destroy(); + } + + @Rule + public WebRule webRule = new WebRule(this); + @WebResource + protected WebDriver driver; + @WebResource + protected LoginPage loginPage; + + public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString(); + @Test + public void testLoginSSOAndLogout() throws Exception { + driver.navigate().to("http://localhost:8080/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke")); + + // test logout + + String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) + .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal").build("demo").toString(); + driver.navigate().to(logoutUri); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + driver.navigate().to("http://localhost:8080/customer-portal"); + String currentUrl = driver.getCurrentUrl(); + Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); + + // test servletRequest.logout() + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal/"); + pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke")); + driver.navigate().to("http://localhost:8080/customer-portal/logout"); + pageSource = driver.getPageSource(); + Assert.assertTrue(pageSource.contains("logout")); + driver.navigate().to("http://localhost:8080/customer-portal"); + currentUrl = driver.getCurrentUrl(); + Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); + + + } + + @Test + @Ignore + public void runit() throws Exception { + Thread.sleep(10000000); + } +} diff --git a/testsuite/jetty/jetty92/src/test/resources/jetty-test/demorealm.json b/testsuite/jetty/jetty92/src/test/resources/jetty-test/demorealm.json new file mode 100755 index 0000000000..a4a6ec9903 --- /dev/null +++ b/testsuite/jetty/jetty92/src/test/resources/jetty-test/demorealm.json @@ -0,0 +1,58 @@ +{ + "id": "demo", + "realm": "demo", + "enabled": true, + "accessTokenLifespan": 3000, + "accessCodeLifespan": 10, + "accessCodeLifespanUserAction": 6000, + "sslRequired": "external", + "registrationAllowed": false, + "social": false, + "passwordCredentialGrantAllowed": true, + "updateProfileOnInitialSocialLogin": 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", "admin" ], + "applicationRoles": { + "account": [ "manage-account" ] + } + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "User privileges" + }, + { + "name": "admin", + "description": "Administrator privileges" + } + ] + }, + "applications": [ + { + "name": "customer-portal", + "enabled": true, + "fullScopeAllowed": true, + "adminUrl": "http://localhost:8080/customer-portal", + "baseUrl": "http://localhost:8080/customer-portal", + "redirectUris": [ + "http://localhost:8080/customer-portal/*" + ], + "secret": "password" + } + ] +} diff --git a/testsuite/jetty/jetty92/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json b/testsuite/jetty/jetty92/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json new file mode 100755 index 0000000000..4e2fe1e556 --- /dev/null +++ b/testsuite/jetty/jetty92/src/test/resources/jetty-test/webapp/WEB-INF/keycloak.json @@ -0,0 +1,10 @@ +{ + "realm": "demo", + "resource": "customer-portal", + "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "http://localhost:8081/auth", + "ssl-required" : "external", + "credentials": { + "secret": "password" + } +} diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 28d0567fc4..1e0b788b7f 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -27,7 +27,8 @@ integration tomcat7 - jetty9 + jetty/jetty92 + jetty/jetty91 performance tools performance-web