created endpoint for effective client roles (#3165)
This commit is contained in:
parent
711a780be6
commit
b5ea27c194
12 changed files with 306 additions and 83 deletions
|
@ -9,7 +9,7 @@ import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider
|
||||||
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator
|
||||||
|
|
||||||
class AdminUIRestEndpointProvider : AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
|
class AvailableRoleMappingProvider : AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
|
||||||
override fun create(session: KeycloakSession): AdminRealmResourceProvider {
|
override fun create(session: KeycloakSession): AdminRealmResourceProvider {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class AdminUIRestEndpointProvider : AdminRealmResourceProviderFactory, AdminReal
|
||||||
override fun postInit(factory: KeycloakSessionFactory) {}
|
override fun postInit(factory: KeycloakSessionFactory) {}
|
||||||
override fun close() {}
|
override fun close() {}
|
||||||
override fun getId(): String {
|
override fun getId(): String {
|
||||||
return "admin-ui"
|
return "admin-ui-available-roles"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResource(
|
override fun getResource(
|
||||||
|
@ -27,6 +27,6 @@ class AdminUIRestEndpointProvider : AdminRealmResourceProviderFactory, AdminReal
|
||||||
auth: AdminPermissionEvaluator,
|
auth: AdminPermissionEvaluator,
|
||||||
adminEvent: AdminEventBuilder
|
adminEvent: AdminEventBuilder
|
||||||
): Any {
|
): Any {
|
||||||
return AdminUIExtendedResource(realm, auth)
|
return AvailableRoleMappingResource(realm, auth)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,10 +20,10 @@ import javax.ws.rs.core.Context
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
|
|
||||||
@Path("/")
|
@Path("/")
|
||||||
open class AdminUIExtendedResource(
|
open class AvailableRoleMappingResource(
|
||||||
private var realm: RealmModel,
|
private var realm: RealmModel,
|
||||||
private var auth: AdminPermissionEvaluator,
|
private var auth: AdminPermissionEvaluator,
|
||||||
) {
|
) : RoleMappingResource(realm, auth) {
|
||||||
@Context
|
@Context
|
||||||
var session: KeycloakSession? = null
|
var session: KeycloakSession? = null
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ open class AdminUIExtendedResource(
|
||||||
val scopeContainer = realm.getClientScopeById(id) ?: throw NotFoundException("Could not find client scope")
|
val scopeContainer = realm.getClientScopeById(id) ?: throw NotFoundException("Could not find client scope")
|
||||||
auth.clients().requireView(scopeContainer)
|
auth.clients().requireView(scopeContainer)
|
||||||
|
|
||||||
return availableMapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
|
return mapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -84,7 +84,7 @@ open class AdminUIExtendedResource(
|
||||||
val scopeContainer = realm.getClientById(id) ?: throw NotFoundException("Could not find client")
|
val scopeContainer = realm.getClientById(id) ?: throw NotFoundException("Could not find client")
|
||||||
auth.clients().requireView(scopeContainer)
|
auth.clients().requireView(scopeContainer)
|
||||||
|
|
||||||
return availableMapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
|
return mapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectScope(r) }.negate(), first, max, search)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -114,7 +114,7 @@ open class AdminUIExtendedResource(
|
||||||
val scopeContainer = realm.getGroupById(id) ?: throw NotFoundException("Could not find group")
|
val scopeContainer = realm.getGroupById(id) ?: throw NotFoundException("Could not find group")
|
||||||
auth.groups().requireView(scopeContainer)
|
auth.groups().requireView(scopeContainer)
|
||||||
|
|
||||||
return availableMapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectRole(r) }.negate(), first, max, search)
|
return mapping(Predicate<RoleModel?> { r -> scopeContainer.hasDirectRole(r) }.negate(), first, max, search)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ open class AdminUIExtendedResource(
|
||||||
?: if (auth.users().canQuery()) throw NotFoundException("User not found") else throw ForbiddenException()
|
?: if (auth.users().canQuery()) throw NotFoundException("User not found") else throw ForbiddenException()
|
||||||
auth.users().requireView(user)
|
auth.users().requireView(user)
|
||||||
|
|
||||||
return availableMapping(Predicate<RoleModel?> { r -> user.hasDirectRole(r) }.negate(), first, max, search)
|
return mapping(Predicate<RoleModel?> { r -> user.hasDirectRole(r) }.negate(), first, max, search)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,33 +173,6 @@ open class AdminUIExtendedResource(
|
||||||
@QueryParam("max") @DefaultValue("10") max: Long,
|
@QueryParam("max") @DefaultValue("10") max: Long,
|
||||||
@QueryParam("search") @DefaultValue("") search: String
|
@QueryParam("search") @DefaultValue("") search: String
|
||||||
): List<ClientRole> {
|
): List<ClientRole> {
|
||||||
val clients = realm.clientsStream
|
return mapping({ true }, first, max, search)
|
||||||
val mapper = Mappers.getMapper(RoleMapper::class.java)
|
|
||||||
return clients
|
|
||||||
.flatMap { c -> c.rolesStream }
|
|
||||||
.map { r -> mapper.convertToRepresentation(r, realm.clientsStream) }
|
|
||||||
.skip(first)
|
|
||||||
.limit(max)
|
|
||||||
.collect(Collectors.toList()) ?: Collections.emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun availableMapping(
|
|
||||||
predicate: Predicate<RoleModel?>,
|
|
||||||
first: Long,
|
|
||||||
max: Long,
|
|
||||||
search: String
|
|
||||||
): List<ClientRole> {
|
|
||||||
val clients = realm.clientsStream
|
|
||||||
val mapper = Mappers.getMapper(RoleMapper::class.java)
|
|
||||||
return clients
|
|
||||||
.flatMap { c -> c.rolesStream }
|
|
||||||
.filter(predicate)
|
|
||||||
.filter(auth.roles()::canMapClientScope)
|
|
||||||
|
|
||||||
.map { r -> mapper.convertToRepresentation(r, realm.clientsStream) }
|
|
||||||
.filter { r -> r.client?.indexOf(search) != -1 || r.role.indexOf(search) != -1 }
|
|
||||||
.skip(first)
|
|
||||||
.limit(max)
|
|
||||||
.collect(Collectors.toList()) ?: Collections.emptyList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
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<ClientRole> {
|
||||||
|
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<ClientRole> {
|
||||||
|
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<ClientRole> {
|
||||||
|
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<ClientRole> {
|
||||||
|
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<ClientRole> {
|
||||||
|
return mapping { true }.collect(Collectors.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
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<RoleModel?>,
|
||||||
|
): Stream<ClientRole> {
|
||||||
|
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<RoleModel?>,
|
||||||
|
first: Long,
|
||||||
|
max: Long,
|
||||||
|
search: String
|
||||||
|
): List<ClientRole> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ data class ClientRole(
|
||||||
@field:Schema(required = true) var id: String,
|
@field:Schema(required = true) var id: String,
|
||||||
@field:Schema(required = true) var role: String,
|
@field:Schema(required = true) var role: String,
|
||||||
@field:Schema(required = true) var client: String?,
|
@field:Schema(required = true) var client: String?,
|
||||||
|
@field:Schema(required = true) var clientId: String?,
|
||||||
var description: String?
|
var description: String?
|
||||||
) {
|
) {
|
||||||
}
|
}
|
|
@ -14,6 +14,8 @@ abstract class RoleMapper {
|
||||||
|
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
fun convert(role: RoleModel, @MappingTarget clientRole: ClientRole, @Context list: Stream<ClientModel>) {
|
fun convert(role: RoleModel, @MappingTarget clientRole: ClientRole, @Context list: Stream<ClientModel>) {
|
||||||
clientRole.client = list.filter { c -> role.containerId == c.id }.findFirst().get().clientId
|
val clientModel = list.filter { c -> role.containerId == c.id }.findFirst().get()
|
||||||
|
clientRole.clientId = clientModel.id
|
||||||
|
clientRole.client = clientModel.clientId
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,4 +15,5 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
org.keycloak.admin.ui.rest.AdminUIRestEndpointProvider
|
org.keycloak.admin.ui.rest.AvailableRoleMappingProvider
|
||||||
|
org.keycloak.admin.ui.rest.EffectiveRoleMappingProvider
|
|
@ -89,7 +89,7 @@ export const AddRoleMappingModal = ({
|
||||||
|
|
||||||
return localeSort(
|
return localeSort(
|
||||||
roles.map((e) => ({
|
roles.map((e) => ({
|
||||||
client: { clientId: e.client },
|
client: { clientId: e.client, id: e.clientId },
|
||||||
role: { id: e.id, name: e.role, description: e.description },
|
role: { id: e.id, name: e.role, description: e.description },
|
||||||
})),
|
})),
|
||||||
compareRow
|
compareRow
|
||||||
|
|
|
@ -20,12 +20,9 @@ import { useAlerts } from "../alert/Alerts";
|
||||||
import { useConfirmDialog } from "../confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../confirm-dialog/ConfirmDialog";
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||||
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
||||||
import {
|
import { deleteMapping, getEffectiveRoles, getMapping } from "./queries";
|
||||||
deleteMapping,
|
import { getEffectiveClientRoles } from "./resource";
|
||||||
getEffectiveClientRoles,
|
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||||
getEffectiveRoles,
|
|
||||||
getMapping,
|
|
||||||
} from "./queries";
|
|
||||||
|
|
||||||
import "./role-mapping.css";
|
import "./role-mapping.css";
|
||||||
|
|
||||||
|
@ -92,6 +89,7 @@ export const RoleMapping = ({
|
||||||
}: RoleMappingProps) => {
|
}: RoleMappingProps) => {
|
||||||
const { t } = useTranslation(type);
|
const { t } = useTranslation(type);
|
||||||
const { adminClient } = useAdminClient();
|
const { adminClient } = useAdminClient();
|
||||||
|
const { realm } = useRealm();
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
|
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
|
@ -107,23 +105,28 @@ export const RoleMapping = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
const effectiveRoles = await getEffectiveRoles(adminClient, type, id);
|
let effectiveRoles: Row[] = [];
|
||||||
|
|
||||||
let effectiveClientRoles: Row[] = [];
|
let effectiveClientRoles: Row[] = [];
|
||||||
if (!hide) {
|
if (!hide) {
|
||||||
const clients = await adminClient.clients.find();
|
effectiveRoles = await getEffectiveRoles(adminClient, type, id);
|
||||||
|
|
||||||
effectiveClientRoles = (
|
effectiveClientRoles = (
|
||||||
await Promise.all(
|
await getEffectiveClientRoles({
|
||||||
clients.map(async (client) =>
|
adminClient,
|
||||||
getEffectiveClientRoles(adminClient, type, id, client)
|
realm,
|
||||||
)
|
type,
|
||||||
)
|
id,
|
||||||
).flat();
|
})
|
||||||
|
).map((e) => ({
|
||||||
|
client: { clientId: e.client, id: e.clientId },
|
||||||
|
role: { id: e.id, name: e.role, description: e.description },
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const roles = await getMapping(adminClient, type, id);
|
const roles = await getMapping(adminClient, type, id);
|
||||||
const realmRoles = roles.realmMappings?.map((role) => ({ role }));
|
const realmRolesMapping =
|
||||||
const client = Object.values(roles.clientMappings || {})
|
roles.realmMappings?.map((role) => ({ role })) || [];
|
||||||
|
const clientMapping = Object.values(roles.clientMappings || {})
|
||||||
.map((client) =>
|
.map((client) =>
|
||||||
client.mappings.map((role: RoleRepresentation) => ({
|
client.mappings.map((role: RoleRepresentation) => ({
|
||||||
client: { clientId: client.client, ...client },
|
client: { clientId: client.client, ...client },
|
||||||
|
@ -133,8 +136,11 @@ export const RoleMapping = ({
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...mapRoles(realmRoles || [], effectiveRoles, hide),
|
...mapRoles(
|
||||||
...[...client, ...effectiveClientRoles],
|
[...realmRolesMapping, ...clientMapping],
|
||||||
|
[...effectiveClientRoles, ...effectiveRoles],
|
||||||
|
hide
|
||||||
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||||
import type MappingsRepresentation from "@keycloak/keycloak-admin-client/lib/defs/mappingsRepresentation";
|
import type MappingsRepresentation from "@keycloak/keycloak-admin-client/lib/defs/mappingsRepresentation";
|
||||||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
|
||||||
import type { ClientScopes } from "@keycloak/keycloak-admin-client/lib/resources/clientScopes";
|
import type { ClientScopes } from "@keycloak/keycloak-admin-client/lib/resources/clientScopes";
|
||||||
import type { Groups } from "@keycloak/keycloak-admin-client/lib/resources/groups";
|
import type { Groups } from "@keycloak/keycloak-admin-client/lib/resources/groups";
|
||||||
import type { Roles } from "@keycloak/keycloak-admin-client/lib/resources/roles";
|
import type { Roles } from "@keycloak/keycloak-admin-client/lib/resources/roles";
|
||||||
|
@ -155,22 +154,6 @@ export const getEffectiveRoles = async (
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEffectiveClientRoles = async (
|
|
||||||
adminClient: KeycloakAdminClient,
|
|
||||||
type: ResourcesKey,
|
|
||||||
id: string,
|
|
||||||
client: ClientRepresentation
|
|
||||||
): Promise<Row[]> => {
|
|
||||||
const query = mapping[type]!.listEffective[2];
|
|
||||||
return (
|
|
||||||
await applyQuery(adminClient, type, query, {
|
|
||||||
id,
|
|
||||||
client: client.id,
|
|
||||||
clientUniqueId: client.id,
|
|
||||||
})
|
|
||||||
).map((role) => ({ role, client: { clientId: client.id, ...client } }));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAvailableRoles = async (
|
export const getAvailableRoles = async (
|
||||||
adminClient: KeycloakAdminClient,
|
adminClient: KeycloakAdminClient,
|
||||||
type: ResourcesKey,
|
type: ResourcesKey,
|
||||||
|
|
|
@ -2,24 +2,35 @@ import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
|
||||||
import { addTrailingSlash } from "../../util";
|
import { addTrailingSlash } from "../../util";
|
||||||
import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders";
|
import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders";
|
||||||
|
|
||||||
type AvailableClientRolesQuery = {
|
type BaseClientRolesQuery = {
|
||||||
adminClient: KeycloakAdminClient;
|
adminClient: KeycloakAdminClient;
|
||||||
id: string;
|
id: string;
|
||||||
realm: string;
|
realm: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AvailableClientRolesQuery = BaseClientRolesQuery & {
|
||||||
first: number;
|
first: number;
|
||||||
max: number;
|
max: number;
|
||||||
search?: string;
|
search?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type EffectiveClientRolesQuery = BaseClientRolesQuery;
|
||||||
|
|
||||||
|
type Query = Partial<Omit<AvailableClientRolesQuery, "adminClient">> & {
|
||||||
|
adminClient: KeycloakAdminClient;
|
||||||
|
endpoint: string;
|
||||||
|
};
|
||||||
|
|
||||||
type ClientRole = {
|
type ClientRole = {
|
||||||
id: string;
|
id: string;
|
||||||
role: string;
|
role: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
client?: string;
|
client: string;
|
||||||
|
clientId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAvailableClientRoles = async ({
|
const fetchRoles = async ({
|
||||||
adminClient,
|
adminClient,
|
||||||
id,
|
id,
|
||||||
realm,
|
realm,
|
||||||
|
@ -27,16 +38,17 @@ export const getAvailableClientRoles = async ({
|
||||||
first,
|
first,
|
||||||
max,
|
max,
|
||||||
search,
|
search,
|
||||||
}: AvailableClientRolesQuery): Promise<ClientRole[]> => {
|
endpoint,
|
||||||
|
}: Query): Promise<ClientRole[]> => {
|
||||||
const accessToken = await adminClient.getAccessToken();
|
const accessToken = await adminClient.getAccessToken();
|
||||||
const baseUrl = adminClient.baseUrl;
|
const baseUrl = adminClient.baseUrl;
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${addTrailingSlash(
|
`${addTrailingSlash(
|
||||||
baseUrl
|
baseUrl
|
||||||
)}admin/realms/${realm}/admin-ui/${type}/${id}?first=${first}&max=${max}${
|
)}admin/realms/${realm}/admin-ui-${endpoint}/${type}/${id}?first=${
|
||||||
search ? "&search=" + search : ""
|
first || 0
|
||||||
}`,
|
}&max=${max || 10}${search ? "&search=" + search : ""}`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: getAuthorizationHeaders(accessToken),
|
headers: getAuthorizationHeaders(accessToken),
|
||||||
|
@ -45,3 +57,15 @@ export const getAvailableClientRoles = async ({
|
||||||
|
|
||||||
return await response.json();
|
return await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAvailableClientRoles = async (
|
||||||
|
query: AvailableClientRolesQuery
|
||||||
|
): Promise<ClientRole[]> => {
|
||||||
|
return fetchRoles({ ...query, endpoint: "available-roles" });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEffectiveClientRoles = async (
|
||||||
|
query: EffectiveClientRolesQuery
|
||||||
|
): Promise<ClientRole[]> => {
|
||||||
|
return fetchRoles({ ...query, endpoint: "effective-roles" });
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue