keycloak-scim/apps/admin-ui/src/realm-settings/TokensTab.tsx

577 lines
19 KiB
TypeScript
Raw Normal View History

import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import {
ActionGroup,
Button,
FormGroup,
NumberInput,
PageSection,
Select,
SelectOption,
SelectVariant,
Switch,
Text,
TextVariants,
} from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormAccess } from "../components/form-access/FormAccess";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput";
import { FormPanel } from "../components/scroll-form/FormPanel";
import {
TimeSelector,
toHumanFormat,
} from "../components/time-selector/TimeSelector";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useWhoAmI } from "../context/whoami/WhoAmI";
import { convertToFormValues } from "../util";
import "./realm-settings-section.css";
type RealmSettingsSessionsTabProps = {
realm: RealmRepresentation;
save: (realm: RealmRepresentation) => void;
reset?: () => void;
};
export const RealmSettingsTokensTab = ({
realm,
reset,
save,
}: RealmSettingsSessionsTabProps) => {
const { t } = useTranslation("realm-settings");
const serverInfo = useServerInfo();
const { whoAmI } = useWhoAmI();
const [defaultSigAlgDrpdwnIsOpen, setDefaultSigAlgDrpdwnOpen] =
useState(false);
const allComponentTypes =
serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? [];
const esOptions = ["ES256", "ES384", "ES512"];
const hmacAlgorithmOptions = allComponentTypes[2].properties[4].options;
const javaKeystoreAlgOptions = allComponentTypes[3].properties[3].options;
const defaultSigAlgOptions = esOptions.concat(
hmacAlgorithmOptions!,
javaKeystoreAlgOptions!
);
const form = useForm<RealmRepresentation>();
const { setValue, control } = form;
const offlineSessionMaxEnabled = useWatch({
control,
name: "offlineSessionMaxLifespanEnabled",
2021-12-14 14:56:36 +00:00
defaultValue: realm.offlineSessionMaxLifespanEnabled,
});
const ssoSessionIdleTimeout = useWatch({
control,
name: "ssoSessionIdleTimeout",
defaultValue: 36000,
});
const revokeRefreshToken = useWatch({
control,
name: "revokeRefreshToken",
defaultValue: false,
});
useEffect(() => {
convertToFormValues(realm, setValue);
}, []);
return (
2021-08-26 12:15:28 +00:00
<PageSection variant="light">
<FormPanel
title={t("realm-settings:general")}
className="kc-sso-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
onSubmit={form.handleSubmit(save)}
>
2021-08-26 12:15:28 +00:00
<FormGroup
label={t("defaultSigAlg")}
fieldId="kc-default-signature-algorithm"
labelIcon={
<HelpItem
helpText="realm-settings-help:defaultSigAlg"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:algorithm"
2021-08-26 12:15:28 +00:00
/>
}
>
2021-08-26 12:15:28 +00:00
<Controller
name="defaultSignatureAlgorithm"
defaultValue={"RS256"}
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<Select
toggleId="kc-default-sig-alg"
onToggle={() =>
setDefaultSigAlgDrpdwnOpen(!defaultSigAlgDrpdwnIsOpen)
}
onSelect={(_, value) => {
field.onChange(value.toString());
2021-08-26 12:15:28 +00:00
setDefaultSigAlgDrpdwnOpen(false);
}}
selections={[field.value?.toString()]}
2021-08-26 12:15:28 +00:00
variant={SelectVariant.single}
aria-label={t("defaultSigAlg")}
isOpen={defaultSigAlgDrpdwnIsOpen}
data-testid="select-default-sig-alg"
>
{defaultSigAlgOptions!.map((p, idx) => (
<SelectOption
selected={p === field.value}
2021-08-26 12:15:28 +00:00
key={`default-sig-alg-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("oAuthDeviceCodeLifespan")}
fieldId="oAuthDeviceCodeLifespan"
labelIcon={
<HelpItem
helpText="realm-settings-help:oAuthDeviceCodeLifespan"
fieldLabelId="realm-settings: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="realm-settings-help:oAuthDevicePollingInterval"
fieldLabelId="realm-settings:oAuthDevicePollingInterval"
/>
}
>
<Controller
name="oauth2DevicePollingInterval"
defaultValue={0}
control={form.control}
render={({ field }) => (
<NumberInput
id="oAuthDevicePollingInterval"
value={field.value}
min={0}
onPlus={() => field.onChange(field.value || 0 + 1)}
onMinus={() => field.onChange(field.value || 0 - 1)}
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="realm-settings-help:shortVerificationUriTooltip"
fieldLabelId="realm-settings:shortVerificationUri"
/>
}
>
<KeycloakTextInput
id="shortVerificationUri"
placeholder={t("shortVerificationUri")}
{...form.register("attributes.shortVerificationUri")}
/>
</FormGroup>
2021-08-26 12:15:28 +00:00
</FormAccess>
</FormPanel>
<FormPanel
title={t("realm-settings:refreshTokens")}
className="kc-client-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
hasNoPaddingTop
label={t("revokeRefreshToken")}
fieldId="kc-revoke-refresh-token"
labelIcon={
<HelpItem
helpText="realm-settings-help:revokeRefreshToken"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:revokeRefreshToken"
2021-08-26 12:15:28 +00:00
/>
}
>
<Controller
name="revokeRefreshToken"
control={form.control}
defaultValue={false}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<Switch
id="kc-revoke-refresh-token"
data-testid="revoke-refresh-token-switch"
aria-label={t("revokeRefreshToken")}
2021-08-26 12:15:28 +00:00
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={field.value}
onChange={field.onChange}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
{revokeRefreshToken && (
<FormGroup
label={t("refreshTokenMaxReuse")}
labelIcon={
<HelpItem
helpText="realm-settings-help:refreshTokenMaxReuse"
fieldLabelId="realm-settings: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>
)}
2021-08-26 12:15:28 +00:00
</FormAccess>
</FormPanel>
<FormPanel
title={t("realm-settings:accessTokens")}
className="kc-offline-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("accessTokenLifespan")}
fieldId="accessTokenLifespan"
helperText={t("recommendedSsoTimeout", {
time: toHumanFormat(ssoSessionIdleTimeout!, whoAmI.getLocale()),
})}
2021-08-26 12:15:28 +00:00
labelIcon={
<HelpItem
helpText="realm-settings-help:accessTokenLifespan"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:accessTokenLifespan"
/>
2021-08-26 12:15:28 +00:00
}
>
<Controller
name="accessTokenLifespan"
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
validated={
field.value! > ssoSessionIdleTimeout!
? "warning"
: "default"
2021-08-26 12:15:28 +00:00
}
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"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<FormGroup
label={t("accessTokenLifespanImplicitFlow")}
fieldId="accessTokenLifespanImplicitFlow"
labelIcon={
<HelpItem
helpText="realm-settings-help:accessTokenLifespanImplicitFlow"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:accessTokenLifespanImplicitFlow"
/>
2021-08-26 12:15:28 +00:00
}
>
2021-08-26 12:15:28 +00:00
<Controller
name="accessTokenLifespanForImplicitFlow"
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
className="kc-access-token-lifespan-implicit"
data-testid="access-token-lifespan-implicit-input"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<FormGroup
label={t("clientLoginTimeout")}
fieldId="clientLoginTimeout"
labelIcon={
<HelpItem
helpText="realm-settings-help:clientLoginTimeout"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:clientLoginTimeout"
/>
2021-08-26 12:15:28 +00:00
}
>
<Controller
name="accessCodeLifespan"
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<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"]}
2021-08-26 12:15:28 +00:00
/>
)}
/>
</FormGroup>
2021-08-26 12:15:28 +00:00
{offlineSessionMaxEnabled && (
<FormGroup
2021-08-26 12:15:28 +00:00
label={t("offlineSessionMax")}
fieldId="offlineSessionMax"
id="offline-session-max-label"
labelIcon={
<HelpItem
2021-08-26 12:15:28 +00:00
helpText="realm-settings-help:offlineSessionMax"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:offlineSessionMax"
/>
}
>
<Controller
2021-08-26 12:15:28 +00:00
name="offlineSessionMaxLifespan"
control={form.control}
render={({ field }) => (
<TimeSelector
2021-08-26 12:15:28 +00:00
className="kc-offline-session-max"
data-testid="offline-session-max-input"
value={field.value!}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
)}
/>
</FormGroup>
2021-08-26 12:15:28 +00:00
)}
</FormAccess>
</FormPanel>
<FormPanel
className="kc-login-settings-template"
title={t("actionTokens")}
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("userInitiatedActionLifespan")}
id="kc-user-initiated-action-lifespan"
fieldId="userInitiatedActionLifespan"
labelIcon={
<HelpItem
helpText="realm-settings-help:userInitiatedActionLifespan"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:userInitiatedActionLifespan"
2021-08-26 12:15:28 +00:00
/>
}
>
<Controller
name="actionTokenGeneratedByUserLifespan"
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<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"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<FormGroup
label={t("defaultAdminInitiated")}
fieldId="defaultAdminInitiated"
id="default-admin-initiated-label"
labelIcon={
<HelpItem
helpText="realm-settings-help:defaultAdminInitiatedActionLifespan"
2021-12-14 14:56:36 +00:00
fieldLabelId="realm-settings:defaultAdminInitiated"
/>
2021-08-26 12:15:28 +00:00
}
>
<Controller
name="actionTokenGeneratedByAdminLifespan"
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<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"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<Text
className="kc-override-action-tokens-subtitle"
component={TextVariants.h1}
>
2021-08-26 12:15:28 +00:00
{t("overrideActionTokens")}
</Text>
<FormGroup
label={t("emailVerification")}
fieldId="emailVerification"
id="email-verification"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-verify-email"
defaultValue=""
2021-08-26 12:15:28 +00:00
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
className="kc-email-verification"
data-testid="email-verification-input"
value={field.value}
onChange={(value) => field.onChange(value.toString())}
units={["minute", "hour", "day"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<FormGroup
label={t("idpAccountEmailVerification")}
fieldId="idpAccountEmailVerification"
id="idp-acct-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-idp-verify-account-via-email"
defaultValue={""}
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
className="kc-idp-email-verification"
data-testid="idp-email-verification-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
/>
2021-08-26 12:15:28 +00:00
)}
/>
</FormGroup>
<FormGroup
label={t("forgotPassword")}
fieldId="forgotPassword"
id="forgot-password-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-reset-credentials"
defaultValue={""}
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
className="kc-forgot-pw"
data-testid="forgot-pw-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
2021-08-26 12:15:28 +00:00
/>
)}
/>
</FormGroup>
<FormGroup
label={t("executeActions")}
fieldId="executeActions"
id="execute-actions"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-execute-actions"
defaultValue={""}
control={form.control}
render={({ field }) => (
2021-08-26 12:15:28 +00:00
<TimeSelector
className="kc-execute-actions"
data-testid="execute-actions-input"
value={field.value}
onChange={field.onChange}
units={["minute", "hour", "day"]}
2021-08-26 12:15:28 +00:00
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="tokens-tab-save"
isDisabled={!form.formState.isDirty}
>
2021-08-26 12:15:28 +00:00
{t("common:save")}
</Button>
<Button variant="link" onClick={reset}>
{t("common:revert")}
</Button>
</ActionGroup>
</FormAccess>
</FormPanel>
</PageSection>
);
};