Make sure authz endpoints are documented in openapi spec
Closes: #25259 Signed-off-by: Sophie Tauchert <sophie@999eagle.moe>
This commit is contained in:
parent
80aa1d1367
commit
1d56e0371e
6 changed files with 85 additions and 40 deletions
|
@ -21,16 +21,19 @@ package org.keycloak.authorization.admin;
|
|||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.Path;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class AuthorizationService {
|
||||
|
||||
private final AdminPermissionEvaluator auth;
|
||||
|
@ -48,7 +51,7 @@ public class AuthorizationService {
|
|||
}
|
||||
|
||||
@Path("/resource-server")
|
||||
public Object resourceServer() {
|
||||
public ResourceServerService resourceServer() {
|
||||
if (resourceServer == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
|
|
@ -19,17 +19,20 @@ package org.keycloak.authorization.admin;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class PermissionService extends PolicyService {
|
||||
|
||||
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import jakarta.ws.rs.core.MediaType;
|
|||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -56,13 +57,15 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati
|
|||
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class PolicyService {
|
||||
|
||||
protected final ResourceServer resourceServer;
|
||||
|
|
|
@ -31,6 +31,8 @@ import jakarta.ws.rs.core.MediaType;
|
|||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
|
@ -46,6 +48,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
|
@ -54,6 +57,7 @@ import java.util.Collections;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class ResourceServerService {
|
||||
|
||||
private final AuthorizationProvider authorization;
|
||||
|
@ -91,8 +95,9 @@ public class ResourceServerService {
|
|||
}
|
||||
|
||||
@PUT
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@APIResponse(responseCode = "204", description = "No Content")
|
||||
public Response update(ResourceServerRepresentation server) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
|
||||
|
@ -111,15 +116,15 @@ public class ResourceServerService {
|
|||
}
|
||||
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response findById() {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ResourceServerRepresentation findById() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
return Response.ok(toRepresentation(this.resourceServer, this.client)).build();
|
||||
return toRepresentation(this.resourceServer, this.client);
|
||||
}
|
||||
|
||||
@Path("/settings")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response exportSettings() {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
return Response.ok(ModelToRepresentation.toResourceServerRepresentation(session, client)).build();
|
||||
|
@ -128,6 +133,7 @@ public class ResourceServerService {
|
|||
@Path("/import")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@APIResponse(responseCode = "204", description = "No Content")
|
||||
public Response importSettings(ResourceServerRepresentation rep) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
|
||||
|
@ -156,7 +162,7 @@ public class ResourceServerService {
|
|||
}
|
||||
|
||||
@Path("/permission")
|
||||
public Object getPermissionTypeResource() {
|
||||
public PermissionService getPermissionTypeResource() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
return new PermissionService(this.resourceServer, this.authorization, this.auth, adminEvent);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import jakarta.ws.rs.QueryParam;
|
|||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
|
@ -67,12 +68,14 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio
|
|||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class ResourceSetService {
|
||||
|
||||
private final AuthorizationProvider authorization;
|
||||
|
@ -131,11 +134,11 @@ public class ResourceSetService {
|
|||
return toRepresentation(toModel(resource, this.resourceServer, authorization), resourceServer, authorization);
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{resource-id}")
|
||||
@PUT
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response update(@PathParam("id") String id, ResourceRepresentation resource) {
|
||||
public Response update(@PathParam("resource-id") String id, ResourceRepresentation resource) {
|
||||
requireManage();
|
||||
resource.setId(id);
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
|
@ -153,9 +156,9 @@ public class ResourceSetService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{resource-id}")
|
||||
@DELETE
|
||||
public Response delete(@PathParam("id") String id) {
|
||||
public Response delete(@PathParam("resource-id") String id) {
|
||||
requireManage();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource resource = storeFactory.getResourceStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
@ -174,11 +177,11 @@ public class ResourceSetService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{resource-id}")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response findById(@PathParam("id") String id) {
|
||||
public Response findById(@PathParam("resource-id") String id) {
|
||||
return findById(id, resource -> toRepresentation(resource, resourceServer, authorization, true));
|
||||
}
|
||||
|
||||
|
@ -194,11 +197,11 @@ public class ResourceSetService {
|
|||
return Response.ok(toRepresentation.apply(model)).build();
|
||||
}
|
||||
|
||||
@Path("{id}/scopes")
|
||||
@Path("{resource-id}/scopes")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response getScopes(@PathParam("id") String id) {
|
||||
public Response getScopes(@PathParam("resource-id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
@ -237,11 +240,11 @@ public class ResourceSetService {
|
|||
return Response.ok(scopes).build();
|
||||
}
|
||||
|
||||
@Path("{id}/permissions")
|
||||
@Path("{resource-id}/permissions")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response getPermissions(@PathParam("id") String id) {
|
||||
public Response getPermissions(@PathParam("resource-id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
|
@ -295,11 +298,11 @@ public class ResourceSetService {
|
|||
return Response.ok(representation).build();
|
||||
}
|
||||
|
||||
@Path("{id}/attributes")
|
||||
@Path("{resource-id}/attributes")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response getAttributes(@PathParam("id") String id) {
|
||||
public Response getAttributes(@PathParam("resource-id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
*/
|
||||
package org.keycloak.authorization.admin;
|
||||
|
||||
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -33,8 +39,9 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
|
@ -54,6 +61,7 @@ import java.util.EnumMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
||||
|
@ -61,6 +69,7 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@Extension(name = KeycloakOpenAPI.Profiles.ADMIN, value = "")
|
||||
public class ScopeService {
|
||||
|
||||
private final AuthorizationProvider authorization;
|
||||
|
@ -92,11 +101,11 @@ public class ScopeService {
|
|||
return Response.status(Status.CREATED).entity(scope).build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{scope-id}")
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(@PathParam("id") String id, ScopeRepresentation scope) {
|
||||
public Response update(@PathParam("scope-id") String id, ScopeRepresentation scope) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
scope.setId(id);
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
@ -113,9 +122,9 @@ public class ScopeService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{scope-id}")
|
||||
@DELETE
|
||||
public Response delete(@PathParam("id") String id) {
|
||||
public Response delete(@PathParam("scope-id") String id) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
RealmModel realm = resourceServer.getRealm();
|
||||
|
@ -151,11 +160,18 @@ public class ScopeService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@Path("{scope-id}")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response findById(@PathParam("id") String id) {
|
||||
@APIResponses(value = {
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
content = @Content(schema = @Schema(implementation = ScopeRepresentation.class))
|
||||
),
|
||||
@APIResponse(responseCode = "404", description = "Not found")
|
||||
})
|
||||
public Response findById(@PathParam("scope-id") String id) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
||||
|
@ -166,11 +182,18 @@ public class ScopeService {
|
|||
return Response.ok(toRepresentation(model)).build();
|
||||
}
|
||||
|
||||
@Path("{id}/resources")
|
||||
@Path("{scope-id}/resources")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getResources(@PathParam("id") String id) {
|
||||
@APIResponses(value = {
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
content = @Content(schema = @Schema(implementation = ResourceRepresentation.class, type = SchemaType.ARRAY))
|
||||
),
|
||||
@APIResponse(responseCode = "404", description = "Not found")
|
||||
})
|
||||
public Response getResources(@PathParam("scope-id") String id) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
Scope model = storeFactory.getScopeStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
@ -189,11 +212,18 @@ public class ScopeService {
|
|||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("{id}/permissions")
|
||||
@Path("{scope-id}/permissions")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getPermissions(@PathParam("id") String id) {
|
||||
@APIResponses(value = {
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
content = @Content(schema = @Schema(implementation = PolicyRepresentation.class, type = SchemaType.ARRAY))
|
||||
),
|
||||
@APIResponse(responseCode = "404", description = "Not found")
|
||||
})
|
||||
public Response getPermissions(@PathParam("scope-id") String id) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
Scope model = storeFactory.getScopeStore().findById(resourceServer.getRealm(), resourceServer, id);
|
||||
|
@ -238,8 +268,8 @@ public class ScopeService {
|
|||
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response findAll(@QueryParam("scopeId") String id,
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Stream<ScopeRepresentation> findAll(@QueryParam("scopeId") String id,
|
||||
@QueryParam("name") String name,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
|
@ -255,11 +285,8 @@ public class ScopeService {
|
|||
search.put(Scope.FilterOption.NAME, new String[] {name});
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
this.authorization.getStoreFactory().getScopeStore().findByResourceServer(this.resourceServer, search, firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(scope -> toRepresentation(scope))
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
return this.authorization.getStoreFactory().getScopeStore().findByResourceServer(this.resourceServer, search, firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(scope -> toRepresentation(scope));
|
||||
}
|
||||
|
||||
private void audit(ScopeRepresentation resource, OperationType operation) {
|
||||
|
|
Loading…
Reference in a new issue