import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import React, { useState, KeyboardEvent } from "react"; import { useTranslation } from "react-i18next"; import { FormGroup, Select, SelectVariant, SelectOption, PageSection, ActionGroup, Button, Switch, ExpandableSection, TextInput, ButtonVariant, InputGroup, Toolbar, ToolbarGroup, ToolbarItem, } from "@patternfly/react-core"; import { Controller, useFormContext } from "react-hook-form"; import { FormAccess } from "../../components/form-access/FormAccess"; import { HelpItem } from "../../components/help-enabler/HelpItem"; import { FormPanel } from "../../components/scroll-form/FormPanel"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; import type ResourceEvaluation from "@keycloak/keycloak-admin-client/lib/defs/resourceEvaluation"; import { useRealm } from "../../context/realm-context/RealmContext"; import { AttributeInput } from "../../components/attribute-input/AttributeInput"; import { defaultContextAttributes } from "../utils"; import type EvaluationResultRepresentation from "@keycloak/keycloak-admin-client/lib/defs/evaluationResultRepresentation"; import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation"; import { useParams } from "react-router-dom"; import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation"; import type { KeyValueType } from "../../components/attribute-form/attribute-convert"; import { TableComposable, Th, Thead, Tr } from "@patternfly/react-table"; import "./auth-evaluate.css"; import { AuthorizationEvaluateResource } from "./AuthorizationEvaluateResource"; import { SearchIcon } from "@patternfly/react-icons"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; interface EvaluateFormInputs extends Omit { applyToResource: boolean; alias: string; authScopes: string[]; context: { attributes: Record[]; }; resources: Record[]; } export type AttributeType = { key: string; name: string; custom?: boolean; values?: { [key: string]: string; }[]; }; type ClientSettingsProps = { clients: ClientRepresentation[]; clientName?: string; save: () => void; users: UserRepresentation[]; clientRoles: RoleRepresentation[]; }; export type AttributeForm = Omit< EvaluateFormInputs, "context" | "resources" > & { context: { attributes?: KeyValueType[]; }; resources?: KeyValueType[]; }; type Props = ClientSettingsProps & EvaluationResultRepresentation; export const AuthorizationEvaluate = ({ clients, clientRoles, clientName, users, }: Props) => { const form = useFormContext(); const { control, reset, trigger } = form; const { t } = useTranslation("clients"); const adminClient = useAdminClient(); const realm = useRealm(); const { clientId } = useParams<{ clientId: string }>(); const [clientsDropdownOpen, setClientsDropdownOpen] = useState(false); const [scopesDropdownOpen, setScopesDropdownOpen] = useState(false); const [userDropdownOpen, setUserDropdownOpen] = useState(false); const [roleDropdownOpen, setRoleDropdownOpen] = useState(false); const [isExpanded, setIsExpanded] = useState(false); const [applyToResourceType, setApplyToResourceType] = useState(false); const [resources, setResources] = useState([]); const [scopes, setScopes] = useState([]); const [selectedClient, setSelectedClient] = useState(); const [selectedUser, setSelectedUser] = useState(); const [evaluateResults, setEvaluateResults] = useState< EvaluationResultRepresentation[] >([]); const [showEvaluateResults, setShowEvaluateResults] = useState(false); const [searchVal, setSearchVal] = useState(""); const [filteredResources, setFilteredResources] = useState< EvaluationResultRepresentation[] >([]); const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); const [key, setKey] = useState(0); const refresh = () => { setKey(new Date().getTime()); }; const FilterType = { allResults: t("allResults"), resultPermit: t("resultPermit"), resultDeny: t("resultDeny"), }; const [filterType, setFilterType] = useState(FilterType.allResults); useFetch( async () => Promise.all([ adminClient.clients.listResources({ id: clientId, }), adminClient.clients.listAllScopes({ id: clientId, }), ]), ([resources, scopes]) => { setResources(resources); setScopes(scopes); }, [key, filterType] ); const evaluate = async () => { if (!(await trigger())) { return; } const formValues = form.getValues(); const keys = formValues.resources.map(({ key }) => key); const resEval: ResourceEvaluation = { roleIds: formValues.roleIds ?? [], clientId: selectedClient ? selectedClient.id! : clientId, userId: selectedUser?.id!, resources: resources.filter((resource) => keys.includes(resource.name!)), entitlements: false, context: { attributes: Object.fromEntries( formValues.context.attributes .filter((item) => item.key || item.value !== "") .map(({ key, value }) => [key, value]) ), }, }; const evaluation = await adminClient.clients.evaluateResource( { id: clientId!, realm: realm.realm }, resEval ); setEvaluateResults(evaluation.results); setShowEvaluateResults(true); return evaluateResults; }; const onSearch = () => { if (searchVal !== "") { setSearchVal(searchVal); const filtered = evaluateResults.filter((resource) => resource.resource?.name?.includes(searchVal) ); setFilteredResources(filtered); } else { setSearchVal(""); setFilteredResources(evaluateResults); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Enter") { onSearch(); } }; const handleInputChange = (value: string) => { setSearchVal(value); }; const noEvaluatedData = evaluateResults.length === 0; const noFilteredData = filteredResources.length === 0; const options = [ , , , ]; return showEvaluateResults ? ( {!noEvaluatedData && !noFilteredData && ( {t("resource")} {t("overallResults")} {t("scopes")} {(filterType == FilterType.allResults ? evaluateResults : filteredResources ).map((resource, rowIndex) => ( ))} )} {noEvaluatedData || (noFilteredData && ( ))} ) : ( } fieldId="client" > value.length > 0, }} defaultValue={clientName} control={control} render={({ onChange, value }) => ( )} /> } fieldId="loginTheme" > value.length > 0, }} defaultValue="" control={control} render={({ onChange, value }) => ( )} /> } fieldId="realmRole" > ( )} /> } > ( { onChange(value.toString()); setApplyToResourceType(value); }} /> )} /> {!applyToResourceType && ( } helperTextInvalid={t("common:required")} fieldId="resourcesAndAuthScopes" > ((item) => ({ name: item.name!, key: item._id!, }))} resources={resources} isKeySelectable name="resources" /> )} {applyToResourceType && ( <> } fieldId="client" > } fieldId="authScopes" > ( )} /> )} setIsExpanded(!isExpanded)} isExpanded={isExpanded} > } helperTextInvalid={t("common:required")} fieldId="contextualAttributes" > ); };