KEYCLOAK-1749 Client registration service and client java api
This commit is contained in:
parent
7fec1677ee
commit
366a1629e5
21 changed files with 910 additions and 41 deletions
31
client-api/pom.xml
Executable file
31
client-api/pom.xml
Executable file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.6.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-client-api</artifactId>
|
||||
<name>Keycloak Client API</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,275 @@
|
|||
package org.keycloak.client.registration;
|
||||
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.util.Base64;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientRegistration {
|
||||
|
||||
private String clientRegistrationUrl;
|
||||
private HttpClient httpClient;
|
||||
private Auth auth;
|
||||
|
||||
public static ClientRegistrationBuilder create() {
|
||||
return new ClientRegistrationBuilder();
|
||||
}
|
||||
|
||||
private ClientRegistration() {
|
||||
}
|
||||
|
||||
public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
|
||||
String content = serialize(client);
|
||||
InputStream resultStream = doPost(content);
|
||||
return deserialize(resultStream, ClientRepresentation.class);
|
||||
}
|
||||
|
||||
public ClientRepresentation get() throws ClientRegistrationException {
|
||||
if (auth instanceof ClientIdSecretAuth) {
|
||||
String clientId = ((ClientIdSecretAuth) auth).clientId;
|
||||
return get(clientId);
|
||||
} else {
|
||||
throw new ClientRegistrationException("Requires client authentication");
|
||||
}
|
||||
}
|
||||
|
||||
public ClientRepresentation get(String clientId) throws ClientRegistrationException {
|
||||
InputStream resultStream = doGet(clientId);
|
||||
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
|
||||
}
|
||||
|
||||
public void update(ClientRepresentation client) throws ClientRegistrationException {
|
||||
String content = serialize(client);
|
||||
doPut(content, client.getClientId());
|
||||
}
|
||||
|
||||
public void delete() throws ClientRegistrationException {
|
||||
if (auth instanceof ClientIdSecretAuth) {
|
||||
String clientId = ((ClientIdSecretAuth) auth).clientId;
|
||||
delete(clientId);
|
||||
} else {
|
||||
throw new ClientRegistrationException("Requires client authentication");
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(String clientId) throws ClientRegistrationException {
|
||||
doDelete(clientId);
|
||||
}
|
||||
|
||||
public void close() throws ClientRegistrationException {
|
||||
if (httpClient instanceof CloseableHttpClient) {
|
||||
try {
|
||||
((CloseableHttpClient) httpClient).close();
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to close http client", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream doPost(String content) throws ClientRegistrationException {
|
||||
try {
|
||||
HttpPost request = new HttpPost(clientRegistrationUrl);
|
||||
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
|
||||
request.setHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
request.setEntity(new StringEntity(content));
|
||||
|
||||
auth.addAuth(request);
|
||||
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
InputStream responseStream = null;
|
||||
if (response.getEntity() != null) {
|
||||
responseStream = response.getEntity().getContent();
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() == 201) {
|
||||
return responseStream;
|
||||
} else {
|
||||
responseStream.close();
|
||||
throw new HttpErrorException(response.getStatusLine());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to send request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream doGet(String endpoint) throws ClientRegistrationException {
|
||||
try {
|
||||
HttpGet request = new HttpGet(clientRegistrationUrl + "/" + endpoint);
|
||||
|
||||
request.setHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
|
||||
auth.addAuth(request);
|
||||
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
InputStream responseStream = null;
|
||||
if (response.getEntity() != null) {
|
||||
responseStream = response.getEntity().getContent();
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() == 200) {
|
||||
return responseStream;
|
||||
} else if (response.getStatusLine().getStatusCode() == 404) {
|
||||
responseStream.close();
|
||||
return null;
|
||||
} else {
|
||||
responseStream.close();
|
||||
throw new HttpErrorException(response.getStatusLine());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to send request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doPut(String content, String endpoint) throws ClientRegistrationException {
|
||||
try {
|
||||
HttpPut request = new HttpPut(clientRegistrationUrl + "/" + endpoint);
|
||||
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
|
||||
request.setHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
request.setEntity(new StringEntity(content));
|
||||
|
||||
auth.addAuth(request);
|
||||
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
if (response.getEntity() != null) {
|
||||
response.getEntity().getContent().close();
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new HttpErrorException(response.getStatusLine());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to send request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doDelete(String endpoint) throws ClientRegistrationException {
|
||||
try {
|
||||
HttpDelete request = new HttpDelete(clientRegistrationUrl + "/" + endpoint);
|
||||
|
||||
auth.addAuth(request);
|
||||
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
if (response.getEntity() != null) {
|
||||
response.getEntity().getContent().close();
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new HttpErrorException(response.getStatusLine());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to send request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String serialize(ClientRepresentation client) throws ClientRegistrationException {
|
||||
try {
|
||||
return JsonSerialization.writeValueAsString(client);
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to write json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T deserialize(InputStream inputStream, Class<T> clazz) throws ClientRegistrationException {
|
||||
try {
|
||||
return JsonSerialization.readValue(inputStream, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new ClientRegistrationException("Failed to read json object", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClientRegistrationBuilder {
|
||||
|
||||
private String realm;
|
||||
|
||||
private String authServerUrl;
|
||||
|
||||
private Auth auth;
|
||||
|
||||
private HttpClient httpClient;
|
||||
|
||||
public ClientRegistrationBuilder realm(String realm) {
|
||||
this.realm = realm;
|
||||
return this;
|
||||
}
|
||||
public ClientRegistrationBuilder authServerUrl(String authServerUrl) {
|
||||
this.authServerUrl = authServerUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientRegistrationBuilder auth(String token) {
|
||||
this.auth = new TokenAuth(token);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientRegistrationBuilder auth(String clientId, String clientSecret) {
|
||||
this.auth = new ClientIdSecretAuth(clientId, clientSecret);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientRegistrationBuilder httpClient(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientRegistration build() {
|
||||
ClientRegistration clientRegistration = new ClientRegistration();
|
||||
clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration";
|
||||
|
||||
clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
|
||||
clientRegistration.auth = auth;
|
||||
|
||||
return clientRegistration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface Auth {
|
||||
void addAuth(HttpRequest httpRequest);
|
||||
}
|
||||
|
||||
public static class AuthorizationHeaderAuth implements Auth {
|
||||
private String credentials;
|
||||
|
||||
public AuthorizationHeaderAuth(String credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public void addAuth(HttpRequest httpRequest) {
|
||||
httpRequest.setHeader(HttpHeaders.AUTHORIZATION, credentials);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TokenAuth extends AuthorizationHeaderAuth {
|
||||
public TokenAuth(String token) {
|
||||
super("Bearer " + token);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClientIdSecretAuth extends AuthorizationHeaderAuth {
|
||||
private String clientId;
|
||||
|
||||
public ClientIdSecretAuth(String clientId, String clientSecret) {
|
||||
super("Basic " + Base64.encodeBytes((clientId + ":" + clientSecret).getBytes()));
|
||||
this.clientId = clientId;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.keycloak.client.registration;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientRegistrationException extends Exception {
|
||||
|
||||
public ClientRegistrationException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
|
||||
public ClientRegistrationException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.keycloak.client.registration;
|
||||
|
||||
import org.apache.http.StatusLine;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class HttpErrorException extends IOException {
|
||||
|
||||
private StatusLine statusLine;
|
||||
|
||||
public HttpErrorException(StatusLine statusLine) {
|
||||
this.statusLine = statusLine;
|
||||
}
|
||||
|
||||
public StatusLine getStatusLine() {
|
||||
return statusLine;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
|
|
@ -69,7 +69,16 @@ public enum EventType {
|
|||
IMPERSONATE(true),
|
||||
CUSTOM_REQUIRED_ACTION(true),
|
||||
CUSTOM_REQUIRED_ACTION_ERROR(true),
|
||||
EXECUTE_ACTIONS(true);
|
||||
EXECUTE_ACTIONS(true),
|
||||
|
||||
CLIENT_INFO(false),
|
||||
CLIENT_INFO_ERROR(false),
|
||||
CLIENT_REGISTER(true),
|
||||
CLIENT_REGISTER_ERROR(true),
|
||||
CLIENT_UPDATE(true),
|
||||
CLIENT_UPDATE_ERROR(true),
|
||||
CLIENT_DELETE(true),
|
||||
CLIENT_DELETE_ERROR(true);
|
||||
|
||||
private boolean saveByDefault;
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ personalInfo=Personal Info:
|
|||
role_admin=Admin
|
||||
role_realm-admin=Realm Admin
|
||||
role_create-realm=Create realm
|
||||
role_create-client=Create client
|
||||
role_view-realm=View realm
|
||||
role_view-users=View users
|
||||
role_view-applications=View applications
|
||||
|
|
|
@ -70,6 +70,15 @@ public class MigrateTo1_6_0 {
|
|||
if ((adminConsoleClient != null) && !localeMapperAdded(adminConsoleClient)) {
|
||||
adminConsoleClient.addProtocolMapper(localeMapper);
|
||||
}
|
||||
|
||||
ClientModel client = realm.getMasterAdminClient();
|
||||
if (client.getRole(AdminRoles.CREATE_CLIENT) == null) {
|
||||
RoleModel role = client.addRole(AdminRoles.CREATE_CLIENT);
|
||||
role.setDescription("${role_" + AdminRoles.CREATE_CLIENT + "}");
|
||||
role.setScopeParamRequired(false);
|
||||
|
||||
realm.getRole(AdminRoles.ADMIN).addCompositeRole(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ public class AdminRoles {
|
|||
public static String REALM_ADMIN = "realm-admin";
|
||||
|
||||
public static String CREATE_REALM = "create-realm";
|
||||
public static String CREATE_CLIENT = "create-client";
|
||||
|
||||
public static String VIEW_REALM = "view-realm";
|
||||
public static String VIEW_USERS = "view-users";
|
||||
|
@ -26,6 +27,6 @@ public class AdminRoles {
|
|||
public static String MANAGE_CLIENTS = "manage-clients";
|
||||
public static String MANAGE_EVENTS = "manage-events";
|
||||
|
||||
public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
|
||||
public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
|
||||
|
||||
}
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -137,6 +137,7 @@
|
|||
<module>common</module>
|
||||
<module>core</module>
|
||||
<module>core-jaxrs</module>
|
||||
<module>client-api</module>
|
||||
<module>connections</module>
|
||||
<module>dependencies</module>
|
||||
<module>events</module>
|
||||
|
@ -650,6 +651,11 @@
|
|||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-client-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core-jaxrs</artifactId>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.keycloak.admin.client.Keycloak;
|
|||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||
|
||||
|
@ -55,6 +57,10 @@ public class ContainersTestEnricher {
|
|||
@ClassScoped
|
||||
private InstanceProducer<Keycloak> adminClient;
|
||||
|
||||
@Inject
|
||||
@ClassScoped
|
||||
private InstanceProducer<OAuthClient> oauthClient;
|
||||
|
||||
private ContainerController controller;
|
||||
|
||||
private final boolean migrationTests = System.getProperty("migration", "false").equals("true");
|
||||
|
@ -92,6 +98,7 @@ public class ContainersTestEnricher {
|
|||
|
||||
initializeTestContext(testClass);
|
||||
initializeAdminClient();
|
||||
initializeOAuthClient();
|
||||
}
|
||||
|
||||
private void initializeTestContext(Class testClass) {
|
||||
|
@ -116,6 +123,10 @@ public class ContainersTestEnricher {
|
|||
MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
|
||||
}
|
||||
|
||||
private void initializeOAuthClient() {
|
||||
oauthClient.set(new OAuthClient(getAuthServerContextRootFromSystemProperty() + "/auth"));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param testClass
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package org.keycloak.testsuite.arquillian;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.provider.URLProvider;
|
||||
import org.keycloak.testsuite.arquillian.provider.SuiteContextProvider;
|
||||
import org.keycloak.testsuite.arquillian.provider.TestContextProvider;
|
||||
import org.keycloak.testsuite.arquillian.provider.*;
|
||||
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
|
||||
import org.jboss.arquillian.container.test.impl.enricher.resource.URLResourceProvider;
|
||||
import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
|
||||
|
@ -12,7 +10,6 @@ import org.jboss.arquillian.graphene.location.CustomizableURLResourceProvider;
|
|||
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
||||
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
|
||||
import org.keycloak.testsuite.arquillian.jira.JiraTestExecutionDecider;
|
||||
import org.keycloak.testsuite.arquillian.provider.AdminClientProvider;
|
||||
import org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer;
|
||||
|
||||
/**
|
||||
|
@ -27,7 +24,8 @@ public class KeycloakArquillianExtension implements LoadableExtension {
|
|||
builder
|
||||
.service(ResourceProvider.class, SuiteContextProvider.class)
|
||||
.service(ResourceProvider.class, TestContextProvider.class)
|
||||
.service(ResourceProvider.class, AdminClientProvider.class);
|
||||
.service(ResourceProvider.class, AdminClientProvider.class)
|
||||
.service(ResourceProvider.class, OAuthClientProvider.class);
|
||||
|
||||
builder
|
||||
.service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package org.keycloak.testsuite.arquillian.provider;
|
||||
|
||||
import org.jboss.arquillian.core.api.Instance;
|
||||
import org.jboss.arquillian.core.api.annotation.Inject;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class OAuthClientProvider implements ResourceProvider {
|
||||
|
||||
@Inject
|
||||
Instance<OAuthClient> oauthClient;
|
||||
|
||||
@Override
|
||||
public boolean canProvide(Class<?> type) {
|
||||
return OAuthClient.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
|
||||
return oauthClient.get();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.keycloak.testsuite.util;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class OAuthClient {
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
public OAuthClient(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public AccessTokenResponse getToken(String realm, String clientId, String clientSecret, String username, String password) {
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
try {
|
||||
HttpPost post = new HttpPost(OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl)).build(realm));
|
||||
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
|
||||
parameters.add(new BasicNameValuePair("username", username));
|
||||
parameters.add(new BasicNameValuePair("password", password));
|
||||
if (clientSecret != null) {
|
||||
String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
|
||||
post.setHeader("Authorization", authorization);
|
||||
} else {
|
||||
parameters.add(new BasicNameValuePair("client_id", clientId));
|
||||
}
|
||||
|
||||
UrlEncodedFormEntity formEntity;
|
||||
try {
|
||||
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
post.setEntity(formEntity);
|
||||
|
||||
CloseableHttpResponse response = httpclient.execute(post);
|
||||
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new RuntimeException("Failed to retrieve token: " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
|
||||
}
|
||||
|
||||
return JsonSerialization.readValue(response.getEntity().getContent(), AccessTokenResponse.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
httpclient.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.keycloak.testsuite.auth.page.AuthServer;
|
||||
import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
|
||||
|
@ -51,6 +52,9 @@ public abstract class AbstractKeycloakTest {
|
|||
@ArquillianResource
|
||||
protected Keycloak adminClient;
|
||||
|
||||
@ArquillianResource
|
||||
protected OAuthClient oauthClient;
|
||||
|
||||
protected List<RealmRepresentation> testRealmReps;
|
||||
|
||||
@Drone
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
package org.keycloak.testsuite.client;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.client.registration.ClientRegistration;
|
||||
import org.keycloak.client.registration.ClientRegistrationException;
|
||||
import org.keycloak.client.registration.HttpErrorException;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientRegistrationTest extends AbstractKeycloakTest {
|
||||
|
||||
private static final String REALM_NAME = "test";
|
||||
private static final String CLIENT_ID = "test-client";
|
||||
private static final String CLIENT_SECRET = "test-client-secret";
|
||||
|
||||
private ClientRegistration clientRegistrationAsAdmin;
|
||||
private ClientRegistration clientRegistrationAsClient;
|
||||
|
||||
@Before
|
||||
public void before() throws ClientRegistrationException {
|
||||
clientRegistrationAsAdmin = clientBuilder().auth(getToken("manage-clients", "password")).build();
|
||||
clientRegistrationAsClient = clientBuilder().auth(CLIENT_ID, CLIENT_SECRET).build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws ClientRegistrationException {
|
||||
clientRegistrationAsAdmin.close();
|
||||
clientRegistrationAsClient.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation rep = new RealmRepresentation();
|
||||
rep.setEnabled(true);
|
||||
rep.setRealm(REALM_NAME);
|
||||
rep.setUsers(new LinkedList<UserRepresentation>());
|
||||
|
||||
LinkedList<CredentialRepresentation> credentials = new LinkedList<>();
|
||||
CredentialRepresentation password = new CredentialRepresentation();
|
||||
password.setType(CredentialRepresentation.PASSWORD);
|
||||
password.setValue("password");
|
||||
credentials.add(password);
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setEnabled(true);
|
||||
user.setUsername("manage-clients");
|
||||
user.setCredentials(credentials);
|
||||
user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS)));
|
||||
|
||||
rep.getUsers().add(user);
|
||||
|
||||
UserRepresentation user2 = new UserRepresentation();
|
||||
user2.setEnabled(true);
|
||||
user2.setUsername("create-clients");
|
||||
user2.setCredentials(credentials);
|
||||
user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT)));
|
||||
|
||||
rep.getUsers().add(user2);
|
||||
|
||||
UserRepresentation user3 = new UserRepresentation();
|
||||
user3.setEnabled(true);
|
||||
user3.setUsername("no-access");
|
||||
user3.setCredentials(credentials);
|
||||
|
||||
rep.getUsers().add(user3);
|
||||
|
||||
testRealms.add(rep);
|
||||
}
|
||||
|
||||
private void registerClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setClientId(CLIENT_ID);
|
||||
client.setSecret(CLIENT_SECRET);
|
||||
|
||||
ClientRepresentation createdClient = clientRegistration.create(client);
|
||||
assertEquals(CLIENT_ID, createdClient.getClientId());
|
||||
|
||||
client = adminClient.realm(REALM_NAME).clients().get(createdClient.getId()).toRepresentation();
|
||||
assertEquals(CLIENT_ID, client.getClientId());
|
||||
|
||||
AccessTokenResponse token2 = oauthClient.getToken(REALM_NAME, CLIENT_ID, CLIENT_SECRET, "manage-clients", "password");
|
||||
assertNotNull(token2.getToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerClientAsAdmin() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||
try {
|
||||
registerClient(clientRegistration);
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||
try {
|
||||
registerClient(clientRegistration);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||
try {
|
||||
clientRegistration.get(CLIENT_ID);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongClient() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setClientId("test-client-2");
|
||||
client.setSecret("test-client-2-secret");
|
||||
|
||||
clientRegistrationAsAdmin.create(client);
|
||||
|
||||
ClientRegistration clientRegistration = clientBuilder().auth("test-client-2", "test-client-2-secret").build();
|
||||
|
||||
client = clientRegistration.get("test-client-2");
|
||||
assertNotNull(client);
|
||||
assertEquals("test-client-2", client.getClientId());
|
||||
|
||||
try {
|
||||
try {
|
||||
clientRegistration.get(CLIENT_ID);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
client = clientRegistrationAsAdmin.get(CLIENT_ID);
|
||||
try {
|
||||
clientRegistration.update(client);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
try {
|
||||
clientRegistration.delete(CLIENT_ID);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||
try {
|
||||
clientRegistration.get(CLIENT_ID);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||
ClientRepresentation client = clientRegistration.get(CLIENT_ID);
|
||||
client.setRedirectUris(Collections.singletonList("http://localhost:8080/app"));
|
||||
|
||||
clientRegistration.update(client);
|
||||
|
||||
ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID);
|
||||
|
||||
assertEquals(1, updatedClient.getRedirectUris().size());
|
||||
assertEquals("http://localhost:8080/app", updatedClient.getRedirectUris().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateClientAsAdmin() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
updateClient(clientRegistrationAsAdmin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||
try {
|
||||
updateClient(clientRegistration);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||
try {
|
||||
updateClient(clientRegistration);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateClientAsClient() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
updateClient(clientRegistrationAsClient);
|
||||
}
|
||||
|
||||
private void deleteClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
|
||||
clientRegistration.delete(CLIENT_ID);
|
||||
|
||||
// Can't authenticate as client after client is deleted
|
||||
ClientRepresentation client = clientRegistrationAsAdmin.get(CLIENT_ID);
|
||||
assertNull(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientAsAdmin() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
deleteClient(clientRegistrationAsAdmin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
|
||||
try {
|
||||
deleteClient(clientRegistration);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientAsAdminWithNoAccess() throws ClientRegistrationException {
|
||||
ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
|
||||
try {
|
||||
deleteClient(clientRegistration);
|
||||
fail("Expected 403");
|
||||
} catch (ClientRegistrationException e) {
|
||||
assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
clientRegistration.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClientAsClient() throws ClientRegistrationException {
|
||||
registerClient(clientRegistrationAsAdmin);
|
||||
deleteClient(clientRegistrationAsClient);
|
||||
}
|
||||
|
||||
private ClientRegistration.ClientRegistrationBuilder clientBuilder() {
|
||||
return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth");
|
||||
}
|
||||
|
||||
private String getToken(String username, String password) {
|
||||
return oauthClient.getToken(REALM_NAME, "security-admin-console", null, username, password).getToken();
|
||||
}
|
||||
|
||||
}
|
|
@ -201,6 +201,10 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-client-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
|
|
Loading…
Reference in a new issue