mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
[admin-api-v2] Provide simple validation with Jakarta/Hibernate Validation (#41110)
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
parent
9e1e0dbad3
commit
eca1333027
@ -68,6 +68,10 @@
|
||||
<groupId>org.eclipse.microprofile.openapi</groupId>
|
||||
<artifactId>microprofile-openapi-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
||||
@ -3,8 +3,11 @@ package org.keycloak.representations.admin.v2;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
import org.keycloak.representations.admin.v2.validation.CreateClient;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
@ -13,6 +16,7 @@ public class ClientRepresentation extends BaseRepresentation {
|
||||
|
||||
public static final String OIDC = "openid-connect";
|
||||
|
||||
@NotBlank(groups = CreateClient.class)
|
||||
@JsonPropertyDescription("ID uniquely identifying this client")
|
||||
private String clientId;
|
||||
|
||||
@ -29,28 +33,31 @@ public class ClientRepresentation extends BaseRepresentation {
|
||||
@JsonPropertyDescription("Whether this client is enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
@URL
|
||||
@JsonPropertyDescription("URL to the application's homepage that is represented by this client")
|
||||
private String appUrl;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonPropertyDescription("URLs that the browser can redirect to after login")
|
||||
private Set<String> appRedirectUrls = new LinkedHashSet<String>();
|
||||
private Set<@NotBlank @URL(message = "Each redirect URL must be valid") String> appRedirectUrls = new LinkedHashSet<String>();
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonPropertyDescription("Login flows that are enabled for this client")
|
||||
private Set<String> loginFlows = new LinkedHashSet<String>();
|
||||
private Set<@NotBlank String> loginFlows = new LinkedHashSet<String>();
|
||||
|
||||
@Valid
|
||||
@JsonPropertyDescription("Authentication configuration for this client")
|
||||
private Auth auth;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonPropertyDescription("Web origins that are allowed to make requests to this client")
|
||||
private Set<String> webOrigins = new LinkedHashSet<String>();
|
||||
private Set<@NotBlank String> webOrigins = new LinkedHashSet<String>();
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonPropertyDescription("Roles associated with this client")
|
||||
private Set<String> roles = new LinkedHashSet<String>();
|
||||
private Set<@NotBlank String> roles = new LinkedHashSet<String>();
|
||||
|
||||
@Valid
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonPropertyDescription("Service account configuration for this client")
|
||||
private ServiceAccount serviceAccount;
|
||||
@ -160,6 +167,7 @@ public class ClientRepresentation extends BaseRepresentation {
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
public static class Auth {
|
||||
|
||||
@NotNull
|
||||
@JsonPropertyDescription("Whether authentication is enabled for this client")
|
||||
private Boolean enabled;
|
||||
|
||||
@ -208,6 +216,7 @@ public class ClientRepresentation extends BaseRepresentation {
|
||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||
public static class ServiceAccount {
|
||||
|
||||
@NotNull
|
||||
@JsonPropertyDescription("Whether the service account is enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
package org.keycloak.representations.admin.v2.validation;
|
||||
|
||||
// Jakarta Validation Group - validation is done only when creating a client
|
||||
public interface CreateClient {
|
||||
}
|
||||
17
pom.xml
17
pom.xml
@ -91,6 +91,8 @@
|
||||
<hibernate-orm.plugin.version>6.2.13.Final</hibernate-orm.plugin.version>
|
||||
<hibernate.c3p0.version>6.2.13.Final</hibernate.c3p0.version>
|
||||
<infinispan.version>15.0.19.Final</infinispan.version>
|
||||
<hibernate-validator.version>9.0.1.Final</hibernate-validator.version>
|
||||
<expressly.version>6.0.0</expressly.version>
|
||||
<protostream.version>5.0.14.Final</protostream.version> <!-- For the annotation processor: keep in sync with the version shipped with Infinispan -->
|
||||
<protostream.plugin.version>${protostream.version}</protostream.plugin.version>
|
||||
|
||||
@ -584,6 +586,21 @@
|
||||
<artifactId>h2</artifactId>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator-cdi</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>${expressly.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-c3p0</artifactId>
|
||||
|
||||
@ -138,6 +138,10 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-validator-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-orm-deployment</artifactId>
|
||||
|
||||
@ -140,6 +140,7 @@ import org.keycloak.transaction.JBossJtaTransactionManagerLookup;
|
||||
import org.keycloak.userprofile.config.UPConfigUtils;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.utils.StringUtil;
|
||||
import org.keycloak.validation.jakarta.HibernateValidatorProviderFactory;
|
||||
import org.keycloak.vault.FilesKeystoreVaultProviderFactory;
|
||||
import org.keycloak.vault.FilesPlainTextVaultProviderFactory;
|
||||
|
||||
@ -197,6 +198,7 @@ class KeycloakProcessor {
|
||||
JBossJtaTransactionManagerLookup.class,
|
||||
DefaultJpaConnectionProviderFactory.class,
|
||||
DefaultLiquibaseConnectionProvider.class,
|
||||
//HibernateValidatorProviderFactory.class,
|
||||
FolderThemeProviderFactory.class,
|
||||
LiquibaseJpaUpdaterProviderFactory.class,
|
||||
FilesKeystoreVaultProviderFactory.class,
|
||||
|
||||
@ -140,6 +140,12 @@
|
||||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate validator -->
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SmallRye -->
|
||||
<dependency>
|
||||
<groupId>io.smallrye.config</groupId>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package org.keycloak.admin.api.client;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.PATCH;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.keycloak.admin.api.FieldValidation;
|
||||
@ -25,11 +27,12 @@ public interface ClientApi {
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
ClientRepresentation createOrUpdateClient(ClientRepresentation client, @PathParam("fieldValidation") FieldValidation fieldValidation);
|
||||
ClientRepresentation createOrUpdateClient(@Valid ClientRepresentation client,
|
||||
@QueryParam("fieldValidation") FieldValidation fieldValidation);
|
||||
|
||||
@PATCH
|
||||
@Consumes({MediaType.APPLICATION_JSON_PATCH_JSON, CONENT_TYPE_MERGE_PATCH})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
ClientRepresentation patchClient(JsonNode patch, @PathParam("fieldValidation") FieldValidation fieldValidation);
|
||||
ClientRepresentation patchClient(JsonNode patch, @QueryParam("fieldValidation") FieldValidation fieldValidation);
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@ package org.keycloak.admin.api.client;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.groups.ConvertGroup;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import org.keycloak.admin.api.FieldValidation;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
@ -13,6 +16,7 @@ import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.keycloak.representations.admin.v2.validation.CreateClient;
|
||||
|
||||
public interface ClientsApi extends Provider {
|
||||
|
||||
@ -24,7 +28,8 @@ public interface ClientsApi extends Provider {
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
ClientRepresentation createClient(ClientRepresentation client, @PathParam("fieldValidation") FieldValidation fieldValidation);
|
||||
ClientRepresentation createClient(@Valid @ConvertGroup(to = CreateClient.class) ClientRepresentation client,
|
||||
@QueryParam("fieldValidation") FieldValidation fieldValidation);
|
||||
|
||||
@Path("{id}")
|
||||
ClientApi client(@PathParam("id") String id);
|
||||
|
||||
@ -2,11 +2,14 @@ package org.keycloak.admin.api.client;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.groups.ConvertGroup;
|
||||
import org.keycloak.admin.api.FieldValidation;
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
import org.keycloak.representations.admin.v2.validation.CreateClient;
|
||||
import org.keycloak.services.ServiceException;
|
||||
import org.keycloak.services.client.ClientService;
|
||||
|
||||
@ -35,7 +38,8 @@ public class DefaultClientsApi implements ClientsApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientRepresentation createClient(ClientRepresentation client, FieldValidation fieldValidation) {
|
||||
public ClientRepresentation createClient(@Valid @ConvertGroup(to = CreateClient.class) ClientRepresentation client,
|
||||
FieldValidation fieldValidation) {
|
||||
try {
|
||||
response.setStatus(Response.Status.CREATED.getStatusCode());
|
||||
return clientService.createOrUpdate(realm, client, false).representation();
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package org.keycloak.validation.jakarta;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
public interface JakartaValidatorProvider extends Provider {
|
||||
|
||||
Validator getValidator();
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package org.keycloak.validation.jakarta;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
public interface JakartaValidatorProviderFactory extends ProviderFactory<JakartaValidatorProvider> {
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package org.keycloak.validation.jakarta;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
public class JakartaValidatorSpi implements Spi {
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "jakarta-validator";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return JakartaValidatorProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory<?>> getProviderFactoryClass() {
|
||||
return JakartaValidatorProviderFactory.class;
|
||||
}
|
||||
}
|
||||
@ -108,6 +108,7 @@ org.keycloak.logging.MappedDiagnosticContextSpi
|
||||
org.keycloak.services.KeycloakServicesSpi
|
||||
org.keycloak.services.client.ClientServiceSpi
|
||||
org.keycloak.models.mapper.ModelMapperSpi
|
||||
org.keycloak.validation.jakarta.JakartaValidatorSpi
|
||||
org.keycloak.models.policy.ResourceActionSpi
|
||||
org.keycloak.models.policy.ResourcePolicySpi
|
||||
org.keycloak.models.policy.ResourcePolicyConditionSpi
|
||||
|
||||
@ -10,22 +10,22 @@ import org.keycloak.services.ServiceException;
|
||||
|
||||
public interface ClientService extends Service {
|
||||
|
||||
public static class ClientSearchOptions {
|
||||
class ClientSearchOptions {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public static class ClientProjectionOptions {
|
||||
class ClientProjectionOptions {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public static class ClientSortAndSliceOptions {
|
||||
class ClientSortAndSliceOptions {
|
||||
// order by
|
||||
// offset
|
||||
// limit
|
||||
// NOTE: this is not always the most desirable way to do pagination
|
||||
}
|
||||
|
||||
public record CreateOrUpdateResult(ClientRepresentation representation, boolean created) {}
|
||||
record CreateOrUpdateResult(ClientRepresentation representation, boolean created) {}
|
||||
|
||||
Optional<ClientRepresentation> getClient(RealmModel realm, String clientId, ClientProjectionOptions projectionOptions);
|
||||
|
||||
|
||||
@ -86,6 +86,16 @@
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator-cdi</artifactId>
|
||||
<version>${hibernate-validator.version}</version> <!--Not sure why we need to set it as it should be part of dependencyManagement-->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>${expressly.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
package org.keycloak.services.client;
|
||||
|
||||
import jakarta.enterprise.inject.spi.CDI;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.hibernate.validator.HibernateValidatorFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.mapper.ClientModelMapper;
|
||||
import org.keycloak.models.mapper.ModelMapper;
|
||||
import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
import org.keycloak.representations.admin.v2.validation.CreateClient;
|
||||
import org.keycloak.services.ServiceException;
|
||||
import org.keycloak.validation.jakarta.HibernateValidatorProvider;
|
||||
import org.keycloak.validation.jakarta.JakartaValidatorProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
@ -17,10 +26,12 @@ import java.util.stream.Stream;
|
||||
public class DefaultClientService implements ClientService {
|
||||
private final KeycloakSession session;
|
||||
private final ClientModelMapper mapper;
|
||||
private final Validator validator;
|
||||
|
||||
public DefaultClientService(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.mapper = session.getProvider(ModelMapper.class).clients();
|
||||
this.validator = session.getProvider(JakartaValidatorProvider.class).getValidator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,6 +56,7 @@ public class DefaultClientService implements ClientService {
|
||||
throw new ServiceException("Client already exists", Response.Status.CONFLICT);
|
||||
}
|
||||
} else {
|
||||
validator.validate(client, CreateClient.class); // TODO improve it to avoid second validation when we know it is create and not update
|
||||
model = realm.addClient(client.getClientId());
|
||||
created = true;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import static org.keycloak.services.resources.KeycloakApplication.getSessionFact
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import jakarta.validation.ValidationException;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
@ -100,6 +101,8 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
||||
error.setErrorDescription("Cannot parse the JSON");
|
||||
} else if (isServerError) {
|
||||
error.setErrorDescription("For more on this error consult the server log.");
|
||||
} else if (throwable instanceof ValidationException) {
|
||||
error.setErrorDescription(throwable.getMessage());
|
||||
}
|
||||
|
||||
return Response.status(responseStatus)
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package org.keycloak.validation.jakarta;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
|
||||
public class HibernateValidatorProvider implements JakartaValidatorProvider {
|
||||
private final Validator validator;
|
||||
|
||||
public HibernateValidatorProvider(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator getValidator() {
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package org.keycloak.validation.jakarta;
|
||||
|
||||
import jakarta.enterprise.inject.spi.CDI;
|
||||
import jakarta.validation.Validator;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
public class HibernateValidatorProviderFactory implements JakartaValidatorProviderFactory {
|
||||
public static final String PROVIDER_ID = "default";
|
||||
private static HibernateValidatorProvider SINGLETON;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JakartaValidatorProvider create(KeycloakSession session) {
|
||||
if (SINGLETON == null) {
|
||||
SINGLETON = new HibernateValidatorProvider(CDI.current().select(Validator.class).get());
|
||||
}
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.keycloak.validation.jakarta.HibernateValidatorProviderFactory
|
||||
@ -17,14 +17,17 @@
|
||||
|
||||
package org.keycloak.tests.admin;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
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.entity.StringEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.api.client.ClientApi;
|
||||
@ -32,31 +35,38 @@ import org.keycloak.representations.admin.v2.ClientRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectHttpClient;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@KeycloakIntegrationTest()
|
||||
public class AdminV2Test {
|
||||
|
||||
private static final String HOSTNAME_LOCAL_ADMIN = "http://localhost:8080/admin/api/v2";
|
||||
private static ObjectMapper mapper;
|
||||
|
||||
@InjectHttpClient
|
||||
private HttpClient client;
|
||||
|
||||
@BeforeAll
|
||||
public static void setupMapper() {
|
||||
mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetClient() throws Exception {
|
||||
public void getClient() throws Exception {
|
||||
HttpGet request = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
HttpResponse response = client.execute(request);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ClientRepresentation client = mapper.createParser(response.getEntity().getContent()).readValueAs(ClientRepresentation.class);
|
||||
assertEquals("account", client.getClientId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonPatchClient() throws Exception {
|
||||
public void jsonPatchClient() throws Exception {
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
request.setEntity(new StringEntity("not json"));
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_PATCH_JSON);
|
||||
@ -72,22 +82,19 @@ public class AdminV2Test {
|
||||
response = client.execute(request);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ClientRepresentation client = mapper.createParser(response.getEntity().getContent()).readValueAs(ClientRepresentation.class);
|
||||
assertEquals("I'm a description", client.getDescription());
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
public void testJsonMergePatchClient() throws Exception {
|
||||
public void jsonMergePatchClient() throws Exception {
|
||||
HttpPatch request = new HttpPatch(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/account");
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, ClientApi.CONENT_TYPE_MERGE_PATCH);
|
||||
|
||||
ClientRepresentation patch = new ClientRepresentation();
|
||||
patch.setDescription("I'm also a description");
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
request.setEntity(new StringEntity(mapper.writeValueAsString(patch)));
|
||||
|
||||
HttpResponse response = client.execute(request);
|
||||
@ -97,4 +104,22 @@ public class AdminV2Test {
|
||||
assertEquals("I'm also a description", client.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientRepresentationValidation() throws Exception {
|
||||
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");
|
||||
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
request.setEntity(new StringEntity("""
|
||||
{
|
||||
"displayName": "something",
|
||||
"appUrl": "notUrl"
|
||||
}
|
||||
"""));
|
||||
|
||||
var response = client.execute(request);
|
||||
assertThat(response, notNullValue());
|
||||
System.err.println(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(400));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user