Add User federation kerberos settings controllers (#224)

* add form controllers to user fed kerberos settings

* comments out test buttons

* add optional settings with todos

* correct the kerberos help text

* updates cache policy dropdowns

* use ComponentRepresentation

* use FormAccess component

* fix required settings and i18n

* remove unused form

* remove unused file

Co-authored-by: jenny-s51 <jshandel12@gmail.com>
This commit is contained in:
Sarah Rambacher 2020-11-24 08:56:31 -05:00 committed by GitHub
parent dc2ceef27b
commit ad09c883e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 330 additions and 93 deletions

View file

@ -56,6 +56,14 @@
"required": "Required field", "required": "Required field",
"maxLength": "Max length {{length}}", "maxLength": "Max length {{length}}",
"createRealm": "Create Realm" "createRealm": "Create Realm",
"Sunday": "Sunday",
"Monday": "Monday",
"Tuesday": "Tuesday",
"Wednesday": "Wednesday",
"Thursday": "Thursday",
"Friday": "Friday",
"Saturday": "Saturday"
} }
} }

View file

@ -1,16 +1,55 @@
import { Form, FormGroup, Select, SelectOption } from "@patternfly/react-core"; import {
FormGroup,
Select,
SelectOption,
SelectVariant,
TextInput,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import React from "react";
import { HelpItem } from "../components/help-enabler/HelpItem"; import { HelpItem } from "../components/help-enabler/HelpItem";
import React, { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation";
import { FormAccess } from "../components/form-access/FormAccess";
export const KerberosSettingsCache = () => { export const KerberosSettingsCache = () => {
const { t } = useTranslation("user-federation"); const { t } = useTranslation("user-federation");
const helpText = useTranslation("user-federation-help").t; const helpText = useTranslation("user-federation-help").t;
const [isCachePolicyDropdownOpen, setIsCachePolicyDropdownOpen] = useState(
false
);
const [isEvictionHourDropdownOpen, setIsEvictionHourDropdownOpen] = useState(
false
);
const [
isEvictionMinuteDropdownOpen,
setIsEvictionMinuteDropdownOpen,
] = useState(false);
const [isEvictionDayDropdownOpen, setIsEvictionDayDropdownOpen] = useState(
false
);
const { control, register } = useForm<ComponentRepresentation>();
const hourOptions = [
<SelectOption key={0} value={t("common:selectOne")} isPlaceholder />,
];
for (let index = 1; index <= 24; index++) {
hourOptions.push(<SelectOption key={index + 1} value={index} />);
}
const minuteOptions = [
<SelectOption key={0} value={t("common:selectOne")} isPlaceholder />,
];
for (let index = 1; index <= 60; index++) {
minuteOptions.push(<SelectOption key={index + 1} value={index} />);
}
return ( return (
<> <>
{/* Cache settings */} {/* Cache settings */}
<Form isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup <FormGroup
label={t("cachePolicy")} label={t("cachePolicy")}
labelIcon={ labelIcon={
@ -22,30 +61,180 @@ export const KerberosSettingsCache = () => {
} }
fieldId="kc-cache-policy" fieldId="kc-cache-policy"
> >
<Controller
name="cachePolicy"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select <Select
toggleId="kc-cache-policy" toggleId="kc-cache-policy"
// isOpen={openType} required
onToggle={() => {}} onToggle={() =>
// variant={SelectVariant.single} setIsCachePolicyDropdownOpen(!isCachePolicyDropdownOpen)
// value={selected} }
// selections={selected} isOpen={isCachePolicyDropdownOpen}
// onSelect={(_, value) => { onSelect={(_, value) => {
// setSelected(value as string); onChange(value as string);
// setOpenType(false); setIsCachePolicyDropdownOpen(false);
// }} }}
aria-label="Select Input" selections={value}
variant={SelectVariant.single}
> >
{/* {configFormats.map((configFormat) => ( */}
<SelectOption <SelectOption
key={"key"} key={0}
value={"value"} value={t("common:selectOne")}
// isSelected={selected === configFormat.id} isPlaceholder
> />
{"display name"} <SelectOption key={1} value="Default" />
</SelectOption> <SelectOption key={2} value="Something" />
</Select> </Select>
)}
></Controller>
</FormGroup> </FormGroup>
</Form>
{/* TODO: Field shows only if cache policy is EVICT_WEEKLY */}
<FormGroup
label={t("evictionDay")}
labelIcon={
<HelpItem
helpText={helpText("evictionDayHelp")}
forLabel={t("evictionDay")}
forID="kc-eviction-day"
/>
}
fieldId="kc-eviction-day"
>
<Controller
name="evictionDay"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="kc-eviction-day"
required
onToggle={() =>
setIsEvictionDayDropdownOpen(!isEvictionDayDropdownOpen)
}
isOpen={isEvictionDayDropdownOpen}
onSelect={(_, value) => {
onChange(value as string);
setIsEvictionDayDropdownOpen(false);
}}
selections={value}
variant={SelectVariant.single}
>
<SelectOption
key={0}
value={t("common:selectOne")}
isPlaceholder
/>
<SelectOption key={1} value={t("common:Sunday")} />
<SelectOption key={2} value={t("common:Monday")} />
<SelectOption key={3} value={t("common:Tuesday")} />
<SelectOption key={4} value={t("common:Wednesday")} />
<SelectOption key={5} value={t("common:Thursday")} />
<SelectOption key={6} value={t("common:Friday")} />
<SelectOption key={7} value={t("common:Saturday")} />
</Select>
)}
></Controller>
</FormGroup>
{/* TODO: Field shows only if cache policy is EVICT_WEEKLY or EVICT_DAILY */}
{/* TODO: Investigate whether this should be a number field instead of a dropdown/text field */}
<FormGroup
label={t("evictionHour")}
labelIcon={
<HelpItem
helpText={helpText("evictionHourHelp")}
forLabel={t("evictionHour")}
forID="kc-eviction-hour"
/>
}
fieldId="kc-eviction-hour"
>
<Controller
name="evictionHour"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="kc-eviction-hour"
onToggle={() =>
setIsEvictionHourDropdownOpen(!isEvictionHourDropdownOpen)
}
isOpen={isEvictionHourDropdownOpen}
onSelect={(_, value) => {
onChange(value as string);
setIsEvictionHourDropdownOpen(false);
}}
selections={value}
variant={SelectVariant.single}
>
{hourOptions}
</Select>
)}
></Controller>
</FormGroup>
{/* TODO: Field shows only if cache policy is EVICT_WEEKLY or EVICT_DAILY */}
{/* TODO: Investigate whether this should be a number field instead of a dropdown/text field */}
<FormGroup
label={t("evictionMinute")}
labelIcon={
<HelpItem
helpText={helpText("evictionMinuteHelp")}
forLabel={t("evictionMinute")}
forID="kc-eviction-minute"
/>
}
fieldId="kc-eviction-minute"
>
<Controller
name="evictionMinute"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="kc-eviction-minute"
onToggle={() =>
setIsEvictionMinuteDropdownOpen(!isEvictionMinuteDropdownOpen)
}
isOpen={isEvictionMinuteDropdownOpen}
onSelect={(_, value) => {
onChange(value as string);
setIsEvictionMinuteDropdownOpen(false);
}}
selections={value}
variant={SelectVariant.single}
>
{minuteOptions}
</Select>
)}
></Controller>
</FormGroup>
{/* TODO: Field shows only if cache policy is MAX_LIFESPAN */}
<FormGroup
label={t("maxLifespan")}
labelIcon={
<HelpItem
helpText={helpText("maxLifespanHelp")}
forLabel={t("maxLifespan")}
forID="kc-max-lifespan"
/>
}
fieldId="kc-max-lifespan"
>
<TextInput
isRequired
type="text"
id="kc-max-lifespan"
name="maxLifespan"
ref={register}
/>
</FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -1,23 +1,29 @@
import { import {
Form,
FormGroup, FormGroup,
Select, Select,
SelectOption, SelectOption,
SelectVariant,
Switch, Switch,
TextInput, TextInput,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import React from "react";
import { HelpItem } from "../components/help-enabler/HelpItem"; import { HelpItem } from "../components/help-enabler/HelpItem";
import React, { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation";
import { FormAccess } from "../components/form-access/FormAccess";
export const KerberosSettingsRequired = () => { export const KerberosSettingsRequired = () => {
const { t } = useTranslation("user-federation"); const { t } = useTranslation("user-federation");
const helpText = useTranslation("user-federation-help").t; const helpText = useTranslation("user-federation-help").t;
const [isEditModeDropdownOpen, setIsEditModeDropdownOpen] = useState(false);
const { register, control } = useForm<ComponentRepresentation>();
return ( return (
<> <>
{/* Required settings */} {/* Required settings */}
<Form isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup <FormGroup
label={t("consoleDisplayName")} label={t("consoleDisplayName")}
labelIcon={ labelIcon={
@ -34,9 +40,8 @@ export const KerberosSettingsRequired = () => {
isRequired isRequired
type="text" type="text"
id="kc-console-display-name" id="kc-console-display-name"
name="kc-console-display-name" name="consoleDisplayName"
// value={value1} ref={register}
// onChange={this.handleTextInputChange1}
/> />
</FormGroup> </FormGroup>
@ -56,9 +61,8 @@ export const KerberosSettingsRequired = () => {
isRequired isRequired
type="text" type="text"
id="kc-kerberos-realm" id="kc-kerberos-realm"
name="kc-kerberos-realm" name="kerberosRealm"
// value={value1} ref={register}
// onChange={this.handleTextInputChange1}
/> />
</FormGroup> </FormGroup>
@ -78,9 +82,8 @@ export const KerberosSettingsRequired = () => {
isRequired isRequired
type="text" type="text"
id="kc-server-principal" id="kc-server-principal"
name="kc-server-principal" name="serverPrincipal"
// value={value1} ref={register}
// onChange={this.handleTextInputChange1}
/> />
</FormGroup> </FormGroup>
@ -100,9 +103,8 @@ export const KerberosSettingsRequired = () => {
isRequired isRequired
type="text" type="text"
id="kc-key-tab" id="kc-key-tab"
name="kc-key-tab" name="keyTab"
// value={value1} ref={register}
// onChange={this.handleTextInputChange1}
/> />
</FormGroup> </FormGroup>
@ -118,14 +120,22 @@ export const KerberosSettingsRequired = () => {
fieldId="kc-debug" fieldId="kc-debug"
hasNoPaddingTop hasNoPaddingTop
> >
{" "}
<Controller
name="debug"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch <Switch
id={"kc-debug"} id={"kc-debug"}
isChecked={true}
isDisabled={false} isDisabled={false}
onChange={() => undefined as any} onChange={onChange}
isChecked={value}
label={t("common:on")} label={t("common:on")}
labelOff={t("common:off")} labelOff={t("common:off")}
/> />
)}
></Controller>
</FormGroup> </FormGroup>
<FormGroup <FormGroup
@ -140,50 +150,64 @@ export const KerberosSettingsRequired = () => {
fieldId="kc-allow-password-authentication" fieldId="kc-allow-password-authentication"
hasNoPaddingTop hasNoPaddingTop
> >
<Controller
name="allowPasswordAuthentication"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch <Switch
id={"kc-allow-password-authentication"} id={"kc-allow-password-authentication"}
isChecked={true}
isDisabled={false} isDisabled={false}
onChange={() => undefined as any} onChange={onChange}
isChecked={value}
label={t("common:on")} label={t("common:on")}
labelOff={t("common:off")} labelOff={t("common:off")}
/> />
)}
></Controller>
</FormGroup> </FormGroup>
{/* TODO: Field shows only if allowPasswordAuthentication is TRUE */}
<FormGroup <FormGroup
label={t("editMode")} label={t("editMode")}
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText={helpText("editModeHelp")} helpText={helpText("editModeKerberosHelp")}
forLabel={t("editMode")} forLabel={t("editMode")}
forID="kc-edit-mode" forID="kc-edit-mode"
/> />
} }
fieldId="kc-edit-mode" fieldId="kc-edit-mode"
> >
{" "}
<Controller
name="editMode"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select <Select
toggleId="kc-edit-mode" toggleId="kc-edit-mode"
// isOpen={openType} required
onToggle={() => {}} onToggle={() =>
// variant={SelectVariant.single} setIsEditModeDropdownOpen(!isEditModeDropdownOpen)
// value={selected} }
// selections={selected} isOpen={isEditModeDropdownOpen}
// onSelect={(_, value) => { onSelect={(_, value) => {
// setSelected(value as string); onChange(value as string);
// setOpenType(false); setIsEditModeDropdownOpen(false);
// }} }}
aria-label="edit mode" // TODO selections={value}
variant={SelectVariant.single}
> >
{/* {configFormats.map((configFormat) => ( */}
<SelectOption <SelectOption
key={"key"} key={0}
value={"value"} value={t("common:selectOne")}
// isSelected={selected === configFormat.id} isPlaceholder
> />
{"display name"} <SelectOption key={1} value="UNSYNCED" />
</SelectOption>
{/* ))} */}
</Select> </Select>
)}
></Controller>
</FormGroup> </FormGroup>
<FormGroup <FormGroup
@ -198,16 +222,23 @@ export const KerberosSettingsRequired = () => {
fieldId="kc-update-first-login" fieldId="kc-update-first-login"
hasNoPaddingTop hasNoPaddingTop
> >
<Controller
name="updateFirstLogin"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch <Switch
id={"kc-update-first-login"} id={"kc-update-first-login"}
isChecked={true}
isDisabled={false} isDisabled={false}
onChange={() => undefined as any} onChange={onChange}
isChecked={value}
label={t("common:on")} label={t("common:on")}
labelOff={t("common:off")} labelOff={t("common:off")}
/> />
)}
></Controller>
</FormGroup> </FormGroup>
</Form> </FormAccess>
</> </>
); );
}; };

View file

@ -22,7 +22,7 @@ export const LdapSettingsSearching = () => {
label={t("editMode")} label={t("editMode")}
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText={helpText("editModeHelp")} helpText={helpText("editModeLdapHelp")}
forLabel={t("editMode")} forLabel={t("editMode")}
forID="kc-edit-mode" forID="kc-edit-mode"
/> />

View file

@ -13,7 +13,7 @@
"bindDnHelp": "DN of the LDAP admin, which will be used by Keycloak to access LDAP server", "bindDnHelp": "DN of the LDAP admin, which will be used by Keycloak to access LDAP server",
"bindCredentialsHelp": "Password of LDAP admin. This field is able to obtain its value from vault, use ${vault.ID} format.", "bindCredentialsHelp": "Password of LDAP admin. This field is able to obtain its value from vault, use ${vault.ID} format.",
"editModeHelp": "READ_ONLY is a read-only LDAP store. WRITABLE means data will be synced back to LDAP on demand. UNSYNCED means user data will be imported, but not synced back to LDAP.", "editModeLdapHelp": "READ_ONLY is a read-only LDAP store. WRITABLE means data will be synced back to LDAP on demand. UNSYNCED means user data will be imported, but not synced back to LDAP.",
"usersDNHelp": "Full DN of LDAP tree where your users are. This DN is the parent of LDAP users. It could be for example 'ou=users,dc=example,dc=com' assuming that your typical user will have DN like 'uid='john',ou=users,dc=example,dc=com'", "usersDNHelp": "Full DN of LDAP tree where your users are. This DN is the parent of LDAP users. It could be for example 'ou=users,dc=example,dc=com' assuming that your typical user will have DN like 'uid='john',ou=users,dc=example,dc=com'",
"usernameLdapAttributeHelp": "Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server vendors it can be 'uid'. For Active directory it can be 'sAMAccountName' or 'cn'. The attribute should be filled for all LDAP user records you want to import from LDAP to Keycloak.", "usernameLdapAttributeHelp": "Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server vendors it can be 'uid'. For Active directory it can be 'sAMAccountName' or 'cn'. The attribute should be filled for all LDAP user records you want to import from LDAP to Keycloak.",
"rdnLdapAttributeHelp": "Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN. Usually it's the same as Username LDAP attribute, however it is not required. For example for Active directory, it is common to use 'cn' as RDN attribute when username attribute might be 'sAMAccountName'.", "rdnLdapAttributeHelp": "Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN. Usually it's the same as Username LDAP attribute, however it is not required. For example for Active directory, it is common to use 'cn' as RDN attribute when username attribute might be 'sAMAccountName'.",
@ -33,6 +33,10 @@
"useKerberosForPasswordAuthenticationHelp": "User Kerberos login module for authenticate username/password against Kerberos server instead of authenticating against LDAP server with Directory Service API", "useKerberosForPasswordAuthenticationHelp": "User Kerberos login module for authenticate username/password against Kerberos server instead of authenticating against LDAP server with Directory Service API",
"cachePolicyHelp": "Cache Policy for this storage provider. 'DEFAULT' is whatever the default settings are for the global cache. 'EVICT_DAILY' is a time of day every day that the cache will be invalidated. 'EVICT_WEEKLY' is a day of the week and time the cache will be invalidated. 'MAX_LIFESPAN' is the time in milliseconds that will be the lifespan of a cache entry.", "cachePolicyHelp": "Cache Policy for this storage provider. 'DEFAULT' is whatever the default settings are for the global cache. 'EVICT_DAILY' is a time of day every day that the cache will be invalidated. 'EVICT_WEEKLY' is a day of the week and time the cache will be invalidated. 'MAX_LIFESPAN' is the time in milliseconds that will be the lifespan of a cache entry.",
"evictionDayHelp": "Day of the week the entry will become invalid",
"evictionHourHelp": "Hour of the day the entry will become invalid",
"evictionMinuteHelp": "Minute of the hour the entry will become invalid",
"maxLifespanHelp": "Max lifespan of cache entry in milliseconds",
"enableLdapv3PasswordHelp": "Use the LDAPv3 Password Modify Extended Operation (RFC-3062). The password modify extended operation usually requires that LDAP user already has password in the LDAP server. So when this is used with 'Sync Registrations', it can be good to add also 'Hardcoded LDAP attribute mapper' with randomly generated initial password.", "enableLdapv3PasswordHelp": "Use the LDAPv3 Password Modify Extended Operation (RFC-3062). The password modify extended operation usually requires that LDAP user already has password in the LDAP server. So when this is used with 'Sync Registrations', it can be good to add also 'Hardcoded LDAP attribute mapper' with randomly generated initial password.",
"validatePasswordPolicyHelp": "Determines if Keycloak should validate the password with the realm password policy before updating it", "validatePasswordPolicyHelp": "Determines if Keycloak should validate the password with the realm password policy before updating it",
@ -46,6 +50,7 @@
"keyTabHelp": "Location of Kerberos KeyTab file containing the credentials of server principal. For example, /etc/krb5.keytab", "keyTabHelp": "Location of Kerberos KeyTab file containing the credentials of server principal. For example, /etc/krb5.keytab",
"debugHelp": "Enable/disable debug logging to standard output for Krb5LoginModule", "debugHelp": "Enable/disable debug logging to standard output for Krb5LoginModule",
"allowPasswordAuthenticationHelp": "Enable/disable possibility of username/password authentication against Kerberos database", "allowPasswordAuthenticationHelp": "Enable/disable possibility of username/password authentication against Kerberos database",
"editModeKerberosHelp": "READ_ONLY means that password updates are not allowed and user always authenticates with Kerberos password. UNSYNCED means that the user can change the password in the Keycloak database and this one will be used instead of the Kerberos password",
"updateFirstLoginHelp": "Update profile on first login" "updateFirstLoginHelp": "Update profile on first login"
} }
} }

View file

@ -52,6 +52,10 @@
"cacheSettings": "Cache settings", "cacheSettings": "Cache settings",
"cachePolicy": "Cache policy", "cachePolicy": "Cache policy",
"evictionDay": "Eviction day",
"evictionHour": "Eviction hour",
"evictionMinute": "Eviction minute",
"maxLifespan": "Max lifespan",
"advancedSettings": "Advanced settings", "advancedSettings": "Advanced settings",
"enableLdapv3Password": "Enable the LDAPv3 password modify extended operation", "enableLdapv3Password": "Enable the LDAPv3 password modify extended operation",