diff --git a/src/client-scopes/form/ClientScopeForm.tsx b/src/client-scopes/form/ClientScopeForm.tsx
index 960c6e318c..888de30dae 100644
--- a/src/client-scopes/form/ClientScopeForm.tsx
+++ b/src/client-scopes/form/ClientScopeForm.tsx
@@ -19,7 +19,11 @@ import { convertFormValuesToObject } from "../../util";
import { MapperList } from "../details/MapperList";
import { ScopeForm } from "../details/ScopeForm";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
-import { RoleMapping, Row } from "../../components/role-mapping/RoleMapping";
+import {
+ mapRoles,
+ RoleMapping,
+ Row,
+} from "../../components/role-mapping/RoleMapping";
import type { RoleMappingPayload } from "keycloak-admin/lib/defs/roleRepresentation";
import {
AllClientScopes,
@@ -61,41 +65,35 @@ export const ClientScopeForm = () => {
);
const loader = async () => {
- const assignedRoles = hide
- ? await adminClient.clientScopes.listRealmScopeMappings({ id })
- : await adminClient.clientScopes.listCompositeRealmScopeMappings({ id });
+ const assignedRoles = await adminClient.clientScopes.listRealmScopeMappings(
+ { id }
+ );
+ const effectiveRoles = await adminClient.clientScopes.listCompositeRealmScopeMappings(
+ { id }
+ );
const clients = await adminClient.clients.find();
const clientRoles = (
await Promise.all(
clients.map(async (client) => {
- const clientScope = hide
- ? await adminClient.clientScopes.listClientScopeMappings({
- id,
- client: client.id!,
- })
- : await adminClient.clientScopes.listCompositeClientScopeMappings({
- id,
- client: client.id!,
- });
- return clientScope.map((scope) => {
- return {
- client,
- role: scope,
- };
- });
+ const clientAssignedRoles = await adminClient.clientScopes.listClientScopeMappings(
+ {
+ id,
+ client: client.id!,
+ }
+ );
+ const clientEffectiveRoles = await adminClient.clientScopes.listCompositeClientScopeMappings(
+ {
+ id,
+ client: client.id!,
+ }
+ );
+ return mapRoles(clientAssignedRoles, clientEffectiveRoles, hide);
})
)
).flat();
- return [
- ...assignedRoles.map((role) => {
- return {
- role,
- };
- }),
- ...clientRoles,
- ];
+ return [...mapRoles(assignedRoles, effectiveRoles, hide), ...clientRoles];
};
const save = async (clientScopes: ClientScopeDefaultOptionalType) => {
diff --git a/src/clients/ClientDetails.tsx b/src/clients/ClientDetails.tsx
index 82eed5bed4..b42326d188 100644
--- a/src/clients/ClientDetails.tsx
+++ b/src/clients/ClientDetails.tsx
@@ -358,7 +358,7 @@ export const ClientDetails = () => {
eventKey="serviceAccount"
title={{t("serviceAccount")}}
>
-
+
)}
{
+export const ServiceAccount = ({ client }: ServiceAccountProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
- const { realm } = useContext(RealmContext);
const { addAlert } = useAlerts();
const [hide, setHide] = useState(false);
- const [serviceAccountId, setServiceAccountId] = useState("");
- const [name, setName] = useState("");
+ const [serviceAccount, setServiceAccount] = useState();
+
+ useFetch(
+ () =>
+ adminClient.clients.getServiceAccountUser({
+ id: client.id!,
+ }),
+ (serviceAccount) => setServiceAccount(serviceAccount),
+ []
+ );
const loader = async () => {
const serviceAccount = await adminClient.clients.getServiceAccountUser({
- id: clientId,
+ id: client.id!,
});
- setServiceAccountId(serviceAccount.id!);
+ const id = serviceAccount.id!;
+
+ const assignedRoles = await adminClient.users.listRealmRoleMappings({ id });
const effectiveRoles = await adminClient.users.listCompositeRealmRoleMappings(
- { id: serviceAccount.id! }
+ { id }
);
- const assignedRoles = await adminClient.users.listRealmRoleMappings({
- id: serviceAccount.id!,
- });
const clients = await adminClient.clients.find();
- setName(clients.find((c) => c.id === clientId)?.clientId!);
const clientRoles = (
await Promise.all(
clients.map(async (client) => {
- return {
- client,
- roles: await adminClient.users.listClientRoleMappings({
- id: serviceAccount.id!,
+ const clientAssignedRoles = await adminClient.users.listClientRoleMappings(
+ {
+ id,
clientUniqueId: client.id!,
- }),
- };
+ }
+ );
+ const clientEffectiveRoles = await adminClient.users.listCompositeClientRoleMappings(
+ {
+ id,
+ clientUniqueId: client.id!,
+ }
+ );
+ return mapRoles(clientAssignedRoles, clientEffectiveRoles, hide);
})
)
- ).filter((rows) => rows.roles.length > 0);
+ ).flat();
- 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) => {
- return {
- client: findClient(role),
- role,
- } as Row;
- });
+ return [...mapRoles(assignedRoles, effectiveRoles, hide), ...clientRoles];
};
const assignRoles = async (rows: Row[]) => {
@@ -103,7 +76,7 @@ export const ServiceAccount = ({ clientId }: ServiceAccountProps) => {
.map((row) => row.role as RoleMappingPayload)
.flat();
adminClient.users.addRealmRoleMappings({
- id: serviceAccountId,
+ id: serviceAccount?.id!,
roles: realmRoles,
});
await Promise.all(
@@ -111,7 +84,7 @@ export const ServiceAccount = ({ clientId }: ServiceAccountProps) => {
.filter((row) => row.client !== undefined)
.map((row) =>
adminClient.users.addClientRoleMappings({
- id: serviceAccountId,
+ id: serviceAccount?.id!,
clientUniqueId: row.client!.id!,
roles: [row.role as RoleMappingPayload],
})
@@ -128,13 +101,17 @@ export const ServiceAccount = ({ clientId }: ServiceAccountProps) => {
}
};
return (
- setHide(!hide)}
- />
+ <>
+ {serviceAccount && (
+ setHide(!hide)}
+ />
+ )}
+ >
);
};
diff --git a/src/components/role-mapping/RoleMapping.tsx b/src/components/role-mapping/RoleMapping.tsx
index 435a32da01..17e8c5a5b0 100644
--- a/src/components/role-mapping/RoleMapping.tsx
+++ b/src/components/role-mapping/RoleMapping.tsx
@@ -22,11 +22,35 @@ import { useAlerts } from "../alert/Alerts";
export type CompositeRole = RoleRepresentation & {
parent: RoleRepresentation;
+ isInherited?: boolean;
};
export type Row = {
client?: ClientRepresentation;
- role: CompositeRole | RoleRepresentation;
+ role: RoleRepresentation | CompositeRole;
+};
+
+export const mapRoles = (
+ assignedRoles: RoleRepresentation[],
+ effectiveRoles: RoleRepresentation[],
+ hide: boolean
+) => {
+ return [
+ ...(hide
+ ? assignedRoles.map((role) => ({
+ role: {
+ ...role,
+ isInherited: false,
+ },
+ }))
+ : effectiveRoles.map((role) => ({
+ role: {
+ ...role,
+ isInherited:
+ assignedRoles.find((r) => r.id === role.id) === undefined,
+ },
+ }))),
+ ];
};
export const ServiceRole = ({ role, client }: Row) => (
@@ -152,10 +176,13 @@ export const RoleMapping = ({
data-testid="assigned-roles"
key={key}
loader={loader}
- canSelectAll={hide}
- onSelect={hide ? (rows) => setSelected(rows) : undefined}
+ canSelectAll
+ onSelect={(rows) => setSelected(rows)}
searchPlaceholderKey="clients:searchByName"
ariaLabelKey="clients:clientScopeList"
+ isRowDisabled={(value) =>
+ (value.role as CompositeRole).isInherited || false
+ }
toolbarItem={
<>