2022-03-16 09:32:23 +00:00
|
|
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
|
|
/* eslint-disable prettier/prettier */
|
|
|
|
import React, { useState } from "react";
|
|
|
|
import {
|
|
|
|
Divider,
|
|
|
|
FormGroup,
|
|
|
|
Radio,
|
|
|
|
Select,
|
|
|
|
SelectOption,
|
|
|
|
SelectVariant,
|
|
|
|
Switch,
|
|
|
|
TextInput,
|
|
|
|
} from "@patternfly/react-core";
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import { HelpItem } from "../../../components/help-enabler/HelpItem";
|
2022-03-21 14:46:01 +00:00
|
|
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
2022-03-16 09:32:23 +00:00
|
|
|
import { FormAccess } from "../../../components/form-access/FormAccess";
|
|
|
|
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
|
|
|
|
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
|
2022-03-31 13:28:48 +00:00
|
|
|
import type { AttributeParams } from "../../routes/Attribute";
|
|
|
|
import { useParams } from "react-router-dom";
|
|
|
|
import { isEqual } from "lodash-es";
|
|
|
|
|
2022-03-16 09:32:23 +00:00
|
|
|
import "../../realm-settings-section.css";
|
|
|
|
|
|
|
|
const REQUIRED_FOR = [
|
2022-03-21 14:46:01 +00:00
|
|
|
{ label: "Both users and admins", value: ["admin", "user"] },
|
|
|
|
{ label: "Only users", value: ["user"] },
|
|
|
|
{ label: "Only admins", value: ["admin"] },
|
|
|
|
] as const;
|
2022-03-16 09:32:23 +00:00
|
|
|
|
|
|
|
export const AttributeGeneralSettings = () => {
|
|
|
|
const { t } = useTranslation("realm-settings");
|
|
|
|
const adminClient = useAdminClient();
|
|
|
|
const form = useFormContext();
|
|
|
|
const [clientScopes, setClientScopes] =
|
|
|
|
useState<ClientScopeRepresentation[]>();
|
|
|
|
const [selectEnabledWhenOpen, setSelectEnabledWhenOpen] = useState(false);
|
|
|
|
const [selectRequiredForOpen, setSelectRequiredForOpen] = useState(false);
|
|
|
|
const [isAttributeGroupDropdownOpen, setIsAttributeGroupDropdownOpen] =
|
|
|
|
useState(false);
|
2022-03-31 13:28:48 +00:00
|
|
|
const { attributeName } = useParams<AttributeParams>();
|
|
|
|
const editMode = attributeName ? true : false;
|
|
|
|
|
|
|
|
const selectedScopes = useWatch({
|
|
|
|
control: form.control,
|
|
|
|
name: "selector.scopes",
|
|
|
|
defaultValue: [],
|
|
|
|
});
|
2022-03-16 09:32:23 +00:00
|
|
|
|
2022-03-31 13:28:48 +00:00
|
|
|
const requiredScopes = useWatch({
|
2022-03-21 14:46:01 +00:00
|
|
|
control: form.control,
|
2022-03-31 13:28:48 +00:00
|
|
|
name: "required.scopes",
|
|
|
|
defaultValue: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
const required = useWatch({
|
|
|
|
control: form.control,
|
|
|
|
name: "isRequired",
|
2022-03-21 14:46:01 +00:00
|
|
|
defaultValue: false,
|
|
|
|
});
|
|
|
|
|
2022-03-16 09:32:23 +00:00
|
|
|
useFetch(
|
|
|
|
() => adminClient.clientScopes.find(),
|
|
|
|
(clientScopes) => {
|
|
|
|
setClientScopes(clientScopes);
|
|
|
|
},
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<FormAccess role="manage-realm" isHorizontal>
|
|
|
|
<FormGroup
|
|
|
|
label={t("attributeName")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText="realm-settings-help:attributeNameHelp"
|
|
|
|
fieldLabelId="realm-settings:attributeName"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
fieldId="kc-attribute-name"
|
|
|
|
isRequired
|
2022-03-29 07:49:43 +00:00
|
|
|
validated={form.errors.name ? "error" : "default"}
|
|
|
|
helperTextInvalid={form.errors.name?.message}
|
2022-03-16 09:32:23 +00:00
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
isRequired
|
|
|
|
type="text"
|
|
|
|
id="kc-attribute-name"
|
|
|
|
name="name"
|
|
|
|
defaultValue=""
|
|
|
|
ref={form.register({
|
|
|
|
required: {
|
|
|
|
value: true,
|
2022-03-31 13:28:48 +00:00
|
|
|
message: t("validateName"),
|
2022-03-16 09:32:23 +00:00
|
|
|
},
|
|
|
|
})}
|
|
|
|
data-testid="attribute-name"
|
2022-03-31 13:28:48 +00:00
|
|
|
isDisabled={editMode}
|
2022-03-29 07:49:43 +00:00
|
|
|
validated={form.errors.name ? "error" : "default"}
|
2022-03-16 09:32:23 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("attributeDisplayName")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText="realm-settings-help:attributeDisplayNameHelp"
|
|
|
|
fieldLabelId="realm-settings:attributeDisplayName"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
fieldId="kc-attribute-display-name"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
type="text"
|
|
|
|
id="kc-attribute-display-name"
|
|
|
|
name="displayName"
|
|
|
|
defaultValue=""
|
|
|
|
ref={form.register}
|
|
|
|
data-testid="attribute-display-name"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("attributeGroup")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText="realm-setting-help:attributeGroupHelp"
|
|
|
|
fieldLabelId="realm-setting:attributeGroup"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
fieldId="kc-attribute-group"
|
|
|
|
>
|
|
|
|
<Controller
|
|
|
|
name="attributeGroup"
|
|
|
|
defaultValue=""
|
|
|
|
control={form.control}
|
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<Select
|
|
|
|
toggleId="kc-attributeGroup"
|
|
|
|
onToggle={() =>
|
|
|
|
setIsAttributeGroupDropdownOpen(!isAttributeGroupDropdownOpen)
|
|
|
|
}
|
|
|
|
isOpen={isAttributeGroupDropdownOpen}
|
|
|
|
onSelect={(_, value) => {
|
|
|
|
onChange(value.toString());
|
|
|
|
setIsAttributeGroupDropdownOpen(false);
|
|
|
|
}}
|
|
|
|
selections={value}
|
|
|
|
variant={SelectVariant.single}
|
|
|
|
>
|
|
|
|
<SelectOption key={0} value="" isPlaceholder>
|
|
|
|
Select a group
|
|
|
|
</SelectOption>
|
|
|
|
<SelectOption key={1} value=""></SelectOption>
|
|
|
|
</Select>
|
|
|
|
)}
|
|
|
|
></Controller>
|
|
|
|
</FormGroup>
|
|
|
|
<Divider />
|
|
|
|
<FormGroup label={t("enabledWhen")} fieldId="enabledWhen" hasNoPaddingTop>
|
2022-03-31 13:28:48 +00:00
|
|
|
<Radio
|
|
|
|
id="always"
|
|
|
|
data-testid="always"
|
|
|
|
isChecked={selectedScopes.length === clientScopes?.length}
|
2022-03-16 09:32:23 +00:00
|
|
|
name="enabledWhen"
|
2022-03-31 13:28:48 +00:00
|
|
|
label={t("always")}
|
|
|
|
onChange={(value) => {
|
|
|
|
if (value) {
|
|
|
|
form.setValue(
|
|
|
|
"selector.scopes",
|
|
|
|
clientScopes?.map((s) => s.name)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
form.setValue("selector.scopes", []);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
className="pf-u-mb-md"
|
|
|
|
/>
|
|
|
|
<Radio
|
|
|
|
id="scopesAsRequested"
|
|
|
|
data-testid="scopesAsRequested"
|
|
|
|
isChecked={selectedScopes.length !== clientScopes?.length}
|
|
|
|
name="enabledWhen"
|
|
|
|
label={t("scopesAsRequested")}
|
|
|
|
onChange={(value) => {
|
|
|
|
if (value) {
|
|
|
|
form.setValue("selector.scopes", []);
|
|
|
|
} else {
|
|
|
|
form.setValue(
|
|
|
|
"selector.scopes",
|
|
|
|
clientScopes?.map((s) => s.name)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
className="pf-u-mb-md"
|
2022-03-16 09:32:23 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup fieldId="kc-scope-enabled-when">
|
|
|
|
<Controller
|
2022-03-31 13:28:48 +00:00
|
|
|
name="selector.scopes"
|
2022-03-16 09:32:23 +00:00
|
|
|
control={form.control}
|
2022-03-21 14:46:01 +00:00
|
|
|
defaultValue={[]}
|
2022-03-31 13:28:48 +00:00
|
|
|
render={({ onChange, value }) => (
|
2022-03-16 09:32:23 +00:00
|
|
|
<Select
|
|
|
|
name="scopes"
|
|
|
|
data-testid="enabled-when-scope-field"
|
|
|
|
variant={SelectVariant.typeaheadMulti}
|
|
|
|
typeAheadAriaLabel="Select"
|
|
|
|
chipGroupProps={{
|
|
|
|
numChips: 3,
|
|
|
|
expandedText: t("common:hide"),
|
|
|
|
collapsedText: t("common:showRemaining"),
|
|
|
|
}}
|
|
|
|
onToggle={(isOpen) => setSelectEnabledWhenOpen(isOpen)}
|
|
|
|
selections={value}
|
|
|
|
onSelect={(_, selectedValue) => {
|
|
|
|
const option = selectedValue.toString();
|
|
|
|
let changedValue = [""];
|
2022-03-21 14:46:01 +00:00
|
|
|
if (value) {
|
2022-03-16 09:32:23 +00:00
|
|
|
changedValue = value.includes(option)
|
2022-03-31 13:28:48 +00:00
|
|
|
? value.filter((item: string) => item !== option)
|
2022-03-16 09:32:23 +00:00
|
|
|
: [...value, option];
|
|
|
|
} else {
|
|
|
|
changedValue = [option];
|
2022-03-21 14:46:01 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 09:32:23 +00:00
|
|
|
onChange(changedValue);
|
|
|
|
}}
|
|
|
|
onClear={(selectedValues) => {
|
|
|
|
selectedValues.stopPropagation();
|
|
|
|
onChange([]);
|
|
|
|
}}
|
|
|
|
isOpen={selectEnabledWhenOpen}
|
2022-03-31 13:28:48 +00:00
|
|
|
isDisabled={selectedScopes.length === clientScopes?.length}
|
2022-03-16 09:32:23 +00:00
|
|
|
aria-labelledby={"scope"}
|
|
|
|
>
|
|
|
|
{clientScopes?.map((option) => (
|
|
|
|
<SelectOption key={option.name} value={option.name} />
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<Divider />
|
|
|
|
<FormGroup
|
|
|
|
label={t("required")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText="realm-settings-help:requiredHelp"
|
|
|
|
fieldLabelId="realm-settings:required"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
fieldId="kc-required"
|
|
|
|
hasNoPaddingTop
|
|
|
|
>
|
|
|
|
<Controller
|
2022-03-31 13:28:48 +00:00
|
|
|
name="isRequired"
|
|
|
|
data-testid="required"
|
2022-03-21 14:46:01 +00:00
|
|
|
defaultValue={false}
|
2022-03-16 09:32:23 +00:00
|
|
|
control={form.control}
|
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<Switch
|
|
|
|
id={"kc-required"}
|
2022-03-31 13:28:48 +00:00
|
|
|
onChange={onChange}
|
|
|
|
isChecked={value}
|
2022-03-16 09:32:23 +00:00
|
|
|
label={t("common:on")}
|
|
|
|
labelOff={t("common:off")}
|
|
|
|
/>
|
|
|
|
)}
|
2022-03-31 13:28:48 +00:00
|
|
|
/>
|
2022-03-16 09:32:23 +00:00
|
|
|
</FormGroup>
|
2022-03-31 13:28:48 +00:00
|
|
|
{required && (
|
2022-03-21 14:46:01 +00:00
|
|
|
<>
|
|
|
|
<FormGroup
|
|
|
|
label={t("requiredFor")}
|
|
|
|
fieldId="requiredFor"
|
|
|
|
hasNoPaddingTop
|
|
|
|
>
|
|
|
|
<Controller
|
2022-03-31 13:28:48 +00:00
|
|
|
name="required.roles"
|
2022-03-21 14:46:01 +00:00
|
|
|
data-testid="requiredFor"
|
|
|
|
defaultValue={REQUIRED_FOR[0].value}
|
|
|
|
control={form.control}
|
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<div className="kc-requiredFor">
|
|
|
|
{REQUIRED_FOR.map((option) => (
|
|
|
|
<Radio
|
|
|
|
id={option.label}
|
|
|
|
key={option.label}
|
2022-03-31 13:28:48 +00:00
|
|
|
data-testid={option.label}
|
|
|
|
isChecked={isEqual(value, option.value)}
|
2022-03-21 14:46:01 +00:00
|
|
|
name="roles"
|
2022-03-31 13:28:48 +00:00
|
|
|
onChange={() => {
|
|
|
|
onChange(option.value);
|
|
|
|
}}
|
2022-03-21 14:46:01 +00:00
|
|
|
label={option.label}
|
|
|
|
className="kc-requiredFor-option"
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("requiredWhen")}
|
|
|
|
fieldId="requiredWhen"
|
|
|
|
hasNoPaddingTop
|
|
|
|
>
|
2022-03-31 13:28:48 +00:00
|
|
|
<Radio
|
|
|
|
id="requiredAlways"
|
|
|
|
data-testid="requiredAlways"
|
|
|
|
isChecked={requiredScopes.length === clientScopes?.length}
|
2022-03-21 14:46:01 +00:00
|
|
|
name="requiredWhen"
|
2022-03-31 13:28:48 +00:00
|
|
|
label={t("always")}
|
|
|
|
onChange={(value) => {
|
|
|
|
if (value) {
|
|
|
|
form.setValue(
|
|
|
|
"required.scopes",
|
|
|
|
clientScopes?.map((s) => s.name)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
form.setValue("required.scopes", []);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
className="pf-u-mb-md"
|
|
|
|
/>
|
|
|
|
<Radio
|
|
|
|
id="requiredScopesAsRequested"
|
|
|
|
data-testid="requiredScopesAsRequested"
|
|
|
|
isChecked={requiredScopes.length !== clientScopes?.length}
|
|
|
|
name="requiredWhen"
|
|
|
|
label={t("scopesAsRequested")}
|
|
|
|
onChange={(value) => {
|
|
|
|
if (value) {
|
|
|
|
form.setValue("required.scopes", []);
|
|
|
|
} else {
|
|
|
|
form.setValue(
|
|
|
|
"required.scopes",
|
|
|
|
clientScopes?.map((s) => s.name)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
className="pf-u-mb-md"
|
2022-03-21 14:46:01 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup fieldId="kc-scope-required-when">
|
|
|
|
<Controller
|
2022-03-31 13:28:48 +00:00
|
|
|
name="required.scopes"
|
2022-03-21 14:46:01 +00:00
|
|
|
control={form.control}
|
|
|
|
defaultValue={[]}
|
2022-03-31 13:28:48 +00:00
|
|
|
render={({ onChange, value }) => (
|
2022-03-21 14:46:01 +00:00
|
|
|
<Select
|
|
|
|
name="scopeRequired"
|
|
|
|
data-testid="required-when-scope-field"
|
|
|
|
variant={SelectVariant.typeaheadMulti}
|
|
|
|
typeAheadAriaLabel="Select"
|
|
|
|
chipGroupProps={{
|
|
|
|
numChips: 3,
|
|
|
|
expandedText: t("common:hide"),
|
|
|
|
collapsedText: t("common:showRemaining"),
|
2022-03-16 09:32:23 +00:00
|
|
|
}}
|
2022-03-21 14:46:01 +00:00
|
|
|
onToggle={(isOpen) => setSelectRequiredForOpen(isOpen)}
|
|
|
|
selections={value}
|
|
|
|
onSelect={(_, selectedValue) => {
|
|
|
|
const option = selectedValue.toString();
|
|
|
|
let changedValue = [""];
|
|
|
|
if (value) {
|
|
|
|
changedValue = value.includes(option)
|
2022-03-31 13:28:48 +00:00
|
|
|
? value.filter((item: string) => item !== option)
|
2022-03-21 14:46:01 +00:00
|
|
|
: [...value, option];
|
|
|
|
} else {
|
|
|
|
changedValue = [option];
|
|
|
|
}
|
|
|
|
onChange(changedValue);
|
|
|
|
}}
|
|
|
|
onClear={(selectedValues) => {
|
|
|
|
selectedValues.stopPropagation();
|
|
|
|
onChange([]);
|
|
|
|
}}
|
|
|
|
isOpen={selectRequiredForOpen}
|
2022-03-31 13:28:48 +00:00
|
|
|
isDisabled={requiredScopes.length === clientScopes?.length}
|
2022-03-21 14:46:01 +00:00
|
|
|
aria-labelledby={"scope"}
|
|
|
|
>
|
|
|
|
{clientScopes?.map((option) => (
|
|
|
|
<SelectOption key={option.name} value={option.name} />
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
</>
|
|
|
|
)}
|
2022-03-16 09:32:23 +00:00
|
|
|
</FormAccess>
|
|
|
|
);
|
|
|
|
};
|