From 91ef59aa764ace468fc8e48ec49cd626053164a0 Mon Sep 17 00:00:00 2001 From: Robin Meese <39960884+robson90@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:15:38 +0200 Subject: [PATCH 1/2] is-45364 migrate Client Api V2 tests to utilize adminClient Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> --- .../client/v2/AbstractClientApiV2Test.java | 37 +- .../admin/client/v2/AdminEventV2Test.java | 118 ++--- .../v2/ClientApiV2AuthorizationTest.java | 474 +++++++----------- .../client/v2/ClientApiV2DisabledTest.java | 23 +- .../admin/client/v2/ClientApiV2Test.java | 103 ++-- .../admin/client/v2/ClientPoliciesV2Test.java | 266 +++------- .../tests/admin/client/v2/InteropTest.java | 229 +++------ 7 files changed, 473 insertions(+), 777 deletions(-) diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AbstractClientApiV2Test.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AbstractClientApiV2Test.java index b774cd3586b0..673fa0350f79 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AbstractClientApiV2Test.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AbstractClientApiV2Test.java @@ -2,6 +2,9 @@ import jakarta.ws.rs.core.HttpHeaders; +import org.keycloak.admin.api.AdminRootV2; +import org.keycloak.admin.api.client.ClientApi; +import org.keycloak.admin.api.client.ClientsApi; import org.keycloak.admin.client.Keycloak; import org.keycloak.testframework.annotations.InjectAdminClient; @@ -23,7 +26,39 @@ public String getClientsApiUrl() { return "http://localhost:8080/admin/api/%s/clients/v2".formatted(getRealmName()); } public String getClientApiUrl(String clientId) { - return "http://localhost:8080/admin/api/%s/clients/v2/%s".formatted(getRealmName(), clientId); + return getClientApiUrl(getRealmName(), clientId); + } + + public String getClientApiUrl(String realmName, String clientId) { + return "http://localhost:8080/admin/api/%s/clients/v2/%s".formatted(realmName, clientId); + } + + public ClientsApi getClientsApi() { + return adminClient.proxy(AdminRootV2.class).adminApi(getRealmName()).clientsV2(); + } + + public ClientsApi getClientsApi(String realmName) { + return getClientsApi(adminClient, realmName); + } + + public ClientsApi getClientsApi(Keycloak adminClient) { + return getClientsApi(adminClient, getRealmName()); + } + + public ClientsApi getClientsApi(Keycloak adminClient, String realmName) { + return adminClient.proxy(AdminRootV2.class).adminApi(realmName).clientsV2(); + } + + public ClientApi getClientApi(String clientId) { + return getClientApi(getRealmName(), clientId); + } + + public ClientApi getClientApi(String realmName, String clientId) { + return getClientApi(adminClient, realmName, clientId); + } + + public ClientApi getClientApi(Keycloak adminClient, String realmName, String clientId) { + return adminClient.proxy(AdminRootV2.class).adminApi(realmName).clientsV2().client(clientId); } @BeforeAll diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AdminEventV2Test.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AdminEventV2Test.java index f96d6ccb0049..96c583c486b4 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AdminEventV2Test.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/AdminEventV2Test.java @@ -17,12 +17,9 @@ package org.keycloak.tests.admin.client.v2; +import java.io.ByteArrayInputStream; import java.util.List; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; - -import org.keycloak.admin.api.PatchTypeNames; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.common.Profile; import org.keycloak.events.admin.OperationType; @@ -30,7 +27,6 @@ import org.keycloak.representations.admin.v2.SAMLClientRepresentation; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.RealmEventsConfigRepresentation; -import org.keycloak.testframework.annotations.InjectHttpClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; @@ -39,13 +35,6 @@ import org.keycloak.testframework.server.KeycloakServerConfig; import org.keycloak.testframework.server.KeycloakServerConfigBuilder; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpPatch; -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.util.EntityUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,6 +50,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -71,15 +61,17 @@ public class AdminEventV2Test extends AbstractClientApiV2Test { private static final String TEST_CLIENT_ID = "v2-rep-test-client"; - @InjectHttpClient - CloseableHttpClient client; - - @InjectRealm(attachTo = "master", ref = "master") - ManagedRealm masterRealm; - @InjectRunOnServer RunOnServerClient runOnServer; + @InjectRealm + ManagedRealm testRealm; + + @Override + public String getRealmName() { + return testRealm.getName(); + } + @BeforeEach public void setupAndClearEvents() { runOnServer.run(session -> { @@ -87,14 +79,14 @@ public void setupAndClearEvents() { System.setProperty("kc.admin-v2.client-service.events.enabled", "true"); }); - // Enable admin events on master realm - RealmEventsConfigRepresentation eventsConfig = masterRealm.admin().getRealmEventsConfig(); + // Enable admin events on test realm + RealmEventsConfigRepresentation eventsConfig = testRealm.admin().getRealmEventsConfig(); eventsConfig.setAdminEventsEnabled(true); eventsConfig.setAdminEventsDetailsEnabled(true); - masterRealm.admin().updateRealmEventsConfig(eventsConfig); + testRealm.admin().updateRealmEventsConfig(eventsConfig); // Clear any existing events - masterRealm.admin().clearAdminEvents(); + testRealm.admin().clearAdminEvents(); } @AfterEach @@ -110,7 +102,7 @@ public void createClientFiresV2Event() throws Exception { createTestClient(); try { // Verify v2 events were fired - List events = masterRealm.admin().getAdminEvents(); + List events = testRealm.admin().getAdminEvents(); // Should have at least 1 event assertThat("Should have at least 1 event", events.size(), greaterThanOrEqualTo(1)); @@ -133,20 +125,13 @@ public void createClientFiresV2Event() throws Exception { public void updateClientFiresV2Event() throws Exception { createTestClient(); try { - masterRealm.admin().clearAdminEvents(); - - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/" + TEST_CLIENT_ID); - setAuthHeader(updateRequest); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - + testRealm.admin().clearAdminEvents(); OIDCClientRepresentation rep = getTestClientRepresentation(); // update the client rep.setDescription("Updated description"); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(updateRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(TEST_CLIENT_ID).createOrUpdateClient(rep)) { + assertEquals(200, response.getStatus()); } assertUpdateEventFired("Updated description"); @@ -159,21 +144,12 @@ public void updateClientFiresV2Event() throws Exception { public void patchClientFiresV2Event() throws Exception { createTestClient(); try { - masterRealm.admin().clearAdminEvents(); - - HttpPatch patchRequest = new HttpPatch(getClientsApiUrl() + "/" + TEST_CLIENT_ID); - setAuthHeader(patchRequest); - - patchRequest.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); + testRealm.admin().clearAdminEvents(); OIDCClientRepresentation patch = new OIDCClientRepresentation(); patch.setDescription("Patched description"); - patchRequest.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - - try (var response = client.execute(patchRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - } + assertNotNull(getClientApi(TEST_CLIENT_ID).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch)))); assertUpdateEventFired("Patched description"); } finally { @@ -185,17 +161,14 @@ public void patchClientFiresV2Event() throws Exception { public void deleteClientFiresV2Event() throws Exception { createTestClient(); try { - masterRealm.admin().clearAdminEvents(); + testRealm.admin().clearAdminEvents(); - HttpDelete deleteRequest = new HttpDelete(getClientsApiUrl() + "/" + TEST_CLIENT_ID); - setAuthHeader(deleteRequest); - - try (var response = client.execute(deleteRequest)) { - assertEquals(204, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(TEST_CLIENT_ID).deleteClient()) { + assertEquals(204, response.getStatus()); } // Verify v2 DELETE event was fired - List events = masterRealm.admin().getAdminEvents(); + List events = testRealm.admin().getAdminEvents(); // Find the v2 event AdminEventRepresentation v2Event = events.stream() @@ -227,21 +200,19 @@ public void deleteClientFiresV2Event() throws Exception { assertThat(eventRepresentation.getAuth().getSecret(), is(not(testClientRep.getAuth().getSecret()))); assertThat(eventRepresentation.getAuth().getSecret(), is("**********")); - try (var response = client.execute(deleteRequest)) { - assertEquals(404, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(TEST_CLIENT_ID).deleteClient()) { + assertEquals(404, response.getStatus()); } } finally { - HttpDelete deleteRequest = new HttpDelete(getClientsApiUrl() + "/" + TEST_CLIENT_ID); - setAuthHeader(deleteRequest); - try (var response = client.execute(deleteRequest)) { - assertThat(response.getStatusLine().getStatusCode(), anyOf(is(204), is(404))); + try (var response = getClientApi(TEST_CLIENT_ID).deleteClient()) { + assertThat(response.getStatus(), anyOf(is(204), is(404))); } } } private void assertUpdateEventFired(String newDescription){ // Verify v2 UPDATE event was fired - List events = masterRealm.admin().getAdminEvents(); + List events = testRealm.admin().getAdminEvents(); // Find the v2 event (has apiVersion=v2 in details) AdminEventRepresentation v2Event = events.stream() @@ -264,7 +235,7 @@ public void v2EventContainsV2Representation() throws Exception { createTestClient(); try { // Verify v2 event contains the v2 representation format - List events = masterRealm.admin().getAdminEvents(); + List events = testRealm.admin().getAdminEvents(); // Find the v2 event AdminEventRepresentation v2Event = events.stream() @@ -292,10 +263,6 @@ public void v2EventContainsV2Representation() throws Exception { public void stripSamlSigningCertificateFromRepresentation() throws Exception { var SAML_CLIENT_ID = "saml-with-certificate"; - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - SAMLClientRepresentation samlRep = new SAMLClientRepresentation(); samlRep.setEnabled(true); samlRep.setClientId(SAML_CLIENT_ID); @@ -311,15 +278,13 @@ public void stripSamlSigningCertificateFromRepresentation() throws Exception { LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL+... -----END CERTIFICATE----- """); - request.setEntity(new StringEntity(mapper.writeValueAsString(samlRep))); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertEquals(201, response.getStatusLine().getStatusCode()); + try (var response = getClientsApi().createClient(samlRep)) { + assertEquals(201, response.getStatus()); } try { - List events = masterRealm.admin().getAdminEvents(); + List events = testRealm.admin().getAdminEvents(); // Find the v2 event AdminEventRepresentation v2Event = events.stream() @@ -339,15 +304,8 @@ public void stripSamlSigningCertificateFromRepresentation() throws Exception { private void createTestClient() throws Exception { // Create a client via v2 API (representation details already enabled in @BeforeEach) - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - - request.setEntity(new StringEntity(mapper.writeValueAsString(getTestClientRepresentation()))); - - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertEquals(201, response.getStatusLine().getStatusCode()); + try (var response = getClientsApi().createClient(getTestClientRepresentation())) { + assertEquals(201, response.getStatus()); } } @@ -356,10 +314,8 @@ private void deleteTestClient() throws Exception { } private void deleteClient(String clientId) throws Exception { - HttpDelete deleteRequest = new HttpDelete(getClientsApiUrl() + "/" + clientId); - setAuthHeader(deleteRequest); - try (var response = client.execute(deleteRequest)) { - assertEquals(204, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(clientId).deleteClient()) { + assertEquals(204, response.getStatus()); } } diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java index 746246e70943..2ee92c3c8ad7 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java @@ -1,16 +1,14 @@ package org.keycloak.tests.admin.client.v2; -import java.util.HashMap; +import java.io.ByteArrayInputStream; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.ForbiddenException; +import jakarta.ws.rs.NotFoundException; -import org.keycloak.admin.api.PatchTypeNames; import org.keycloak.admin.client.Keycloak; import org.keycloak.authorization.fgap.AdminPermissionsSchema; import org.keycloak.common.Profile; @@ -23,12 +21,12 @@ import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation; import org.keycloak.representations.idm.authorization.UserPolicyRepresentation; import org.keycloak.testframework.admin.AdminClientFactory; +import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectAdminClientFactory; import org.keycloak.testframework.annotations.InjectClient; import org.keycloak.testframework.annotations.InjectHttpClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.testframework.annotations.TestSetup; import org.keycloak.testframework.realm.ClientBuilder; import org.keycloak.testframework.realm.ManagedClient; import org.keycloak.testframework.realm.ManagedRealm; @@ -38,13 +36,7 @@ import org.keycloak.testframework.server.KeycloakServerConfig; import org.keycloak.testframework.server.KeycloakServerConfigBuilder; -import com.fasterxml.jackson.core.type.TypeReference; -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; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.junit.jupiter.api.Test; @@ -52,9 +44,12 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Authorization tests for Client API V2. @@ -79,18 +74,45 @@ public class ClientApiV2AuthorizationTest extends AbstractClientApiV2Test { @InjectClient(attachTo = Constants.ADMIN_PERMISSIONS_CLIENT_ID) ManagedClient adminPermissionClient; + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "realm-admin", + client = "test-client", + ref = "realmAdmin") + Keycloak realmAdminAdminClient; + + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "view-clients", + client = "test-client", + ref = "viewClients") + Keycloak viewClientsAdminClient; + + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "query-clients", + client = "test-client", + ref = "queryClients") + Keycloak queryClientsAdminClient; + + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "manage-clients", + client = "test-client", + ref = "manageClients") + Keycloak manageClientsAdminClient; + + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "no-access", + client = "test-client", + ref = "noAccess") + Keycloak noAccessAdminClient; + + @InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, + user = "fgap-user", + client = "test-client", + ref = "fgapUser") + Keycloak fgapAdminClient; + @Override public String getRealmName() { - return "authztest"; - } - - private static final Map adminClients = new HashMap<>(); - - @TestSetup - public void setupClients() { - for (String currentUser : CURRENT_USERS) { - adminClients.put(currentUser, createAdminClient(currentUser)); - } + return testRealm.getName(); } /** @@ -98,44 +120,34 @@ public void setupClients() { * Permissions: auth.clients().requireList() || auth.clients().requireView() */ @Test - public void listClients() throws Exception { - HttpGet request = new HttpGet(getClientsApiUrl()); - + public void listClients() { // realm-admin: should be able to list clients (has all permissions) - setAuthHeader(request, adminClients.get("realm-admin")); - try (var response = client.execute(request)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - var clients = mapper.readValue(response.getEntity().getContent(), new TypeReference>() { - }); - assertThat(clients.size(), greaterThan(0)); + try (var response = getClientsApi(realmAdminAdminClient).getClients()) { + assertThat(response.toList().size(), greaterThan(0)); } // view-clients: should be able to list clients (has requireList via canView) - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertEquals(200, response.getStatusLine().getStatusCode()); + try (var response = getClientsApi(viewClientsAdminClient).getClients()) { + assertThat(response.toList().size(), greaterThan(0)); } // query-clients: should be able to list clients (has requireList via QUERY_CLIENTS role) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertEquals(200, response.getStatusLine().getStatusCode()); + try (var response = getClientsApi(queryClientsAdminClient).getClients()) { + assertThat(response.toList().size(), equalTo(0)); } // manage-clients: should be able to list clients (has requireList via MANAGE_CLIENTS role) - setAuthHeader(request, adminClients.get("manage-clients")); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertEquals(200, response.getStatusLine().getStatusCode()); + try (var response = getClientsApi(manageClientsAdminClient).getClients()) { + assertThat(response.toList().size(), greaterThan(0)); } // no-access: should get 403 (lacks requireList) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertEquals(403, response.getStatusLine().getStatusCode()); - } + ForbiddenException ex = assertThrows( + ForbiddenException.class, + () -> getClientsApi(noAccessAdminClient).getClients() + ); + + assertTrue(ex.getMessage().contains("HTTP 403 Forbidden")); } /** @@ -145,44 +157,29 @@ public void listClients() throws Exception { @Test public void createClient() throws Exception { // realm-admin: should be able to create clients (has requireManage) - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request, adminClients.get("realm-admin")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-create-realm-admin", "new-role1", "new-role2")))); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(201)); + try (var response = getClientsApi(realmAdminAdminClient).createClient(createClientRep("test-create-admin", "new-role1", "new-role2"))) { + assertThat(response.getStatus(), is(201)); } // manage-clients: should be able to create clients (has requireManage) - request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request, adminClients.get("manage-clients")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-create-manage", "new-role1", "new-role2")))); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(201)); + try (var response = getClientsApi(manageClientsAdminClient).createClient(createClientRep("test-create-manage", "new-role1", "new-role2"))) { + assertThat(response.getStatus(), is(201)); } + var testRep = createClientRep("test-create-view", "new-role1", "new-role2"); // view-clients: should get 403 (lacks requireManage) - request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request, adminClients.get("view-clients")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-create-view", "new-role1", "new-role2")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientsApi(viewClientsAdminClient).createClient(testRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden when creating client without access"); } // query-clients: should get 403 (lacks requireManage) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientsApi(queryClientsAdminClient).createClient(testRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden when creating client without access"); } // no-access: should get 403 (lacks requireManage) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientsApi(noAccessAdminClient).createClient(testRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden when creating client without access"); } } @@ -191,38 +188,29 @@ public void createClient() throws Exception { * Permissions: auth.clients().requireView(client) + ClientPolicyEvent.VIEW */ @Test - public void getClient() throws Exception { - HttpGet request = new HttpGet(getClientsApiUrl() + "/test-client"); - + public void getClient() { // view-clients: should be able to get individual client (has requireView) - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - OIDCClientRepresentation client = mapper.createParser(response.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); - assertThat(client.getClientId(), is("test-client")); - } + String testClientId = "test-client"; + BaseClientRepresentation baseRep = getClientsApi(viewClientsAdminClient).client(testClientId).getClient(); + assertThat(baseRep.getClientId(), is(testClientId)); // manage-clients: should be able to get individual client (has requireView) - setAuthHeader(request, adminClients.get("manage-clients")); - try (var response = client.execute(request)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - OIDCClientRepresentation client = mapper.createParser(response.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); - assertThat(client.getClientId(), is("test-client")); - } + baseRep = getClientsApi(manageClientsAdminClient).client(testClientId).getClient(); + assertThat(baseRep.getClientId(), is(testClientId)); // query-clients: should get 403 (can list but not view individual clients) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + ForbiddenException ex = assertThrows( + ForbiddenException.class, + () -> getClientsApi(queryClientsAdminClient).client(testClientId).getClient() + ); + assertTrue(ex.getMessage().contains("HTTP 403 Forbidden")); // no-access: should get 403 (lacks requireView) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + ex = assertThrows( + ForbiddenException.class, + () -> getClientsApi(noAccessAdminClient).client(testClientId).getClient() + ); + assertTrue(ex.getMessage().contains("HTTP 403 Forbidden")); } /** @@ -230,33 +218,25 @@ public void getClient() throws Exception { * Permissions: if auth.clients().canList() return 404, else return 403 */ @Test - public void getNonExistentClient() throws Exception { - HttpGet request = new HttpGet(getClientsApiUrl() + "/non-existent-client"); + public void getNonExistentClient() { + String nonExistentClientId = "non-existent-client"; // view-clients: should get 404 (has canList, client doesn't exist) - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); - } + assertThrows(NotFoundException.class, + () -> getClientsApi(viewClientsAdminClient).client(nonExistentClientId).getClient()); // manage-clients: should get 404 (has canList, client doesn't exist) - setAuthHeader(request, adminClients.get("manage-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); - } + assertThrows(NotFoundException.class, + () -> getClientsApi(manageClientsAdminClient).client(nonExistentClientId).getClient()); // query-clients: should get 404 (has canList, client doesn't exist) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); - } + assertThrows(NotFoundException.class, + () -> getClientsApi(queryClientsAdminClient).client(nonExistentClientId).getClient()); // no-access: should get 403 (lacks canList, prevents ID phishing) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("HTTP 403 Forbidden")); - } + ForbiddenException ex = assertThrows(ForbiddenException.class, + () -> getClientsApi(noAccessAdminClient).client(nonExistentClientId).getClient()); + assertTrue(ex.getMessage().contains("HTTP 403 Forbidden")); } /** @@ -267,46 +247,34 @@ public void getNonExistentClient() throws Exception { public void updateClient() throws Exception { // realm-admin: should be able to update clients (has requireConfigure) createTestClient("test-update-admin"); - HttpPut request = new HttpPut(getClientsApiUrl() + "/test-update-admin"); - setAuthHeader(request, adminClients.get("realm-admin")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-update-admin", "role123", "my-role")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation client = mapper.createParser(response.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); + BaseClientRepresentation updateRep = createClientRep("test-update-admin", "role123", "my-role"); + try (var response = getClientApi(realmAdminAdminClient, getRealmName(), "test-update-admin").createOrUpdateClient(updateRep)) { + assertThat(response.getStatus(), is(200)); + OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); assertThat(client.getClientId(), is("test-update-admin")); assertThat(client.getRoles(), containsInAnyOrder("role123", "my-role")); } // manage-clients: should be able to update clients (has requireConfigure) createTestClient("test-update-manage"); - request = new HttpPut(getClientsApiUrl() + "/test-update-manage"); - setAuthHeader(request, adminClients.get("manage-clients")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-update-manage", "role123", "my-role")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation client = mapper.createParser(response.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); + updateRep = createClientRep("test-update-manage", "role123", "my-role"); + try (var response = getClientApi(manageClientsAdminClient, getRealmName(), "test-update-manage").createOrUpdateClient(updateRep)) { + assertThat(response.getStatus(), is(200)); + OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); assertThat(client.getClientId(), is("test-update-manage")); assertThat(client.getRoles(), containsInAnyOrder("role123", "my-role")); } // view-clients: should get 403 (lacks requireConfigure) createTestClient("test-update-view"); - request = new HttpPut(getClientsApiUrl() + "/test-update-view"); - setAuthHeader(request, adminClients.get("view-clients")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("test-update-view", "role123", "my-role")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + BaseClientRepresentation viewRep = createClientRep("test-update-view", "role123", "my-role"); + try (var response = getClientApi(viewClientsAdminClient, getRealmName(), "test-update-view").createOrUpdateClient(viewRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden when updating client without access"); } // query-clients: should get 403 (lacks requireConfigure) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(queryClientsAdminClient, getRealmName(), "test-update-view").createOrUpdateClient(viewRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden when updating client without access"); } } @@ -318,58 +286,32 @@ public void updateClient() throws Exception { public void patchClient() throws Exception { // realm-admin: should be able to patch clients (has requireConfigure) createTestClient("test-patch-admin"); - HttpPatch request = new HttpPatch(getClientsApiUrl() + "/test-patch-admin"); - setAuthHeader(request, adminClients.get("realm-admin")); - request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); - OIDCClientRepresentation patch = new OIDCClientRepresentation(); + final OIDCClientRepresentation patch = new OIDCClientRepresentation(); patch.setDescription("Patched"); - request.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + getClientApi(realmAdminAdminClient, getRealmName(), "test-patch-admin").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))); // view-clients: should get 403 (lacks requireConfigure) createTestClient("test-patch-view"); - request = new HttpPatch(getClientsApiUrl() + "/test-patch-view"); - setAuthHeader(request, adminClients.get("view-clients")); - request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); - request.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> getClientApi(viewClientsAdminClient, getRealmName(), "test-patch-view").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch)))); // query-clients: should get 403 (lacks requireConfigure) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> getClientApi(queryClientsAdminClient, getRealmName(), "test-patch-view").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch)))); // manage-clients: should be able to patch clients (has manage-clients) - setAuthHeader(request, adminClients.get("manage-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + getClientApi(manageClientsAdminClient, getRealmName(), "test-patch-view").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))); // does not exist - request = new HttpPatch(getClientsApiUrl() + "/does-not-exist"); - request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); - patch = new OIDCClientRepresentation(); - patch.setDescription("Patched-non-existing"); - request.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - // no-access: not existing - should get 403 (lacks canList, prevents ID phishing) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("HTTP 403 Forbidden")); - } + final OIDCClientRepresentation noAccessPatch = new OIDCClientRepresentation(); + noAccessPatch.setDescription("Patched-non-existing"); + assertThrows(ForbiddenException.class, + () -> getClientApi(noAccessAdminClient, getRealmName(), "does-not-exist").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(noAccessPatch)))); // view-clients: not existing - should get 404 - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); - } + assertThrows(NotFoundException.class, + () -> getClientApi(viewClientsAdminClient, getRealmName(), "does-not-exist").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(noAccessPatch)))); } /** @@ -380,52 +322,40 @@ public void patchClient() throws Exception { public void deleteClient() throws Exception { // realm-admin: should be able to delete clients (has requireManage) createTestClient("test-delete-admin"); - HttpDelete request = new HttpDelete(getClientsApiUrl() + "/test-delete-admin"); - setAuthHeader(request, adminClients.get("realm-admin")); - try (var response = client.execute(request)) { - assertEquals(204, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(realmAdminAdminClient, getRealmName(), "test-delete-admin").deleteClient()) { + assertEquals(204, response.getStatus()); } // manage-clients: should be able to delete clients (has requireManage) createTestClient("test-delete-manage"); - request = new HttpDelete(getClientsApiUrl() + "/test-delete-manage"); - setAuthHeader(request, adminClients.get("manage-clients")); - try (var response = client.execute(request)) { - assertEquals(204, response.getStatusLine().getStatusCode()); + try (var response = getClientApi(manageClientsAdminClient, getRealmName(), "test-delete-manage").deleteClient()) { + assertEquals(204, response.getStatus()); } // view-clients: should get 403 (lacks requireManage) createTestClient("test-delete-view"); - request = new HttpDelete(getClientsApiUrl() + "/test-delete-view"); - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(viewClientsAdminClient, getRealmName(), "test-delete-view").deleteClient()) { + assertEquals(403, response.getStatus()); } // query-clients: should get 403 (lacks requireManage) - setAuthHeader(request, adminClients.get("query-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(queryClientsAdminClient, getRealmName(), "test-delete-view").deleteClient()) { + assertEquals(403, response.getStatus()); } // no-access: should get 403 (lacks requireManage) - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(noAccessAdminClient, getRealmName(), "test-delete-view").deleteClient()) { + assertEquals(403, response.getStatus()); } // no-access: not existing - should get 403 (lacks canList, prevents ID phishing) - request = new HttpDelete(getClientsApiUrl() + "/does-not-exist"); - setAuthHeader(request, adminClients.get("no-access")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("HTTP 403 Forbidden")); + try (var response = getClientApi(noAccessAdminClient, getRealmName(), "does-not-exist").deleteClient()) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden"); } // view-clients: not existing - should get 404 - setAuthHeader(request, adminClients.get("view-clients")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); + try (var response = getClientApi(viewClientsAdminClient, getRealmName(), "does-not-exist").deleteClient()) { + assertEquals(404, response.getStatus()); } } @@ -434,27 +364,21 @@ public void deleteClient() throws Exception { * Should not allow deletion of the admin permissions client (used for FGAP) */ @Test - public void cannotDeleteAdminPermissionsClient() throws Exception { + public void cannotDeleteAdminPermissionsClient() { var adminPermissionsClientRep = Optional.ofNullable(testRealm.admin().clients().findByClientId(Constants.ADMIN_PERMISSIONS_CLIENT_ID)) - .filter(f -> !f.isEmpty()) - .map(f -> f.get(0)) - .orElseThrow(() -> new AssertionError("Cannot find admin permissions client")); - - HttpDelete request = new HttpDelete(getClientsApiUrl() + "/" + adminPermissionsClientRep.getClientId()); - setAuthHeader(request, adminClients.get("realm-admin")); + .filter(f -> !f.isEmpty()) + .map(f -> f.get(0)) + .orElseThrow(() -> new AssertionError("Cannot find admin permissions client")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(400)); - var body = EntityUtils.toString(response.getEntity()); + try (var response = getClientApi(realmAdminAdminClient, getRealmName(), adminPermissionsClientRep.getClientId()).deleteClient()) { + assertThat(response.getStatus(), is(400)); + var body = response.readEntity(String.class); assertThat(body, containsString("Not supported for this client")); } // Verify the client still exists (was not deleted) - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/" + adminPermissionsClientRep.getClientId()); - setAuthHeader(getRequest, adminClients.get("realm-admin")); - try (var response = client.execute(getRequest)) { - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + BaseClientRepresentation rep = getClientApi(realmAdminAdminClient, getRealmName(), adminPermissionsClientRep.getClientId()).getClient(); + assertThat(rep.getClientId(), is(adminPermissionsClientRep.getClientId())); } /** @@ -468,7 +392,7 @@ public void getRealmAdmin() throws Exception { HttpGet request = new HttpGet(ownRealmUrl); // should successfully access own realm (200 OK) - setAuthHeader(request, adminClients.get("realm-admin")); + setAuthHeader(request, realmAdminAdminClient); try (var response = client.execute(request)) { EntityUtils.consumeQuietly(response.getEntity()); assertThat(response.getStatusLine().getStatusCode(), is(200)); @@ -477,7 +401,7 @@ public void getRealmAdmin() throws Exception { // Test accessing non-existent realm - should return 404 String nonExistentRealmUrl = "http://localhost:8080/admin/realms/non-existent-realm"; request = new HttpGet(nonExistentRealmUrl); - setAuthHeader(request, adminClients.get("realm-admin")); + setAuthHeader(request, realmAdminAdminClient); try (var response = client.execute(request)) { assertThat(response.getStatusLine().getStatusCode(), is(404)); } @@ -487,7 +411,7 @@ public void getRealmAdmin() throws Exception { // trying to access another realm's admin resource should be forbidden String differentRealmUrl = "http://localhost:8080/admin/realms/master"; request = new HttpGet(differentRealmUrl); - setAuthHeader(request, adminClients.get("realm-admin")); + setAuthHeader(request, realmAdminAdminClient); try (var response = client.execute(request)) { assertThat(response.getStatusLine().getStatusCode(), is(403)); } @@ -498,95 +422,68 @@ public void fgapGetClient() throws Exception { createTestClient("fgap-view-test"); // BEFORE permission: fgap-user cannot view fgap-view-test (403) - HttpGet request = new HttpGet(getClientsApiUrl() + "/fgap-view-test"); - setAuthHeader(request, adminClients.get("fgap-user")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> getClientApi(fgapAdminClient, getRealmName(), "fgap-view-test").getClient()); // Create FGAP permission for fgap-view-test createFgapPermissionForClient("fgap-view-test"); // AFTER permission: fgap-user CAN view fgap-view-test (200) - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + BaseClientRepresentation rep = getClientApi(fgapAdminClient, getRealmName(), "fgap-view-test").getClient(); + assertThat(rep.getClientId(), is("fgap-view-test")); // fgap-user CANNOT view fgap-denied-client (no permission granted) (403) - request = new HttpGet(getClientsApiUrl() + "/fgap-denied-client"); - setAuthHeader(request, adminClients.get("fgap-user")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> getClientApi(fgapAdminClient, getRealmName(), "fgap-denied-client").getClient()); } @Test public void fgapUpdateClient() throws Exception { createTestClient("fgap-update-test"); + BaseClientRepresentation updateRep = createClientRep("fgap-update-test", "updated-role"); // BEFORE permission: fgap-user cannot update fgap-update-test (403) - HttpPut request = new HttpPut(getClientsApiUrl() + "/fgap-update-test"); - setAuthHeader(request, adminClients.get("fgap-user")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("fgap-update-test", "updated-role")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-update-test").createOrUpdateClient(updateRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden before permission is granted"); + assertThat(response.readEntity(String.class), containsString("HTTP 403 Forbidden")); } // Create FGAP permission for fgap-update-test createFgapPermissionForClient("fgap-update-test"); // AFTER permission: fgap-user CAN update fgap-update-test (200) - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-update-test").createOrUpdateClient(updateRep)) { + assertThat(response.getStatus(), is(200)); } // fgap-user CANNOT update fgap-denied-client (no permission granted) (403) - request = new HttpPut(getClientsApiUrl() + "/fgap-denied-client"); - setAuthHeader(request, adminClients.get("fgap-user")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep("fgap-denied-client", "updated-role")))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + BaseClientRepresentation deniedRep = createClientRep("fgap-denied-client", "updated-role"); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-denied-client").createOrUpdateClient(deniedRep)) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden for denied client"); + assertThat(response.readEntity(String.class), containsString("HTTP 403 Forbidden")); } } @Test public void fgapPatchClient() throws Exception { createTestClient("fgap-patch-test"); - OIDCClientRepresentation patch = new OIDCClientRepresentation(); patch.setDescription("Patched by FGAP user"); - // BEFORE permission: fgap-user cannot patch fgap-patch-test (403) - HttpPatch request = new HttpPatch(getClientsApiUrl() + "/fgap-patch-test"); - setAuthHeader(request, adminClients.get("fgap-user")); - request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); - request.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + // BEFORE permission: fgap-user cannot patch fgap-patch-test (403) + assertThrows(ForbiddenException.class, + () -> getClientApi(fgapAdminClient, getRealmName(), "fgap-patch-test").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch)))); // Create FGAP permission for fgap-patch-test createFgapPermissionForClient("fgap-patch-test"); // AFTER permission: fgap-user CAN patch fgap-patch-test (200) - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + getClientApi(fgapAdminClient, getRealmName(), "fgap-patch-test").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))); // fgap-user CANNOT patch fgap-denied-client (no permission granted) (403) - request = new HttpPatch(getClientsApiUrl() + "/fgap-denied-client"); - setAuthHeader(request, adminClients.get("fgap-user")); - request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); patch.setDescription("Should not be patched"); - request.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> getClientApi(fgapAdminClient, getRealmName(), "fgap-denied-client").patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch)))); } @Test @@ -594,25 +491,23 @@ public void fgapDeleteClient() throws Exception { createTestClient("fgap-delete-test"); // BEFORE permission: fgap-user cannot delete fgap-delete-test (403) - HttpDelete request = new HttpDelete(getClientsApiUrl() + "/fgap-delete-test"); - setAuthHeader(request, adminClients.get("fgap-user")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-delete-test").deleteClient()) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden before permission is granted"); + assertThat(response.readEntity(String.class), containsString("HTTP 403 Forbidden")); } // Create FGAP permission for fgap-delete-test createFgapPermissionForClient("fgap-delete-test"); // AFTER permission: fgap-user CAN delete fgap-delete-test (204) - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(204)); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-delete-test").deleteClient()) { + assertThat(response.getStatus(), is(204)); } // fgap-user CANNOT delete fgap-denied-client (no permission granted) (403) - request = new HttpDelete(getClientsApiUrl() + "/fgap-denied-client"); - setAuthHeader(request, adminClients.get("fgap-user")); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); + try (var response = getClientApi(fgapAdminClient, getRealmName(), "fgap-denied-client").deleteClient()) { + assertEquals(403, response.getStatus(), "Expected 403 Forbidden for denied client"); + assertThat(response.readEntity(String.class), containsString("HTTP 403 Forbidden")); } } @@ -624,14 +519,9 @@ private OIDCClientRepresentation createClientRep(String clientId, String... role return rep; } - private void createTestClient(String clientId) throws Exception { - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request, adminClients.get("realm-admin")); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - request.setEntity(new StringEntity(mapper.writeValueAsString(createClientRep(clientId, "test-role1", "test-role2")))); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(201)); + private void createTestClient(String clientId) { + try (var response = getClientsApi(realmAdminAdminClient).createClient(createClientRep(clientId, "test-role1", "test-role2"))) { + assertThat(response.getStatus(), is(201)); } } diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2DisabledTest.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2DisabledTest.java index 2b91988a93ed..54d9f12ca133 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2DisabledTest.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2DisabledTest.java @@ -1,27 +1,26 @@ package org.keycloak.tests.admin.client.v2; -import org.keycloak.testframework.annotations.InjectHttpClient; +import jakarta.ws.rs.NotFoundException; + import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Vaclav Muzikar */ @KeycloakIntegrationTest public class ClientApiV2DisabledTest extends AbstractClientApiV2Test { - @InjectHttpClient - CloseableHttpClient client; - @Test - public void getClient() throws Exception { - HttpGet request = new HttpGet(getClientsApiUrl() + "/realms/master/clients/account"); - try (var response = client.execute(request)) { - assertEquals(404, response.getStatusLine().getStatusCode()); - } + public void getClient() { + NotFoundException ex = assertThrows( + NotFoundException.class, + () -> getClientApi("account").getClient() + ); + + assertTrue(ex.getMessage().contains("HTTP 404 Not Found")); } } 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 c524dd5efbcc..a8e98f8ad048 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 @@ -27,9 +27,7 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; -import org.keycloak.admin.api.AdminRootV2; import org.keycloak.admin.api.PatchTypeNames; -import org.keycloak.admin.api.client.ClientsApi; import org.keycloak.admin.client.Keycloak; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; @@ -88,28 +86,29 @@ public class ClientApiV2Test extends AbstractClientApiV2Test{ @InjectHttpClient CloseableHttpClient client; - @InjectRealm(config = NoAccessRealmConfig.class) + @InjectRealm(config = NoAccessRealmConfig.class, ref = "testRealm") ManagedRealm testRealm; @InjectRealm(attachTo = "master", ref = "master") ManagedRealm masterRealm; - @InjectAdminClient(ref = "noAccessClient", client = "myclient", mode = InjectAdminClient.Mode.MANAGED_REALM) + @InjectAdminClient(ref = "noAccessClient", realmRef = "testRealm", client = "myclient", mode = InjectAdminClient.Mode.MANAGED_REALM) Keycloak noAccessAdminClient; - @InjectClient(realmRef = "master") + @InjectClient(realmRef = "testRealm") ManagedClient testClient; + @Override + public String getRealmName() { + return testRealm.getName(); + } + @Test public void getClient() { - var client = clients(testRealm.getName()).client("account").getClient(); + var client = getClientApi("account").getClient(); assertEquals("account", client.getClientId()); } - private ClientsApi clients(String realmName) { - return adminClient.proxy(AdminRootV2.class).adminApi(realmName).clientsV2(); - } - @Test public void jsonPatchClient() throws Exception { HttpPatch request = new HttpPatch(getClientApiUrl(testClient.getClientId())); @@ -125,13 +124,14 @@ public void jsonPatchClient() throws Exception { public void jsonMergePatchClient() throws JsonProcessingException { OIDCClientRepresentation patch = new OIDCClientRepresentation(); patch.setDescription("I'm also a description"); - BaseClientRepresentation baseRep = clients(masterRealm.getName()).client(testClient.getClientId()).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))); + BaseClientRepresentation baseRep = getClientsApi().client(testClient.getClientId()).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))); + assertEquals("I'm also a description", baseRep.getDescription()); } @Test public void jsonMergePatchClientInvalid() throws Exception { - HttpPatch request = new HttpPatch(getClientApiUrl(testClient.getClientId())); + HttpPatch request = new HttpPatch(getClientApiUrl(getRealmName(), testClient.getClientId())); setAuthHeader(request); request.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); @@ -182,7 +182,7 @@ public void putFailsWithDifferentClientId() { OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("other"); - try (var response = clients(testRealm.getName()).client("account").createOrUpdateClient(rep)) { + try (var response = getClientApi("account").createOrUpdateClient(rep)) { assertEquals(400, response.getStatus()); } } @@ -195,7 +195,7 @@ public void putCreateOrUpdates() { rep.setClientId(clientId); rep.setDescription("I'm new"); - try (var response = clients(testRealm.getName()).client(clientId).createOrUpdateClient(rep)) { + try (var response = getClientsApi().client(clientId).createOrUpdateClient(rep)) { assertEquals(201, response.getStatus()); OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); assertEquals("I'm new", client.getDescription()); @@ -203,8 +203,7 @@ public void putCreateOrUpdates() { } rep.setDescription("I'm updated"); - - try (var response = clients(testRealm.getName()).client(clientId).createOrUpdateClient(rep)) { + try (var response = getClientsApi().client(clientId).createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); assertEquals("I'm updated", client.getDescription()); @@ -220,7 +219,7 @@ public void createClient() { rep.setClientId(clientId); rep.setDescription("I'm new"); - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertThat(response.getStatus(), is(201)); OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); assertThat(client.getEnabled(), is(true)); @@ -228,7 +227,7 @@ public void createClient() { assertThat(client.getDescription(), is("I'm new")); } - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertThat(response.getStatus(), is(409)); } } @@ -240,21 +239,20 @@ public void deleteClient() { rep.setClientId(clientIdToDelete); rep.setEnabled(true); - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertEquals(201, response.getStatus()); } - - var baseClientRepresentation = clients(testRealm.getName()).client(clientIdToDelete).getClient(); + var baseClientRepresentation = getClientsApi().client(clientIdToDelete).getClient(); assertEquals(clientIdToDelete, baseClientRepresentation.getClientId()); - try (var response = clients(testRealm.getName()).client(clientIdToDelete).deleteClient()) { + try (var response = getClientsApi().client(clientIdToDelete).deleteClient()) { assertEquals(204, response.getStatus()); } NotFoundException exception = assertThrows( NotFoundException.class, - () -> clients(testRealm.getName()).client(clientIdToDelete).getClient()); + () -> getClientsApi().client(clientIdToDelete).getClient()); assertTrue(exception.getMessage().contains("HTTP 404 Not Found")); } @@ -270,7 +268,7 @@ public void getClientsMixedProtocols() throws JsonProcessingException { oidcRep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.STANDARD, OIDCClientRepresentation.Flow.DIRECT_GRANT)); oidcRep.setWebOrigins(Set.of("http://localhost:3000", "http://localhost:4000")); - try (var response = clients(testRealm.getName()).createClient(oidcRep)) { + try (var response = getClientsApi().createClient(oidcRep)) { assertEquals(201, response.getStatus()); OIDCClientRepresentation created = response.readEntity(OIDCClientRepresentation.class); assertThat(created, notNullValue()); @@ -289,7 +287,7 @@ public void getClientsMixedProtocols() throws JsonProcessingException { samlRep.setForcePostBinding(true); samlRep.setFrontChannelLogout(false); - try (var response = clients(testRealm.getName()).createClient(samlRep)) { + try (var response = getClientsApi().createClient(samlRep)) { assertEquals(201, response.getStatus()); SAMLClientRepresentation created = response.readEntity(SAMLClientRepresentation.class); assertThat(created, notNullValue()); @@ -297,7 +295,7 @@ public void getClientsMixedProtocols() throws JsonProcessingException { } // Get all clients - this should work with mixed protocols - try (Stream baseClientRepresentationStream = clients(testRealm.getName()).getClients()) { + try (Stream baseClientRepresentationStream = getClientsApi().getClients()) { List clients = baseClientRepresentationStream.toList(); // Verify OIDC client with protocol-specific fields @@ -328,13 +326,13 @@ public void getClientsMixedProtocols() throws JsonProcessingException { // Get individual OIDC client and verify OIDC-specific fields - OIDCClientRepresentation oidcClient = (OIDCClientRepresentation) clients(testRealm.getName()).client(oidcRep.getClientId()).getClient(); + OIDCClientRepresentation oidcClient = (OIDCClientRepresentation) getClientsApi().client(oidcRep.getClientId()).getClient(); assertEquals("mixed-test-oidc", oidcClient.getClientId()); assertThat(oidcClient.getLoginFlows(), is(Set.of(OIDCClientRepresentation.Flow.STANDARD, OIDCClientRepresentation.Flow.DIRECT_GRANT))); assertThat(oidcClient.getWebOrigins(), is(Set.of("http://localhost:3000", "http://localhost:4000"))); // Get individual SAML client and verify SAML-specific fields - SAMLClientRepresentation samlClient = (SAMLClientRepresentation) clients(testRealm.getName()).client(samlRep.getClientId()).getClient(); + SAMLClientRepresentation samlClient = (SAMLClientRepresentation) getClientsApi().client(samlRep.getClientId()).getClient(); assertEquals("mixed-test-saml", samlClient.getClientId()); assertEquals("SAML client for mixed protocol test", samlClient.getDescription()); assertThat(samlClient.getNameIdFormat(), is("email")); @@ -350,7 +348,7 @@ public void OIDCClientRepresentationValidation() { oidcRep.setDisplayName("something"); oidcRep.setAppUrl("notUrl"); - try (var response = clients(testRealm.getName()).createClient(oidcRep)) { + try (var response = getClientsApi().createClient(oidcRep)) { assertThat(response, notNullValue()); assertThat(response.getStatus(), is(400)); @@ -370,7 +368,7 @@ public void OIDCClientRepresentationValidation() { auth.setMethod("missing-enabled"); oidcRep.setAuth(auth); - try (var response = clients(testRealm.getName()).createClient(oidcRep)) { + try (var response = getClientsApi().createClient(oidcRep)) { assertThat(response, notNullValue()); assertThat(response.getStatus(), is(400)); var body = response.readEntity(ViolationExceptionResponse.class); @@ -393,8 +391,7 @@ public void authenticationRequired() throws Exception { @Test public void createFullClient() { OIDCClientRepresentation rep = getTestingFullClientRep(); - - try (var response = clients(testRealm.getName()).createClient(rep);) { + try (var response = getClientsApi().createClient(rep);) { assertEquals(201, response.getStatus()); OIDCClientRepresentation client = response.readEntity(OIDCClientRepresentation.class); rep.setUuid(client.getUuid()); // needed for the use of equals() @@ -407,7 +404,7 @@ public void createFullClientWrongServiceAccountRoles() { OIDCClientRepresentation rep = getTestingFullClientRep(); rep.setServiceAccountRoles(Set.of("non-existing", "bad-role")); - try (var response = clients(testRealm.getName()).createClient(rep);) { + try (var response = getClientsApi().createClient(rep);) { assertEquals(400, response.getStatus()); assertThat(response.readEntity(String.class), containsString("Cannot assign role to the service account (field 'serviceAccount.roles') as it does not exist")); } @@ -421,7 +418,7 @@ public void declarativeRoleManagement() { rep.setEnabled(true); rep.setRoles(Set.of("role1", "role2", "role3")); - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertEquals(201, response.getStatus()); OIDCClientRepresentation created = response.readEntity(OIDCClientRepresentation.class); assertThat(created.getRoles(), is(Set.of("role1", "role2", "role3"))); @@ -429,7 +426,7 @@ public void declarativeRoleManagement() { // 2. Update with completely new roles - should remove old ones and add new ones rep.setRoles(Set.of("new-role1", "new-role2")); - try (var response = clients(testRealm.getName()).client("declarative-role-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("declarative-role-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getRoles(), is(Set.of("new-role1", "new-role2"))); @@ -437,7 +434,7 @@ public void declarativeRoleManagement() { // 3. Update with partial overlap - keep some, add some, remove some rep.setRoles(Set.of("new-role1", "add-role3", "add-role4")); - try (var response = clients(testRealm.getName()).client("declarative-role-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("declarative-role-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getRoles(), is(Set.of("new-role1", "add-role3", "add-role4"))); @@ -445,7 +442,7 @@ public void declarativeRoleManagement() { // 4. Update with same roles - should be idempotent rep.setRoles(Set.of("new-role1", "add-role3", "add-role4")); - try (var response = clients(testRealm.getName()).client("declarative-role-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("declarative-role-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getRoles(), is(Set.of("new-role1", "add-role3", "add-role4"))); @@ -453,7 +450,7 @@ public void declarativeRoleManagement() { // 5. Update with empty set - should remove all roles rep.setRoles(Set.of()); - try (var response = clients(testRealm.getName()).client("declarative-role-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("declarative-role-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getRoles(), is(Set.of())); @@ -463,7 +460,7 @@ public void declarativeRoleManagement() { @Test public void declarativeServiceAccountRoleManagement() { // 1. Create a client with service account and initial realm roles - String defaultRealmRoles = "default-roles-%s".formatted(testRealm.getName()); + String defaultRealmRoles = "default-roles-%s".formatted(testRealm.getName().toLowerCase()); OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("sa-declarative-test"); rep.setEnabled(true); @@ -471,7 +468,7 @@ public void declarativeServiceAccountRoleManagement() { rep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.SERVICE_ACCOUNT)); rep.setServiceAccountRoles(Set.of(defaultRealmRoles, "offline_access")); - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi(adminClient, getRealmName()).createClient(rep)) { assertEquals(201, response.getStatus()); OIDCClientRepresentation created = response.readEntity(OIDCClientRepresentation.class); assertThat(created.getServiceAccountRoles(), is(Set.of(defaultRealmRoles, "offline_access"))); @@ -479,7 +476,7 @@ public void declarativeServiceAccountRoleManagement() { // 2. Update with completely new roles - should remove old ones and add new ones rep.setServiceAccountRoles(Set.of("uma_authorization", "offline_access")); - try (var response = clients(testRealm.getName()).client("sa-declarative-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("sa-declarative-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getServiceAccountRoles(), is(Set.of("uma_authorization", "offline_access"))); @@ -487,7 +484,7 @@ public void declarativeServiceAccountRoleManagement() { // 3. Update with partial overlap - keep some, add some, remove some rep.setServiceAccountRoles(Set.of("offline_access", defaultRealmRoles)); - try (var response = clients(testRealm.getName()).client("sa-declarative-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("sa-declarative-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getServiceAccountRoles(), is(Set.of("offline_access", defaultRealmRoles))); @@ -495,7 +492,7 @@ public void declarativeServiceAccountRoleManagement() { // 4. Update with same roles - should be idempotent rep.setServiceAccountRoles(Set.of("offline_access", defaultRealmRoles)); - try (var response = clients(testRealm.getName()).client("sa-declarative-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("sa-declarative-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getServiceAccountRoles(), is(Set.of("offline_access", defaultRealmRoles))); @@ -503,7 +500,7 @@ public void declarativeServiceAccountRoleManagement() { // 5. Update with empty set - should remove all roles rep.setServiceAccountRoles(Set.of()); - try (var response = clients(testRealm.getName()).client("sa-declarative-test").createOrUpdateClient(rep)) { + try (var response = getClientsApi().client("sa-declarative-test").createOrUpdateClient(rep)) { assertEquals(200, response.getStatus()); OIDCClientRepresentation updated = response.readEntity(OIDCClientRepresentation.class); assertThat(updated.getServiceAccountRoles(), is(Set.of())); @@ -677,14 +674,14 @@ void createClientWithPostAndGeneratedSecret(String authenticationMethod) { auth.setSecret(null); client.setAuth(auth); - try (var response = clients(testRealm.getName()).createClient(client)) { + try (var response = getClientsApi().createClient(client)) { var createdClient = response.readEntity(OIDCClientRepresentation.class); assertThat(createdClient.getAuth(), notNullValue()); assertThat(createdClient.getAuth().getSecret(), Matchers.not(emptyOrNullString())); } // make sure that the created model was persisted and GET method returns the newly generated secret - var persistedClient = (OIDCClientRepresentation) clients(testRealm.getName()).client(clientId).getClient(); + var persistedClient = (OIDCClientRepresentation) getClientsApi().client(clientId).getClient(); assertEquals(clientId, persistedClient.getClientId()); assertThat(persistedClient.getAuth().getSecret(), Matchers.not(emptyOrNullString())); } @@ -883,7 +880,7 @@ void usePutToTurnConfidentialClientToPublicOne(String authenticationMethod) thro private OIDCClientRepresentation.Auth getResultingAuthConfigPost(OIDCClientRepresentation.Auth auth, String clientId, String... additionalFields) throws IllegalArgumentException { var rep = getResultingClientRep(auth, clientId, additionalFields); - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertThat(response.getStatus(), Matchers.anyOf(is(201), is(200))); OIDCClientRepresentation createdClient = response.readEntity(OIDCClientRepresentation.class); return assertClientEnabledIdDescriptionAndAuth(rep, createdClient); @@ -892,7 +889,7 @@ private OIDCClientRepresentation.Auth getResultingAuthConfigPost(OIDCClientRepre private OIDCClientRepresentation.Auth getResultingAuthConfigPut(OIDCClientRepresentation.Auth auth, String clientId, String... additionalFields) throws IllegalArgumentException { var rep = getResultingClientRep(auth, clientId, additionalFields); - try (var response = clients(testRealm.getName()).client(clientId).createOrUpdateClient(rep)) { + try (var response = getClientsApi().client(clientId).createOrUpdateClient(rep)) { assertThat(response.getStatus(), Matchers.anyOf(is(201), is(200))); OIDCClientRepresentation createdClient = response.readEntity(OIDCClientRepresentation.class); return assertClientEnabledIdDescriptionAndAuth(rep, createdClient); @@ -901,7 +898,7 @@ private OIDCClientRepresentation.Auth getResultingAuthConfigPut(OIDCClientRepres private OIDCClientRepresentation.Auth getResultingAuthConfigPatch(OIDCClientRepresentation.Auth auth, String clientId, String... additionalFields) throws IllegalArgumentException, JsonProcessingException { var rep = getResultingClientRep(auth, clientId, additionalFields); - OIDCClientRepresentation createdClient = (OIDCClientRepresentation) clients(testRealm.getName()).client(clientId).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(rep))); + OIDCClientRepresentation createdClient = (OIDCClientRepresentation) getClientsApi().client(clientId).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(rep))); return assertClientEnabledIdDescriptionAndAuth(rep, createdClient); } @@ -940,7 +937,7 @@ private OIDCClientRepresentation.Auth assertClientEnabledIdDescriptionAndAuth(OI * This verifies that ValidationUtil.validateClient is called after the full model is populated. */ private void assertClientCreationFailsWithError(BaseClientRepresentation rep, String expectedErrorMessage) throws Exception { - try (var response = clients(testRealm.getName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertThat(response.getStatus(), is(400)); String body = response.readEntity(String.class); assertThat(body, containsString(expectedErrorMessage)); @@ -964,7 +961,7 @@ private void assertClientUpdateFailsWithError(BaseClientRepresentation rep, Stri validRep.setClientId(clientId); validRep.setEnabled(true); - try (var response = clients(testRealm.getName()).createClient(validRep)) { + try (var response = getClientsApi().createClient(validRep)) { assertThat(response.getStatus(), is(201)); BaseClientRepresentation created = response.readEntity(BaseClientRepresentation.class); assertThat(created, notNullValue()); @@ -972,7 +969,7 @@ private void assertClientUpdateFailsWithError(BaseClientRepresentation rep, Stri } // Now try to update with invalid data - try (var response = clients(testRealm.getName()).client(clientId).createOrUpdateClient(rep)) { + try (var response = getClientsApi().client(clientId).createOrUpdateClient(rep)) { assertThat(response.getStatus(), is(400)); String body = response.readEntity(String.class); assertThat(body, containsString(expectedErrorMessage)); @@ -1001,7 +998,7 @@ private OIDCClientRepresentation getTestingFullClientRep() { rep.setRoles(Set.of("view-consent", "manage-account")); rep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.SERVICE_ACCOUNT)); // TODO when roles are not set and SA is enabled, the default role 'default-roles-master' for the SA is used for the master realm - rep.setServiceAccountRoles(Set.of("default-roles-%s".formatted(testRealm.getName()))); + rep.setServiceAccountRoles(Set.of("default-roles-%s".formatted(testRealm.getName()).toLowerCase())); // not implemented yet // rep.setAdditionalFields(Map.of("key1", "val1", "key2", "val2")); return rep; diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java index 895eb5365a84..b5150f09347f 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java @@ -17,17 +17,15 @@ package org.keycloak.tests.admin.client.v2; +import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.function.Consumer; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.BadRequestException; -import org.keycloak.admin.api.PatchTypeNames; -import org.keycloak.admin.client.Keycloak; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator; @@ -47,9 +45,9 @@ import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutor; import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutorFactory; -import org.keycloak.testframework.annotations.InjectAdminClient; -import org.keycloak.testframework.annotations.InjectHttpClient; +import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; import org.keycloak.testframework.server.KeycloakServerConfig; @@ -58,14 +56,6 @@ import org.keycloak.util.JsonSerialization; import com.fasterxml.jackson.databind.JsonNode; -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; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.util.EntityUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -77,6 +67,9 @@ import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests that client policies are properly executed when creating/updating clients via the Admin API v2. @@ -98,16 +91,18 @@ public class ClientPoliciesV2Test extends AbstractClientApiV2Test { private static final String PROFILE_NAME = "TestProfile"; private static final String POLICY_NAME = "TestPolicy"; - - @InjectHttpClient - CloseableHttpClient client; - @InjectAdminClient - Keycloak adminClient; - - @InjectRunOnServer + @InjectRunOnServer(realmRef = "testRealm") RunOnServerClient runOnServer; + @InjectRealm(ref = "testRealm") + ManagedRealm testRealm; + + @Override + public String getRealmName() { + return testRealm.getName(); + } + @AfterEach public void cleanup() { // Clean up any test clients @@ -133,10 +128,6 @@ public void createClientWithUnacceptableAuthType() throws Exception { setupPolicyClientIdAndSecretNotAcceptable(); // Try to create a client with client-secret authenticator (which should be rejected) - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("test-policy-client"); rep.setEnabled(true); @@ -145,12 +136,11 @@ public void createClientWithUnacceptableAuthType() throws Exception { auth.setSecret("secret"); rep.setAuth(auth); - request.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(request)) { + try (var response = getClientsApi().createClient(rep)) { // Should fail with 400 Bad Request due to policy violation - assertEquals(400, response.getStatusLine().getStatusCode()); - String body = EntityUtils.toString(response.getEntity()); + assertEquals(400, response.getStatus()); + String body = response.readEntity(String.class); + // TODO might be more consistent in error messages assertThat(body, containsString("Invalid client metadata: token_endpoint_auth_method")); } } @@ -168,10 +158,6 @@ public void createClientWithAcceptableAuthType() throws Exception { setupPolicyWithAutoConfiguration(); // Create a confidential client with an auth method - policy should allow it - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("test-policy-client"); rep.setEnabled(true); @@ -183,11 +169,9 @@ public void createClientWithAcceptableAuthType() throws Exception { // Add a login flow to ensure it's treated as a confidential client rep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.STANDARD)); - request.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(request)) { - int statusCode = response.getStatusLine().getStatusCode(); - String body = EntityUtils.toString(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + int statusCode = response.getStatus(); + String body = response.readEntity(String.class); assertEquals(201, statusCode, "Expected 201 but got " + statusCode + ": " + body); OIDCClientRepresentation created = mapper.readValue(body, OIDCClientRepresentation.class); assertEquals("test-policy-client", created.getClientId()); @@ -205,25 +189,20 @@ public void createClientWithAcceptableAuthType() throws Exception { @Test public void publicClientWithoutAuth() throws Exception { // Create a client without specifying auth - should be created as public client - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("test-auto-config-client"); rep.setEnabled(true); rep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.STANDARD)); - request.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(request)) { - int statusCode = response.getStatusLine().getStatusCode(); - String body = EntityUtils.toString(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + int statusCode = response.getStatus(); + String body = response.readEntity(String.class); assertEquals(201, statusCode, "Expected 201 but got " + statusCode + ": " + body); OIDCClientRepresentation created = mapper.readValue(body, OIDCClientRepresentation.class); assertEquals("test-auto-config-client", created.getClientId()); // Public clients don't have auth configuration // The auth field is null for public clients in the v2 API + assertNull(created.getAuth()); } } @@ -244,52 +223,34 @@ private OIDCClientRepresentation getPutUpdateClientRep() { @Test public void updateClientViaPutWithUnacceptableAuthType() throws Exception { // First create a client with acceptable auth type before policy is set - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = getPutUpdateClientRep(); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + assertEquals(201, response.getStatus()); } // Now setup policy setupPolicyClientIdAndSecretNotAcceptable(); // Try to update the client to use an unacceptable auth type - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(updateRequest); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - rep.setAuth(new OIDCClientRepresentation.Auth()); rep.getAuth().setMethod(ClientIdAndSecretAuthenticator.PROVIDER_ID); rep.getAuth().setSecret("newsecret"); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(updateRequest)) { + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { // Should fail with 400 Bad Request due to policy violation - assertEquals(400, response.getStatusLine().getStatusCode()); - String body = EntityUtils.toString(response.getEntity()); + assertEquals(400, response.getStatus()); + String body = response.readEntity(String.class); + // TODO might be more consistent in error messages assertThat(body, containsString("Invalid client metadata: token_endpoint_auth_method")); - } - // Verify the client was NOT updated (transaction rollback worked) - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(getRequest); - try (var response = client.execute(getRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - String body = EntityUtils.toString(response.getEntity()); - OIDCClientRepresentation current = mapper.readValue(body, OIDCClientRepresentation.class); + // Verify the client was NOT updated (transaction rollback worked) + OIDCClientRepresentation current = (OIDCClientRepresentation) getClientsApi().client("test-put-update-client").getClient(); // Should still have the original auth method, not the rejected one assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, current.getAuth().getMethod(), - "Client auth method should remain unchanged after policy rejection"); + "Client auth method should remain unchanged after policy rejection"); assertEquals("secret", current.getAuth().getSecret(), - "Client secret should remain unchanged after policy rejection"); + "Client secret should remain unchanged after policy rejection"); } } @@ -300,33 +261,21 @@ public void updateClientViaPutWithUnacceptableAuthType() throws Exception { @Test public void updateClientViaPutWithAcceptableAuthType() throws Exception { // First create a client BEFORE the policy is set - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = getPutUpdateClientRep(); - - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + assertEquals(201, response.getStatus()); } // Now setup policy setupPolicyClientIdAndSecretNotAcceptable(); // Update the client to use another acceptable auth type - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(updateRequest); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); rep.getAuth().setMethod(JWTClientAuthenticator.PROVIDER_ID); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - try (var response = client.execute(updateRequest)) { - int statusCode = response.getStatusLine().getStatusCode(); - String body = EntityUtils.toString(response.getEntity()); + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { + int statusCode = response.getStatus(); + String body = response.readEntity(String.class); assertEquals(200, statusCode, "Expected 200 but got " + statusCode + ": " + body); OIDCClientRepresentation updated = mapper.readValue(body, OIDCClientRepresentation.class); assertThat(updated.getAuth().getMethod(), is(JWTClientAuthenticator.PROVIDER_ID)); @@ -339,9 +288,6 @@ public void updateClientViaPutWithAcceptableAuthType() throws Exception { @Test public void updateClientViaPatchWithUnacceptableAuthType() throws Exception { // First create a client with acceptable auth type - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-patch-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("test-patch-update-client"); @@ -351,49 +297,34 @@ public void updateClientViaPatchWithUnacceptableAuthType() throws Exception { auth.setSecret("secret"); rep.setAuth(auth); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + assertEquals(201, response.getStatus()); } // Now setup policy setupPolicyClientIdAndSecretNotAcceptable(); // Try to patch the client to use an unacceptable auth type - HttpPatch patchRequest = new HttpPatch(getClientsApiUrl() + "/test-patch-update-client"); - setAuthHeader(patchRequest); - patchRequest.setHeader(HttpHeaders.CONTENT_TYPE, PatchTypeNames.JSON_MERGE); OIDCClientRepresentation patch = new OIDCClientRepresentation(); var patchAuth = new OIDCClientRepresentation.Auth(); patchAuth.setMethod(ClientIdAndSecretAuthenticator.PROVIDER_ID); patch.setAuth(patchAuth); - patchRequest.setEntity(new StringEntity(mapper.writeValueAsString(patch))); - - try (var response = client.execute(patchRequest)) { - // Should fail with 400 Bad Request due to policy violation - assertEquals(400, response.getStatusLine().getStatusCode()); - String body = EntityUtils.toString(response.getEntity()); - assertThat(body, containsString("Invalid client metadata: token_endpoint_auth_method")); - } + BadRequestException ex = assertThrows( + BadRequestException.class, + () -> getClientsApi().client(rep.getClientId()).patchClient(new ByteArrayInputStream(mapper.writeValueAsBytes(patch))) + ); + assertTrue(ex.getMessage().contains("HTTP 400 Bad Request")); // Verify the client was NOT updated (transaction rollback worked) - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/test-patch-update-client"); - setAuthHeader(getRequest); - try (var response = client.execute(getRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - String body = EntityUtils.toString(response.getEntity()); - OIDCClientRepresentation current = mapper.readValue(body, OIDCClientRepresentation.class); + OIDCClientRepresentation current = (OIDCClientRepresentation) getClientsApi().client("test-patch-update-client").getClient(); - // Should still have the original auth method, not the rejected one - assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, current.getAuth().getMethod(), - "Client auth method should remain unchanged after policy rejection"); - assertEquals("secret", current.getAuth().getSecret(), - "Client secret should remain unchanged after policy rejection"); - } + // Should still have the original auth method, not the rejected one + assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, current.getAuth().getMethod(), + "Client auth method should remain unchanged after policy rejection"); + assertEquals("secret", current.getAuth().getSecret(), + "Client secret should remain unchanged after policy rejection"); } @@ -404,34 +335,21 @@ public void updateClientViaPatchWithUnacceptableAuthType() throws Exception { @Test public void policyAppliedOnUpdateWithoutAuthTypeChange() throws Exception { // Create a client BEFORE the policy is set - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = getPutUpdateClientRep(); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + assertEquals(201, response.getStatus()); } // Now setup policy setupPolicyClientIdAndSecretNotAcceptable(); // Update client without changing auth type (just change description) - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(updateRequest); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - rep.setDescription("Updated description"); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(updateRequest)) { + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { // Should succeed since auth type is still acceptable - int statusCode = response.getStatusLine().getStatusCode(); - String body = EntityUtils.toString(response.getEntity()); + int statusCode = response.getStatus(); + String body = response.readEntity(String.class); assertEquals(200, statusCode, "Expected 200 but got " + statusCode + ": " + body); OIDCClientRepresentation updated = mapper.readValue(body, OIDCClientRepresentation.class); assertThat(updated.getDescription(), is("Updated description")); @@ -447,11 +365,7 @@ public void policyAppliedOnUpdateWithoutAuthTypeChange() throws Exception { public void getClientViewEvent() throws Exception { setupAlwaysAppliedTestPolicy(); - HttpGet getClient = new HttpGet(getClientsApiUrl() + "/account"); - setAuthHeader(getClient); - try (var response = client.execute(getClient)) { - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + assertNotNull(getClientApi("account").getClient()); assertClientPolicyEventIsEmitted(ClientPolicyEvent.VIEW); } @@ -464,17 +378,11 @@ public void getClientViewEvent() throws Exception { public void createClientRegisterEvent() throws Exception { setupAlwaysAppliedTestPolicy(); - HttpPost request = new HttpPost(getClientsApiUrl()); - setAuthHeader(request); - request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = new OIDCClientRepresentation(); rep.setClientId("client-123"); - request.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(201)); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().createClient(rep)) { + assertThat(response.getStatus(), is(201)); } assertClientPolicyEventIsEmitted(ClientPolicyEvent.REGISTER, ClientPolicyEvent.REGISTERED); @@ -490,34 +398,22 @@ public void createClientRegisterEvent() throws Exception { public void updateClientUpdateEvent() throws Exception { setupAlwaysAppliedTestPolicy(); - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - OIDCClientRepresentation rep = getPutUpdateClientRep(); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { + assertEquals(201, response.getStatus()); } assertClientPolicyEventIsEmitted(ClientPolicyEvent.REGISTER, ClientPolicyEvent.REGISTERED); - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(updateRequest); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - rep = getPutUpdateClientRep(); rep.setDescription("Updated"); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - try (var response = client.execute(updateRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { + assertEquals(200, response.getStatus()); } - assertClientPolicyEventIsEmitted(ClientPolicyEvent.UPDATE, ClientPolicyEvent.UPDATED); + assertClientPolicyEventIsEmitted(ClientPolicyEvent.VIEW, ClientPolicyEvent.UPDATE, ClientPolicyEvent.UPDATED); } /** @@ -526,23 +422,17 @@ public void updateClientUpdateEvent() throws Exception { */ @Test public void deleteClientUnregisterEvent() throws Exception { - HttpPut createRequest = new HttpPut(getClientsApiUrl() + "/test-put-update-client"); - setAuthHeader(createRequest); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); OIDCClientRepresentation rep = getPutUpdateClientRep(); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep))); - - try (var response = client.execute(createRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().client(rep.getClientId()).createOrUpdateClient(rep)) { + assertEquals(201, response.getStatus()); } setupAlwaysAppliedTestPolicy(); cleanupClient(rep.getClientId()); - assertClientPolicyEventIsEmitted(ClientPolicyEvent.UNREGISTER); + assertClientPolicyEventIsEmitted(ClientPolicyEvent.VIEW, ClientPolicyEvent.UNREGISTER); } private void assertClientPolicyEventIsEmitted(ClientPolicyEvent... events) { @@ -657,7 +547,7 @@ private void setupPolicy(String description, String profileName, String policyNa ClientProfilesRepresentation profilesRep = new ClientProfilesRepresentation(); profilesRep.setProfiles(List.of(profileRep)); - adminClient.realm("master").clientPoliciesProfilesResource().updateProfiles(profilesRep); + adminClient.realm(getRealmName()).clientPoliciesProfilesResource().updateProfiles(profilesRep); // Create policy ClientPolicyRepresentation policyRep = new ClientPolicyRepresentation(); @@ -672,14 +562,14 @@ private void setupPolicy(String description, String profileName, String policyNa ClientPoliciesRepresentation policiesRep = new ClientPoliciesRepresentation(); policiesRep.setPolicies(List.of(policyRep)); - adminClient.realm("master").clientPoliciesPoliciesResource().updatePolicies(policiesRep); + adminClient.realm(getRealmName()).clientPoliciesPoliciesResource().updatePolicies(policiesRep); } private void revertToBuiltinProfiles() { try { ClientProfilesRepresentation emptyProfiles = new ClientProfilesRepresentation(); emptyProfiles.setProfiles(List.of()); - adminClient.realm("master").clientPoliciesProfilesResource().updateProfiles(emptyProfiles); + adminClient.realm(getRealmName()).clientPoliciesProfilesResource().updateProfiles(emptyProfiles); } catch (Exception e) { // Ignore cleanup errors } @@ -689,7 +579,7 @@ private void revertToBuiltinPolicies() { try { ClientPoliciesRepresentation emptyPolicies = new ClientPoliciesRepresentation(); emptyPolicies.setPolicies(List.of()); - adminClient.realm("master").clientPoliciesPoliciesResource().updatePolicies(emptyPolicies); + adminClient.realm(getRealmName()).clientPoliciesPoliciesResource().updatePolicies(emptyPolicies); } catch (Exception e) { // Ignore cleanup errors } @@ -697,10 +587,8 @@ private void revertToBuiltinPolicies() { private void cleanupClient(String clientId) { try { - HttpDelete deleteRequest = new HttpDelete(getClientsApiUrl() + "/" + clientId); - setAuthHeader(deleteRequest); - try (var response = client.execute(deleteRequest)) { - EntityUtils.consumeQuietly(response.getEntity()); + try (var response = getClientsApi().client(clientId).deleteClient()) { + // Nothing needed here. } } catch (Exception e) { // Ignore cleanup errors diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java index 378bb4cb3f7a..df6ca0d8764b 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java @@ -21,29 +21,26 @@ import java.util.Set; import java.util.stream.Collectors; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.Profile; +import org.keycloak.representations.admin.v2.BaseClientRepresentation; import org.keycloak.representations.admin.v2.OIDCClientRepresentation; import org.keycloak.representations.admin.v2.SAMLClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.testframework.annotations.InjectAdminClient; import org.keycloak.testframework.annotations.InjectHttpClient; +import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.server.KeycloakServerConfig; import org.keycloak.testframework.server.KeycloakServerConfigBuilder; import org.keycloak.testframework.util.ApiUtil; import org.keycloak.tests.admin.mapper.ClientRepresentationComparator; -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.junit.jupiter.api.Test; @@ -76,12 +73,20 @@ public class InteropTest extends AbstractClientApiV2Test { @InjectAdminClient Keycloak adminClient; + @InjectRealm + ManagedRealm testRealm; + + @Override + public String getRealmName() { + return testRealm.getName(); + } + /** * Test: Create a client using v1 API, then assert/read using v2 API. */ @Test - public void createWithV1AssertWithV2() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createWithV1AssertWithV2() { + RealmResource realm = adminClient.realm(getRealmName()); ClientRepresentation v1Client = new ClientRepresentation(); v1Client.setClientId("v1-created-client"); @@ -103,17 +108,11 @@ public void createWithV1AssertWithV2() throws Exception { String clientUuid = ApiUtil.getCreatedId(response); response.close(); - ClientRepresentation createdV1Client = realm.clients().get(clientUuid).toRepresentation(); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v1-created-client"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation v2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); + try { + ClientRepresentation createdV1Client = realm.clients().get(clientUuid).toRepresentation(); - ClientRepresentationComparator.ComparisonResult result = + BaseClientRepresentation v2Client = getClientsApi().client("v1-created-client").getClient(); + ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(createdV1Client, v2Client); assertTrue(result.allMatch(), "V1 and V2 representations should match:\n" + result); @@ -126,8 +125,8 @@ public void createWithV1AssertWithV2() throws Exception { * Test: Create a client using v2 API, then assert/read using v1 API. */ @Test - public void createWithV2AssertWithV1() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createWithV2AssertWithV1() { + RealmResource realm = adminClient.realm(getRealmName()); OIDCClientRepresentation v2Client = new OIDCClientRepresentation(); v2Client.setClientId("v2-created-client"); @@ -146,28 +145,17 @@ public void createWithV2AssertWithV1() throws Exception { auth.setSecret("v2-secret-456"); v2Client.setAuth(auth); - HttpPost createRequest = new HttpPost(getClientsApiUrl()); - setAuthHeader(createRequest, adminClient); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(v2Client))); - - try (var httpResponse = httpClient.execute(createRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(201)); + try (var response = getClientsApi().createClient(v2Client)) { + assertThat(response.getStatus(), is(201)); } ClientRepresentation v1Client = realm.clients().findByClientId("v2-created-client").get(0); String clientUuid = v1Client.getId(); - ClientRepresentation fullV1Client = realm.clients().get(clientUuid).toRepresentation(); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v2-created-client"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); - ClientRepresentationComparator.ComparisonResult result = + try { + ClientRepresentation fullV1Client = realm.clients().get(clientUuid).toRepresentation(); + BaseClientRepresentation fetchedV2Client = getClientsApi().client("v2-created-client").getClient(); + ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(fullV1Client, fetchedV2Client); assertTrue(result.allMatch(), "V1 and V2 representations should match:\n" + result); @@ -181,8 +169,8 @@ public void createWithV2AssertWithV1() throws Exception { * Test: Create a client using v1 API, update it using v2 API, then assert using v1 API. */ @Test - public void updateWithV2AssertWithV1() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void updateWithV2AssertWithV1() { + RealmResource realm = adminClient.realm(getRealmName()); ClientRepresentation v1Client = new ClientRepresentation(); v1Client.setClientId("update-test-client"); @@ -191,7 +179,7 @@ public void updateWithV2AssertWithV1() throws Exception { v1Client.setEnabled(true); v1Client.setPublicClient(false); v1Client.setProtocol("openid-connect"); - v1Client.setRedirectUris(Arrays.asList("http://localhost:5000/*")); + v1Client.setRedirectUris(List.of("http://localhost:5000/*")); v1Client.setStandardFlowEnabled(true); Response response = realm.clients().create(v1Client); @@ -216,26 +204,15 @@ public void updateWithV2AssertWithV1() throws Exception { auth.setSecret("updated-secret"); v2Update.setAuth(auth); - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/update-test-client"); - setAuthHeader(updateRequest, adminClient); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(v2Update))); - - try (var httpResponse = httpClient.execute(updateRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); + try (var httpResponse = getClientsApi().client("update-test-client").createOrUpdateClient(v2Update)) { + assertThat(httpResponse.getStatus(), is(200)); } - ClientRepresentation updatedV1Client = realm.clients().get(clientUuid).toRepresentation(); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/update-test-client"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); + try { + ClientRepresentation updatedV1Client = realm.clients().get(clientUuid).toRepresentation(); + OIDCClientRepresentation fetchedV2Client = (OIDCClientRepresentation) getClientsApi().client("update-test-client").getClient(); - ClientRepresentationComparator.ComparisonResult result = + ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(updatedV1Client, fetchedV2Client); assertTrue(result.allMatch(), "V1 and V2 representations should match after update:\n" + result); @@ -252,8 +229,8 @@ public void updateWithV2AssertWithV1() throws Exception { * Test: Create a SAML client using v1 API, then assert/read using v2 API. */ @Test - public void createSamlWithV1AssertWithV2() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createSamlWithV1AssertWithV2() { + RealmResource realm = adminClient.realm(getRealmName()); ClientRepresentation v1Client = new ClientRepresentation(); v1Client.setClientId("v1-saml-client"); @@ -279,15 +256,9 @@ public void createSamlWithV1AssertWithV2() throws Exception { String clientUuid = ApiUtil.getCreatedId(response); response.close(); - ClientRepresentation createdV1Client = realm.clients().get(clientUuid).toRepresentation(); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v1-saml-client"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - SAMLClientRepresentation v2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(SAMLClientRepresentation.class); + try { + ClientRepresentation createdV1Client = realm.clients().get(clientUuid).toRepresentation(); + SAMLClientRepresentation v2Client = (SAMLClientRepresentation) getClientsApi().client("v1-saml-client").getClient(); ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(createdV1Client, v2Client); @@ -302,8 +273,8 @@ public void createSamlWithV1AssertWithV2() throws Exception { * Test: Create a SAML client using v2 API, then assert/read using v1 API. */ @Test - public void createSamlWithV2AssertWithV1() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createSamlWithV2AssertWithV1() { + RealmResource realm = adminClient.realm(getRealmName()); SAMLClientRepresentation v2Client = new SAMLClientRepresentation(); v2Client.setClientId("v2-saml-client"); @@ -322,26 +293,18 @@ public void createSamlWithV2AssertWithV1() throws Exception { v2Client.setForcePostBinding(true); v2Client.setSignatureAlgorithm("RSA_SHA256"); - HttpPost createRequest = new HttpPost(getClientsApiUrl()); - setAuthHeader(createRequest, adminClient); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(v2Client))); - - try (var httpResponse = httpClient.execute(createRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(201)); + try (var response = getClientsApi().createClient(v2Client)) { + assertThat(response.getStatus(), is(201)); } ClientRepresentation v1Client = realm.clients().findByClientId("v2-saml-client").get(0); String clientUuid = v1Client.getId(); - ClientRepresentation fullV1Client = realm.clients().get(clientUuid).toRepresentation(); - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v2-saml-client"); - setAuthHeader(getRequest, adminClient); - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - SAMLClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(SAMLClientRepresentation.class); + try { + ClientRepresentation fullV1Client = realm.clients().get(clientUuid).toRepresentation(); + + SAMLClientRepresentation fetchedV2Client = (SAMLClientRepresentation) getClientsApi().client("v2-saml-client").getClient(); ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(fullV1Client, fetchedV2Client); @@ -356,8 +319,8 @@ public void createSamlWithV2AssertWithV1() throws Exception { * Test: Create a SAML client using v1 API, update it using v2 API, then assert using v1 API. */ @Test - public void updateSamlWithV2AssertWithV1() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void updateSamlWithV2AssertWithV1() { + RealmResource realm = adminClient.realm(getRealmName()); ClientRepresentation v1Client = new ClientRepresentation(); v1Client.setClientId("update-saml-client"); @@ -388,24 +351,12 @@ public void updateSamlWithV2AssertWithV1() throws Exception { v2Update.setForcePostBinding(true); v2Update.setSignatureAlgorithm("RSA_SHA512"); - HttpPut updateRequest = new HttpPut(getClientsApiUrl() + "/update-saml-client"); - setAuthHeader(updateRequest, adminClient); - updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - updateRequest.setEntity(new StringEntity(mapper.writeValueAsString(v2Update))); - - try (var httpResponse = httpClient.execute(updateRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); + try (var httpResponse = getClientsApi().client("update-saml-client").createOrUpdateClient(v2Update)) { + assertThat(httpResponse.getStatus(), is(200)); } - - ClientRepresentation updatedV1Client = realm.clients().get(clientUuid).toRepresentation(); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/update-saml-client"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - SAMLClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(SAMLClientRepresentation.class); + try { + ClientRepresentation updatedV1Client = realm.clients().get(clientUuid).toRepresentation(); + SAMLClientRepresentation fetchedV2Client = (SAMLClientRepresentation) getClientsApi().client("update-saml-client").getClient(); ClientRepresentationComparator.ComparisonResult result = ClientRepresentationComparator.compare(updatedV1Client, fetchedV2Client); @@ -425,8 +376,8 @@ public void updateSamlWithV2AssertWithV1() throws Exception { * Test: Create a client with roles using v2 API, then assert roles using v1 API. */ @Test - public void createWithV2RolesAssertWithV1() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createWithV2RolesAssertWithV1() { + RealmResource realm = adminClient.realm(getRealmName()); OIDCClientRepresentation v2Client = new OIDCClientRepresentation(); v2Client.setClientId("v2-client-with-roles"); @@ -437,13 +388,8 @@ public void createWithV2RolesAssertWithV1() throws Exception { v2Client.setRedirectUris(Set.of("http://localhost:3000/*")); v2Client.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.STANDARD)); - HttpPost createRequest = new HttpPost(getClientsApiUrl()); - setAuthHeader(createRequest, adminClient); - createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - createRequest.setEntity(new StringEntity(mapper.writeValueAsString(v2Client))); - - try (var httpResponse = httpClient.execute(createRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(201)); + try (var response = getClientsApi().createClient(v2Client)) { + assertThat(response.getStatus(), is(201)); } ClientRepresentation v1Client = realm.clients().findByClientId("v2-client-with-roles").get(0); @@ -456,17 +402,8 @@ public void createWithV2RolesAssertWithV1() throws Exception { .collect(Collectors.toSet()); assertThat(roleNames, containsInAnyOrder("viewer", "editor", "admin")); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v2-client-with-roles"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); - - assertThat(fetchedV2Client.getRoles(), containsInAnyOrder("viewer", "editor", "admin")); - } + OIDCClientRepresentation fetchedV2Client = (OIDCClientRepresentation) getClientsApi().client("v2-client-with-roles").getClient(); + assertThat(fetchedV2Client.getRoles(), containsInAnyOrder("viewer", "editor", "admin")); } finally { realm.clients().get(clientUuid).remove(); } @@ -476,8 +413,8 @@ public void createWithV2RolesAssertWithV1() throws Exception { * Test: Create a client using v1 API, add roles via v1 API, then assert roles using v2 API. */ @Test - public void createWithV1RolesAssertWithV2() throws Exception { - RealmResource realm = adminClient.realm("master"); + public void createWithV1RolesAssertWithV2() { + RealmResource realm = adminClient.realm(getRealmName()); ClientRepresentation v1Client = new ClientRepresentation(); v1Client.setClientId("v1-client-with-roles"); @@ -492,31 +429,25 @@ public void createWithV1RolesAssertWithV2() throws Exception { String clientUuid = ApiUtil.getCreatedId(response); response.close(); + + RoleRepresentation role1 = new RoleRepresentation(); + role1.setName("read-access"); + role1.setDescription("Read access role"); + realm.clients().get(clientUuid).roles().create(role1); + + RoleRepresentation role2 = new RoleRepresentation(); + role2.setName("write-access"); + role2.setDescription("Write access role"); + realm.clients().get(clientUuid).roles().create(role2); + + RoleRepresentation role3 = new RoleRepresentation(); + role3.setName("delete-access"); + realm.clients().get(clientUuid).roles().create(role3); + + try { - RoleRepresentation role1 = new RoleRepresentation(); - role1.setName("read-access"); - role1.setDescription("Read access role"); - realm.clients().get(clientUuid).roles().create(role1); - - RoleRepresentation role2 = new RoleRepresentation(); - role2.setName("write-access"); - role2.setDescription("Write access role"); - realm.clients().get(clientUuid).roles().create(role2); - - RoleRepresentation role3 = new RoleRepresentation(); - role3.setName("delete-access"); - realm.clients().get(clientUuid).roles().create(role3); - - HttpGet getRequest = new HttpGet(getClientsApiUrl() + "/v1-client-with-roles"); - setAuthHeader(getRequest, adminClient); - - try (var httpResponse = httpClient.execute(getRequest)) { - assertThat(httpResponse.getStatusLine().getStatusCode(), is(200)); - OIDCClientRepresentation fetchedV2Client = mapper.createParser(httpResponse.getEntity().getContent()) - .readValueAs(OIDCClientRepresentation.class); - - assertThat(fetchedV2Client.getRoles(), containsInAnyOrder("read-access", "write-access", "delete-access")); - } + OIDCClientRepresentation fetchedV2Client = (OIDCClientRepresentation) getClientsApi().client("v1-client-with-roles").getClient(); + assertThat(fetchedV2Client.getRoles(), containsInAnyOrder("read-access", "write-access", "delete-access")); } finally { realm.clients().get(clientUuid).remove(); } From 916c088d3e176efd5fa1c94fad917727a49fd03b Mon Sep 17 00:00:00 2001 From: Peter Zaoral Date: Mon, 20 Apr 2026 15:17:47 +0200 Subject: [PATCH 2/2] Migrate getRealmAdmin() to use @InjectAdminClient and remove redundant AdminClientFactory setup Signed-off-by: Peter Zaoral is-45364 fix PR comments Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> --- .../v2/ClientApiV2AuthorizationTest.java | 59 ++----------------- .../admin/client/v2/ClientApiV2Test.java | 2 +- .../admin/client/v2/ClientPoliciesV2Test.java | 4 +- .../tests/admin/client/v2/InteropTest.java | 11 ---- 4 files changed, 9 insertions(+), 67 deletions(-) diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java index 2ee92c3c8ad7..5487bff888c6 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientApiV2AuthorizationTest.java @@ -20,11 +20,8 @@ import org.keycloak.representations.idm.authorization.Logic; import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation; import org.keycloak.representations.idm.authorization.UserPolicyRepresentation; -import org.keycloak.testframework.admin.AdminClientFactory; import org.keycloak.testframework.annotations.InjectAdminClient; -import org.keycloak.testframework.annotations.InjectAdminClientFactory; import org.keycloak.testframework.annotations.InjectClient; -import org.keycloak.testframework.annotations.InjectHttpClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ClientBuilder; @@ -36,9 +33,6 @@ import org.keycloak.testframework.server.KeycloakServerConfig; import org.keycloak.testframework.server.KeycloakServerConfigBuilder; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.util.EntityUtils; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -62,15 +56,9 @@ public class ClientApiV2AuthorizationTest extends AbstractClientApiV2Test { private static final String FGAP_USER_ID = "00000000000000000000"; - @InjectHttpClient - CloseableHttpClient client; - @InjectRealm(config = AuthorizationRealmConfig.class) ManagedRealm testRealm; - @InjectAdminClientFactory - AdminClientFactory adminClientFactory; - @InjectClient(attachTo = Constants.ADMIN_PERMISSIONS_CLIENT_ID) ManagedClient adminPermissionClient; @@ -386,35 +374,19 @@ public void cannotDeleteAdminPermissionsClient() { * Permissions: if realm == null return 404, if !isAdministrationRealm && !auth.realm.equals(realm) return 403 */ @Test - public void getRealmAdmin() throws Exception { - // Users authenticated to 'authztest' should be able to access 'authztest' realm admin resources - String ownRealmUrl = "http://localhost:8080/admin/realms/%s".formatted(getRealmName()); - HttpGet request = new HttpGet(ownRealmUrl); - + public void getRealmAdmin() { // should successfully access own realm (200 OK) - setAuthHeader(request, realmAdminAdminClient); - try (var response = client.execute(request)) { - EntityUtils.consumeQuietly(response.getEntity()); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - } + assertThat(realmAdminAdminClient.realm(getRealmName()).toRepresentation().getRealm(), is(getRealmName())); // Test accessing non-existent realm - should return 404 - String nonExistentRealmUrl = "http://localhost:8080/admin/realms/non-existent-realm"; - request = new HttpGet(nonExistentRealmUrl); - setAuthHeader(request, realmAdminAdminClient); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(404)); - } + assertThrows(NotFoundException.class, + () -> realmAdminAdminClient.realm("non-existent-realm").toRepresentation()); // When accessing a different realm from a non-administration realm, should return 403 // Note: This test validates that when authenticated to 'authztest' realm (which is not an admin realm), // trying to access another realm's admin resource should be forbidden - String differentRealmUrl = "http://localhost:8080/admin/realms/master"; - request = new HttpGet(differentRealmUrl); - setAuthHeader(request, realmAdminAdminClient); - try (var response = client.execute(request)) { - assertThat(response.getStatusLine().getStatusCode(), is(403)); - } + assertThrows(ForbiddenException.class, + () -> realmAdminAdminClient.realm("master").toRepresentation()); } @Test @@ -525,16 +497,6 @@ private void createTestClient(String clientId) { } } - private Keycloak createAdminClient(String username) { - return adminClientFactory.create() - .realm(getRealmName()) - .clientId("test-client") - .clientSecret("test-secret") - .username(username) - .password("password") - .build(); - } - private void createFgapPermissionForClient(String clientId) throws Exception { createFgapPermissionForClient(clientId, FGAP_USER_ID, AdminPermissionsSchema.MANAGE, AdminPermissionsSchema.VIEW); } @@ -583,8 +545,6 @@ public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) } } - private static final Set CURRENT_USERS = new HashSet<>(); - public static class AuthorizationRealmConfig implements RealmConfig { @Override public RealmBuilder configure(RealmBuilder realm) { @@ -599,7 +559,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)); - CURRENT_USERS.add("realm-admin"); // Role: view-clients realm.users(UserBuilder.create("view-clients") @@ -608,7 +567,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.VIEW_CLIENTS)); - CURRENT_USERS.add("view-clients"); // Role: manage-clients realm.users(UserBuilder.create("manage-clients") @@ -617,7 +575,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_CLIENTS)); - CURRENT_USERS.add("manage-clients"); // Role: query-clients realm.users(UserBuilder.create("query-clients") @@ -626,7 +583,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.QUERY_CLIENTS)); - CURRENT_USERS.add("query-clients"); // NO role realm.users(UserBuilder.create("no-access") @@ -634,7 +590,6 @@ public RealmBuilder configure(RealmBuilder realm) { .email("noaccess@localhost") .emailVerified(true) .password("password")); - CURRENT_USERS.add("no-access"); // Role: manage-realm realm.users(UserBuilder.create("manage-realm") @@ -643,7 +598,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_REALM)); - CURRENT_USERS.add("manage-realm"); // FGAP v2 // fgap-user has QUERY_CLIENTS role but will be granted fine-grained permissions for specific clients @@ -654,7 +608,6 @@ public RealmBuilder configure(RealmBuilder realm) { .emailVerified(true) .password("password") .clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.QUERY_CLIENTS)); - CURRENT_USERS.add("fgap-user"); realm.clients(ClientBuilder.create("fgap-denied-client").enabled(true)); 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 a8e98f8ad048..87131497d1fd 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 @@ -468,7 +468,7 @@ public void declarativeServiceAccountRoleManagement() { rep.setLoginFlows(Set.of(OIDCClientRepresentation.Flow.SERVICE_ACCOUNT)); rep.setServiceAccountRoles(Set.of(defaultRealmRoles, "offline_access")); - try (var response = getClientsApi(adminClient, getRealmName()).createClient(rep)) { + try (var response = getClientsApi().createClient(rep)) { assertEquals(201, response.getStatus()); OIDCClientRepresentation created = response.readEntity(OIDCClientRepresentation.class); assertThat(created.getServiceAccountRoles(), is(Set.of(defaultRealmRoles, "offline_access"))); diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java index b5150f09347f..752d6254c653 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/ClientPoliciesV2Test.java @@ -413,7 +413,7 @@ public void updateClientUpdateEvent() throws Exception { assertEquals(200, response.getStatus()); } - assertClientPolicyEventIsEmitted(ClientPolicyEvent.VIEW, ClientPolicyEvent.UPDATE, ClientPolicyEvent.UPDATED); + assertClientPolicyEventIsEmitted(ClientPolicyEvent.UPDATE, ClientPolicyEvent.UPDATED); } /** @@ -432,7 +432,7 @@ public void deleteClientUnregisterEvent() throws Exception { setupAlwaysAppliedTestPolicy(); cleanupClient(rep.getClientId()); - assertClientPolicyEventIsEmitted(ClientPolicyEvent.VIEW, ClientPolicyEvent.UNREGISTER); + assertClientPolicyEventIsEmitted(ClientPolicyEvent.UNREGISTER); } private void assertClientPolicyEventIsEmitted(ClientPolicyEvent... events) { diff --git a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java index df6ca0d8764b..19f3020d4df4 100644 --- a/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java +++ b/rest/admin-v2/tests/src/test/java/org/keycloak/tests/admin/client/v2/InteropTest.java @@ -23,7 +23,6 @@ import jakarta.ws.rs.core.Response; -import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.Profile; import org.keycloak.representations.admin.v2.BaseClientRepresentation; @@ -31,8 +30,6 @@ import org.keycloak.representations.admin.v2.SAMLClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.testframework.annotations.InjectAdminClient; -import org.keycloak.testframework.annotations.InjectHttpClient; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.realm.ManagedRealm; @@ -41,7 +38,6 @@ import org.keycloak.testframework.util.ApiUtil; import org.keycloak.tests.admin.mapper.ClientRepresentationComparator; -import org.apache.http.impl.client.CloseableHttpClient; import org.junit.jupiter.api.Test; import static org.keycloak.protocol.saml.SamlConfigAttributes.SAML_ASSERTION_SIGNATURE; @@ -66,13 +62,6 @@ @KeycloakIntegrationTest(config = InteropTest.ServerConfig.class) public class InteropTest extends AbstractClientApiV2Test { - - @InjectHttpClient - CloseableHttpClient httpClient; - - @InjectAdminClient - Keycloak adminClient; - @InjectRealm ManagedRealm testRealm;