2020-12-15 07:21:17 +00:00
|
|
|
import React, { useEffect, useState } from "react";
|
2021-01-29 13:59:03 +00:00
|
|
|
import { useHistory, useParams, useRouteMatch } from "react-router-dom";
|
2020-12-15 07:21:17 +00:00
|
|
|
import {
|
|
|
|
AlertVariant,
|
2020-12-04 20:37:29 +00:00
|
|
|
ButtonVariant,
|
|
|
|
DropdownItem,
|
|
|
|
PageSection,
|
2020-12-15 07:21:17 +00:00
|
|
|
Tab,
|
|
|
|
TabTitleText,
|
|
|
|
} from "@patternfly/react-core";
|
|
|
|
import { useTranslation } from "react-i18next";
|
2020-12-04 20:37:29 +00:00
|
|
|
import { useFieldArray, useForm } from "react-hook-form";
|
2020-12-15 07:21:17 +00:00
|
|
|
|
|
|
|
import { useAlerts } from "../components/alert/Alerts";
|
2020-12-04 20:37:29 +00:00
|
|
|
import { useAdminClient } from "../context/auth/AdminClient";
|
2020-12-15 07:21:17 +00:00
|
|
|
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
2021-02-04 20:50:13 +00:00
|
|
|
import Composites from "keycloak-admin/lib/defs/roleRepresentation";
|
2020-12-04 20:37:29 +00:00
|
|
|
import { KeyValueType, RoleAttributes } from "./RoleAttributes";
|
|
|
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
|
|
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
|
|
|
import { RealmRoleForm } from "./RealmRoleForm";
|
2021-02-04 20:50:13 +00:00
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
2021-01-20 21:10:25 +00:00
|
|
|
import { AssociatedRolesModal } from "./AssociatedRolesModal";
|
2021-01-13 20:47:40 +00:00
|
|
|
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
2021-02-04 20:50:13 +00:00
|
|
|
import { AssociatedRolesTab } from "./AssociatedRolesTab";
|
2020-12-15 07:21:17 +00:00
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const arrayToAttributes = (attributeArray: KeyValueType[]) => {
|
|
|
|
const initValue: { [index: string]: string[] } = {};
|
|
|
|
return attributeArray.reduce((acc, attribute) => {
|
|
|
|
acc[attribute.key] = [attribute.value];
|
|
|
|
return acc;
|
|
|
|
}, initValue);
|
|
|
|
};
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const attributesToArray = (attributes?: {
|
|
|
|
[key: string]: string[];
|
|
|
|
}): KeyValueType[] => {
|
2020-12-04 20:37:29 +00:00
|
|
|
if (!attributes || Object.keys(attributes).length == 0) {
|
2020-12-04 20:37:29 +00:00
|
|
|
return [];
|
2020-12-04 20:37:29 +00:00
|
|
|
}
|
|
|
|
return Object.keys(attributes).map((key) => ({
|
|
|
|
key: key,
|
2020-12-04 20:37:29 +00:00
|
|
|
value: attributes[key][0],
|
2020-12-04 20:37:29 +00:00
|
|
|
}));
|
|
|
|
};
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
export type RoleFormType = Omit<RoleRepresentation, "attributes"> & {
|
|
|
|
attributes: KeyValueType[];
|
|
|
|
};
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
export const RealmRoleTabs = () => {
|
2020-12-15 07:21:17 +00:00
|
|
|
const { t } = useTranslation("roles");
|
2020-12-04 20:37:29 +00:00
|
|
|
const form = useForm<RoleFormType>({ mode: "onChange" });
|
2020-12-15 07:21:17 +00:00
|
|
|
const history = useHistory();
|
2021-02-04 20:50:13 +00:00
|
|
|
// const [name, setName] = useState("");
|
|
|
|
|
2020-12-15 07:21:17 +00:00
|
|
|
const adminClient = useAdminClient();
|
2020-12-04 20:37:29 +00:00
|
|
|
const [role, setRole] = useState<RoleFormType>();
|
2020-12-15 07:21:17 +00:00
|
|
|
|
2021-01-29 13:59:03 +00:00
|
|
|
const { id, clientId } = useParams<{ id: string; clientId: string }>();
|
|
|
|
const { url } = useRouteMatch();
|
2021-02-04 20:50:13 +00:00
|
|
|
|
|
|
|
const { realm } = useRealm();
|
|
|
|
|
|
|
|
const [key, setKey] = useState("");
|
|
|
|
|
|
|
|
const refresh = () => {
|
|
|
|
setKey(`${new Date().getTime()}`);
|
|
|
|
};
|
|
|
|
|
|
|
|
const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>(
|
|
|
|
[]
|
|
|
|
);
|
2020-12-15 07:21:17 +00:00
|
|
|
const { addAlert } = useAlerts();
|
|
|
|
|
2021-01-20 21:10:25 +00:00
|
|
|
const [open, setOpen] = useState(false);
|
2020-12-04 20:37:29 +00:00
|
|
|
const convert = (role: RoleRepresentation) => {
|
|
|
|
const { attributes, ...rest } = role;
|
|
|
|
return {
|
|
|
|
attributes: attributesToArray(attributes),
|
|
|
|
...rest,
|
|
|
|
};
|
|
|
|
};
|
2021-01-20 21:10:25 +00:00
|
|
|
|
2020-12-15 07:21:17 +00:00
|
|
|
useEffect(() => {
|
2021-02-04 20:50:13 +00:00
|
|
|
const update = async () => {
|
2020-12-04 20:37:29 +00:00
|
|
|
if (id) {
|
|
|
|
const fetchedRole = await adminClient.roles.findOneById({ id });
|
2021-02-04 20:50:13 +00:00
|
|
|
const allAdditionalRoles = await adminClient.roles.getCompositeRoles({
|
|
|
|
id,
|
|
|
|
});
|
|
|
|
setAdditionalRoles(allAdditionalRoles);
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const convertedRole = convert(fetchedRole);
|
|
|
|
Object.entries(convertedRole).map((entry) => {
|
|
|
|
form.setValue(entry[0], entry[1]);
|
|
|
|
});
|
|
|
|
setRole(convertedRole);
|
2021-01-05 13:39:27 +00:00
|
|
|
}
|
2021-02-04 20:50:13 +00:00
|
|
|
};
|
|
|
|
setTimeout(update, 100);
|
|
|
|
}, [key]);
|
2020-12-15 07:21:17 +00:00
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const { fields, append, remove } = useFieldArray({
|
|
|
|
control: form.control,
|
|
|
|
name: "attributes",
|
|
|
|
});
|
2020-12-15 07:21:17 +00:00
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
useEffect(() => append({ key: "", value: "" }), [append, role]);
|
2021-01-19 19:30:52 +00:00
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const save = async (role: RoleFormType) => {
|
2020-12-15 07:21:17 +00:00
|
|
|
try {
|
2020-12-04 20:37:29 +00:00
|
|
|
const { attributes, ...rest } = role;
|
|
|
|
const roleRepresentation: RoleRepresentation = rest;
|
2020-12-04 20:37:29 +00:00
|
|
|
if (id) {
|
2020-12-04 20:37:29 +00:00
|
|
|
if (attributes) {
|
|
|
|
roleRepresentation.attributes = arrayToAttributes(attributes);
|
2020-12-04 20:37:29 +00:00
|
|
|
}
|
2021-01-29 13:59:03 +00:00
|
|
|
if (!clientId) {
|
|
|
|
await adminClient.roles.updateById({ id }, roleRepresentation);
|
|
|
|
} else {
|
|
|
|
await adminClient.clients.updateRole(
|
|
|
|
{ id: clientId, roleName: role.name! },
|
|
|
|
roleRepresentation
|
|
|
|
);
|
|
|
|
}
|
2021-02-04 20:50:13 +00:00
|
|
|
|
|
|
|
await adminClient.roles.createComposite(
|
|
|
|
{ roleId: id, realm },
|
|
|
|
additionalRoles
|
|
|
|
);
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
setRole(role);
|
2020-12-04 20:37:29 +00:00
|
|
|
} else {
|
2021-01-29 13:59:03 +00:00
|
|
|
let createdRole;
|
|
|
|
if (!clientId) {
|
|
|
|
await adminClient.roles.create(roleRepresentation);
|
|
|
|
createdRole = await adminClient.roles.findOneByName({
|
|
|
|
name: role.name!,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await adminClient.clients.createRole({
|
|
|
|
id: clientId,
|
|
|
|
name: role.name,
|
|
|
|
});
|
|
|
|
if (role.description) {
|
|
|
|
await adminClient.clients.updateRole(
|
|
|
|
{ id: clientId, roleName: role.name! },
|
|
|
|
roleRepresentation
|
|
|
|
);
|
|
|
|
}
|
|
|
|
createdRole = await adminClient.clients.findRole({
|
|
|
|
id: clientId,
|
|
|
|
roleName: role.name!,
|
|
|
|
});
|
|
|
|
}
|
2020-12-04 20:37:29 +00:00
|
|
|
setRole(convert(createdRole));
|
2021-02-04 20:50:13 +00:00
|
|
|
history.push(
|
|
|
|
url.substr(0, url.lastIndexOf("/") + 1) + createdRole.id + "/details"
|
|
|
|
);
|
2020-12-04 20:37:29 +00:00
|
|
|
}
|
|
|
|
addAlert(t(id ? "roleSaveSuccess" : "roleCreated"), AlertVariant.success);
|
2020-12-15 07:21:17 +00:00
|
|
|
} catch (error) {
|
2020-12-04 20:37:29 +00:00
|
|
|
addAlert(
|
|
|
|
t((id ? "roleSave" : "roleCreate") + "Error", {
|
|
|
|
error: error.response.data?.errorMessage || error,
|
|
|
|
}),
|
|
|
|
AlertVariant.danger
|
|
|
|
);
|
2020-12-15 07:21:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-04 20:50:13 +00:00
|
|
|
const addComposites = async (composites: Composites[]): Promise<void> => {
|
|
|
|
const compositeArray = composites;
|
|
|
|
setAdditionalRoles([...additionalRoles, ...compositeArray]);
|
|
|
|
|
|
|
|
try {
|
|
|
|
await adminClient.roles.createComposite(
|
|
|
|
{ roleId: id, realm: realm },
|
|
|
|
compositeArray
|
|
|
|
);
|
|
|
|
history.push(url.substr(0, url.lastIndexOf("/") + 1) + "AssociatedRoles");
|
|
|
|
refresh();
|
|
|
|
addAlert(t("addAssociatedRolesSuccess"), AlertVariant.success);
|
|
|
|
} catch (error) {
|
|
|
|
addAlert(t("addAssociatedRolesError", { error }), AlertVariant.danger);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-12-04 20:37:29 +00:00
|
|
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
|
|
|
titleKey: "roles:roleDeleteConfirm",
|
2020-12-04 20:37:29 +00:00
|
|
|
messageKey: t("roles:roleDeleteConfirmDialog", {
|
|
|
|
name: role?.name || t("createRole"),
|
|
|
|
}),
|
2020-12-04 20:37:29 +00:00
|
|
|
continueButtonLabel: "common:delete",
|
|
|
|
continueButtonVariant: ButtonVariant.danger,
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
2021-01-29 13:59:03 +00:00
|
|
|
if (!clientId) {
|
|
|
|
await adminClient.roles.delById({ id });
|
|
|
|
} else {
|
2021-02-04 20:50:13 +00:00
|
|
|
await adminClient.clients.delRole({
|
|
|
|
id: clientId,
|
|
|
|
roleName: role!.name as string,
|
|
|
|
});
|
2021-01-29 13:59:03 +00:00
|
|
|
}
|
2020-12-04 20:37:29 +00:00
|
|
|
addAlert(t("roleDeletedSuccess"), AlertVariant.success);
|
2021-01-29 13:59:03 +00:00
|
|
|
const loc = url.replace(/\/attributes/g, "");
|
|
|
|
history.replace(`${loc.substr(0, loc.lastIndexOf("/"))}`);
|
2020-12-04 20:37:29 +00:00
|
|
|
} catch (error) {
|
|
|
|
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
2020-12-15 07:21:17 +00:00
|
|
|
|
2021-02-17 07:17:04 +00:00
|
|
|
const [
|
|
|
|
toggleDeleteAllAssociatedRolesDialog,
|
|
|
|
DeleteAllAssociatedRolesConfirm,
|
|
|
|
] = useConfirmDialog({
|
|
|
|
titleKey: t("roles:removeAllAssociatedRoles") + "?",
|
|
|
|
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
|
|
|
name: role?.name || t("createRole"),
|
|
|
|
}),
|
|
|
|
continueButtonLabel: "common:delete",
|
|
|
|
continueButtonVariant: ButtonVariant.danger,
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await adminClient.roles.delCompositeRoles({ id }, additionalRoles);
|
|
|
|
addAlert(
|
|
|
|
t("compositeRoleOff"),
|
|
|
|
AlertVariant.success,
|
|
|
|
t("compositesRemovedAlertDescription")
|
|
|
|
);
|
|
|
|
const loc = url.replace(/\/AssociatedRoles/g, "/details");
|
|
|
|
history.push(loc);
|
|
|
|
refresh();
|
|
|
|
} catch (error) {
|
|
|
|
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2021-01-20 21:10:25 +00:00
|
|
|
const toggleModal = () => setOpen(!open);
|
|
|
|
|
2020-12-15 07:21:17 +00:00
|
|
|
return (
|
|
|
|
<>
|
2020-12-04 20:37:29 +00:00
|
|
|
<DeleteConfirm />
|
2021-02-17 07:17:04 +00:00
|
|
|
<DeleteAllAssociatedRolesConfirm />
|
2021-02-04 20:50:13 +00:00
|
|
|
<AssociatedRolesModal
|
|
|
|
onConfirm={addComposites}
|
|
|
|
existingCompositeRoles={additionalRoles}
|
|
|
|
open={open}
|
|
|
|
toggleDialog={() => setOpen(!open)}
|
|
|
|
/>
|
2020-12-04 20:37:29 +00:00
|
|
|
<ViewHeader
|
2020-12-04 20:37:29 +00:00
|
|
|
titleKey={role?.name || t("createRole")}
|
2021-02-17 07:17:04 +00:00
|
|
|
badge={additionalRoles.length > 0 ? t("composite") : ""}
|
|
|
|
badgeId="composite-role-badge"
|
|
|
|
badgeIsRead={true}
|
2020-12-04 20:37:29 +00:00
|
|
|
subKey={id ? "" : "roles:roleCreateExplain"}
|
2021-02-04 20:50:13 +00:00
|
|
|
actionsDropdownId="roles-actions-dropdown"
|
2020-12-04 20:37:29 +00:00
|
|
|
dropdownItems={
|
2021-02-17 07:17:04 +00:00
|
|
|
url.includes("AssociatedRoles")
|
|
|
|
? [
|
|
|
|
<DropdownItem
|
|
|
|
key="delete-all-associated"
|
|
|
|
component="button"
|
|
|
|
onClick={() => toggleDeleteAllAssociatedRolesDialog()}
|
|
|
|
>
|
|
|
|
{t("roles:removeAllAssociatedRoles")}
|
|
|
|
</DropdownItem>,
|
|
|
|
<DropdownItem
|
|
|
|
key="delete-role"
|
|
|
|
component="button"
|
|
|
|
onClick={() => toggleDeleteDialog()}
|
|
|
|
>
|
|
|
|
{t("deleteRole")}
|
|
|
|
</DropdownItem>,
|
|
|
|
]
|
|
|
|
: id
|
2020-12-04 20:37:29 +00:00
|
|
|
? [
|
|
|
|
<DropdownItem
|
2021-01-27 20:22:26 +00:00
|
|
|
key="delete-role"
|
2020-12-04 20:37:29 +00:00
|
|
|
component="button"
|
|
|
|
onClick={() => toggleDeleteDialog()}
|
|
|
|
>
|
|
|
|
{t("deleteRole")}
|
|
|
|
</DropdownItem>,
|
2021-01-20 21:10:25 +00:00
|
|
|
<DropdownItem
|
2021-01-27 20:22:26 +00:00
|
|
|
key="toggle-modal"
|
2021-02-04 20:50:13 +00:00
|
|
|
id="add-roles"
|
2021-01-20 21:10:25 +00:00
|
|
|
component="button"
|
|
|
|
onClick={() => toggleModal()}
|
|
|
|
>
|
|
|
|
{t("addAssociatedRolesText")}
|
|
|
|
</DropdownItem>,
|
2020-12-04 20:37:29 +00:00
|
|
|
]
|
|
|
|
: undefined
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<PageSection variant="light">
|
|
|
|
{id && (
|
2021-01-13 20:47:40 +00:00
|
|
|
<KeycloakTabs isBox>
|
2020-12-04 20:37:29 +00:00
|
|
|
<Tab
|
2021-01-13 20:47:40 +00:00
|
|
|
eventKey="details"
|
2020-12-04 20:37:29 +00:00
|
|
|
title={<TabTitleText>{t("details")}</TabTitleText>}
|
2020-12-15 07:21:17 +00:00
|
|
|
>
|
2021-01-26 20:06:16 +00:00
|
|
|
<RealmRoleForm
|
|
|
|
reset={() => form.reset(role)}
|
|
|
|
form={form}
|
|
|
|
save={save}
|
|
|
|
editMode={true}
|
|
|
|
/>
|
2020-12-04 20:37:29 +00:00
|
|
|
</Tab>
|
2021-02-04 20:50:13 +00:00
|
|
|
{additionalRoles.length > 0 ? (
|
|
|
|
<Tab
|
|
|
|
eventKey="AssociatedRoles"
|
|
|
|
title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>}
|
|
|
|
>
|
2021-02-17 07:17:04 +00:00
|
|
|
<AssociatedRolesTab
|
|
|
|
additionalRoles={additionalRoles}
|
|
|
|
addComposites={addComposites}
|
|
|
|
parentRole={role!}
|
|
|
|
onRemove={() => refresh()}
|
|
|
|
/>
|
2021-02-04 20:50:13 +00:00
|
|
|
</Tab>
|
|
|
|
) : null}
|
2020-12-04 20:37:29 +00:00
|
|
|
<Tab
|
2021-01-13 20:47:40 +00:00
|
|
|
eventKey="attributes"
|
2020-12-04 20:37:29 +00:00
|
|
|
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
2021-01-05 19:49:33 +00:00
|
|
|
>
|
2020-12-04 20:37:29 +00:00
|
|
|
<RoleAttributes
|
|
|
|
form={form}
|
|
|
|
save={save}
|
|
|
|
array={{ fields, append, remove }}
|
2021-01-26 20:06:16 +00:00
|
|
|
reset={() => form.reset(role)}
|
2020-12-04 20:37:29 +00:00
|
|
|
/>
|
2020-12-04 20:37:29 +00:00
|
|
|
</Tab>
|
2021-01-13 20:47:40 +00:00
|
|
|
</KeycloakTabs>
|
2020-12-04 20:37:29 +00:00
|
|
|
)}
|
2021-01-26 20:06:16 +00:00
|
|
|
{!id && (
|
|
|
|
<RealmRoleForm
|
|
|
|
reset={() => form.reset()}
|
|
|
|
form={form}
|
|
|
|
save={save}
|
|
|
|
editMode={false}
|
|
|
|
/>
|
|
|
|
)}
|
2020-12-04 20:37:29 +00:00
|
|
|
</PageSection>
|
2020-12-15 07:21:17 +00:00
|
|
|
</>
|
|
|
|
);
|
2021-01-26 20:06:16 +00:00
|
|
|
};
|