import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation"; import { DecisionStrategy } from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation"; import { ActionGroup, AlertVariant, Button, ButtonVariant, DropdownItem, FormGroup, PageSection, Radio, SelectVariant, Switch, } from "@patternfly/react-core"; import { useState } from "react"; import { Controller, FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { HelpItem } from "ui-shared"; import { adminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form-access/FormAccess"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { KeycloakTextArea } from "../../components/keycloak-text-area/KeycloakTextArea"; import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; import { ViewHeader } from "../../components/view-header/ViewHeader"; import { useFetch } from "../../utils/useFetch"; import { toUpperCase } from "../../util"; import { useParams } from "../../utils/useParams"; import { toAuthorizationTab } from "../routes/AuthenticationTab"; import type { NewPermissionParams } from "../routes/NewPermission"; import { PermissionDetailsParams, toPermissionDetails, } from "../routes/PermissionDetails"; import { ResourcesPolicySelect } from "./ResourcesPolicySelect"; import { ScopeSelect } from "./ScopeSelect"; type FormFields = PolicyRepresentation & { resourceType: string; }; export default function PermissionDetails() { const { t } = useTranslation("clients"); const form = useForm({ mode: "onChange", }); const { register, control, reset, formState: { errors }, handleSubmit, } = form; const navigate = useNavigate(); const { id, realm, permissionType, permissionId, selectedId } = useParams< NewPermissionParams & PermissionDetailsParams >(); const { addAlert, addError } = useAlerts(); const [permission, setPermission] = useState(); const [applyToResourceTypeFlag, setApplyToResourceTypeFlag] = useState(false); useFetch( async () => { if (!permissionId) { return {}; } const [permission, resources, policies, scopes] = await Promise.all([ adminClient.clients.findOnePermission({ id, type: permissionType, permissionId, }), adminClient.clients.getAssociatedResources({ id, permissionId, }), adminClient.clients.getAssociatedPolicies({ id, permissionId, }), adminClient.clients.getAssociatedScopes({ id, permissionId, }), ]); if (!permission) { throw new Error(t("common:notFound")); } return { permission, resources: resources.map((r) => r._id), policies: policies.map((p) => p.id!), scopes: scopes.map((s) => s.id!), }; }, ({ permission, resources, policies, scopes }) => { reset({ ...permission, resources, policies, scopes }); if (permission && "resourceType" in permission) { setApplyToResourceTypeFlag( !!(permission as { resourceType: string }).resourceType ); } setPermission({ ...permission, resources, policies }); }, [] ); const save = async (permission: PolicyRepresentation) => { try { if (permissionId) { await adminClient.clients.updatePermission( { id, type: permissionType, permissionId }, permission ); } else { const result = await adminClient.clients.createPermission( { id, type: permissionType }, permission ); navigate( toPermissionDetails({ realm, id, permissionType, permissionId: result.id!, }) ); } addAlert( t((permissionId ? "update" : "create") + "PermissionSuccess"), AlertVariant.success ); } catch (error) { addError("clients:permissionSaveError", error); } }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "clients:deletePermission", messageKey: t("deletePermissionConfirm", { permission: permission?.name, }), continueButtonVariant: ButtonVariant.danger, continueButtonLabel: "clients:confirm", onConfirm: async () => { try { await adminClient.clients.delPermission({ id, type: permissionType, permissionId: permissionId, }); addAlert(t("permissionDeletedSuccess"), AlertVariant.success); navigate( toAuthorizationTab({ realm, clientId: id, tab: "permissions" }) ); } catch (error) { addError("clients:permissionDeletedError", error); } }, }); const resourcesIds = useWatch({ control, name: "resources", defaultValue: [], }); if (!permission) { return ; } return ( <> toggleDeleteDialog()} > {t("common:delete")} , ] : undefined } /> } > } validated={errors.description ? "error" : "default"} helperTextInvalid={errors.description?.message} > } > {applyToResourceTypeFlag ? ( } isRequired={permissionType === "scope"} > ) : ( } helperTextInvalid={t("common:required")} validated={errors.resources ? "error" : "default"} isRequired={permissionType !== "scope"} > )} {permissionType === "scope" && ( } helperTextInvalid={t("common:required")} validated={errors.scopes ? "error" : "default"} isRequired > )} } > } fieldId="policyEnforcementMode" hasNoPaddingTop > ( <> {Object.values(DecisionStrategy).map((strategy) => ( field.onChange(strategy)} label={t(`decisionStrategies.${strategy}`)} className="pf-u-mb-md" /> ))} )} />
); }