mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-18 18:37:54 -05:00
[admin-api-v2] Every distinct Admin API should be versioned (#44527)
Closes #44527 Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
parent
cca5ef44fa
commit
b61a00cbba
11 changed files with 130 additions and 122 deletions
|
|
@ -1,11 +1,17 @@
|
|||
package org.keycloak.admin.api;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
|
||||
import org.keycloak.admin.api.realm.RealmsApi;
|
||||
import org.keycloak.admin.api.client.ClientsApi;
|
||||
|
||||
public interface AdminApi {
|
||||
|
||||
@Path("realms")
|
||||
RealmsApi realms();
|
||||
String CONTENT_TYPE_MERGE_PATCH = "application/merge-patch+json";
|
||||
|
||||
/**
|
||||
* Retrieve the Clients API group by version
|
||||
*/
|
||||
@Path("clients/{version:v\\d+}")
|
||||
ClientsApi clients(@PathParam("version") String version);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package org.keycloak.admin.api;
|
|||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.OPTIONS;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
|
|
@ -19,18 +21,19 @@ public class AdminRootV2 {
|
|||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
@Path("v2")
|
||||
public AdminApi adminApi() {
|
||||
@Path("{realmName}")
|
||||
public AdminApi adminApi(@PathParam("realmName") String realmName) {
|
||||
checkApiEnabled();
|
||||
return new DefaultAdminApi(session);
|
||||
return new DefaultAdminApi(session, realmName);
|
||||
}
|
||||
|
||||
@Path("{any:.*}")
|
||||
// TODO Fix preflights
|
||||
@Path("{realmName}/{any:.*}")
|
||||
@OPTIONS
|
||||
@Operation(hidden = true)
|
||||
public Object preFlight() {
|
||||
public Response preFlight() {
|
||||
checkApiEnabled();
|
||||
return new AdminCorsPreflightService();
|
||||
return new AdminCorsPreflightService().preflight();
|
||||
}
|
||||
|
||||
private void checkApiEnabled() {
|
||||
|
|
|
|||
|
|
@ -1,24 +1,28 @@
|
|||
package org.keycloak.admin.api;
|
||||
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.admin.api.realm.DefaultRealmsApi;
|
||||
import org.keycloak.admin.api.realm.RealmsApi;
|
||||
import org.keycloak.admin.api.client.ClientsApi;
|
||||
import org.keycloak.admin.api.client.DefaultClientsApi;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.services.resources.admin.AdminAuth;
|
||||
import org.keycloak.services.resources.admin.AdminRoot;
|
||||
import org.keycloak.services.resources.admin.RealmAdminResource;
|
||||
import org.keycloak.services.resources.admin.RealmsAdminResource;
|
||||
|
||||
public class DefaultAdminApi implements AdminApi {
|
||||
private final KeycloakSession session;
|
||||
private final RealmsAdminResource realmsAdminResource;
|
||||
private final RealmAdminResource realmAdminResource;
|
||||
private final AdminAuth auth;
|
||||
|
||||
public DefaultAdminApi(KeycloakSession session) {
|
||||
public DefaultAdminApi(KeycloakSession session, String realmName) {
|
||||
this.session = session;
|
||||
this.auth = AdminRoot.authenticateRealmAdminRequest(session);
|
||||
|
||||
|
|
@ -27,12 +31,15 @@ public class DefaultAdminApi implements AdminApi {
|
|||
throw new NotAuthorizedException("Wrong permissions");
|
||||
}
|
||||
this.realmsAdminResource = new RealmsAdminResource(session, auth, new TokenManager());
|
||||
this.realmAdminResource = realmsAdminResource.getRealmAdmin(realmName);
|
||||
}
|
||||
|
||||
@Path("realms")
|
||||
@Path("clients/{version:v\\d+}")
|
||||
@Override
|
||||
public RealmsApi realms() {
|
||||
return new DefaultRealmsApi(session, realmsAdminResource);
|
||||
public ClientsApi clients(@PathParam("version") String version) {
|
||||
return switch (version) {
|
||||
case "v2" -> new DefaultClientsApi(session, realmAdminResource);
|
||||
default -> throw new NotFoundException();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ import org.keycloak.representations.admin.v2.BaseClientRepresentation;
|
|||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public interface ClientApi {
|
||||
import static org.keycloak.admin.api.AdminApi.CONTENT_TYPE_MERGE_PATCH;
|
||||
|
||||
// TODO move these
|
||||
String CONTENT_TYPE_MERGE_PATCH = "application/merge-patch+json";
|
||||
public interface ClientApi {
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@ package org.keycloak.admin.api.client;
|
|||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.PATCH;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.admin.api.AdminApi;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.admin.v2.BaseClientRepresentation;
|
||||
|
|
@ -30,7 +34,6 @@ public class DefaultClientApi implements ClientApi {
|
|||
|
||||
private final KeycloakSession session;
|
||||
private final RealmModel realm;
|
||||
private final ClientModel client;
|
||||
private final ClientService clientService;
|
||||
|
||||
private final ClientResource clientResource;
|
||||
|
|
@ -45,18 +48,19 @@ public class DefaultClientApi implements ClientApi {
|
|||
this.clientId = clientId;
|
||||
|
||||
this.realm = Objects.requireNonNull(session.getContext().getRealm());
|
||||
this.client = Objects.requireNonNull(session.getContext().getClient());
|
||||
this.clientService = new DefaultClientService(session, realmAdminResource, clientResource);
|
||||
|
||||
this.objectMapper = MAPPER;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Override
|
||||
public BaseClientRepresentation getClient() {
|
||||
return clientService.getClient(realm, client.getClientId(), null)
|
||||
return clientService.getClient(realm, clientId, null)
|
||||
.orElseThrow(() -> new NotFoundException("Cannot find the specified client"));
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Override
|
||||
public Response createOrUpdateClient(BaseClientRepresentation client) {
|
||||
try {
|
||||
|
|
@ -71,13 +75,14 @@ public class DefaultClientApi implements ClientApi {
|
|||
}
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Override
|
||||
public BaseClientRepresentation patchClient(JsonNode patch) {
|
||||
BaseClientRepresentation client = getClient();
|
||||
try {
|
||||
String contentType = session.getContext().getHttpRequest().getHttpHeaders().getHeaderString(HttpHeaders.CONTENT_TYPE);
|
||||
MediaType mediaType = contentType == null ? null : MediaType.valueOf(contentType);
|
||||
MediaType mergePatch = MediaType.valueOf(ClientApi.CONTENT_TYPE_MERGE_PATCH);
|
||||
MediaType mergePatch = MediaType.valueOf(AdminApi.CONTENT_TYPE_MERGE_PATCH);
|
||||
if (mediaType == null || !mediaType.isCompatible(mergePatch)) {
|
||||
throw new WebApplicationException("Unsupported media type", Response.Status.UNSUPPORTED_MEDIA_TYPE);
|
||||
}
|
||||
|
|
@ -96,6 +101,7 @@ public class DefaultClientApi implements ClientApi {
|
|||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Override
|
||||
public void deleteClient() {
|
||||
if (clientResource == null) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import java.util.Optional;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
|
@ -39,11 +42,13 @@ public class DefaultClientsApi implements ClientsApi {
|
|||
this.clientsResource = realmAdminResource.getClients();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Override
|
||||
public Stream<BaseClientRepresentation> getClients() {
|
||||
return clientService.getClients(realm, null, null, null);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Override
|
||||
public Response createClient(@Valid BaseClientRepresentation client) {
|
||||
try {
|
||||
|
|
@ -57,6 +62,7 @@ public class DefaultClientsApi implements ClientsApi {
|
|||
}
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Override
|
||||
public ClientApi client(@PathParam("id") String clientId) {
|
||||
var client = Optional.ofNullable(session.clients().getClientByClientId(realm, clientId));
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package org.keycloak.admin.api.realm;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
|
||||
import org.keycloak.admin.api.client.ClientsApi;
|
||||
import org.keycloak.admin.api.client.DefaultClientsApi;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.resources.admin.RealmAdminResource;
|
||||
|
||||
public class DefaultRealmApi implements RealmApi {
|
||||
private final KeycloakSession session;
|
||||
private final RealmAdminResource realmAdminResource;
|
||||
|
||||
public DefaultRealmApi(KeycloakSession session, RealmAdminResource realmAdmin) {
|
||||
this.session = session;
|
||||
this.realmAdminResource = realmAdmin;
|
||||
}
|
||||
|
||||
@Path("clients")
|
||||
@Override
|
||||
public ClientsApi clients() {
|
||||
return new DefaultClientsApi(session, realmAdminResource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package org.keycloak.admin.api.realm;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.resources.admin.RealmsAdminResource;
|
||||
|
||||
public class DefaultRealmsApi implements RealmsApi {
|
||||
private final KeycloakSession session;
|
||||
private final RealmsAdminResource realmsAdminResource;
|
||||
|
||||
public DefaultRealmsApi(KeycloakSession session, RealmsAdminResource realmsAdminResource) {
|
||||
this.session = session;
|
||||
this.realmsAdminResource = realmsAdminResource;
|
||||
}
|
||||
|
||||
@Path("{name}")
|
||||
@Override
|
||||
public RealmApi realm(@PathParam("name") String name) {
|
||||
var realmAdmin = realmsAdminResource.getRealmAdmin(name);
|
||||
return new DefaultRealmApi(session, realmAdmin);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package org.keycloak.admin.api.realm;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
|
||||
import org.keycloak.admin.api.client.ClientsApi;
|
||||
|
||||
public interface RealmApi {
|
||||
|
||||
@Path("clients")
|
||||
ClientsApi clients();
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package org.keycloak.admin.api.realm;
|
||||
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
|
||||
public interface RealmsApi {
|
||||
|
||||
@Path("{name}")
|
||||
RealmApi realm(@PathParam("name") String name);
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import java.util.Set;
|
|||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.keycloak.admin.api.client.ClientApi;
|
||||
import org.keycloak.admin.api.AdminApi;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.representations.admin.v2.BaseClientRepresentation;
|
||||
|
|
@ -45,6 +45,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import org.apache.http.HttpMessage;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpOptions;
|
||||
import org.apache.http.client.methods.HttpPatch;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
|
|
@ -54,6 +55,9 @@ import org.apache.http.util.EntityUtils;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.keycloak.services.cors.Cors.ACCESS_CONTROL_ALLOW_METHODS;
|
||||
import static org.keycloak.services.cors.Cors.ORIGIN_HEADER;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
|
@ -63,7 +67,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
@KeycloakIntegrationTest(config = ClientApiV2Test.AdminV2Config.class)
|
||||
public class ClientApiV2Test {
|
||||
|
||||
public static final String HOSTNAME_LOCAL_ADMIN = "http://localhost:8080/admin/api/v2";
|
||||
public static final String HOSTNAME_LOCAL_ADMIN = "http://localhost:8080/admin/api/master/clients/v2";
|
||||
private static ObjectMapper mapper;
|
||||
|
||||
@InjectHttpClient
|
||||
|
|
@ -85,7 +89,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void getClient() throws Exception {
|
||||
HttpGet request = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpGet request = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/account");
|
||||
setAuthHeader(request);
|
||||
try (var response = client.execute(request)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
|
|
@ -96,7 +100,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void jsonPatchClient() throws Exception {
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/account");
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_PATCH_JSON);
|
||||
try (var response = client.execute(request)) {
|
||||
|
|
@ -107,9 +111,9 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void jsonMergePatchClient() throws Exception {
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/account");
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, ClientApi.CONTENT_TYPE_MERGE_PATCH);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, AdminApi.CONTENT_TYPE_MERGE_PATCH);
|
||||
|
||||
OIDCClientRepresentation patch = new OIDCClientRepresentation();
|
||||
patch.setDescription("I'm also a description");
|
||||
|
|
@ -126,7 +130,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void putFailsWithDifferentClientId() throws Exception {
|
||||
HttpPut request = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpPut request = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/account");
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -142,7 +146,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void putCreateOrUpdates() throws Exception {
|
||||
HttpPut request = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/other");
|
||||
HttpPut request = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/other");
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -171,7 +175,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void createClient() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -197,7 +201,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void deleteClient() throws Exception {
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/to-delete");
|
||||
setAuthHeader(createRequest);
|
||||
createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -211,13 +215,13 @@ public class ClientApiV2Test {
|
|||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
|
||||
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/to-delete");
|
||||
setAuthHeader(getRequest);
|
||||
try (var response = client.execute(getRequest)) {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
HttpDelete deleteRequest = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
|
||||
HttpDelete deleteRequest = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/to-delete");
|
||||
setAuthHeader(deleteRequest);
|
||||
try (var response = client.execute(deleteRequest)) {
|
||||
assertEquals(204, response.getStatusLine().getStatusCode());
|
||||
|
|
@ -231,7 +235,7 @@ public class ClientApiV2Test {
|
|||
@Test
|
||||
public void getClientsMixedProtocols() throws Exception {
|
||||
// Create an OIDC client with OIDC-specific fields
|
||||
HttpPost oidcRequest = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost oidcRequest = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(oidcRequest);
|
||||
oidcRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -250,7 +254,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// Create a SAML client with SAML-specific fields
|
||||
HttpPost samlRequest = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost samlRequest = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(samlRequest);
|
||||
samlRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -272,7 +276,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// Get all clients - this should work with mixed protocols
|
||||
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(getRequest);
|
||||
|
||||
try (var response = client.execute(getRequest)) {
|
||||
|
|
@ -308,7 +312,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// Get individual OIDC client and verify OIDC-specific fields
|
||||
HttpGet getOidcRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/mixed-test-oidc");
|
||||
HttpGet getOidcRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/mixed-test-oidc");
|
||||
setAuthHeader(getOidcRequest);
|
||||
|
||||
try (var response = client.execute(getOidcRequest)) {
|
||||
|
|
@ -321,7 +325,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// Get individual SAML client and verify SAML-specific fields
|
||||
HttpGet getSamlRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/mixed-test-saml");
|
||||
HttpGet getSamlRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/mixed-test-saml");
|
||||
setAuthHeader(getSamlRequest);
|
||||
|
||||
try (var response = client.execute(getSamlRequest)) {
|
||||
|
|
@ -338,13 +342,13 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// Cleanup
|
||||
HttpDelete deleteOidc = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/mixed-test-oidc");
|
||||
HttpDelete deleteOidc = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/mixed-test-oidc");
|
||||
setAuthHeader(deleteOidc);
|
||||
try (var response = client.execute(deleteOidc)) {
|
||||
assertEquals(204, response.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
HttpDelete deleteSaml = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/mixed-test-saml");
|
||||
HttpDelete deleteSaml = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/mixed-test-saml");
|
||||
setAuthHeader(deleteSaml);
|
||||
try (var response = client.execute(deleteSaml)) {
|
||||
assertEquals(204, response.getStatusLine().getStatusCode());
|
||||
|
|
@ -353,7 +357,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void OIDCClientRepresentationValidation() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -401,7 +405,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void authenticationRequired() throws Exception {
|
||||
HttpGet request = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpGet request = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/account");
|
||||
setAuthHeader(request, noAccessAdminClient);
|
||||
try (var response = client.execute(request)) {
|
||||
assertEquals(401, response.getStatusLine().getStatusCode());
|
||||
|
|
@ -410,7 +414,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void createFullClient() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -426,7 +430,7 @@ public class ClientApiV2Test {
|
|||
|
||||
@Test
|
||||
public void createFullClientWrongServiceAccountRoles() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN);
|
||||
setAuthHeader(request);
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -443,7 +447,7 @@ public class ClientApiV2Test {
|
|||
@Test
|
||||
public void declarativeRoleManagement() throws Exception {
|
||||
// 1. Create a client with initial roles
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/declarative-role-test");
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/declarative-role-test");
|
||||
setAuthHeader(createRequest);
|
||||
createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -461,7 +465,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// 2. Update with completely new roles - should remove old ones and add new ones
|
||||
HttpPut updateRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/declarative-role-test");
|
||||
HttpPut updateRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/declarative-role-test");
|
||||
setAuthHeader(updateRequest);
|
||||
updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -508,7 +512,7 @@ public class ClientApiV2Test {
|
|||
@Test
|
||||
public void declarativeServiceAccountRoleManagement() throws Exception {
|
||||
// 1. Create a client with service account and initial realm roles
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/sa-declarative-test");
|
||||
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/sa-declarative-test");
|
||||
setAuthHeader(createRequest);
|
||||
createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -528,7 +532,7 @@ public class ClientApiV2Test {
|
|||
}
|
||||
|
||||
// 2. Update with completely new roles - should remove old ones and add new ones
|
||||
HttpPut updateRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/sa-declarative-test");
|
||||
HttpPut updateRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/sa-declarative-test");
|
||||
setAuthHeader(updateRequest);
|
||||
updateRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
|
|
@ -572,6 +576,54 @@ public class ClientApiV2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void versionedClientsApi() throws Exception {
|
||||
final var ADMIN_API_URL = "http://localhost:8080/admin/api/master";
|
||||
|
||||
// no version specified - default
|
||||
HttpGet request = new HttpGet(ADMIN_API_URL + "/clients");
|
||||
setAuthHeader(request);
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(405)); // 405 for now due to the preflight check (needs to be fixed)
|
||||
}
|
||||
|
||||
// v2 specified
|
||||
request = new HttpGet(ADMIN_API_URL + "/clients/v2");
|
||||
setAuthHeader(request);
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
}
|
||||
|
||||
// unknown version
|
||||
request = new HttpGet(ADMIN_API_URL + "/clients/v3");
|
||||
setAuthHeader(request);
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(404));
|
||||
}
|
||||
|
||||
// invalid version
|
||||
request = new HttpGet(ADMIN_API_URL + "/clients/4");
|
||||
setAuthHeader(request);
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(405)); // 405 for now due to the preflight check (needs to be fixed)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflight() throws Exception {
|
||||
HttpOptions request = new HttpOptions(HOSTNAME_LOCAL_ADMIN);
|
||||
request.setHeader(ORIGIN_HEADER, "http://localhost:8080");
|
||||
|
||||
// we can improve preflight logic in follow-up issues
|
||||
try (var response = client.execute(request)) {
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
var header = response.getFirstHeader(ACCESS_CONTROL_ALLOW_METHODS);
|
||||
assertThat(header, notNullValue());
|
||||
assertThat(header.getValue(), is("DELETE, POST, GET, PUT"));
|
||||
}
|
||||
}
|
||||
|
||||
private OIDCClientRepresentation getTestingFullClientRep() {
|
||||
var rep = new OIDCClientRepresentation();
|
||||
rep.setClientId("my-client");
|
||||
|
|
|
|||
Loading…
Reference in a new issue