added user role mapping tab to user details (#1075)
* added user role mapping tab to user details * fix imports to admin client * Update src/user/UserRoleMapping.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/user/UserRoleMapping.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * added await * fixed merge error Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
f87463d036
commit
e187314b1f
7 changed files with 115 additions and 7 deletions
|
@ -1,6 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
groups: {
|
groups: {
|
||||||
groups: "Groups",
|
|
||||||
groupDetails: "Group details",
|
groupDetails: "Group details",
|
||||||
childGroups: "Child groups",
|
childGroups: "Child groups",
|
||||||
createGroup: "Create group",
|
createGroup: "Create group",
|
||||||
|
|
|
@ -61,7 +61,7 @@ export const UsersInRoleTab = () => {
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() => history.push(`/${realm}/groups`)}
|
onClick={() => history.push(`/${realm}/groups`)}
|
||||||
>
|
>
|
||||||
{t("groups")}
|
{t("common:groups")}
|
||||||
</Button>
|
</Button>
|
||||||
{t("or")}
|
{t("or")}
|
||||||
<Button
|
<Button
|
||||||
|
@ -98,7 +98,7 @@ export const UsersInRoleTab = () => {
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() => history.push(`/${realm}/groups`)}
|
onClick={() => history.push(`/${realm}/groups`)}
|
||||||
>
|
>
|
||||||
{t("groups")}
|
{t("common:groups")}
|
||||||
</Button>
|
</Button>
|
||||||
{t("or")}
|
{t("or")}
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -72,7 +72,6 @@ export default {
|
||||||
"Only the users with this role directly assigned will appear under this tab. If you need to find users assigned to this role, go to",
|
"Only the users with this role directly assigned will appear under this tab. If you need to find users assigned to this role, go to",
|
||||||
noUsersEmptyStateDescriptionContinued:
|
noUsersEmptyStateDescriptionContinued:
|
||||||
"to find them. Users that already have this role as an effective role cannot be added here.",
|
"to find them. Users that already have this role as an effective role cannot be added here.",
|
||||||
groups: "Groups",
|
|
||||||
or: "or",
|
or: "or",
|
||||||
users: "Users",
|
users: "Users",
|
||||||
userName: "Username",
|
userName: "Username",
|
||||||
|
|
|
@ -310,7 +310,7 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<DataList
|
<DataList
|
||||||
aria-label={t("groups")}
|
aria-label={t("common:groups")}
|
||||||
onDragFinish={onDragFinish}
|
onDragFinish={onDragFinish}
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
onDragMove={onDragMove}
|
onDragMove={onDragMove}
|
||||||
|
|
101
src/user/UserRoleMapping.tsx
Normal file
101
src/user/UserRoleMapping.tsx
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { AlertVariant } from "@patternfly/react-core";
|
||||||
|
|
||||||
|
import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||||
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
import {
|
||||||
|
mapRoles,
|
||||||
|
RoleMapping,
|
||||||
|
Row,
|
||||||
|
} from "../components/role-mapping/RoleMapping";
|
||||||
|
|
||||||
|
type UserRoleMappingProps = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserRoleMapping = ({ id, name }: UserRoleMappingProps) => {
|
||||||
|
const { t } = useTranslation("clients");
|
||||||
|
const adminClient = useAdminClient();
|
||||||
|
const { addAlert, addError } = useAlerts();
|
||||||
|
|
||||||
|
const [hide, setHide] = useState(false);
|
||||||
|
|
||||||
|
const loader = async () => {
|
||||||
|
const [assignedRoles, effectiveRoles] = await Promise.all([
|
||||||
|
adminClient.users
|
||||||
|
.listRealmRoleMappings({ id })
|
||||||
|
.then((roles) => roles.map((role) => ({ role }))),
|
||||||
|
adminClient.users
|
||||||
|
.listCompositeRealmRoleMappings({ id })
|
||||||
|
.then((roles) => roles.map((role) => ({ role }))),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const clients = await adminClient.clients.find();
|
||||||
|
const clientRoles = (
|
||||||
|
await Promise.all(
|
||||||
|
clients.map(async (client) => {
|
||||||
|
const [clientAssignedRoles, clientEffectiveRoles] = await Promise.all(
|
||||||
|
[
|
||||||
|
adminClient.users
|
||||||
|
.listClientRoleMappings({
|
||||||
|
id,
|
||||||
|
clientUniqueId: client.id!,
|
||||||
|
})
|
||||||
|
.then((roles) => roles.map((role) => ({ role, client }))),
|
||||||
|
adminClient.users
|
||||||
|
.listCompositeClientRoleMappings({
|
||||||
|
id,
|
||||||
|
clientUniqueId: client.id!,
|
||||||
|
})
|
||||||
|
.then((roles) => roles.map((role) => ({ role, client }))),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return mapRoles(clientAssignedRoles, clientEffectiveRoles, hide);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
|
|
||||||
|
return [...mapRoles(assignedRoles, effectiveRoles, hide), ...clientRoles];
|
||||||
|
};
|
||||||
|
|
||||||
|
const assignRoles = async (rows: Row[]) => {
|
||||||
|
try {
|
||||||
|
const realmRoles = rows
|
||||||
|
.filter((row) => row.client === undefined)
|
||||||
|
.map((row) => row.role as RoleMappingPayload)
|
||||||
|
.flat();
|
||||||
|
await adminClient.users.addRealmRoleMappings({
|
||||||
|
id,
|
||||||
|
roles: realmRoles,
|
||||||
|
});
|
||||||
|
await Promise.all(
|
||||||
|
rows
|
||||||
|
.filter((row) => row.client !== undefined)
|
||||||
|
.map((row) =>
|
||||||
|
adminClient.users.addClientRoleMappings({
|
||||||
|
id,
|
||||||
|
clientUniqueId: row.client!.id!,
|
||||||
|
roles: [row.role as RoleMappingPayload],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
addAlert(t("roleMappingUpdatedSuccess"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addError("clients:roleMappingUpdatedError", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RoleMapping
|
||||||
|
name={name}
|
||||||
|
id={id}
|
||||||
|
type="service-account"
|
||||||
|
loader={loader}
|
||||||
|
save={assignRoles}
|
||||||
|
onHideRolesToggle={() => setHide(!hide)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -25,6 +25,7 @@ import { UserIdentityProviderLinks } from "./UserIdentityProviderLinks";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { toUser } from "./routes/User";
|
import { toUser } from "./routes/User";
|
||||||
import { toUsers } from "./routes/Users";
|
import { toUsers } from "./routes/Users";
|
||||||
|
import { UserRoleMapping } from "./UserRoleMapping";
|
||||||
|
|
||||||
export const UsersTabs = () => {
|
export const UsersTabs = () => {
|
||||||
const { t } = useTranslation("users");
|
const { t } = useTranslation("users");
|
||||||
|
@ -89,7 +90,7 @@ export const UsersTabs = () => {
|
||||||
history.push(toUser({ id: createdUser.id, realm, tab: "settings" }));
|
history.push(toUser({ id: createdUser.id, realm, tab: "settings" }));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("users:userCreateError", error);
|
addError("userCreateError", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +136,7 @@ export const UsersTabs = () => {
|
||||||
<ImpersonateConfirm />
|
<ImpersonateConfirm />
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
titleKey={user?.username || t("users:createUser")}
|
titleKey={user?.username || t("createUser")}
|
||||||
divider={!id}
|
divider={!id}
|
||||||
dropdownItems={[
|
dropdownItems={[
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
|
@ -183,6 +184,13 @@ export const UsersTabs = () => {
|
||||||
>
|
>
|
||||||
<UserConsents />
|
<UserConsents />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
eventKey="role-mapping"
|
||||||
|
data-testid="role-mapping-tab"
|
||||||
|
title={<TabTitleText>{t("roleMapping")}</TabTitleText>}
|
||||||
|
>
|
||||||
|
<UserRoleMapping id={id} name={user.username!} />
|
||||||
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
eventKey="identity-provider-links"
|
eventKey="identity-provider-links"
|
||||||
data-testid="identity-provider-links-tab"
|
data-testid="identity-provider-links-tab"
|
||||||
|
|
|
@ -106,6 +106,7 @@ export default {
|
||||||
"Are you sure you want to revoke all granted client scopes for {{clientId}}?",
|
"Are you sure you want to revoke all granted client scopes for {{clientId}}?",
|
||||||
deleteGrantsSuccess: "Grants successfully revoked.",
|
deleteGrantsSuccess: "Grants successfully revoked.",
|
||||||
deleteGrantsError: "Error deleting grants.",
|
deleteGrantsError: "Error deleting grants.",
|
||||||
|
roleMapping: "Role mapping",
|
||||||
unlockAllUsers: "Unlock all users",
|
unlockAllUsers: "Unlock all users",
|
||||||
unlockUsersConfirm:
|
unlockUsersConfirm:
|
||||||
"All the users that are temporarily locked will be unlocked.",
|
"All the users that are temporarily locked will be unlocked.",
|
||||||
|
|
Loading…
Reference in a new issue