mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-21 06:20:05 -06:00
[admin-api-v2] Create client does not return 201 status code (#44541)
Closes #44540 Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import jakarta.ws.rs.PATCH;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
|
||||
@@ -22,10 +23,13 @@ public interface ClientApi {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
ClientRepresentation getClient();
|
||||
|
||||
/**
|
||||
* @return {@link ClientRepresentation} of created/updated client
|
||||
*/
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
ClientRepresentation createOrUpdateClient(@Valid ClientRepresentation client);
|
||||
Response createOrUpdateClient(@Valid ClientRepresentation client);
|
||||
|
||||
@PATCH
|
||||
@Consumes(CONTENT_TYPE_MERGE_PATCH)
|
||||
|
||||
@@ -10,6 +10,7 @@ import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
@@ -28,11 +29,14 @@ public interface ClientsApi {
|
||||
@Operation(summary = "Get all clients", description = "Returns a list of all clients in the realm")
|
||||
Stream<ClientRepresentation> getClients();
|
||||
|
||||
/**
|
||||
* @return {@link ClientRepresentation} of created client
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Create a new client", description = "Creates a new client in the realm")
|
||||
ClientRepresentation createClient(@Valid ClientRepresentation client);
|
||||
Response createClient(@Valid ClientRepresentation client);
|
||||
|
||||
@Path("{id}")
|
||||
ClientApi client(@PathParam("id") String id);
|
||||
|
||||
@@ -9,7 +9,6 @@ import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
@@ -33,7 +32,6 @@ public class DefaultClientApi implements ClientApi {
|
||||
private final RealmModel realm;
|
||||
private final ClientModel client;
|
||||
private final ClientService clientService;
|
||||
private HttpResponse response;
|
||||
|
||||
private final ClientResource clientResource;
|
||||
private final ClientsResource clientsResource;
|
||||
@@ -47,7 +45,6 @@ public class DefaultClientApi implements ClientApi {
|
||||
this.realm = Objects.requireNonNull(session.getContext().getRealm());
|
||||
this.client = Objects.requireNonNull(session.getContext().getClient());
|
||||
this.clientService = new DefaultClientService(session);
|
||||
this.response = session.getContext().getHttpResponse();
|
||||
this.clientsResource = clientsResource;
|
||||
this.clientResource = clientResource;
|
||||
this.clientId = clientId;
|
||||
@@ -61,17 +58,14 @@ public class DefaultClientApi implements ClientApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientRepresentation createOrUpdateClient(ClientRepresentation client) {
|
||||
public Response createOrUpdateClient(ClientRepresentation client) {
|
||||
try {
|
||||
if (!Objects.equals(clientId, client.getClientId())) {
|
||||
throw new WebApplicationException("cliendId in payload does not match the clientId in the path", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
validateUnknownFields(client, response);
|
||||
validateUnknownFields(client);
|
||||
var result = clientService.createOrUpdate(clientsResource, clientResource, realm, client, true);
|
||||
if (result.created()) {
|
||||
response.setStatus(Response.Status.CREATED.getStatusCode());
|
||||
}
|
||||
return result.representation();
|
||||
return Response.status(result.created() ? Response.Status.CREATED : Response.Status.OK).entity(result.representation()).build();
|
||||
} catch (ServiceException e) {
|
||||
throw new WebApplicationException(e.getMessage(), e.getSuggestedResponseStatus().orElse(Response.Status.BAD_REQUEST));
|
||||
}
|
||||
@@ -91,7 +85,7 @@ public class DefaultClientApi implements ClientApi {
|
||||
final ObjectReader objectReader = objectMapper.readerForUpdating(client);
|
||||
ClientRepresentation updated = objectReader.readValue(patch);
|
||||
|
||||
validateUnknownFields(updated, response);
|
||||
validateUnknownFields(updated);
|
||||
return clientService.createOrUpdate(clientsResource, clientResource, realm, updated, true).representation();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebApplicationException("Unsupported media type", Response.Status.UNSUPPORTED_MEDIA_TYPE);
|
||||
@@ -110,7 +104,7 @@ public class DefaultClientApi implements ClientApi {
|
||||
clientResource.deleteClient();
|
||||
}
|
||||
|
||||
static void validateUnknownFields(ClientRepresentation rep, HttpResponse response) {
|
||||
static void validateUnknownFields(ClientRepresentation rep) {
|
||||
if (!rep.getAdditionalFields().isEmpty()) {
|
||||
throw new WebApplicationException("Payload contains unknown fields: " + rep.getAdditionalFields().keySet(), Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
@@ -24,7 +23,6 @@ import org.keycloak.validation.jakarta.JakartaValidatorProvider;
|
||||
public class DefaultClientsApi implements ClientsApi {
|
||||
private final KeycloakSession session;
|
||||
private final RealmModel realm;
|
||||
private final HttpResponse response;
|
||||
private final ClientService clientService;
|
||||
private final JakartaValidatorProvider validator;
|
||||
private final ClientsResource clientsResource;
|
||||
@@ -33,7 +31,6 @@ public class DefaultClientsApi implements ClientsApi {
|
||||
this.session = session;
|
||||
this.realm = Objects.requireNonNull(session.getContext().getRealm());
|
||||
this.clientService = new DefaultClientService(session);
|
||||
this.response = session.getContext().getHttpResponse();
|
||||
this.validator = new HibernateValidatorProvider();
|
||||
this.clientsResource = clientsResource;
|
||||
}
|
||||
@@ -44,12 +41,13 @@ public class DefaultClientsApi implements ClientsApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientRepresentation createClient(@Valid ClientRepresentation client) {
|
||||
public Response createClient(@Valid ClientRepresentation client) {
|
||||
try {
|
||||
DefaultClientApi.validateUnknownFields(client, response);
|
||||
DefaultClientApi.validateUnknownFields(client);
|
||||
validator.validate(client, CreateClientDefault.class);
|
||||
response.setStatus(Response.Status.CREATED.getStatusCode());
|
||||
return clientService.createOrUpdate(clientsResource, null, realm, client, false).representation();
|
||||
return Response.status(Response.Status.CREATED)
|
||||
.entity(clientService.createOrUpdate(clientsResource, null, realm, client, false).representation())
|
||||
.build();
|
||||
} catch (ServiceException e) {
|
||||
throw new WebApplicationException(e.getMessage(), e.getSuggestedResponseStatus().orElse(Response.Status.BAD_REQUEST));
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class ClientApiV2Test {
|
||||
request.setEntity(new StringEntity(mapper.writeValueAsString(rep)));
|
||||
|
||||
try (var response = client.execute(request)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
ClientRepresentation client = mapper.createParser(response.getEntity().getContent()).readValueAs(ClientRepresentation.class);
|
||||
assertEquals("I'm new", client.getDescription());
|
||||
}
|
||||
@@ -162,6 +162,32 @@ public class ClientApiV2Test {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createClient() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
ClientRepresentation rep = new ClientRepresentation();
|
||||
rep.setEnabled(true);
|
||||
rep.setClientId("client-123");
|
||||
rep.setDescription("I'm new");
|
||||
|
||||
request.setEntity(new StringEntity(mapper.writeValueAsString(rep)));
|
||||
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(),is(201));
|
||||
ClientRepresentation client = mapper.createParser(response.getEntity().getContent()).readValueAs(ClientRepresentation.class);
|
||||
assertThat(client.getEnabled(),is(true));
|
||||
assertThat(client.getClientId(),is("client-123"));
|
||||
assertThat(client.getDescription(),is("I'm new"));
|
||||
}
|
||||
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(),is(409));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteClient() throws Exception {
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
|
||||
@@ -175,7 +201,7 @@ public class ClientApiV2Test {
|
||||
createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep)));
|
||||
|
||||
try (var response = client.execute(createRequest)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
|
||||
|
||||
Reference in New Issue
Block a user