Add some missing 409 REST response codes

Closes #42269

Signed-off-by: Andrés Maldonado <maldonado@codelutin.com>
This commit is contained in:
am97 2025-09-01 14:48:47 +02:00 committed by GitHub
parent f4ec4cff1a
commit 23b9a1fa21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 88 additions and 22 deletions

View File

@ -20,6 +20,7 @@ import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
@ -242,7 +243,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Create a new authentication flow")
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response createFlow(@Parameter( description = "Authentication flow representation") AuthenticationFlowRepresentation flow) {
auth.realm().requireManageRealm();
@ -304,7 +308,10 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Update an authentication flow")
@APIResponse(responseCode = "204", description = "No Content")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public void updateFlow(@PathParam("id") String id, AuthenticationFlowRepresentation flow) {
auth.realm().requireManageRealm();
@ -393,7 +400,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Copy existing authentication flow under a new name The new name is given as 'newName' attribute of the passed JSON object")
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response copy(@Parameter(description="name of the existing authentication flow") @PathParam("flowAlias") String flowAlias, Map<String, String> data) {
auth.realm().requireManageRealm();
@ -495,7 +505,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Add new flow with new execution to existing flow")
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response addExecutionFlow(@Parameter(description = "Alias of parent authentication flow") @PathParam("flowAlias") String flowAlias, @Parameter(description = "New authentication flow / execution JSON data containing 'alias', 'type', 'provider', 'priority', and 'description' attributes") Map<String, Object> data) {
auth.realm().requireManageRealm();
@ -763,7 +776,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Update authentication executions of a Flow")
@APIResponse(responseCode = "204", description = "No Content")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public void updateExecutions(@Parameter(description = "Flow alias") @PathParam("flowAlias") String flowAlias, @Parameter(description = "AuthenticationExecutionInfoRepresentation") AuthenticationExecutionInfoRepresentation rep) {
auth.realm().requireManageRealm();
@ -1037,7 +1053,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Update execution with new configuration")
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response newExecutionConfig(@Parameter(description = "Execution id") @PathParam("executionId") String execution, @Parameter(description = "JSON with new configuration") AuthenticatorConfigRepresentation json) {
auth.realm().requireManageRealm();
@ -1557,7 +1576,10 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.AUTHENTICATION_MANAGEMENT)
@Operation( summary = "Create new authenticator configuration", deprecated = true)
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
@Deprecated
public Response createAuthenticatorConfig(@Parameter(description = "JSON describing new authenticator configuration") AuthenticatorConfigRepresentation rep) {
auth.realm().requireManageRealm();

View File

@ -20,6 +20,7 @@ import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
@ -141,6 +142,11 @@ public class ClientResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENTS)
@Operation( summary = "Update the client")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response update(final ClientRepresentation rep) {
auth.clients().requireConfigure(client);

View File

@ -20,6 +20,7 @@ import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
@ -185,7 +186,10 @@ public class ClientsResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENTS)
@Operation( summary = "Create a new client Clients client_id must be unique!")
@APIResponse(responseCode = "201", description = "Created")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response createClient(final ClientRepresentation rep) {
auth.clients().requireManage();

View File

@ -19,6 +19,8 @@ package org.keycloak.services.resources.admin;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.authorization.fgap.AdminPermissionsSchema;
@ -117,6 +119,11 @@ public class GroupResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.GROUPS)
@Operation( summary = "Update group, ignores subgroups.")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response updateGroup(GroupRepresentation rep) {
this.auth.groups().requireManage(group);
@ -137,13 +144,13 @@ public class GroupResource {
throw ErrorResponse.exists("Sibling group named '" + groupName + "' already exists.");
}
}
updateGroup(rep, group, realm, session);
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(rep).success();
return Response.noContent().build();
}
private Stream<GroupModel> siblings() {
if (group.getParentId() == null) {
return session.groups().getTopLevelGroupsStream(realm);
@ -208,6 +215,12 @@ public class GroupResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.GROUPS)
@Operation( summary = "Set or create child.", description = "This will just set the parent if it exists. Create it and set the parent if the group doesnt exist.")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response addChild(GroupRepresentation rep) {
this.auth.groups().requireManage(group);
@ -313,7 +326,7 @@ public class GroupResource {
@Parameter(description = "Only return basic information (only guaranteed to return id, username, created, first and last name, email, enabled state, email verification state, federation link, and access. Note that it means that namely user attributes, required actions, and not before are not returned.)")
@QueryParam("briefRepresentation") Boolean briefRepresentation) {
this.auth.groups().requireViewMembers(group);
firstResult = firstResult != null ? firstResult : 0;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
@ -381,4 +394,3 @@ public class GroupResource {
}
}
}

View File

@ -185,8 +185,10 @@ public class GroupsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "204", description = "No Content")
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
@Tag(name = KeycloakOpenAPI.Admin.Tags.GROUPS)
@Operation( summary = "create or add a top level realm groupSet or create child.",

View File

@ -21,6 +21,8 @@ import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
@ -157,6 +159,11 @@ public class IdentityProviderResource {
@NoCache
@Tag(name = KeycloakOpenAPI.Admin.Tags.IDENTITY_PROVIDERS)
@Operation( summary = "Update the identity provider")
@APIResponses(value = {
@APIResponse(responseCode = "204", description = "No Content"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response update(IdentityProviderRepresentation providerRep) {
this.auth.realm().requireManageIdentityProviders();

View File

@ -20,6 +20,8 @@ package org.keycloak.services.resources.admin;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.broker.provider.IdentityProvider;
@ -218,6 +220,11 @@ public class IdentityProvidersResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.IDENTITY_PROVIDERS)
@Operation( summary = "Create a new identity provider")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response create(@Parameter(description = "JSON body") IdentityProviderRepresentation representation) {
this.auth.realm().requireManageIdentityProviders();

View File

@ -22,6 +22,7 @@ import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
@ -127,6 +128,10 @@ public class ProtocolMappersResource {
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = KeycloakOpenAPI.Admin.Tags.PROTOCOL_MAPPERS)
@Operation(summary = "Create a mapper")
@APIResponses(value = {
@APIResponse(responseCode = "201", description = "Created"),
@APIResponse(responseCode = "409", description = "Conflict")
})
public Response createMapper(ProtocolMapperRepresentation rep) {
managePermission.require();

View File

@ -151,6 +151,7 @@ public class RoleContainerResource extends RoleResource {
@APIResponse(responseCode = "400", description = "Bad Request"),
@APIResponse(responseCode = "403", description = "Forbidden"),
@APIResponse(responseCode = "404", description = "Not Found"),
@APIResponse(responseCode = "409", description = "Conflict"),
@APIResponse(responseCode = "500", description = "Internal Server Error")
})
public Response createRole(final RoleRepresentation rep) {
@ -566,11 +567,11 @@ public class RoleContainerResource extends RoleResource {
@Parameter(description = "Boolean which defines whether brief representations are returned (default: false)") @QueryParam("briefRepresentation") Boolean briefRepresentation,
@Parameter(description = "first result to return. Ignored if negative or {@code null}.") @QueryParam("first") Integer firstResult,
@Parameter(description = "maximum number of results to return. Ignored if negative or {@code null}.") @QueryParam("max") Integer maxResults) {
auth.roles().requireView(roleContainer);
firstResult = firstResult != null ? firstResult : 0;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@ -582,7 +583,7 @@ public class RoleContainerResource extends RoleResource {
return session.users().getRoleMembersStream(realm, role, firstResult, maxResults)
.map(toRepresentation);
}
/**
* Returns a stream of groups that have the specified role name
*
@ -608,17 +609,17 @@ public class RoleContainerResource extends RoleResource {
@Parameter(description = "first result to return. Ignored if negative or {@code null}.") @QueryParam("first") Integer firstResult,
@Parameter(description = "maximum number of results to return. Ignored if negative or {@code null}.") @QueryParam("max") Integer maxResults,
@Parameter(description = "if false, return a full representation of the {@code GroupRepresentation} objects.") @QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
auth.roles().requireView(roleContainer);
firstResult = firstResult != null ? firstResult : 0;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
}
return session.groups().getGroupsByRoleStream(realm, role, firstResult, maxResults)
.map(g -> ModelToRepresentation.toRepresentation(g, !briefRepresentation));
}
}
}