diff --git a/rest/admin-v2/api/src/main/java/org/keycloak/services/client/ClientService.java b/rest/admin-v2/api/src/main/java/org/keycloak/services/client/ClientService.java index 5405c249b02..17c5df0f3ea 100644 --- a/rest/admin-v2/api/src/main/java/org/keycloak/services/client/ClientService.java +++ b/rest/admin-v2/api/src/main/java/org/keycloak/services/client/ClientService.java @@ -33,8 +33,6 @@ public interface ClientService extends Service { Stream getClients(ClientsResource clientsResource, RealmModel realm, ClientProjectionOptions projectionOptions, ClientSearchOptions searchOptions, ClientSortAndSliceOptions sortAndSliceOptions); - ClientRepresentation deleteClient(RealmModel realm, String clientId); - Stream deleteClients(RealmModel realm, ClientSearchOptions searchOptions); CreateOrUpdateResult createOrUpdate(ClientsResource clientsResource, ClientResource clientResource, RealmModel realm, ClientRepresentation client, boolean allowUpdate) throws ServiceException; diff --git a/rest/admin-v2/api/src/main/java/org/keycloak/services/client/DefaultClientService.java b/rest/admin-v2/api/src/main/java/org/keycloak/services/client/DefaultClientService.java index 212468dcde0..41591d59dbd 100644 --- a/rest/admin-v2/api/src/main/java/org/keycloak/services/client/DefaultClientService.java +++ b/rest/admin-v2/api/src/main/java/org/keycloak/services/client/DefaultClientService.java @@ -77,12 +77,6 @@ public class DefaultClientService implements ClientService { return new CreateOrUpdateResult(updated, created); } - @Override - public ClientRepresentation deleteClient(RealmModel realm, String clientId) { - // TODO Auto-generated method stub - return null; - } - @Override public Stream deleteClients(RealmModel realm, ClientSearchOptions searchOptions) { // TODO Auto-generated method stub diff --git a/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/ClientApi.java b/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/ClientApi.java index a5286f85a5d..a863b502e0f 100644 --- a/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/ClientApi.java +++ b/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/ClientApi.java @@ -2,6 +2,7 @@ package org.keycloak.admin.api.client; import jakarta.validation.Valid; import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.PATCH; import jakarta.ws.rs.PUT; @@ -31,4 +32,8 @@ public interface ClientApi { @Produces(MediaType.APPLICATION_JSON) ClientRepresentation patchClient(JsonNode patch); + @DELETE + @Produces(MediaType.APPLICATION_JSON) + void deleteClient(); + } diff --git a/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/DefaultClientApi.java b/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/DefaultClientApi.java index f6b2f0d187c..ef3583bb541 100644 --- a/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/DefaultClientApi.java +++ b/rest/admin-v2/rest/src/main/java/org/keycloak/admin/api/client/DefaultClientApi.java @@ -102,6 +102,14 @@ public class DefaultClientApi implements ClientApi { } } + @Override + public void deleteClient() { + if (clientResource == null) { + throw new NotFoundException("Cannot find the specified client"); + } + clientResource.deleteClient(); + } + static void validateUnknownFields(ClientRepresentation rep, HttpResponse response) { if (!rep.getAdditionalFields().isEmpty()) { throw new WebApplicationException("Payload contains unknown fields: " + rep.getAdditionalFields().keySet(), Response.Status.BAD_REQUEST); diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2Test.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2Test.java index 6a61a2a9b48..cea74e63d2f 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2Test.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2Test.java @@ -37,6 +37,7 @@ import org.keycloak.testframework.server.KeycloakServerConfigBuilder; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.HttpMessage; +import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; @@ -161,6 +162,39 @@ public class ClientApiV2Test { } } + @Test + public void deleteClient() throws Exception { + HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete"); + setAuthHeader(createRequest); + createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + + ClientRepresentation rep = new ClientRepresentation(); + rep.setClientId("to-delete"); + rep.setEnabled(true); + + createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); + + try (var response = client.execute(createRequest)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + } + + HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete"); + setAuthHeader(getRequest); + try (var response = client.execute(getRequest)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + } + + HttpDelete deleteRequest = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete"); + setAuthHeader(deleteRequest); + try (var response = client.execute(deleteRequest)) { + assertEquals(204, response.getStatusLine().getStatusCode()); + } + + try (var response = client.execute(getRequest)) { + assertEquals(404, response.getStatusLine().getStatusCode()); + } + } + @Test public void clientRepresentationValidation() throws Exception { HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");