KEYCLOAK-1749 Client registration service and client java api

This commit is contained in:
Stian Thorgersen committed 2015-10-08 11:55:42 +02:00
1 parent 7fec1677ee
commit 366a1629e5
21 files changed
+910 -41

No files matched your search

@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@@ -44,7 +45,11 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
String clientSecret = null;
String authorizationHeader = context.getHttpRequest().getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
MediaType mediaType = context.getHttpRequest().getHttpHeaders().getMediaType();
boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
if (authorizationHeader != null) {
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
@@ -54,7 +59,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
} else {
// Don't send 401 if client_id parameter was sent in request. For example IE may automatically send "Authorization: Negotiate" in XHR requests even for public clients
if (!formData.containsKey(OAuth2Constants.CLIENT_ID)) {
if (formData != null && !formData.containsKey(OAuth2Constants.CLIENT_ID)) {
Response challengeResponse = Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getRealm().getName() + "\"").build();
context.challenge(challengeResponse);
return;
@@ -62,7 +67,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
}
}
if (client_id == null) {
if (formData != null && client_id == null) {
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
clientSecret = formData.getFirst("client_secret");
}
@@ -3,6 +3,7 @@ package org.keycloak.services.managers;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.ClientConnection;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -39,6 +40,11 @@ public class AppAuthManager extends AuthenticationManager {
return tokenString;
}
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm) {
KeycloakContext ctx = session.getContext();
return authenticateBearerToken(session, realm, ctx.getUri(), ctx.getConnection(), ctx.getRequestHeaders());
}
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
String tokenString = extractAuthorizationHeaderToken(headers);
if (tokenString == null) return null;
@@ -3,21 +3,27 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.exportimport.KeycloakClientDescriptionConverter;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.*;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
@@ -36,6 +42,8 @@ public class ClientRegistrationService {
@Context
private KeycloakSession session;
private AppAuthManager authManager = new AppAuthManager();
public ClientRegistrationService(RealmModel realm, EventBuilder event) {
this.realm = realm;
this.event = event;
@@ -44,6 +52,10 @@ public class ClientRegistrationService {
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
public Response create(String description, @QueryParam("format") String format) {
event.event(EventType.CLIENT_REGISTER);
authenticate(true, null);
if (format == null) {
format = KeycloakClientDescriptionConverter.ID;
}
@@ -58,6 +70,10 @@ public class ClientRegistrationService {
ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
rep = ModelToRepresentation.toRepresentation(clientModel);
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
logger.infov("Created client {0}", rep.getClientId());
event.client(rep.getClientId()).success();
return Response.created(uri).entity(rep).build();
} catch (ModelDuplicateException e) {
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
@@ -67,34 +83,79 @@ public class ClientRegistrationService {
@GET
@Path("{clientId}")
@Produces(MediaType.APPLICATION_JSON)
public ClientRepresentation get(@PathParam("clientId") String clientId) {
AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
ClientModel client = clientAuth.getClient();
public Response get(@PathParam("clientId") String clientId) {
event.event(EventType.CLIENT_INFO);
ClientModel client = authenticate(false, clientId);
if (client == null) {
throw new NotFoundException("Client not found");
return Response.status(Response.Status.NOT_FOUND).build();
}
return ModelToRepresentation.toRepresentation(client);
return Response.ok(ModelToRepresentation.toRepresentation(client)).build();
}
@PUT
@Path("{clientId}")
@Consumes(MediaType.APPLICATION_JSON)
public void update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
throw new NotFoundException("Client not found");
}
public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
event.event(EventType.CLIENT_UPDATE).client(clientId);
ClientModel client = authenticate(false, clientId);
RepresentationToModel.updateClient(rep, client);
logger.infov("Updated client {0}", rep.getClientId());
event.success();
return Response.status(Response.Status.OK).build();
}
@DELETE
@Path("{clientId}")
public void delete(@PathParam("clientId") String clientId) {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
throw new NotFoundException("Client not found");
public Response delete(@PathParam("clientId") String clientId) {
event.event(EventType.CLIENT_DELETE).client(clientId);
ClientModel client = authenticate(false, clientId);
if (realm.removeClient(client.getId())) {
event.success();
return Response.ok().build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
realm.removeClient(client.getId());
}
private ClientModel authenticate(boolean create, String clientId) {
String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
if (bearer) {
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm);
AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
if (realmAccess != null) {
if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
return create ? null : realm.getClientByClientId(clientId);
}
if (create && realmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
return create ? null : realm.getClientByClientId(clientId);
}
}
} else if (!create) {
ClientModel client;
try {
AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
client = clientAuth.getClient();
if (client != null && !client.isPublicClient() && client.getClientId().equals(clientId)) {
return client;
}
} catch (Throwable t) {
}
}
event.error(Errors.NOT_ALLOWED);
throw new ForbiddenException();
}
}
@@ -112,14 +112,14 @@ public class RealmsResource {
return service;
}
// @Path("{realm}/client-registration")
// public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
// RealmModel realm = init(name);
// EventBuilder event = new EventBuilder(realm, session, clientConnection);
// ClientRegistrationService service = new ClientRegistrationService(realm, event);
// ResteasyProviderFactory.getInstance().injectProperties(service);
// return service;
// }
@Path("{realm}/client-registration")
public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
RealmModel realm = init(name);
EventBuilder event = new EventBuilder(realm, session, clientConnection);
ClientRegistrationService service = new ClientRegistrationService(realm, event);
ResteasyProviderFactory.getInstance().injectProperties(service);
return service;
}
@Path("{realm}/clients-managements")
public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {