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:
Erik Jan de Wit 2021-09-03 16:40:59 +02:00 committed by GitHub
parent f87463d036
commit e187314b1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 7 deletions

View file

@ -1,6 +1,5 @@
export default {
groups: {
groups: "Groups",
groupDetails: "Group details",
childGroups: "Child groups",
createGroup: "Create group",

View file

@ -61,7 +61,7 @@ export const UsersInRoleTab = () => {
variant="link"
onClick={() => history.push(`/${realm}/groups`)}
>
{t("groups")}
{t("common:groups")}
</Button>
{t("or")}
<Button
@ -98,7 +98,7 @@ export const UsersInRoleTab = () => {
variant="link"
onClick={() => history.push(`/${realm}/groups`)}
>
{t("groups")}
{t("common:groups")}
</Button>
{t("or")}
<Button

View file

@ -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",
noUsersEmptyStateDescriptionContinued:
"to find them. Users that already have this role as an effective role cannot be added here.",
groups: "Groups",
or: "or",
users: "Users",
userName: "Username",

View file

@ -310,7 +310,7 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
</ToolbarGroup>
</Toolbar>
<DataList
aria-label={t("groups")}
aria-label={t("common:groups")}
onDragFinish={onDragFinish}
onDragStart={onDragStart}
onDragMove={onDragMove}

View 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)}
/>
);
};

View file

@ -25,6 +25,7 @@ import { UserIdentityProviderLinks } from "./UserIdentityProviderLinks";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { toUser } from "./routes/User";
import { toUsers } from "./routes/Users";
import { UserRoleMapping } from "./UserRoleMapping";
export const UsersTabs = () => {
const { t } = useTranslation("users");
@ -89,7 +90,7 @@ export const UsersTabs = () => {
history.push(toUser({ id: createdUser.id, realm, tab: "settings" }));
}
} catch (error) {
addError("users:userCreateError", error);
addError("userCreateError", error);
}
};
@ -135,7 +136,7 @@ export const UsersTabs = () => {
<ImpersonateConfirm />
<DeleteConfirm />
<ViewHeader
titleKey={user?.username || t("users:createUser")}
titleKey={user?.username || t("createUser")}
divider={!id}
dropdownItems={[
<DropdownItem
@ -183,6 +184,13 @@ export const UsersTabs = () => {
>
<UserConsents />
</Tab>
<Tab
eventKey="role-mapping"
data-testid="role-mapping-tab"
title={<TabTitleText>{t("roleMapping")}</TabTitleText>}
>
<UserRoleMapping id={id} name={user.username!} />
</Tab>
<Tab
eventKey="identity-provider-links"
data-testid="identity-provider-links-tab"

View file

@ -106,6 +106,7 @@ export default {
"Are you sure you want to revoke all granted client scopes for {{clientId}}?",
deleteGrantsSuccess: "Grants successfully revoked.",
deleteGrantsError: "Error deleting grants.",
roleMapping: "Role mapping",
unlockAllUsers: "Unlock all users",
unlockUsersConfirm:
"All the users that are temporarily locked will be unlocked.",