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 + 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);