keycloak-scim/js/apps/admin-ui/src/components/role-mapping/queries.ts

185 lines
5.4 KiB
TypeScript

import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import type MappingsRepresentation from "@keycloak/keycloak-admin-client/lib/defs/mappingsRepresentation";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import type { ClientScopes } from "@keycloak/keycloak-admin-client/lib/resources/clientScopes";
import type { Clients } from "@keycloak/keycloak-admin-client/lib/resources/clients";
import type { Groups } from "@keycloak/keycloak-admin-client/lib/resources/groups";
import type { Roles } from "@keycloak/keycloak-admin-client/lib/resources/roles";
import type { Users } from "@keycloak/keycloak-admin-client/lib/resources/users";
import { adminClient } from "../../admin-client";
import { Row } from "./RoleMapping";
export type ResourcesKey = keyof KeycloakAdminClient;
type DeleteFunctions =
| keyof Pick<Groups, "delClientRoleMappings" | "delRealmRoleMappings">
| keyof Pick<ClientScopes, "delClientScopeMappings" | "delRealmScopeMappings">
| keyof Pick<Roles, "delCompositeRoles">;
type ListEffectiveFunction =
| keyof Pick<Groups, "listRoleMappings" | "listAvailableRealmRoleMappings">
| keyof Pick<
ClientScopes,
| "listScopeMappings"
| "listAvailableRealmScopeMappings"
| "listCompositeClientScopeMappings"
>
| keyof Pick<Roles, "getCompositeRoles" | "getCompositeRolesForClient">
| keyof Pick<
Users,
"listCompositeClientRoleMappings" | "listCompositeRealmRoleMappings"
>;
type ListAvailableFunction =
| keyof Pick<
Groups,
"listAvailableClientRoleMappings" | "listAvailableRealmRoleMappings"
>
| keyof Pick<
ClientScopes,
"listAvailableClientScopeMappings" | "listAvailableRealmScopeMappings"
>
| keyof Pick<Roles, "find">
| keyof Pick<Clients, "listRoles">;
type FunctionMapping = {
delete: DeleteFunctions[];
listAvailable: ListAvailableFunction[];
listEffective: ListEffectiveFunction[];
};
type ResourceMapping = Partial<Record<ResourcesKey, FunctionMapping>>;
const groupFunctions: FunctionMapping = {
delete: ["delClientRoleMappings", "delRealmRoleMappings"],
listEffective: [
"listRoleMappings",
"listCompositeRealmRoleMappings",
"listCompositeClientRoleMappings",
],
listAvailable: [
"listAvailableClientRoleMappings",
"listAvailableRealmRoleMappings",
],
};
const clientFunctions: FunctionMapping = {
delete: ["delClientScopeMappings", "delRealmScopeMappings"],
listEffective: [
"listScopeMappings",
"listAvailableRealmScopeMappings",
"listCompositeClientScopeMappings",
],
listAvailable: [
"listAvailableClientScopeMappings",
"listAvailableRealmScopeMappings",
],
};
const mapping: ResourceMapping = {
groups: groupFunctions,
users: groupFunctions,
clientScopes: clientFunctions,
clients: clientFunctions,
roles: {
delete: ["delCompositeRoles", "delCompositeRoles"],
listEffective: [
"getCompositeRoles",
"getCompositeRoles",
"getCompositeRolesForClient",
],
listAvailable: ["listRoles", "find"],
},
};
type queryType =
| DeleteFunctions
| ListAvailableFunction
| ListEffectiveFunction;
const castAdminClient = (resource: ResourcesKey) =>
adminClient[resource] as unknown as {
[index in queryType]: (...params: any) => Promise<RoleRepresentation[]>;
};
const applyQuery = (
type: ResourcesKey,
query: queryType,
...params: object[]
): Promise<RoleRepresentation[]> => castAdminClient(type)[query](...params);
export const deleteMapping = (type: ResourcesKey, id: string, rows: Row[]) =>
rows.map((row) => {
const role = { id: row.role.id!, name: row.role.name! };
const query = mapping[type]?.delete[row.client ? 0 : 1]!;
return applyQuery(
type,
query,
{
id,
clientUniqueId: row.client?.id,
client: row.client?.id,
roles: [role],
},
[role]
);
});
export const getMapping = async (
type: ResourcesKey,
id: string
): Promise<MappingsRepresentation> => {
const query = mapping[type]!.listEffective[0];
const result = applyQuery(type, query, { id });
if (type !== "roles") {
return result as MappingsRepresentation;
}
const roles = await result;
const clientRoles = await Promise.all(
roles
.filter((r) => r.clientRole)
.map(async (role) => {
const client = await adminClient.clients.findOne({
id: role.containerId!,
});
role.containerId = client?.clientId;
return { ...client, mappings: [role] };
})
);
return {
clientMappings: clientRoles,
realmMappings: roles.filter((r) => !r.clientRole),
};
};
export const getEffectiveRoles = async (
type: ResourcesKey,
id: string
): Promise<Row[]> => {
const query = mapping[type]!.listEffective[1];
if (type !== "roles") {
return (await applyQuery(type, query, { id })).map((role) => ({
role,
}));
}
const roles = await applyQuery(type, query, { id });
const parentRoles = await Promise.all(
roles
.filter((r) => r.composite)
.map((r) => applyQuery(type, query, { id: r.id }))
);
return [...roles, ...parentRoles.flat()].map((role) => ({ role }));
};
export const getAvailableRoles = async (
type: ResourcesKey,
params: Record<string, string | number>
): Promise<Row[]> => {
const query = mapping[type]!.listAvailable[1];
return (await applyQuery(type, query, params)).map((role) => ({
role,
}));
};