diff --git a/integration/jetty9/adapter/pom.xml b/integration/jetty9/adapter/pom.xml
new file mode 100755
index 0000000000..9e20aa10af
--- /dev/null
+++ b/integration/jetty9/adapter/pom.xml
@@ -0,0 +1,126 @@
+
+
+
+ keycloak-parent
+ org.keycloak
+ 1.1.0-Alpha1-SNAPSHOT
+ ../../../pom.xml
+
+ 4.0.0
+
+ keycloak-jetty9-adapter
+ Keycloak Jetty 9 Integration
+
+ 9.1.0.v20131115
+
+
+
+
+
+ org.jboss.logging
+ jboss-logging
+ ${jboss.logging.version}
+
+
+ org.keycloak
+ keycloak-core
+ ${project.version}
+
+
+ org.keycloak
+ keycloak-adapter-core
+ ${project.version}
+
+
+ org.keycloak
+ keycloak-jboss-adapter-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
+ ${jetty.version}
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-jaas
+ ${jetty.version}
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-util
+ ${jetty.version}
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ ${jetty.version}
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-security
+ ${jetty.version}
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${jetty.version}
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
diff --git a/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
new file mode 100755
index 0000000000..7e82b37768
--- /dev/null
+++ b/integration/jetty9/adapter/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -0,0 +1,560 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.adapters.jetty;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MimeTypes;
+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.server.Authentication;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.URIUtil;
+import org.keycloak.adapters.AdapterConstants;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.NodesRegistrationManagement;
+import org.w3c.dom.Document;
+
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionListener;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class KeycloakJettyAuthenticator extends FormAuthenticator {
+ private final static Logger log = Logger.getLogger(""+KeycloakJettyAuthenticator.class);
+
+ protected ServletContext theServletContext = null;
+
+ protected AdapterDeploymentContext deploymentContext;
+ protected NodesRegistrationManagement nodesRegistrationManagement;
+ protected int timerInterval = -1;
+
+ protected Timer timer = null;
+
+ public static final String EMPTY_PASSWORD = "EMPTY_STR";
+
+ protected boolean enableAudit = false;
+
+ public static final String FORM_PRINCIPAL_NOTE = "picketlink.form.principal";
+ public static final String FORM_ROLES_NOTE = "picketlink.form.roles";
+ public static final String FORM_REQUEST_NOTE = "picketlink.REQUEST";
+
+ public static final String logoutPage = "/logout.html"; // get from configuration
+
+ protected String serviceURL = null;
+ protected String identityURL = null;
+ protected String issuerID = null;
+ protected String configFile;
+
+ // Whether the authenticator has to to save and restore request
+ protected boolean saveRestoreRequest = true;
+
+ /**
+ * A Lock for Handler operations in the chain
+ */
+ protected Lock chainLock = new ReentrantLock();
+ protected String canonicalizationMethod = CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS;
+
+
+ public KeycloakJettyAuthenticator() {
+ }
+
+ public KeycloakJettyAuthenticator(String login, String error, boolean dispatch) {
+ super(login, error, dispatch);
+ }
+
+ @Override
+ public void setConfiguration(AuthConfiguration configuration) {
+ super.setConfiguration(configuration);
+ initializeKeycloak();
+ }
+
+ @SuppressWarnings("UseSpecificCatch")
+ @Override
+ 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.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
+ } catch (Exception ex) {
+ log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
+ deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ InputStream configInputStream = getConfigInputStream(theServletContext);
+ KeycloakDeployment kd;
+ if (configInputStream == null) {
+ log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ kd = new KeycloakDeployment();
+ } else {
+ kd = KeycloakDeploymentBuilder.build(configInputStream);
+ }
+ deploymentContext = new AdapterDeploymentContext(kd);
+ log.fine("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) {
+ return null;
+ }
+ log.finest("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
+ log.finest(json);
+ return new ByteArrayInputStream(json.getBytes());
+ }
+
+
+ private InputStream getConfigInputStream(ServletContext servletContext) {
+ InputStream is = getJSONFromServletContext(servletContext);
+ if (is == null) {
+ String path = servletContext.getInitParameter("keycloak.config.file");
+ if (path == null) {
+ log.finest("**** using /WEB-INF/keycloak.json");
+ is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ log.severe("NOT FOUND /WEB-INF/keycloak.json");
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return is;
+ }
+
+
+
+ @Override
+ public Authentication validateRequest(ServletRequest servletRequest, ServletResponse servletResponse, boolean mandatory)
+ throws ServerAuthException {
+ // TODO: Deal with character encoding
+ // request.setCharacterEncoding(xyz)
+
+ String contextPath = ContextHandler.getCurrentContext().getContextPath();
+ theServletContext = ContextHandler.getCurrentContext().getContext(contextPath);
+
+ // Get the session
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ HttpSession session = request.getSession();
+
+ System.out.println("Request ID=" + servletRequest.toString());
+ System.out.println("Session ID=" + session.getId());
+
+ // check if this call is resulting from the redirect after successful authentication.
+ // if so, make the authentication successful and continue the original request
+ if (saveRestoreRequest && matchRequest(request)) {
+ Principal savedPrincipal = (Principal) session.getAttribute(FORM_PRINCIPAL_NOTE);
+ List savedRoles = (List) session.getAttribute(FORM_ROLES_NOTE);
+ Authentication registeredAuthentication = register(request, savedPrincipal, savedRoles);
+
+ // try to restore the original request (including post data, etc...)
+ if (restoreRequest(request, session)) {
+ // success! user is authenticated; continue processing original request
+ return registeredAuthentication;
+ } else {
+ // no saved request found...
+ return Authentication.UNAUTHENTICATED;
+ }
+ }
+ ServiceProviderSAMLWorkflow serviceProviderSAMLWorkflow = new ServiceProviderSAMLWorkflow();
+ serviceProviderSAMLWorkflow.setRedirectionHandler(new JettyRedirectionHandler());
+
+ // Eagerly look for Local LogOut
+ boolean localLogout = serviceProviderSAMLWorkflow.isLocalLogoutRequest(request);
+
+ if (localLogout) {
+ try {
+ serviceProviderSAMLWorkflow.sendToLogoutPage(request, response, session, theServletContext, logoutPage);
+ } catch (ServletException e) {
+ logger.samlLogoutError(e);
+ throw new RuntimeException(e);
+ } catch (IOException e1) {
+ logger.samlLogoutError(e1);
+ throw new RuntimeException(e1);
+ }
+ return Authentication.UNAUTHENTICATED;
+ }
+
+ String samlRequest = request.getParameter(GeneralConstants.SAML_REQUEST_KEY);
+ String samlResponse = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
+
+ Principal principal = request.getUserPrincipal();
+
+ try {
+ // If we have already authenticated the user and there is no request from IDP or logout from user
+ if (principal != null
+ && !(serviceProviderSAMLWorkflow.isLocalLogoutRequest(request) || isNotNull(samlRequest) || isNotNull(samlResponse)))
+ return Authentication.SEND_SUCCESS;
+
+ // General User Request
+ if (!isNotNull(samlRequest) && !isNotNull(samlResponse)) {
+ return generalUserRequest(servletRequest, servletResponse, mandatory);
+ }
+
+ // Handle a SAML Response from IDP
+ if (isNotNull(samlResponse)) {
+ return handleSAMLResponse(servletRequest, servletResponse, mandatory);
+ }
+
+ // Handle SAML Requests from IDP
+ if (isNotNull(samlRequest)) {
+ return handleSAMLRequest(servletRequest, servletResponse, mandatory);
+ }// end if
+
+ // local authentication
+ return localAuthentication(servletRequest, servletResponse, mandatory);
+ } catch (IOException e) {
+ if (StringUtil.isNotNull(spConfiguration.getErrorPage())) {
+ try {
+ request.getRequestDispatcher(spConfiguration.getErrorPage()).forward(request, response);
+ } catch (ServletException e1) {
+ logger.samlErrorPageForwardError(spConfiguration.getErrorPage(), e1);
+ } catch (IOException e1) {
+ logger.samlErrorPageForwardError(spConfiguration.getErrorPage(), e1);
+ }
+ return Authentication.UNAUTHENTICATED;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Handle the user invocation for the first time
+ *
+ * @param servletRequest
+ * @param servletResponse
+ * @param mandatory
+ * @return
+ * @throws java.io.IOException
+ */
+ private Authentication generalUserRequest(ServletRequest servletRequest, ServletResponse servletResponse, boolean mandatory)
+ throws IOException, ServerAuthException {
+ //only perform SAML Authentication if it is mandatory
+ if(!mandatory){
+ Request request = (Request) servletRequest;
+ return request.getAuthentication();
+ }
+ ServiceProviderSAMLWorkflow serviceProviderSAMLWorkflow = new ServiceProviderSAMLWorkflow();
+ serviceProviderSAMLWorkflow.setRedirectionHandler(new JettyRedirectionHandler());
+
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ HttpSession session = request.getSession(false);
+ boolean willSendRequest = false;
+
+ HTTPContext httpContext = new HTTPContext(request, response, theServletContext);
+ Set handlers = chain.handlers();
+
+ boolean postBinding = spConfiguration.getBindingType().equals("POST");
+
+ // Neither saml request nor response from IDP
+ // So this is a user request
+ SAML2HandlerResponse saml2HandlerResponse = null;
+ try {
+ ServiceProviderBaseProcessor baseProcessor = new ServiceProviderBaseProcessor(postBinding, serviceURL,
+ this.picketLinkConfiguration);
+ if (issuerID != null)
+ baseProcessor.setIssuer(issuerID);
+
+ baseProcessor.setIdentityURL(identityURL);
+ baseProcessor.setAuditHelper(auditHelper);
+
+ saml2HandlerResponse = baseProcessor.process(httpContext, handlers, chainLock);
+ } catch (ProcessingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ParsingException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ } catch (ConfigurationException pe) {
+ logger.samlSPHandleRequestError(pe);
+ throw new RuntimeException(pe);
+ }
+
+ willSendRequest = saml2HandlerResponse.getSendRequest();
+
+ Document samlResponseDocument = saml2HandlerResponse.getResultingDocument();
+ String relayState = saml2HandlerResponse.getRelayState();
+
+ String destination = saml2HandlerResponse.getDestination();
+ String destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
+
+ if (destination != null && samlResponseDocument != null) {
+ try {
+ if (saveRestoreRequest) {
+ this.saveRequest(request, session);
+ }
+ if (enableAudit) {
+ PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
+ auditEvent.setType(PicketLinkAuditEventType.REQUEST_TO_IDP);
+ auditEvent.setWhoIsAuditing(theServletContext.getContextPath());
+ auditHelper.audit(auditEvent);
+ }
+ serviceProviderSAMLWorkflow.sendRequestToIDP(destination, samlResponseDocument, relayState, response,
+ willSendRequest, destinationQueryStringWithSignature, isHttpPostBinding());
+ return Authentication.SEND_CONTINUE;
+ } catch (Exception e) {
+ logger.samlSPHandleRequestError(e);
+ throw logger.samlSPProcessingExceptionError(e);
+ }
+ }
+
+ return localAuthentication(servletRequest, servletResponse, mandatory);
+ }
+
+ protected boolean matchRequest(HttpServletRequest request) {
+ HttpSession session = request.getSession(false);
+ synchronized (session) {
+ String j_uri = (String) session.getAttribute(__J_URI);
+ if (j_uri != null) {
+ // check if the request is for the same url as the original and restore
+ // params if it was a post
+ StringBuffer buf = request.getRequestURL();
+ if (request.getQueryString() != null)
+ buf.append("?").append(request.getQueryString());
+
+ if (j_uri.equals(buf.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ protected Authentication register(HttpServletRequest httpServletRequest, Principal principal, List roles) {
+ if (roles == null) {
+ roles = new ArrayList();
+ }
+ HttpSession session = httpServletRequest.getSession(false);
+ session.setAttribute(FORM_PRINCIPAL_NOTE, principal);
+ session.setAttribute(FORM_ROLES_NOTE, roles);
+ Request request = (Request) httpServletRequest;
+ 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);
+ request.setAuthentication(authentication);
+ }
+ return authentication;
+ }
+
+ protected boolean restoreRequest(HttpServletRequest request, HttpSession session) {
+ synchronized (session) {
+ String j_uri = (String) session.getAttribute(__J_URI);
+ if (j_uri != null) {
+ // check if the request is for the same url as the original and restore
+ // params if it was a post
+ StringBuffer buf = request.getRequestURL();
+ if (request.getQueryString() != null)
+ buf.append("?").append(request.getQueryString());
+
+ /*
+ * if (j_uri.equals(buf.toString())) {
+ */
+ MultiMap j_post = (MultiMap) session.getAttribute(__J_POST);
+ if (j_post != null) {
+ Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
+ base_request.setParameters(j_post);
+ }
+ session.removeAttribute(__J_URI);
+ session.removeAttribute(__J_METHOD);
+ session.removeAttribute(__J_POST);
+ // }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ protected void saveRequest(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(__J_URI) == null) {
+ StringBuffer buf = request.getRequestURL();
+ if (request.getQueryString() != null)
+ buf.append("?").append(request.getQueryString());
+ session.setAttribute(__J_URI, buf.toString());
+ session.setAttribute(__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(__J_POST, new MultiMap(base_request.getParameters()));
+ }
+ }
+ }
+ }
+
+ /**
+ * Fall back on local authentication at the service provider side
+ *
+ * @param servletRequest
+ * @param servletRequest
+ * @param mandatory
+ * @return
+ * @throws java.io.IOException
+ */
+ protected Authentication localAuthentication(ServletRequest servletRequest, ServletResponse servletResponse,
+ boolean mandatory) throws IOException, ServerAuthException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ if (request.getUserPrincipal() == null) {
+ logger.samlSPFallingBackToLocalFormAuthentication();// fallback
+ try {
+ return super.validateRequest(servletRequest, servletResponse, mandatory);
+ } catch (NoSuchMethodError e) {
+ /*
+ * // Use Reflection try { Method method = super.getClass().getMethod("authenticate", new Class[] {
+ * HttpServletRequest.class, HttpServletResponse.class, LoginConfig.class }); return (Boolean)
+ * method.invoke(this, new Object[] { request.getRequest(), response.getResponse(), loginConfig }); } catch
+ * (Exception ex) { throw logger.unableLocalAuthentication(ex); }
+ */
+ }
+ } else {
+ return Authentication.SEND_SUCCESS;
+ }
+ return Authentication.UNAUTHENTICATED;
+ }
+
+ protected boolean sessionIsValid(HttpSession session) {
+ try {
+ long sessionTime = session.getCreationTime();
+ } catch (IllegalStateException ise) {
+ return false;
+ }
+ return true;
+ }
+
+ protected String savedRequestURL(HttpSession session) {
+ StringBuilder builder = new StringBuilder();
+ HttpServletRequest request = (HttpServletRequest) session.getAttribute(FORM_REQUEST_NOTE);
+ if (request != null) {
+ builder.append(request.getRequestURI());
+ if (request.getQueryString() != null) {
+ builder.append("?").append(request.getQueryString());
+ }
+ }
+ return builder.toString();
+ }
+
+
+ /**
+ * An instance of {@link org.picketlink.identity.federation.core.saml.workflow.ServiceProviderSAMLWorkflow.RedirectionHandler}
+ * that performs JETTY specific redirection and post workflows
+ */
+ public class JettyRedirectionHandler extends ServiceProviderSAMLWorkflow.RedirectionHandler {
+ @Override
+ public void sendRedirectForRequestor(String destination, HttpServletResponse response) throws IOException {
+ common(destination, response);
+ response.setHeader("Cache-Control", "no-cache, no-store");
+ sendRedirect(response, destination);
+ }
+
+ @Override
+ public void sendRedirectForResponder(String destination, HttpServletResponse response) throws IOException {
+ common(destination, response);
+ response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate,private");
+ sendRedirect(response, destination);
+ }
+
+ private void common(String destination, HttpServletResponse response) {
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Location", destination);
+ response.setHeader("Pragma", "no-cache");
+ }
+
+ private void sendRedirect(HttpServletResponse response, String destination) throws IOException {
+ // response.reset();
+ response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ response.sendRedirect(destination);
+ }
+ }
+}
diff --git a/integration/pom.xml b/integration/pom.xml
index 0b7203abcc..0c52175399 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -20,6 +20,7 @@
jboss-adapter-core
as7-eap6/adapter
tomcat7/adapter
+
undertow
wildfly-adapter
wildfly-subsystem
diff --git a/services/pom.xml b/services/pom.xml
index 4597393cec..1b9e0b06e4 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -229,7 +229,7 @@
com.lunatech.jax-doclets
doclets
- 0.10.0
+ 0.10.2
false
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index a5a4ec9f85..5e5a24211c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -120,6 +120,11 @@ public class ApplicationResource {
return ModelToRepresentation.toRepresentation(application);
}
+ /**
+ *
+ * @param attributePrefix
+ * @return
+ */
@Path("certificates/{attr}")
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index 9e064c4fab..3437407855 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -84,6 +84,10 @@ public class ClientAttributeCertificateResource {
}
}
+ /**
+ *
+ * @return
+ */
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@@ -94,7 +98,10 @@ public class ClientAttributeCertificateResource {
return info;
}
-
+ /**
+ *
+ * @return
+ */
@POST
@NoCache
@Path("generate")
@@ -129,6 +136,13 @@ public class ClientAttributeCertificateResource {
return info;
}
+ /**
+ *
+ * @param uriInfo
+ * @param input
+ * @return
+ * @throws IOException
+ */
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@@ -238,6 +252,11 @@ public class ClientAttributeCertificateResource {
}
}
+ /**
+ *
+ * @param config
+ * @return
+ */
@POST
@NoCache
@Path("/download")
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index 63805e4447..1bdf4109f2 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -73,6 +73,11 @@ public class OAuthClientResource {
return new ClaimResource(oauthClient, auth);
}
+ /**
+ *
+ * @param attributePrefix
+ * @return
+ */
@Path("certificates/{attr}")
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
return new ClientAttributeCertificateResource(realm, auth, oauthClient, session, attributePrefix);