keycloak-scim/js/apps/admin-ui/src/realm-settings/TokensTab.tsx
Erik Jan de Wit 5949fd43d0
remove all use of deprecated Select and Dropdown (#29270)
* removed deprecated select

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* some more deprecation removal

working towards fixing: #28197

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to use new api

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* more deprecation removal

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* small fix

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* no more default text for SelectOption

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to use id

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed dropdown in keycloakCard and test fixes

Signed-off-by: mfrances <mfrances@redhat.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed lint error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix dropdown/select related test failures

Signed-off-by: mfrances <mfrances@redhat.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* i18n label

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* removed

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: mfrances <mfrances@redhat.com>
Co-authored-by: mfrances <mfrances@redhat.com>
2024-05-30 13:45:58 +02:00

621 lines
20 KiB
TypeScript

import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import {
ActionGroup,
Button,
FormGroup,
FormHelperText,
HelperText,
HelperTextItem,
NumberInput,
PageSection,
SelectOption,
Switch,
Text,
TextInput,
TextVariants,
} from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormPanel, HelpItem } from "@keycloak/keycloak-ui-shared";
import { FormAccess } from "../components/form/FormAccess";
import {
TimeSelector,
toHumanFormat,
} from "../components/time-selector/TimeSelector";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useWhoAmI } from "../context/whoami/WhoAmI";
import { beerify, convertToFormValues, sortProviders } from "../util";
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
import "./realm-settings-section.css";
import {
KeycloakSelect,
SelectVariant,
} from "../components/select/KeycloakSelect";
type RealmSettingsSessionsTabProps = {
realm: RealmRepresentation;
save: (realm: RealmRepresentation) => void;
reset?: () => void;
};
export const RealmSettingsTokensTab = ({
realm,
reset,
save,
}: RealmSettingsSessionsTabProps) => {
const { t } = useTranslation();
const serverInfo = useServerInfo();
const isFeatureEnabled = useIsFeatureEnabled();
const { whoAmI } = useWhoAmI();
const [defaultSigAlgDrpdwnIsOpen, setDefaultSigAlgDrpdwnOpen] =
useState(false);
const defaultSigAlgOptions = sortProviders(
serverInfo.providers!["signature"].providers,
);
const form = useForm<RealmRepresentation>();
const { setValue, control } = form;
const offlineSessionMaxEnabled = useWatch({
control,
name: "offlineSessionMaxLifespanEnabled",
defaultValue: realm.offlineSessionMaxLifespanEnabled,
});
const ssoSessionIdleTimeout = useWatch({
control,
name: "ssoSessionIdleTimeout",
defaultValue: 36000,
});
const revokeRefreshToken = useWatch({
control,
name: "revokeRefreshToken",
defaultValue: false,
});
useEffect(() => {
convertToFormValues(realm, setValue);
}, []);
return (
<PageSection variant="light">
<FormPanel title={t("general")} className="kc-sso-session-template">
<FormAccess
isHorizontal
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("defaultSigAlg")}
fieldId="kc-default-signature-algorithm"
labelIcon={
<HelpItem
helpText={t("defaultSigAlgHelp")}
fieldLabelId="algorithm"
/>
}
>
<Controller
name="defaultSignatureAlgorithm"
defaultValue={"RS256"}
control={form.control}
render={({ field }) => (
<KeycloakSelect
toggleId="kc-default-sig-alg"
onToggle={() =>
setDefaultSigAlgDrpdwnOpen(!defaultSigAlgDrpdwnIsOpen)
}
onSelect={(value) => {
field.onChange(value.toString());
setDefaultSigAlgDrpdwnOpen(false);
}}
selections={field.value?.toString()}
variant={SelectVariant.single}
aria-label={t("defaultSigAlg")}
isOpen={defaultSigAlgDrpdwnIsOpen}
data-testid="select-default-sig-alg"
>
{defaultSigAlgOptions!.map((p, idx) => (
<SelectOption
selected={p === field.value}
key={`default-sig-alg-${idx}`}
value={p}
></SelectOption>
))}
</KeycloakSelect>
)}
/>
</FormGroup>
{isFeatureEnabled(Feature.DeviceFlow) && (
<>
<FormGroup
label={t("oAuthDeviceCodeLifespan")}
fieldId="oAuthDeviceCodeLifespan"
labelIcon={
<HelpItem
helpText={t("oAuthDeviceCodeLifespanHelp")}
fieldLabelId="oAuthDeviceCodeLifespan"
/>
}
>
<Controller
name="oauth2DeviceCodeLifespan"
defaultValue={0}
control={form.control}
render={({ field }) => (
<TimeSelector
id="oAuthDeviceCodeLifespan"
data-testid="oAuthDeviceCodeLifespan"
value={field.value || 0}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("oAuthDevicePollingInterval")}
fieldId="oAuthDevicePollingInterval"
labelIcon={
<HelpItem
helpText={t("oAuthDevicePollingIntervalHelp")}
fieldLabelId="oAuthDevicePollingInterval"
/>
}
>
<Controller
name="oauth2DevicePollingInterval"
defaultValue={0}
control={form.control}
render={({ field }) => (
<NumberInput
id="oAuthDevicePollingInterval"
value={field.value}
min={0}
onPlus={() => field.onChange(Number(field?.value) + 1)}
onMinus={() =>
field.onChange(
Number(field?.value) > 0
? Number(field?.value) - 1
: 0,
)
}
onChange={(event) => {
const newValue = Number(event.currentTarget.value);
field.onChange(!isNaN(newValue) ? newValue : 0);
}}
placeholder={t("oAuthDevicePollingInterval")}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("shortVerificationUri")}
fieldId="shortVerificationUri"
labelIcon={
<HelpItem
helpText={t("shortVerificationUriTooltipHelp")}
fieldLabelId="shortVerificationUri"
/>
}
>
<TextInput
id="shortVerificationUri"
placeholder={t("shortVerificationUri")}
{...form.register("attributes.shortVerificationUri")}
/>
</FormGroup>
<FormGroup
label={t("parRequestUriLifespan")}
fieldId="parRequestUriLifespan"
labelIcon={
<HelpItem
helpText={t("parRequestUriLifespanHelp")}
fieldLabelId="parRequestUriLifespan"
/>
}
>
<Controller
name="attributes.parRequestUriLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
id="parRequestUriLifespan"
className="par-request-uri-lifespan"
data-testid="par-request-uri-lifespan-input"
aria-label="par-request-uri-lifespan"
value={field.value}
onChange={field.onChange}
/>
)}
/>
</FormGroup>
</>
)}
</FormAccess>
</FormPanel>
<FormPanel
title={t("refreshTokens")}
className="kc-client-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
hasNoPaddingTop
label={t("revokeRefreshToken")}
fieldId="kc-revoke-refresh-token"
labelIcon={
<HelpItem
helpText={t("revokeRefreshTokenHelp")}
fieldLabelId="revokeRefreshToken"
/>
}
>
<Controller
name="revokeRefreshToken"
control={form.control}
defaultValue={false}
render={({ field }) => (
<Switch
id="kc-revoke-refresh-token"
data-testid="revoke-refresh-token-switch"
aria-label={t("revokeRefreshToken")}
label={t("enabled")}
labelOff={t("disabled")}
isChecked={field.value}
onChange={field.onChange}
/>
)}
/>
</FormGroup>
{revokeRefreshToken && (
<FormGroup
label={t("refreshTokenMaxReuse")}
labelIcon={
<HelpItem
helpText={t("refreshTokenMaxReuseHelp")}
fieldLabelId="refreshTokenMaxReuse"
/>
}
fieldId="refreshTokenMaxReuse"
>
<Controller
name="refreshTokenMaxReuse"
defaultValue={0}
control={form.control}
render={({ field }) => (
<NumberInput
type="text"
id="refreshTokenMaxReuseMs"
value={field.value}
onPlus={() => field.onChange(field.value! + 1)}
onMinus={() => field.onChange(field.value! - 1)}
onChange={(event) =>
field.onChange(
Number((event.target as HTMLInputElement).value),
)
}
/>
)}
/>
</FormGroup>
)}
</FormAccess>
</FormPanel>
<FormPanel
title={t("accessTokens")}
className="kc-offline-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("accessTokenLifespan")}
fieldId="accessTokenLifespan"
labelIcon={
<HelpItem
helpText={t("accessTokenLifespanHelp")}
fieldLabelId="accessTokenLifespan"
/>
}
>
<Controller
name="accessTokenLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
validated={
field.value! > ssoSessionIdleTimeout!
? "warning"
: "default"
}
className="kc-access-token-lifespan"
data-testid="access-token-lifespan-input"
aria-label="access-token-lifespan"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
<FormHelperText>
<HelperText>
<HelperTextItem>
{t("recommendedSsoTimeout", {
time: toHumanFormat(
ssoSessionIdleTimeout!,
whoAmI.getLocale(),
),
})}
</HelperTextItem>
</HelperText>
</FormHelperText>
</FormGroup>
<FormGroup
label={t("accessTokenLifespanImplicitFlow")}
fieldId="accessTokenLifespanImplicitFlow"
labelIcon={
<HelpItem
helpText={t("accessTokenLifespanImplicitFlow")}
fieldLabelId="accessTokenLifespanImplicitFlow"
/>
}
>
<Controller
name="accessTokenLifespanForImplicitFlow"
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-access-token-lifespan-implicit"
data-testid="access-token-lifespan-implicit-input"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("clientLoginTimeout")}
fieldId="clientLoginTimeout"
labelIcon={
<HelpItem
helpText={t("clientLoginTimeoutHelp")}
fieldLabelId="clientLoginTimeout"
/>
}
>
<Controller
name="accessCodeLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-client-login-timeout"
data-testid="client-login-timeout-input"
aria-label="client-login-timeout"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
{offlineSessionMaxEnabled && (
<FormGroup
label={t("offlineSessionMax")}
fieldId="offlineSessionMax"
id="offline-session-max-label"
labelIcon={
<HelpItem
helpText={t("offlineSessionMaxHelp")}
fieldLabelId="offlineSessionMax"
/>
}
>
<Controller
name="offlineSessionMaxLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-offline-session-max"
data-testid="offline-session-max-input"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
)}
</FormAccess>
</FormPanel>
<FormPanel
className="kc-login-settings-template"
title={t("actionTokens")}
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("userInitiatedActionLifespan")}
id="kc-user-initiated-action-lifespan"
fieldId="userInitiatedActionLifespan"
labelIcon={
<HelpItem
helpText={t("userInitiatedActionLifespanHelp")}
fieldLabelId="userInitiatedActionLifespan"
/>
}
>
<Controller
name="actionTokenGeneratedByUserLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-user-initiated-action-lifespan"
data-testid="user-initiated-action-lifespan"
aria-label="user-initiated-action-lifespan"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("defaultAdminInitiated")}
fieldId="defaultAdminInitiated"
id="default-admin-initiated-label"
labelIcon={
<HelpItem
helpText={t("defaultAdminInitiatedActionLifespanHelp")}
fieldLabelId="defaultAdminInitiated"
/>
}
>
<Controller
name="actionTokenGeneratedByAdminLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-default-admin-initiated"
data-testid="default-admin-initated-input"
aria-label="default-admin-initated-input"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<Text
className="kc-override-action-tokens-subtitle"
component={TextVariants.h1}
>
{t("overrideActionTokens")}
</Text>
<FormGroup
label={t("emailVerification")}
fieldId="emailVerification"
id="email-verification"
>
<Controller
name={`attributes.${beerify(
"actionTokenGeneratedByUserLifespan.verify-email",
)}`}
defaultValue=""
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-email-verification"
data-testid="email-verification-input"
value={field.value}
onChange={(value) => field.onChange(value.toString())}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("idpAccountEmailVerification")}
fieldId="idpAccountEmailVerification"
id="idp-acct-label"
>
<Controller
name={`attributes.${beerify(
"actionTokenGeneratedByUserLifespan.idp-verify-account-via-email",
)}`}
defaultValue={""}
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-idp-email-verification"
data-testid="idp-email-verification-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("forgotPassword")}
fieldId="forgotPassword"
id="forgot-password-label"
>
<Controller
name={`attributes.${beerify(
"actionTokenGeneratedByUserLifespan.reset-credentials",
)}`}
defaultValue={""}
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-forgot-pw"
data-testid="forgot-pw-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("executeActions")}
fieldId="executeActions"
id="execute-actions"
>
<Controller
name={`attributes.${beerify(
"actionTokenGeneratedByUserLifespan.execute-actions",
)}`}
defaultValue={""}
control={form.control}
render={({ field }) => (
<TimeSelector
className="kc-execute-actions"
data-testid="execute-actions-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="tokens-tab-save"
isDisabled={!form.formState.isDirty}
>
{t("save")}
</Button>
<Button variant="link" onClick={reset}>
{t("revert")}
</Button>
</ActionGroup>
</FormAccess>
</FormPanel>
</PageSection>
);
};