import { useState } from "react"; import { Link } from "react-router-dom-v5-compat"; import { useNavigate } from "react-router-dom-v5-compat"; import { useTranslation } from "react-i18next"; import { Alert, AlertVariant, Button, DescriptionList, PageSection, ToolbarItem, } from "@patternfly/react-core"; import { ExpandableRowContent, TableComposable, Tbody, Td, Th, Thead, Tr, } from "@patternfly/react-table"; import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation"; import type PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { PaginatingTableToolbar } from "../../components/table-toolbar/PaginatingTableToolbar"; import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; import { useAlerts } from "../../components/alert/Alerts"; import { useRealm } from "../../context/realm-context/RealmContext"; import { toPolicyDetails } from "../routes/PolicyDetails"; import { MoreLabel } from "./MoreLabel"; import { toUpperCase } from "../../util"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; import useToggle from "../../utils/useToggle"; import { NewPolicyDialog } from "./NewPolicyDialog"; import { toCreatePolicy } from "../routes/NewPolicy"; import { toPermissionDetails } from "../routes/PermissionDetails"; import { SearchDropdown, SearchForm } from "./SearchDropdown"; import { DetailDescriptionLink } from "./DetailDescription"; type PoliciesProps = { clientId: string; }; type ExpandablePolicyRepresentation = PolicyRepresentation & { dependentPolicies?: PolicyRepresentation[]; isExpanded: boolean; }; export const AuthorizationPolicies = ({ clientId }: PoliciesProps) => { const { t } = useTranslation("clients"); const { adminClient } = useAdminClient(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); const navigate = useNavigate(); const [policies, setPolicies] = useState(); const [selectedPolicy, setSelectedPolicy] = useState(); const [policyProviders, setPolicyProviders] = useState(); const [key, setKey] = useState(0); const refresh = () => setKey(key + 1); const [max, setMax] = useState(10); const [first, setFirst] = useState(0); const [search, setSearch] = useState({}); const [newDialog, toggleDialog] = useToggle(); useFetch( async () => { const policies = await adminClient.clients.listPolicies({ first, max: max + 1, id: clientId, permission: "false", ...search, }); return await Promise.all([ adminClient.clients.listPolicyProviders({ id: clientId }), ...policies.map(async (policy) => { const dependentPolicies = await adminClient.clients.listDependentPolicies({ id: clientId, policyId: policy.id!, }); return { ...policy, dependentPolicies, isExpanded: false, }; }), ]); }, ([providers, ...policies]) => { setPolicyProviders( providers.filter((p) => p.type !== "resource" && p.type !== "scope") ); setPolicies(policies); }, [key, search, first, max] ); const DependentPoliciesRenderer = ({ row, }: { row: ExpandablePolicyRepresentation; }) => { return ( <> {row.dependentPolicies?.[0]?.name}{" "} ); }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "clients:deletePolicy", children: ( <> {t("deletePolicyConfirm")} {selectedPolicy?.dependentPolicies && selectedPolicy.dependentPolicies.length > 0 && (

{selectedPolicy.dependentPolicies.map((policy) => ( {policy.name} ))}

)} ), continueButtonLabel: "clients:confirm", onConfirm: async () => { try { await adminClient.clients.delPolicy({ id: clientId, policyId: selectedPolicy?.id!, }); addAlert(t("policyDeletedSuccess"), AlertVariant.success); refresh(); } catch (error) { addError("clients:policyDeletedError", error); } }, }); if (!policies) { return ; } const noData = policies.length === 0; const searching = Object.keys(search).length !== 0; return ( {(!noData || searching) && ( <> {newDialog && ( navigate( toCreatePolicy({ id: clientId, realm, policyType: p.type! }) ) } toggleDialog={toggleDialog} /> )} { setFirst(first); setMax(max); }} toolbarItem={ <> } > {!noData && ( {t("common:name")} {t("common:type")} {t("dependentPermission")} {t("common:description")} {policies.map((policy, rowIndex) => ( { const rows = policies.map((policy, index) => index === rowIndex ? { ...policy, isExpanded: !policy.isExpanded } : policy ); setPolicies(rows); }, }} /> {policy.name} {toUpperCase(policy.type!)} {policy.description} { setSelectedPolicy(policy); toggleDeleteDialog(); }, }, ], }} /> {policy.isExpanded && ( p.name!} link={(permission) => toPermissionDetails({ realm, id: clientId, permissionId: permission.id!, permissionType: permission.type!, }) } /> )} ))} )} )} {noData && searching && ( )} {noData && !searching && ( <> {newDialog && ( p.type !== "aggregate" )} onSelect={(p) => navigate( toCreatePolicy({ id: clientId, realm, policyType: p.type! }) ) } toggleDialog={toggleDialog} /> )} )} ); };