import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig"; import type { UserProfileAttribute } from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig"; import { AlertVariant, Button, Form, PageSection, } from "@patternfly/react-core"; import { flatten } from "flat"; import { useState } from "react"; import { FormProvider, useForm, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { FixedButtonsGroup } from "../components/form/FixedButtonGroup"; import { ScrollForm } from "../components/scroll-form/ScrollForm"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { convertToFormValues } from "../util"; import { useFetch } from "../utils/useFetch"; import { useParams } from "../utils/useParams"; import type { AttributeParams } from "./routes/Attribute"; import { toUserProfile } from "./routes/UserProfile"; import { UserProfileProvider } from "./user-profile/UserProfileContext"; import { AttributeAnnotations } from "./user-profile/attribute/AttributeAnnotations"; import { AttributeGeneralSettings } from "./user-profile/attribute/AttributeGeneralSettings"; import { AttributePermission } from "./user-profile/attribute/AttributePermission"; import { AttributeValidations } from "./user-profile/attribute/AttributeValidations"; import "./realm-settings-section.css"; type IndexedAnnotations = { key: string; value?: Record; }; export type IndexedValidations = { key: string; value?: Record; }; type UserProfileAttributeType = Omit< UserProfileAttribute, "validations" | "annotations" > & Attribute & Permission & { validations: IndexedValidations[]; annotations: IndexedAnnotations[]; }; type Attribute = { roles: string[]; scopes: string[]; isRequired: boolean; }; type Permission = { view: PermissionView[]; edit: PermissionEdit[]; }; type PermissionView = [ { adminView: boolean; userView: boolean; }, ]; type PermissionEdit = [ { adminEdit: boolean; userEdit: boolean; }, ]; export const USERNAME_EMAIL = ["username", "email"]; const CreateAttributeFormContent = ({ save, }: { save: (profileConfig: UserProfileConfig) => void; }) => { const { t } = useTranslation(); const form = useFormContext(); const { realm, attributeName } = useParams(); const editMode = attributeName ? true : false; return ( }, { title: t("permission"), panel: }, { title: t("validations"), panel: }, { title: t("annotations"), panel: }, ]} />
{t("common:cancel")}
); }; export default function NewAttributeSettings() { const { realm, attributeName } = useParams(); const form = useForm(); const { t } = useTranslation(); const navigate = useNavigate(); const { addAlert, addError } = useAlerts(); const [config, setConfig] = useState(null); const editMode = attributeName ? true : false; useFetch( () => adminClient.users.getProfile(), (config) => { setConfig(config); const { annotations, validations, permissions, selector, required, ...values } = config.attributes!.find( (attribute) => attribute.name === attributeName, ) || {}; convertToFormValues(values, form.setValue); Object.entries( flatten({ permissions, selector, required }, { safe: true }), ).map(([key, value]) => form.setValue(key as any, value)); form.setValue( "annotations", Object.entries(annotations || {}).map(([key, value]) => ({ key, value: value as Record, })), ); form.setValue( "validations", Object.entries(validations || {}).map(([key, value]) => ({ key, value: value as Record, })), ); form.setValue("isRequired", required !== undefined); }, [], ); const save = async (profileConfig: UserProfileAttributeType) => { const validations = profileConfig.validations.reduce( (prevValidations, currentValidations) => { prevValidations[currentValidations.key] = currentValidations.value || {}; return prevValidations; }, {} as Record, ); const annotations = profileConfig.annotations.reduce( (obj, item) => Object.assign(obj, { [item.key]: item.value }), {}, ); const patchAttributes = () => config?.attributes!.map((attribute) => { if (attribute.name !== attributeName) { return attribute; } delete attribute.required; return Object.assign( { ...attribute, name: attributeName, displayName: profileConfig.displayName!, selector: profileConfig.selector, permissions: profileConfig.permissions!, annotations, validations, }, profileConfig.isRequired ? { required: profileConfig.required } : undefined, profileConfig.group ? { group: profileConfig.group } : { group: null }, ); }); const addAttribute = () => config?.attributes!.concat([ Object.assign( { name: profileConfig.name, displayName: profileConfig.displayName!, required: profileConfig.isRequired ? profileConfig.required : undefined, selector: profileConfig.selector, permissions: profileConfig.permissions!, annotations, validations, }, profileConfig.isRequired ? { required: profileConfig.required } : undefined, profileConfig.group ? { group: profileConfig.group } : undefined, ), ] as UserProfileAttribute); const updatedAttributes = editMode ? patchAttributes() : addAttribute(); try { await adminClient.users.updateProfile({ ...config, attributes: updatedAttributes as UserProfileAttribute[], realm, }); navigate(toUserProfile({ realm, tab: "attributes" })); addAlert( t("realm-settings:createAttributeSuccess"), AlertVariant.success, ); } catch (error) { addError("realm-settings:createAttributeError", error); } }; return ( form.handleSubmit(save)()} /> ); }