167 lines
4.8 KiB
TypeScript
167 lines
4.8 KiB
TypeScript
|
import React, { Fragment, useContext, useState } from "react";
|
||
|
import { useTranslation } from "react-i18next";
|
||
|
import {
|
||
|
Table,
|
||
|
TableBody,
|
||
|
TableHeader,
|
||
|
TableVariant,
|
||
|
} from "@patternfly/react-table";
|
||
|
import { Badge, Button, Checkbox, ToolbarItem } from "@patternfly/react-core";
|
||
|
|
||
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
||
|
import { DataLoader } from "../../components/data-loader/DataLoader";
|
||
|
import { TableToolbar } from "../../components/table-toolbar/TableToolbar";
|
||
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||
|
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||
|
import { emptyFormatter } from "../../util";
|
||
|
|
||
|
import "./service-account.css";
|
||
|
|
||
|
type ServiceAccountProps = {
|
||
|
clientId: string;
|
||
|
};
|
||
|
|
||
|
type CompositeRole = RoleRepresentation & {
|
||
|
parent: RoleRepresentation;
|
||
|
};
|
||
|
|
||
|
export const ServiceAccount = ({ clientId }: ServiceAccountProps) => {
|
||
|
const { t } = useTranslation("clients");
|
||
|
const adminClient = useAdminClient();
|
||
|
const { realm } = useContext(RealmContext);
|
||
|
|
||
|
const [hide, setHide] = useState(false);
|
||
|
|
||
|
const loader = async () => {
|
||
|
const serviceAccount = await adminClient.clients.getServiceAccountUser({
|
||
|
id: clientId,
|
||
|
});
|
||
|
const effectiveRoles = await adminClient.users.listCompositeRealmRoleMappings(
|
||
|
{ id: serviceAccount.id! }
|
||
|
);
|
||
|
const assignedRoles = await adminClient.users.listRealmRoleMappings({
|
||
|
id: serviceAccount.id!,
|
||
|
});
|
||
|
|
||
|
const clients = await adminClient.clients.find();
|
||
|
const clientRoles = (
|
||
|
await Promise.all(
|
||
|
clients.map(async (client) => {
|
||
|
return {
|
||
|
client,
|
||
|
roles: await adminClient.users.listClientRoleMappings({
|
||
|
id: serviceAccount.id!,
|
||
|
clientUniqueId: client.id!,
|
||
|
}),
|
||
|
};
|
||
|
})
|
||
|
)
|
||
|
).filter((rows) => rows.roles.length > 0);
|
||
|
|
||
|
const findClient = (role: RoleRepresentation) => {
|
||
|
const row = clientRoles.filter((row) =>
|
||
|
row.roles.find((r) => r.id === role.id)
|
||
|
)[0];
|
||
|
return row ? row.client : undefined;
|
||
|
};
|
||
|
|
||
|
const clientRolesFlat = clientRoles.map((row) => row.roles).flat();
|
||
|
|
||
|
const addInherentData = await (async () =>
|
||
|
Promise.all(
|
||
|
effectiveRoles.map(async (role) => {
|
||
|
const compositeRoles = await adminClient.roles.getCompositeRolesForRealm(
|
||
|
{ realm, id: role.id! }
|
||
|
);
|
||
|
return compositeRoles.length > 0
|
||
|
? compositeRoles.map((r) => {
|
||
|
return { ...r, parent: role };
|
||
|
})
|
||
|
: { ...role, parent: undefined };
|
||
|
})
|
||
|
))();
|
||
|
const uniqueRolesWithParent = addInherentData
|
||
|
.flat()
|
||
|
.filter(
|
||
|
(role, index, array) =>
|
||
|
array.findIndex((r) => r.id === role.id) === index
|
||
|
);
|
||
|
return ([
|
||
|
...(hide ? assignedRoles : uniqueRolesWithParent),
|
||
|
...clientRolesFlat,
|
||
|
] as CompositeRole[])
|
||
|
.sort((r1, r2) => r1.name!.localeCompare(r2.name!))
|
||
|
.map((role) => {
|
||
|
const client = findClient(role);
|
||
|
return {
|
||
|
cells: [
|
||
|
<Fragment key={role.id}>
|
||
|
{client && (
|
||
|
<Badge
|
||
|
key={client.id}
|
||
|
isRead
|
||
|
className="keycloak-admin--service-account__client-name"
|
||
|
>
|
||
|
{client.clientId}
|
||
|
</Badge>
|
||
|
)}
|
||
|
{role.name}
|
||
|
</Fragment>,
|
||
|
role.parent ? role.parent.name : "",
|
||
|
role.description,
|
||
|
],
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
|
||
|
const filterData = () => {};
|
||
|
|
||
|
return (
|
||
|
<TableToolbar
|
||
|
inputGroupName="clientsServiceAccountRoleToolbarTextInput"
|
||
|
inputGroupPlaceholder={t("searchByName")}
|
||
|
inputGroupOnChange={filterData}
|
||
|
toolbarItem={
|
||
|
<>
|
||
|
<ToolbarItem>
|
||
|
<Checkbox
|
||
|
label={t("hideInheritedRoles")}
|
||
|
id="hideInheritedRoles"
|
||
|
isChecked={hide}
|
||
|
onChange={setHide}
|
||
|
/>
|
||
|
</ToolbarItem>
|
||
|
<ToolbarItem>
|
||
|
<Button>{t("assignRole")}</Button>
|
||
|
</ToolbarItem>
|
||
|
</>
|
||
|
}
|
||
|
>
|
||
|
<DataLoader loader={loader}>
|
||
|
{(clientRoles) => (
|
||
|
<>
|
||
|
{hide ? "" : " "}
|
||
|
<Table
|
||
|
onSelect={() => {}}
|
||
|
variant={TableVariant.compact}
|
||
|
cells={[
|
||
|
t("roles:roleName"),
|
||
|
{
|
||
|
title: t("inherentFrom"),
|
||
|
cellFormatters: [emptyFormatter()],
|
||
|
},
|
||
|
{ title: t("description"), cellFormatters: [emptyFormatter()] },
|
||
|
]}
|
||
|
rows={clientRoles.data}
|
||
|
aria-label="roleList"
|
||
|
>
|
||
|
<TableHeader />
|
||
|
<TableBody />
|
||
|
</Table>
|
||
|
</>
|
||
|
)}
|
||
|
</DataLoader>
|
||
|
</TableToolbar>
|
||
|
);
|
||
|
};
|