185 lines
5.4 KiB
TypeScript
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,
|
|
}));
|
|
};
|