diff --git a/keycloak-theme/pom.xml b/keycloak-theme/pom.xml
index 3236615a46..d64f6d92b6 100644
--- a/keycloak-theme/pom.xml
+++ b/keycloak-theme/pom.xml
@@ -37,8 +37,6 @@
v18.7.0
19.0.1
- 1.7.10
- 1.5.2.Final
@@ -93,27 +91,11 @@
keycloak-services
${keycloak.version}
-
- org.jetbrains.kotlin
- kotlin-stdlib-jdk8
- ${kotlin.version}
-
-
- org.mapstruct
- mapstruct
- ${mapstruct.version}
-
org.eclipse.microprofile.openapi
microprofile-openapi-api
3.0
-
- org.jetbrains.kotlin
- kotlin-test
- ${kotlin.version}
- test
-
org.testng
testng
@@ -191,78 +173,10 @@
-
- org.jetbrains.kotlin
- kotlin-maven-plugin
- ${kotlin.version}
-
-
- compile
- compile
-
- compile
-
-
-
- test-compile
- test-compile
-
- test-compile
-
-
-
- kapt
-
- kapt
-
-
-
- src/main/java
-
-
-
- org.mapstruct
- mapstruct-processor
- ${mapstruct.version}
-
-
-
-
-
-
- 1.8
-
-
org.apache.maven.plugins
maven-compiler-plugin
3.10.1
-
-
-
- default-compile
- none
-
-
-
- default-testCompile
- none
-
-
- java-compile
- compile
-
- compile
-
-
-
- java-test-compile
- test-compile
-
- testCompile
-
-
-
8
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.java
new file mode 100644
index 0000000000..2531384411
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.java
@@ -0,0 +1,35 @@
+package org.keycloak.admin.ui.rest;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.resources.admin.AdminEventBuilder;
+import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
+import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+public final class AvailableRoleMappingProvider implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
+ public AdminRealmResourceProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ public void init(Config.Scope config) {
+
+ }
+
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ public void close() {
+ }
+
+ public String getId() {
+ return "admin-ui-available-roles";
+ }
+
+ public Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
+ return new AvailableRoleMappingResource(realm, auth);
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.kt
deleted file mode 100644
index a6577ed3f2..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingProvider.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.keycloak.admin.ui.rest
-
-import org.keycloak.Config
-import org.keycloak.models.KeycloakSession
-import org.keycloak.models.KeycloakSessionFactory
-import org.keycloak.models.RealmModel
-import org.keycloak.services.resources.admin.AdminEventBuilder
-import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider
-import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory
-import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
-
-class AvailableRoleMappingProvider : AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
- override fun create(session: KeycloakSession): AdminRealmResourceProvider {
- return this
- }
-
- override fun init(config: Config.Scope) {}
- override fun postInit(factory: KeycloakSessionFactory) {}
- override fun close() {}
- override fun getId(): String {
- return "admin-ui-available-roles"
- }
-
- override fun getResource(
- session: KeycloakSession,
- realm: RealmModel,
- auth: AdminPermissionEvaluator,
- adminEvent: AdminEventBuilder
- ): Any {
- return AvailableRoleMappingResource(realm, auth)
- }
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java
new file mode 100644
index 0000000000..20c7b17300
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java
@@ -0,0 +1,184 @@
+package org.keycloak.admin.ui.rest;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
+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.keycloak.admin.ui.rest.model.ClientRole;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+public class AvailableRoleMappingResource extends RoleMappingResource {
+ @Context
+ private KeycloakSession session;
+ private final RealmModel realm;
+ private final AdminPermissionEvaluator auth;
+
+ public AvailableRoleMappingResource(RealmModel realm, AdminPermissionEvaluator auth) {
+ super(realm, auth);
+ this.realm = realm;
+ this.auth = auth;
+ }
+
+ @GET
+ @Path("/clientScopes/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all composite client roles for this client scope",
+ description = "This endpoint returns all the client role mapping for a specific client scope"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeClientScopeRoleMappings(@PathParam("id") String id, @QueryParam("first")
+ @DefaultValue("0") long first, @QueryParam("max") @DefaultValue("10") long max, @QueryParam("search") @DefaultValue("") String search) {
+ ClientScopeModel scopeModel = this.realm.getClientScopeById(id);
+ if (scopeModel == null) {
+ throw new NotFoundException("Could not find client scope");
+ } else {
+ this.auth.clients().requireView(scopeModel);
+ return this.mapping(((Predicate) scopeModel::hasDirectScope).negate(), first, max, search);
+ }
+ }
+
+ @GET
+ @Path("/clients/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all composite client roles for this client",
+ description = "This endpoint returns all the client role mapping for a specific client"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeClientRoleMappings(@PathParam("id") String id, @QueryParam("first")
+ @DefaultValue("0") long first, @QueryParam("max") @DefaultValue("10") long max, @QueryParam("search") @DefaultValue("") String search) {
+ ClientModel client = this.realm.getClientById(id);
+ if (client == null) {
+ throw new NotFoundException("Could not find client");
+ } else {
+ this.auth.clients().requireView(client);
+ return this.mapping(((Predicate) client::hasDirectScope).negate(), first, max, search);
+ }
+ }
+
+ @GET
+ @Path("/groups/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all composite client roles for this group",
+ description = "This endpoint returns all the client role mapping for a specific group"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeGroupRoleMappings(@PathParam("id") String id, @QueryParam("first")
+ @DefaultValue("0") long first, @QueryParam("max") @DefaultValue("10") long max, @QueryParam("search") @DefaultValue("") String search) {
+ GroupModel group = this.realm.getGroupById(id);
+ if (group == null) {
+ throw new NotFoundException("Could not find group");
+ } else {
+ this.auth.groups().requireView(group);
+ return this.mapping(((Predicate) group::hasDirectRole).negate(), first, max, search);
+ }
+ }
+
+ @GET
+ @Path("/users/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all composite client roles for this user",
+ description = "This endpoint returns all the client role mapping for a specific user"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeUserRoleMappings(@PathParam("id") String id, @QueryParam("first") @DefaultValue("0") long first,
+ @QueryParam("max") @DefaultValue("10") long max, @QueryParam("search") @DefaultValue("") String search) {
+ UserProvider users = Objects.requireNonNull(session).users();
+ UserModel userModel = users.getUserById(this.realm, id);
+ if (userModel == null) {
+ if (auth.users().canQuery()) throw new NotFoundException("User not found");
+ else throw new ForbiddenException();
+ }
+
+ this.auth.users().requireView(userModel);
+ return this.mapping(((Predicate) userModel::hasDirectRole).negate(), first, max, search);
+ }
+
+ @GET
+ @Path("/roles/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all composite client roles",
+ description = "This endpoint returns all the client role"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeRoleMappings(@QueryParam("first") @DefaultValue("0") long first,
+ @QueryParam("max") @DefaultValue("10") long max, @QueryParam("search") @DefaultValue("") String search) {
+ return this.mapping(o -> true, first, max, search);
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.kt
deleted file mode 100644
index b61d897f6e..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-package org.keycloak.admin.ui.rest
-
-import org.eclipse.microprofile.openapi.annotations.Operation
-import org.eclipse.microprofile.openapi.annotations.enums.SchemaType
-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.keycloak.admin.ui.rest.model.ClientRole
-import org.keycloak.admin.ui.rest.model.RoleMapper
-import org.keycloak.models.KeycloakSession
-import org.keycloak.models.RealmModel
-import org.keycloak.models.RoleModel
-import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
-import org.mapstruct.factory.Mappers
-import java.util.*
-import java.util.function.Predicate
-import java.util.stream.Collectors
-import javax.ws.rs.*
-import javax.ws.rs.core.Context
-import javax.ws.rs.core.MediaType
-
-@Path("/")
-open class AvailableRoleMappingResource(
- private var realm: RealmModel,
- private var auth: AdminPermissionEvaluator,
-) : RoleMappingResource(realm, auth) {
- @Context
- var session: KeycloakSession? = null
-
- @GET
- @Path("/clientScopes/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all composite client roles for this client scope",
- description = "This endpoint returns all the client role mapping for a specific client scope"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeClientScopeRoleMappings(
- @PathParam("id") id: String,
- @QueryParam("first") @DefaultValue("0") first: Long,
- @QueryParam("max") @DefaultValue("10") max: Long,
- @QueryParam("search") @DefaultValue("") search: String
- ): List {
- val scopeContainer = realm.getClientScopeById(id) ?: throw NotFoundException("Could not find client scope")
- auth.clients().requireView(scopeContainer)
-
- return mapping(Predicate { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
- }
-
- @GET
- @Path("/clients/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all composite client roles for this client",
- description = "This endpoint returns all the client role mapping for a specific client"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeClientRoleMappings(
- @PathParam("id") id: String,
- @QueryParam("first") @DefaultValue("0") first: Long,
- @QueryParam("max") @DefaultValue("10") max: Long,
- @QueryParam("search") @DefaultValue("") search: String
- ): List {
- val scopeContainer = realm.getClientById(id) ?: throw NotFoundException("Could not find client")
- auth.clients().requireView(scopeContainer)
-
- return mapping(Predicate { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
- }
-
- @GET
- @Path("/groups/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all composite client roles for this group",
- description = "This endpoint returns all the client role mapping for a specific group"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeGroupRoleMappings(
- @PathParam("id") id: String,
- @QueryParam("first") @DefaultValue("0") first: Long,
- @QueryParam("max") @DefaultValue("10") max: Long,
- @QueryParam("search") @DefaultValue("") search: String
- ): List {
- val scopeContainer = realm.getGroupById(id) ?: throw NotFoundException("Could not find group")
- auth.groups().requireView(scopeContainer)
-
- return mapping(Predicate { r -> scopeContainer.hasDirectRole(r) }.negate(), first, max, search)
-
- }
-
- @GET
- @Path("/users/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all composite client roles for this user",
- description = "This endpoint returns all the client role mapping for a specific user"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeUserRoleMappings(
- @PathParam("id") id: String,
- @QueryParam("first") @DefaultValue("0") first: Long,
- @QueryParam("max") @DefaultValue("10") max: Long,
- @QueryParam("search") @DefaultValue("") search: String
- ): List {
- val user = session?.users()?.getUserById(realm, id)
- ?: if (auth.users().canQuery()) throw NotFoundException("User not found") else throw ForbiddenException()
- auth.users().requireView(user)
-
- return mapping(Predicate { r -> user.hasDirectRole(r) }.negate(), first, max, search)
-
- }
-
- @GET
- @Path("/roles/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all composite client roles",
- description = "This endpoint returns all the client role"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeRoleMappings(
- @QueryParam("first") @DefaultValue("0") first: Long,
- @QueryParam("max") @DefaultValue("10") max: Long,
- @QueryParam("search") @DefaultValue("") search: String
- ): List {
- return mapping({ true }, first, max, search)
- }
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.java
new file mode 100644
index 0000000000..38c4d3a448
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.java
@@ -0,0 +1,35 @@
+package org.keycloak.admin.ui.rest;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.resources.admin.AdminEventBuilder;
+import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
+import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+public final class EffectiveRoleMappingProvider implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
+ public AdminRealmResourceProvider create(KeycloakSession session) {
+ return this;
+ }
+
+ public void init(Config.Scope config) {
+
+ }
+
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ public void close() {
+ }
+
+ public String getId() {
+ return "admin-ui-effective-roles";
+ }
+
+ public Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
+ return new EffectiveRoleMappingResource(realm, auth);
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.kt
deleted file mode 100644
index ac3a3df1c0..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingProvider.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.keycloak.admin.ui.rest
-
-import org.keycloak.Config
-import org.keycloak.models.KeycloakSession
-import org.keycloak.models.KeycloakSessionFactory
-import org.keycloak.models.RealmModel
-import org.keycloak.services.resources.admin.AdminEventBuilder
-import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider
-import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory
-import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
-
-class EffectiveRoleMappingProvider : AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
- override fun create(session: KeycloakSession): AdminRealmResourceProvider {
- return this
- }
-
- override fun init(config: Config.Scope) {}
- override fun postInit(factory: KeycloakSessionFactory) {}
- override fun close() {}
- override fun getId(): String {
- return "admin-ui-effective-roles"
- }
-
- override fun getResource(
- session: KeycloakSession,
- realm: RealmModel,
- auth: AdminPermissionEvaluator,
- adminEvent: AdminEventBuilder
- ): Any {
- return EffectiveRoleMappingResource(realm, auth)
- }
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.java
new file mode 100644
index 0000000000..72d52e6700
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.java
@@ -0,0 +1,171 @@
+package org.keycloak.admin.ui.rest;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
+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.keycloak.admin.ui.rest.model.ClientRole;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+public class EffectiveRoleMappingResource extends RoleMappingResource {
+ @Context
+ private KeycloakSession session;
+ private RealmModel realm;
+ private AdminPermissionEvaluator auth;
+
+ public EffectiveRoleMappingResource(RealmModel realm, AdminPermissionEvaluator auth) {
+ super(realm, auth);
+ this.realm = realm;
+ this.auth = auth;
+ }
+
+ @GET
+ @Path("/clientScopes/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all effective roles for this client scope",
+ description = "This endpoint returns all the client role mapping for a specific client scope"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeClientScopeRoleMappings(@PathParam("id") String id) {
+ ClientScopeModel clientScope = this.realm.getClientScopeById(id);
+ if (clientScope == null) {
+ throw new NotFoundException("Could not find client scope");
+ }
+
+ this.auth.clients().requireView(clientScope);
+ return this.mapping(clientScope::hasScope).collect(Collectors.toList());
+ }
+
+ @GET
+ @Path("/clients/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all effective roles for this client",
+ description = "This endpoint returns all the client role mapping for a specific client"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeClientsRoleMappings(@PathParam("id") String id) {
+ ClientModel client = this.realm.getClientById(id);
+ if (client == null) {
+ throw new NotFoundException("Could not find client");
+ }
+ auth.clients().requireView(client);
+ return mapping(client::hasScope).collect(Collectors.toList());
+ }
+
+ @GET
+ @Path("/groups/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all effective roles for this group",
+ description = "This endpoint returns all the client role mapping for a specific group"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeGroupsRoleMappings(@PathParam("id") String id) {
+ GroupModel group = this.realm.getGroupById(id);
+ if (group == null) {
+ throw new NotFoundException("Could not find group");
+ }
+
+ return mapping(group::hasDirectRole).collect(Collectors.toList());
+ }
+
+ @GET
+ @Path("/users/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all effective roles for this users",
+ description = "This endpoint returns all the client role mapping for a specific users"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeUsersRoleMappings(@PathParam("id") String id) {
+ UserModel user = session.users().getUserById(this.realm, id);
+ if (user == null) {
+ if (auth.users().canQuery()) throw new NotFoundException("User not found");
+ else throw new ForbiddenException();
+ }
+
+ return mapping(user::hasDirectRole).collect(Collectors.toList());
+ }
+
+ @GET
+ @Path("/roles/{id}")
+ @Consumes({"application/json"})
+ @Produces({"application/json"})
+ @Operation(
+ summary = "List all effective roles for this realm role",
+ description = "This endpoint returns all the client role mapping for a specific realm role"
+ )
+ @APIResponse(
+ responseCode = "200",
+ description = "",
+ content = {@Content(
+ schema = @Schema(
+ implementation = ClientRole.class,
+ type = SchemaType.ARRAY
+ )
+ )}
+ )
+ public final List listCompositeRealmRoleMappings() {
+ return mapping (o -> true ).collect(Collectors.toList());
+ }
+
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.kt
deleted file mode 100644
index 54b29a711d..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/EffectiveRoleMappingResource.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-package org.keycloak.admin.ui.rest
-
-import org.eclipse.microprofile.openapi.annotations.Operation
-import org.eclipse.microprofile.openapi.annotations.enums.SchemaType
-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.keycloak.admin.ui.rest.model.ClientRole
-import org.keycloak.models.KeycloakSession
-import org.keycloak.models.RealmModel
-import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
-import java.util.stream.Collectors
-import javax.ws.rs.*
-import javax.ws.rs.core.Context
-import javax.ws.rs.core.MediaType
-
-@Path("/")
-open class EffectiveRoleMappingResource(
- private var realm: RealmModel,
- private var auth: AdminPermissionEvaluator,
-) : RoleMappingResource(realm, auth) {
- @Context
- var session: KeycloakSession? = null
-
- @GET
- @Path("/clientScopes/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all effective roles for this client scope",
- description = "This endpoint returns all the client role mapping for a specific client scope"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeClientScopeRoleMappings(
- @PathParam("id") id: String,
- ): List {
- val scopeContainer = realm.getClientScopeById(id) ?: throw NotFoundException("Could not find client scope")
- auth.clients().requireView(scopeContainer)
-
- return mapping(scopeContainer::hasScope).collect(Collectors.toList())
- }
-
- @GET
- @Path("/clients/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all effective roles for this client",
- description = "This endpoint returns all the client role mapping for a specific client"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeClientsRoleMappings(
- @PathParam("id") id: String,
- ): List {
- val scopeContainer = realm.getClientById(id) ?: throw NotFoundException("Could not find client")
- auth.clients().requireView(scopeContainer)
-
- return mapping(scopeContainer::hasScope).collect(Collectors.toList())
- }
-
- @GET
- @Path("/groups/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all effective roles for this group",
- description = "This endpoint returns all the client role mapping for a specific group"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeGroupsRoleMappings(
- @PathParam("id") id: String,
- ): List {
- val scopeContainer = realm.getGroupById(id) ?: throw NotFoundException("Could not find group")
-
- return mapping(scopeContainer::hasDirectRole).collect(Collectors.toList())
- }
-
- @GET
- @Path("/users/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all effective roles for this users",
- description = "This endpoint returns all the client role mapping for a specific users"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeUsersRoleMappings(
- @PathParam("id") id: String,
- ): List {
- val user = session?.users()?.getUserById(realm, id)
- ?: if (auth.users().canQuery()) throw NotFoundException("User not found") else throw ForbiddenException()
- auth.users().requireView(user)
-
- return mapping(user::hasDirectRole).collect(Collectors.toList())
- }
-
- @GET
- @Path("/roles/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- @Operation(
- summary = "List all effective roles for this realm role",
- description = "This endpoint returns all the client role mapping for a specific realm role"
- )
- @APIResponse(
- responseCode = "200",
- description = "",
- content = [Content(
- schema = Schema(
- type = SchemaType.ARRAY,
- implementation = ClientRole::class
- )
- )]
- )
- fun listCompositeRealmRoleMappings(
- ): List {
- return mapping { true }.collect(Collectors.toList())
- }
-
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.java
new file mode 100644
index 0000000000..faaaab30f9
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.java
@@ -0,0 +1,35 @@
+package org.keycloak.admin.ui.rest;
+
+import static org.keycloak.admin.ui.rest.model.RoleMapper.convertToRepresentation;
+
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.keycloak.admin.ui.rest.model.ClientRole;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+public abstract class RoleMappingResource {
+ private RealmModel realm;
+ private AdminPermissionEvaluator auth;
+
+ public final Stream mapping(Predicate predicate) {
+ return realm.getClientsStream().flatMap(RoleContainerModel::getRolesStream).filter(predicate)
+ .filter(auth.roles()::canMapClientScope).map(roleModel -> convertToRepresentation(roleModel, realm.getClientsStream()));
+ }
+
+ public final List mapping(Predicate predicate, long first, long max, final String search) {
+
+ return mapping(predicate).filter(clientRole -> clientRole.getClient().contains(search) || clientRole.getRole().contains(search))
+
+ .skip("".equals(search) ? first : 0).limit(max).collect(Collectors.toList());
+ }
+
+ public RoleMappingResource(RealmModel realm, AdminPermissionEvaluator auth) {
+ this.realm = realm;
+ this.auth = auth;
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.kt
deleted file mode 100644
index c31bdadea8..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/RoleMappingResource.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.keycloak.admin.ui.rest
-
-import org.keycloak.admin.ui.rest.model.ClientRole
-import org.keycloak.admin.ui.rest.model.RoleMapper
-import org.keycloak.models.RealmModel
-import org.keycloak.models.RoleModel
-import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
-import org.mapstruct.factory.Mappers
-import java.util.*
-import java.util.function.Predicate
-import java.util.stream.Collectors
-import java.util.stream.Stream
-
-abstract class RoleMappingResource(
- private var realm: RealmModel,
- private var auth: AdminPermissionEvaluator,
-) {
-
- fun mapping(
- predicate: Predicate,
- ): Stream {
- val mapper = Mappers.getMapper(RoleMapper::class.java)
- return realm.clientsStream
- .flatMap { c -> c.rolesStream }
- .filter(predicate)
- .filter(auth.roles()::canMapClientScope)
-
- .map { r -> mapper.convertToRepresentation(r, realm.clientsStream) }
- }
-
- fun mapping(
- predicate: Predicate,
- first: Long,
- max: Long,
- search: String
- ): List {
- return mapping(predicate)
- .filter { r -> r.client!!.contains(search, true) || r.role.contains(search, true) }
-
- .skip(if (search.isBlank()) first else 0)
- .limit(max)
- .collect(Collectors.toList()) ?: Collections.emptyList()
- }
-
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.java
new file mode 100644
index 0000000000..ecb20c72cc
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.java
@@ -0,0 +1,79 @@
+package org.keycloak.admin.ui.rest.model;
+
+import java.util.Objects;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+
+public final class ClientRole {
+ @Schema(required = true) private final String id;
+ @Schema(required = true) private final String role;
+ @Schema(required = true) private String client;
+ @Schema(required = true) private String clientId;
+ private String description;
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getRole() {
+ return this.role;
+ }
+
+ public String getClient() {
+ return this.client;
+ }
+
+ public void setClient(String client) {
+ this.client = client;
+ }
+
+ public String getClientId() {
+ return this.clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ClientRole(String id, String role, String description) {
+ this.id = id;
+ this.role = role;
+ this.description = description;
+ }
+
+ public ClientRole(String id, String role, String client, String clientId, String description) {
+ this.id = id;
+ this.role = role;
+ this.client = client;
+ this.clientId = clientId;
+ this.description = description;
+ }
+
+ public ClientRole copy(String id, String role, String client, String clientId, String description) {
+ return new ClientRole(id, role, client, clientId, description);
+ }
+
+ @Override public String toString() {
+ return "ClientRole{" + "id='" + id + '\'' + ", role='" + role + '\'' + ", client='" + client + '\'' + ", clientId='" + clientId + '\'' + ", description='" + description + '\'' + '}';
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ClientRole that = (ClientRole) o;
+ return id.equals(that.id) && role.equals(that.role) && client.equals(that.client) && clientId.equals(that.clientId);
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(id, role, client, clientId);
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.kt
deleted file mode 100644
index befe7693b1..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/ClientRole.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.keycloak.admin.ui.rest.model
-
-import org.eclipse.microprofile.openapi.annotations.media.Schema
-
-data class ClientRole(
- @field:Schema(required = true) var id: String,
- @field:Schema(required = true) var role: String,
- @field:Schema(required = true) var client: String?,
- @field:Schema(required = true) var clientId: String?,
- var description: String?
-) {
-}
\ No newline at end of file
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.java b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.java
new file mode 100644
index 0000000000..d24c4a58e8
--- /dev/null
+++ b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.java
@@ -0,0 +1,17 @@
+package org.keycloak.admin.ui.rest.model;
+
+import java.util.stream.Stream;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RoleModel;
+
+public class RoleMapper {
+
+ public static ClientRole convertToRepresentation(RoleModel roleModel, Stream clients) {
+ ClientRole clientRole = new ClientRole(roleModel.getId(), roleModel.getName(), roleModel.getDescription());
+ ClientModel clientModel = clients.filter(c -> roleModel.getContainerId().equals(c.getId())).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Could not find referenced client"));
+ clientRole.setClientId(clientModel.getId());
+ clientRole.setClient(clientModel.getClientId());
+ return clientRole;
+ }
+}
diff --git a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.kt b/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.kt
deleted file mode 100644
index 17b889bb48..0000000000
--- a/keycloak-theme/src/main/java/org/keycloak/admin/ui/rest/model/RoleMapper.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.keycloak.admin.ui.rest.model
-
-import org.keycloak.models.ClientModel
-import org.keycloak.models.RoleModel
-import org.mapstruct.*
-import java.util.stream.Stream
-
-@Mapper
-abstract class RoleMapper {
-
- @Mapping(source = "name", target = "role")
- @Mapping(target = "client", ignore = true)
- abstract fun convertToRepresentation(role: RoleModel, @Context clientModel: Stream): ClientRole
-
- @AfterMapping
- fun convert(role: RoleModel, @MappingTarget clientRole: ClientRole, @Context list: Stream) {
- val clientModel = list.filter { c -> role.containerId == c.id }.findFirst().get()
- clientRole.clientId = clientModel.id
- clientRole.client = clientModel.clientId
- }
-}
\ No newline at end of file