From 158bd073989e819cb6b2521ca43298ce202a0763 Mon Sep 17 00:00:00 2001 From: Jenny <32821331+jenny-s51@users.noreply.github.com> Date: Tue, 15 Feb 2022 12:52:46 -0500 Subject: [PATCH] index on authEvaluateTab: f0a2494d Add nexus profile for releases (#1704) (#1961) auth evaluate wip wip auth evaluate tab add identity information and permissions help text and wip evaluate wip contextual attributes wip contextual attributes add conditional dropdown for auth method wip resource fields scopes and context inputs working fix resources error and update onChange package-lock cleanup: remove comments and log stmts add conditional fields when applyToResourceType is true package.json from main PR feedback from Erik Co-authored-by: Erik Jan de Wit Update src/clients/authorization/AuthorizationEvaluate.tsx Co-authored-by: Erik Jan de Wit handleSubmit remove log stmt fix cypress test PR feedback from Erik try fixing policies test PR feedback from Jon Co-authored-by: Jon Koops PR feedback from Jon rename id revert client policy test reset add trigger authEvaluateReset conditionally render based on type Apply suggestions from code review Co-authored-by: Jon Koops PR feedback remove controller Update src/components/attribute-input/AttributeInput.tsx Co-authored-by: Jon Koops PR feedback Update src/clients/authorization/AuthorizationEvaluate.tsx Co-authored-by: Jon Koops remove reset --- src/clients/ClientDetails.tsx | 1 - .../authorization/AuthorizationEvaluate.tsx | 111 ++++++++++++------ .../attribute-form/AttributeForm.tsx | 7 +- .../attribute-input/AttributeInput.tsx | 58 ++++----- src/realm-roles/RealmRoleTabs.tsx | 5 - 5 files changed, 110 insertions(+), 72 deletions(-) diff --git a/src/clients/ClientDetails.tsx b/src/clients/ClientDetails.tsx index 96766f40db..a39437bf2d 100644 --- a/src/clients/ClientDetails.tsx +++ b/src/clients/ClientDetails.tsx @@ -598,7 +598,6 @@ export default function ClientDetails() { clientRoles={clientRoles} users={users} save={save} - reset={() => setupForm(client)} /> { + applyToResource: boolean; + alias: string; + authScopes: string[]; + context: { + attributes: Record[]; + }; + resources: Record[]; +} export type AttributeType = { key: string; @@ -42,20 +54,28 @@ type ClientSettingsProps = { clients: ClientRepresentation[]; clientName?: string; save: () => void; - reset: () => void; users: UserRepresentation[]; clientRoles: RoleRepresentation[]; }; +export type AttributeForm = Omit< + EvaluateFormInputs, + "context" | "resources" +> & { + context: { + attributes?: KeyValueType[]; + }; + resources?: KeyValueType[]; +}; + export const AuthorizationEvaluate = ({ clients, clientRoles, clientName, users, - reset, }: ClientSettingsProps) => { - const form = useFormContext(); - const { control } = form; + const form = useFormContext(); + const { control, reset, trigger } = form; const { t } = useTranslation("clients"); const adminClient = useAdminClient(); const realm = useRealm(); @@ -90,15 +110,27 @@ export const AuthorizationEvaluate = ({ [] ); - const evaluate = (formValues: ResourceEvaluation) => { + 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: formValues.context, - resources: formValues.resources, - clientId: selectedClient?.id!, + context: { + attributes: Object.fromEntries( + formValues.context.attributes + .filter((item) => item.key || item.value !== "") + .map(({ key, value }) => [key, value]) + ), + }, }; + return adminClient.clients.evaluateResource( { id: clientId!, realm: realm.realm }, resEval @@ -128,7 +160,10 @@ export const AuthorizationEvaluate = ({ fieldId="client" > value.length > 0, + }} defaultValue={clientName} control={control} render={({ onChange, value }) => ( @@ -140,7 +175,7 @@ export const AuthorizationEvaluate = ({ onChange((value as ClientRepresentation).clientId); setClientsDropdownOpen(false); }} - selections={value} + selections={selectedClient === value ? value : clientName} variant={SelectVariant.typeahead} aria-label={t("client")} isOpen={clientsDropdownOpen} @@ -171,6 +206,9 @@ export const AuthorizationEvaluate = ({ > value.length > 0, + }} defaultValue="" control={control} render={({ onChange, value }) => ( @@ -212,7 +250,7 @@ export const AuthorizationEvaluate = ({ fieldId="realmRole" > item.name!)} + selectableValues={resources.map((item) => ({ + name: item.name!, + key: item._id!, + }))} resources={resources} isKeySelectable name="resources" @@ -390,35 +431,33 @@ export const AuthorizationEvaluate = ({ fieldId={name!} > item.name - )} + selectableValues={defaultContextAttributes} isKeySelectable - name="context" + name="context.attributes" /> - - - - - + + + + + ); diff --git a/src/components/attribute-form/AttributeForm.tsx b/src/components/attribute-form/AttributeForm.tsx index 339e53444a..e3da98a9a5 100644 --- a/src/components/attribute-form/AttributeForm.tsx +++ b/src/components/attribute-form/AttributeForm.tsx @@ -5,7 +5,10 @@ import { ActionGroup, Button } from "@patternfly/react-core"; import type { RoleRepresentation } from "../../model/role-model"; import type { KeyValueType } from "./attribute-convert"; -import { AttributeInput } from "../attribute-input/AttributeInput"; +import { + AttributeInput, + AttributeType, +} from "../attribute-input/AttributeInput"; import { FormAccess } from "../form-access/FormAccess"; export type AttributeForm = Omit & { @@ -15,7 +18,7 @@ export type AttributeForm = Omit & { export type AttributesFormProps = { form: UseFormMethods; isKeySelectable?: boolean; - selectableValues?: string[]; + selectableValues?: AttributeType[]; save?: (model: AttributeForm) => void; reset?: () => void; }; diff --git a/src/components/attribute-input/AttributeInput.tsx b/src/components/attribute-input/AttributeInput.tsx index 653991a91c..5d5e848d97 100644 --- a/src/components/attribute-input/AttributeInput.tsx +++ b/src/components/attribute-input/AttributeInput.tsx @@ -24,7 +24,7 @@ import { camelCase } from "lodash-es"; import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation"; export type AttributeType = { - key: string; + key?: string; name: string; custom?: boolean; values?: { @@ -34,7 +34,7 @@ export type AttributeType = { type AttributeInputProps = { name: string; - selectableValues?: string[]; + selectableValues?: AttributeType[]; isKeySelectable?: boolean; resources?: ResourceRepresentation[]; }; @@ -56,7 +56,7 @@ export const AttributeInput = ({ if (!fields.length) { append({ key: "", value: "" }); } - }, []); + }, [fields]); const [isKeyOpenArray, setIsKeyOpenArray] = useState([false]); const watchLastKey = watch(`${name}[${fields.length - 1}].key`, ""); @@ -84,16 +84,32 @@ export const AttributeInput = ({ if (selectableValues) { attributeValues = defaultContextAttributes.find( - (attr) => attr.name === getValues().context[rowIndex]?.key + (attr) => attr.key === getValues().context[rowIndex]?.key )?.values; } + const renderSelectOptionType = () => { + if (attributeValues?.length && !resources) { + return attributeValues.map((attr) => ( + + {attr.name} + + )); + } else if (scopeValues?.length) { + return scopeValues.map((scope) => ( + + {scope.name} + + )); + } + }; + const getMessageBundleKey = (attributeName: string) => camelCase(attributeName).replace(/\W/g, ""); return ( - {scopeValues?.length || attributeValues?.length ? ( + {resources || attributeValues?.length ? ( toggleValueSelect(rowIndex, open)} isOpen={isValueOpenArray[rowIndex]} - variant={ - resources - ? SelectVariant.typeaheadMulti - : SelectVariant.typeahead - } + variant={SelectVariant.typeahead} typeAheadAriaLabel={t("clients:selectOrTypeAKey")} placeholderText={t("clients:selectOrTypeAKey")} selections={value} onSelect={(_, v) => { - if (resources) { - const option = v.toString(); - if (value.includes(option)) { - onChange(value.filter((item: string) => item !== option)); - } else { - onChange([...value, option]); - } - } else { - onChange(v); - } + onChange(v); + toggleValueSelect(rowIndex, false); }} > - {(scopeValues || attributeValues)?.map((scope) => ( - - ))} + {renderSelectOptionType()} )} /> @@ -192,18 +194,18 @@ export const AttributeInput = ({ placeholderText={t("clients:selectOrTypeAKey")} selections={value} onSelect={(_, v) => { - onChange(v); + onChange(v.toString()); toggleKeySelect(rowIndex, false); }} > {selectableValues?.map((attribute) => ( - {attribute} + {attribute.name} ))} diff --git a/src/realm-roles/RealmRoleTabs.tsx b/src/realm-roles/RealmRoleTabs.tsx index a2b3d49013..5e43757703 100644 --- a/src/realm-roles/RealmRoleTabs.tsx +++ b/src/realm-roles/RealmRoleTabs.tsx @@ -39,7 +39,6 @@ import { ClientRoleRoute, toClientRole, } from "./routes/ClientRole"; -import { defaultContextAttributes } from "../clients/utils"; export default function RealmRoleTabs() { const { t } = useTranslation("roles"); @@ -378,10 +377,6 @@ export default function RealmRoleTabs() { title={{t("common:attributes")}} > item.key - )} form={form} save={save} reset={() => reset(role)}