Merge pull request #1707 from stianst/client-reg
Refactored client registration service
This commit is contained in:
commit
4f7979d9fc
12 changed files with 231 additions and 40 deletions
|
@ -231,7 +231,7 @@ public class ClientRegistration {
|
||||||
|
|
||||||
public ClientRegistration build() {
|
public ClientRegistration build() {
|
||||||
ClientRegistration clientRegistration = new ClientRegistration();
|
ClientRegistration clientRegistration = new ClientRegistration();
|
||||||
clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration";
|
clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration/default";
|
||||||
|
|
||||||
clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
|
clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
|
||||||
clientRegistration.auth = auth;
|
clientRegistration.auth = auth;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public interface ClientRegistrationProvider extends Provider {
|
||||||
|
|
||||||
|
void setRealm(RealmModel realm);
|
||||||
|
|
||||||
|
void setEvent(EventBuilder event);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public interface ClientRegistrationProviderFactory extends ProviderFactory<ClientRegistrationProvider> {
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.managers.AppAuthManager;
|
||||||
|
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationService {
|
||||||
|
|
||||||
|
private RealmModel realm;
|
||||||
|
|
||||||
|
private EventBuilder event;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
public ClientRegistrationService(RealmModel realm, EventBuilder event) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{provider}")
|
||||||
|
public Object getProvider(@PathParam("provider") String providerId) {
|
||||||
|
ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId);
|
||||||
|
provider.setRealm(realm);
|
||||||
|
provider.setEvent(event);
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationSpi implements Spi {
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "client-registration";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return ClientRegistrationProvider.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return ClientRegistrationProviderFactory.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,9 @@
|
||||||
package org.keycloak.services.resources;
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
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.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.exportimport.ClientDescriptionConverter;
|
|
||||||
import org.keycloak.exportimport.KeycloakClientDescriptionConverter;
|
|
||||||
import org.keycloak.models.*;
|
import org.keycloak.models.*;
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
@ -16,13 +11,11 @@ import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
import org.keycloak.services.managers.AppAuthManager;
|
import org.keycloak.services.managers.AppAuthManager;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -31,52 +24,37 @@ import java.net.URI;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class ClientRegistrationService {
|
public class DefaultClientRegistrationProvider implements ClientRegistrationProvider {
|
||||||
|
|
||||||
protected static final Logger logger = Logger.getLogger(ClientRegistrationService.class);
|
private static final Logger logger = Logger.getLogger(DefaultClientRegistrationProvider.class);
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
private EventBuilder event;
|
||||||
private RealmModel realm;
|
private RealmModel realm;
|
||||||
|
|
||||||
private EventBuilder event;
|
public DefaultClientRegistrationProvider(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
@Context
|
|
||||||
private KeycloakSession session;
|
|
||||||
|
|
||||||
private AppAuthManager authManager = new AppAuthManager();
|
|
||||||
|
|
||||||
public ClientRegistrationService(RealmModel realm, EventBuilder event) {
|
|
||||||
this.realm = realm;
|
|
||||||
this.event = event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response create(String description, @QueryParam("format") String format) {
|
public Response create(ClientRepresentation client) {
|
||||||
event.event(EventType.CLIENT_REGISTER);
|
event.event(EventType.CLIENT_REGISTER);
|
||||||
|
|
||||||
authenticate(true, null);
|
authenticate(true, null);
|
||||||
|
|
||||||
if (format == null) {
|
|
||||||
format = KeycloakClientDescriptionConverter.ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientDescriptionConverter converter = session.getProvider(ClientDescriptionConverter.class, format);
|
|
||||||
if (converter == null) {
|
|
||||||
throw new BadRequestException("Invalid format");
|
|
||||||
}
|
|
||||||
ClientRepresentation rep = converter.convertToInternal(description);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
|
ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true);
|
||||||
rep = ModelToRepresentation.toRepresentation(clientModel);
|
client = ModelToRepresentation.toRepresentation(clientModel);
|
||||||
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
|
URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
|
||||||
|
|
||||||
logger.infov("Created client {0}", rep.getClientId());
|
logger.infov("Created client {0}", client.getClientId());
|
||||||
|
|
||||||
event.client(rep.getClientId()).success();
|
event.client(client.getClientId()).success();
|
||||||
return Response.created(uri).entity(rep).build();
|
return Response.created(uri).entity(client).build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +100,19 @@ public class ClientRegistrationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private ClientModel authenticate(boolean create, String clientId) {
|
private ClientModel authenticate(boolean create, String clientId) {
|
||||||
String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||||
|
|
||||||
boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
|
boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
|
||||||
|
|
||||||
if (bearer) {
|
if (bearer) {
|
||||||
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm);
|
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm);
|
||||||
AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||||
if (realmAccess != null) {
|
if (realmAccess != null) {
|
||||||
if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
|
if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
|
||||||
|
@ -158,4 +142,14 @@ public class ClientRegistrationService {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRealm(RealmModel realm) {
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEvent(EventBuilder event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class DefaultClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientRegistrationProvider create(KeycloakSession session) {
|
||||||
|
return new DefaultClientRegistrationProvider(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class OIDCClientRegistrationProvider implements ClientRegistrationProvider {
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
private RealmModel realm;
|
||||||
|
private EventBuilder event;
|
||||||
|
|
||||||
|
public OIDCClientRegistrationProvider(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRealm(RealmModel realm) {
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEvent(EventBuilder event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.keycloak.services.clientregistration;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class OIDCClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientRegistrationProvider create(KeycloakSession session) {
|
||||||
|
return new OIDCClientRegistrationProvider(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "openid-connect";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.LoginProtocolFactory;
|
import org.keycloak.protocol.LoginProtocolFactory;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
|
import org.keycloak.services.clientregistration.ClientRegistrationService;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
|
|
@ -8,3 +8,4 @@ org.keycloak.authentication.ClientAuthenticatorSpi
|
||||||
org.keycloak.authentication.RequiredActionSpi
|
org.keycloak.authentication.RequiredActionSpi
|
||||||
org.keycloak.authentication.FormAuthenticatorSpi
|
org.keycloak.authentication.FormAuthenticatorSpi
|
||||||
org.keycloak.authentication.FormActionSpi
|
org.keycloak.authentication.FormActionSpi
|
||||||
|
org.keycloak.services.clientregistration.ClientRegistrationSpi
|
|
@ -0,0 +1 @@
|
||||||
|
org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
|
Loading…
Reference in a new issue