fix: removing unknown field validation parameter (#44173)

closes: #43728

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2025-11-12 12:27:05 -05:00 committed by GitHub
parent a7c02076a1
commit 26bdee3052
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 14 additions and 33 deletions

View File

@ -1,7 +0,0 @@
package org.keycloak.admin.api;
public enum FieldValidation {
Ignore,
Strict,
Warn
}

View File

@ -6,10 +6,8 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import org.keycloak.admin.api.FieldValidation;
import org.keycloak.representations.admin.v2.ClientRepresentation;
import com.fasterxml.jackson.databind.JsonNode;
@ -26,12 +24,11 @@ public interface ClientApi {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
ClientRepresentation createOrUpdateClient(@Valid ClientRepresentation client,
@QueryParam("fieldValidation") FieldValidation fieldValidation);
ClientRepresentation createOrUpdateClient(@Valid ClientRepresentation client);
@PATCH
@Consumes({MediaType.APPLICATION_JSON_PATCH_JSON, CONTENT_TYPE_MERGE_PATCH})
@Produces(MediaType.APPLICATION_JSON)
ClientRepresentation patchClient(JsonNode patch, @QueryParam("fieldValidation") FieldValidation fieldValidation);
ClientRepresentation patchClient(JsonNode patch);
}

View File

@ -2,14 +2,13 @@ package org.keycloak.admin.api.client;
import java.util.stream.Stream;
import jakarta.validation.Valid;
import jakarta.ws.rs.QueryParam;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.keycloak.admin.api.FieldValidation;
import org.keycloak.representations.admin.v2.ClientRepresentation;
import org.keycloak.services.resources.KeycloakOpenAPI;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
@ -17,7 +16,6 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.keycloak.services.resources.KeycloakOpenAPI;
@Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENTS_V2)
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
@ -33,8 +31,7 @@ public interface ClientsApi {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Create a new client", description = "Creates a new client in the realm")
ClientRepresentation createClient(@Valid ClientRepresentation client,
@QueryParam("fieldValidation") FieldValidation fieldValidation);
ClientRepresentation createClient(@Valid ClientRepresentation client);
@Path("{id}")
ClientApi client(@PathParam("id") String id);

View File

@ -3,7 +3,6 @@ package org.keycloak.admin.api.client;
import java.io.IOException;
import java.util.Objects;
import org.keycloak.admin.api.FieldValidation;
import org.keycloak.http.HttpResponse;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@ -30,6 +29,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
public class DefaultClientApi implements ClientApi {
private final KeycloakSession session;
private final RealmModel realm;
private final ClientModel client;
@ -58,12 +58,12 @@ public class DefaultClientApi implements ClientApi {
}
@Override
public ClientRepresentation createOrUpdateClient(ClientRepresentation client, FieldValidation fieldValidation) {
public ClientRepresentation createOrUpdateClient(ClientRepresentation client) {
try {
if (!Objects.equals(clientId, client.getClientId())) {
throw new WebApplicationException("cliendId in payload does not match the clientId in the path", Response.Status.BAD_REQUEST);
}
validateUnknownFields(fieldValidation, client, response);
validateUnknownFields(client, response);
var result = clientService.createOrUpdate(clientsResource, clientResource, realm, client, true);
if (result.created()) {
response.setStatus(Response.Status.CREATED.getStatusCode());
@ -75,7 +75,7 @@ public class DefaultClientApi implements ClientApi {
}
@Override
public ClientRepresentation patchClient(JsonNode patch, FieldValidation fieldValidation) {
public ClientRepresentation patchClient(JsonNode patch) {
// patches don't yet allow for creating
ClientRepresentation client = getClient();
try {
@ -93,7 +93,7 @@ public class DefaultClientApi implements ClientApi {
updated = objectReader.readValue(patch);
}
validateUnknownFields(fieldValidation, updated, response);
validateUnknownFields(updated, response);
return clientService.createOrUpdate(clientsResource, clientResource, realm, updated, true).representation();
} catch (JsonPatchException e) {
// TODO: kubernetes uses 422 instead
@ -105,14 +105,9 @@ public class DefaultClientApi implements ClientApi {
}
}
static void validateUnknownFields(FieldValidation fieldValidation, ClientRepresentation rep, HttpResponse response) {
static void validateUnknownFields(ClientRepresentation rep, HttpResponse response) {
if (!rep.getAdditionalFields().isEmpty()) {
if (fieldValidation == null || fieldValidation == FieldValidation.Strict) {
// validation failed
throw new WebApplicationException("Payload contains unknown fields: " + rep.getAdditionalFields().keySet(), Response.Status.BAD_REQUEST);
} else if (fieldValidation == FieldValidation.Warn) {
response.addHeader("WARNING", "Payload contains unknown fields: " + rep.getAdditionalFields().keySet());
}
throw new WebApplicationException("Payload contains unknown fields: " + rep.getAdditionalFields().keySet(), Response.Status.BAD_REQUEST);
}
}

View File

@ -5,7 +5,6 @@ import java.util.Optional;
import java.util.stream.Stream;
import jakarta.validation.Valid;
import org.keycloak.admin.api.FieldValidation;
import org.keycloak.http.HttpResponse;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -46,9 +45,9 @@ public class DefaultClientsApi implements ClientsApi {
}
@Override
public ClientRepresentation createClient(@Valid ClientRepresentation client, FieldValidation fieldValidation) {
public ClientRepresentation createClient(@Valid ClientRepresentation client) {
try {
DefaultClientApi.validateUnknownFields(fieldValidation, client, response);
DefaultClientApi.validateUnknownFields(client, response);
validator.validate(client, CreateClientDefault.class);
response.setStatus(Response.Status.CREATED.getStatusCode());
return clientService.createOrUpdate(clientsResource, null, realm, client, false).representation();