import { ActionGroup, Button, ExpandableSection, FormGroup, PageSection, Select, SelectOption, 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 type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import type EvaluationResultRepresentation from "@keycloak/keycloak-admin-client/lib/defs/evaluationResultRepresentation"; import type PolicyEvaluationResponse from "@keycloak/keycloak-admin-client/lib/defs/policyEvaluationResponse"; import type ResourceEvaluation from "@keycloak/keycloak-admin-client/lib/defs/resourceEvaluation"; import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation"; import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation"; import { ClientSelect } from "../../components/client/ClientSelect"; import { FormAccess } from "../../components/form-access/FormAccess"; import { HelpItem } from "ui-shared"; import { keyValueToArray, KeyValueType, } from "../../components/key-value-form/key-value-convert"; import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; import { FormPanel } from "../../components/scroll-form/FormPanel"; import { UserSelect } from "../../components/users/UserSelect"; import { useAccess } from "../../context/access/Access"; import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; import { useRealm } from "../../context/realm-context/RealmContext"; import { ForbiddenSection } from "../../ForbiddenSection"; import { FormFields } from "../ClientDetails"; import { defaultContextAttributes } from "../utils"; import { Results } from "./evaluate/Results"; import { KeyBasedAttributeInput } from "./KeyBasedAttributeInput"; import { useAlerts } from "../../components/alert/Alerts"; import "./auth-evaluate.css"; interface EvaluateFormInputs extends Omit { alias: string; authScopes: string[]; context: { attributes: Record[]; }; resources?: Record[]; client: FormFields; user: string[]; } export type AttributeType = { key: string; name: string; custom?: boolean; values?: { [key: string]: string; }[]; }; type ClientSettingsProps = { client: ClientRepresentation; save: () => void; }; export type AttributeForm = Omit< EvaluateFormInputs, "context" | "resources" > & { context: { attributes?: KeyValueType[]; }; resources?: KeyValueType[]; }; type Props = ClientSettingsProps & EvaluationResultRepresentation; export const AuthorizationEvaluate = ({ client }: Props) => { const form = useForm({ mode: "onChange" }); const { control, register, reset, trigger, formState: { isValid, errors }, } = form; const { t } = useTranslation("clients"); const { adminClient } = useAdminClient(); const { addError } = useAlerts(); const realm = useRealm(); const [scopesDropdownOpen, setScopesDropdownOpen] = 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 [evaluateResult, setEvaluateResult] = useState(); const [clientRoles, setClientRoles] = useState([]); const { hasAccess } = useAccess(); if (!hasAccess("view-users")) return ; useFetch( () => adminClient.roles.find(), (roles) => { setClientRoles(roles); }, [] ); useFetch( () => Promise.all([ adminClient.clients.listResources({ id: client.id!, }), adminClient.clients.listAllScopes({ id: client.id!, }), ]), ([resources, scopes]) => { setResources(resources); setScopes(scopes); }, [] ); const evaluate = async () => { if (!(await trigger())) { return; } const formValues = form.getValues(); const keys = keyValueToArray(formValues.resources as KeyValueType[]); const resEval: ResourceEvaluation = { roleIds: formValues.roleIds ?? [], clientId: formValues.client.id!, userId: formValues.user![0], resources: resources .filter((resource) => Object.keys(keys).includes(resource.name!)) .map((r) => ({ ...r, scopes: r.scopes?.filter((s) => Object.values(keys) .flatMap((v) => v) .includes(s.name!) ), })), entitlements: false, context: { attributes: Object.fromEntries( formValues.context.attributes .filter((item) => item.key || item.value !== "") .map(({ key, value }) => [key, value]) ), }, }; try { const evaluation = await adminClient.clients.evaluateResource( { id: client.id!, realm: realm.realm }, resEval ); setEvaluateResult(evaluation); } catch (error) { addError("clients:evaluateError", error); } }; const user = useWatch({ control, name: "user", defaultValue: [] }); const roles = useWatch({ control, name: "roleIds", defaultValue: [] }); if (evaluateResult) { return ( setEvaluateResult(undefined)} /> ); } return ( } fieldId="realmRole" validated={errors.roleIds ? "error" : "default"} helperTextInvalid={t("common:required")} isRequired={user.length === 0} > (value || "").length > 0 || user.length > 0, }} render={({ field }) => ( )} /> } > {!applyToResourceType ? ( } fieldId="resourcesAndScopes" > ((item) => ({ name: item.name!, key: item._id!, }))} resources={resources} name="resources" /> ) : ( <> } fieldId="client" validated={errors.alias ? "error" : "default"} helperTextInvalid={t("common:required")} > } fieldId="authScopes" > ( )} /> )} setIsExpanded(!isExpanded)} isExpanded={isExpanded} > } helperTextInvalid={t("common:required")} fieldId="contextualAttributes" > ); };