keycloak-scim/src/clients/ClientSettings.tsx

535 lines
17 KiB
TypeScript
Raw Normal View History

import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import {
FormGroup,
Form,
Switch,
TextArea,
Select,
SelectVariant,
SelectOption,
2022-02-22 11:22:29 +00:00
ValidatedOptions,
} from "@patternfly/react-core";
import { Controller, useFormContext } from "react-hook-form";
import { ScrollForm } from "../components/scroll-form/ScrollForm";
import { ClientDescription } from "./ClientDescription";
import { CapabilityConfig } from "./add/CapabilityConfig";
import { MultiLineInput } from "../components/multi-line-input/MultiLineInput";
import { FormAccess } from "../components/form-access/FormAccess";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { SaveReset } from "./advanced/SaveReset";
2021-10-05 10:32:20 +00:00
import { SamlConfig } from "./add/SamlConfig";
import { SamlSignature } from "./add/SamlSignature";
2022-03-07 17:08:14 +00:00
import environment from "../environment";
import { useRealm } from "../context/realm-context/RealmContext";
type ClientSettingsProps = {
client: ClientRepresentation;
save: () => void;
reset: () => void;
};
export const ClientSettings = ({
client,
save,
reset,
}: ClientSettingsProps) => {
const {
register,
control,
watch,
formState: { errors },
} = useFormContext<ClientRepresentation>();
const { t } = useTranslation("clients");
2022-03-07 17:08:14 +00:00
const { realm } = useRealm();
const [loginThemeOpen, setLoginThemeOpen] = useState(false);
const loginThemes = useServerInfo().themes!["login"];
2021-10-05 10:32:20 +00:00
const consentRequired = watch("consentRequired");
const displayOnConsentScreen: string = watch(
"attributes.display.on.consent.screen"
);
2021-10-05 10:32:20 +00:00
const protocol = watch("protocol");
const frontchannelLogout = watch("frontchannelLogout");
2022-03-07 17:08:14 +00:00
const idpInitiatedSsoUrlName: string = watch(
"attributes.saml_idp_initiated_sso_url_name"
);
const sections = useMemo(() => {
let result = ["generalSettings", "accessSettings"];
if (protocol === "saml") {
result = [...result, "samlCapabilityConfig", "signatureAndEncryption"];
} else if (!client.bearerOnly) {
result = [...result, "capabilityConfig"];
} else {
return result;
}
return [...result, "loginSettings", "logoutSettings"];
}, [protocol, client]);
return (
2021-08-26 12:15:28 +00:00
<ScrollForm
className="pf-u-px-lg"
2021-10-05 10:32:20 +00:00
sections={sections.map((section) => t(section))}
2021-08-26 12:15:28 +00:00
>
<Form isHorizontal>
<ClientDescription protocol={client.protocol} />
2021-08-26 12:15:28 +00:00
</Form>
{protocol === "saml" ? (
<SamlConfig />
) : (
!client.bearerOnly && <CapabilityConfig />
)}
2021-10-05 10:32:20 +00:00
{protocol === "saml" && <SamlSignature />}
2021-08-26 12:15:28 +00:00
<FormAccess isHorizontal role="manage-clients">
{!client.bearerOnly && (
<>
<FormGroup
label={t("rootUrl")}
fieldId="kc-root-url"
labelIcon={
<HelpItem
helpText="clients-help:rootUrl"
fieldLabelId="clients:rootUrl"
/>
}
>
<KeycloakTextInput
type="text"
id="kc-root-url"
name="rootUrl"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("homeURL")}
fieldId="kc-home-url"
labelIcon={
<HelpItem
helpText="clients-help:homeURL"
fieldLabelId="clients:homeURL"
/>
}
>
<KeycloakTextInput
type="text"
id="kc-home-url"
name="baseUrl"
ref={register}
/>
</FormGroup>
<FormGroup
2022-03-07 17:08:14 +00:00
label={t("validRedirectUri")}
fieldId="kc-redirect"
labelIcon={
<HelpItem
2022-03-07 17:08:14 +00:00
helpText="clients-help:validRedirectURIs"
fieldLabelId="clients:validRedirectUri"
/>
}
>
<MultiLineInput
2022-03-07 17:08:14 +00:00
name="redirectUris"
aria-label={t("validRedirectUri")}
addButtonLabel="clients:addRedirectUri"
/>
</FormGroup>
2022-03-07 17:08:14 +00:00
{protocol === "saml" && (
<>
<FormGroup
label={t("idpInitiatedSsoUrlName")}
fieldId="idpInitiatedSsoUrlName"
labelIcon={
<HelpItem
helpText="clients-help:idpInitiatedSsoUrlName"
fieldLabelId="clients:idpInitiatedSsoUrlName"
/>
}
helperText={
idpInitiatedSsoUrlName !== "" &&
t("idpInitiatedSsoUrlNameHelp", {
url: `${environment.authServerUrl}/realms/${realm}/protocol/saml/clients/${idpInitiatedSsoUrlName}`,
})
}
>
<KeycloakTextInput
2022-03-07 17:08:14 +00:00
type="text"
id="idpInitiatedSsoUrlName"
name="attributes.saml_idp_initiated_sso_url_name"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("idpInitiatedSsoRelayState")}
fieldId="idpInitiatedSsoRelayState"
labelIcon={
<HelpItem
helpText="clients-help:idpInitiatedSsoRelayState"
fieldLabelId="clients:idpInitiatedSsoRelayState"
/>
}
>
<KeycloakTextInput
2022-03-07 17:08:14 +00:00
type="text"
id="idpInitiatedSsoRelayState"
name="attributes.saml_idp_initiated_sso_relay_state"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("masterSamlProcessingUrl")}
fieldId="masterSamlProcessingUrl"
labelIcon={
<HelpItem
helpText="clients-help:masterSamlProcessingUrl"
fieldLabelId="clients:masterSamlProcessingUrl"
/>
}
>
<KeycloakTextInput
2022-03-07 17:08:14 +00:00
type="text"
id="masterSamlProcessingUrl"
name="adminUrl"
ref={register}
/>
</FormGroup>
</>
)}
{protocol !== "saml" && (
<FormGroup
label={t("webOrigins")}
fieldId="kc-web-origins"
labelIcon={
<HelpItem
helpText="clients-help:webOrigins"
fieldLabelId="clients:webOrigins"
/>
}
>
<MultiLineInput
name="webOrigins"
aria-label={t("webOrigins")}
addButtonLabel="clients:addWebOrigins"
/>
</FormGroup>
)}
</>
)}
2022-03-07 17:08:14 +00:00
{protocol !== "saml" && (
<FormGroup
label={t("adminURL")}
fieldId="kc-admin-url"
labelIcon={
<HelpItem
helpText="clients-help:adminURL"
fieldLabelId="clients:adminURL"
/>
}
>
<KeycloakTextInput
2022-03-07 17:08:14 +00:00
type="text"
id="kc-admin-url"
name="adminUrl"
ref={register}
/>
2022-03-07 17:08:14 +00:00
</FormGroup>
)}
{client.bearerOnly && (
<SaveReset
className="keycloak__form_actions"
name="settings"
save={save}
reset={reset}
/>
)}
2021-08-26 12:15:28 +00:00
</FormAccess>
<FormAccess isHorizontal role="manage-clients">
<FormGroup
label={t("loginTheme")}
labelIcon={
<HelpItem
helpText="clients-help:loginTheme"
2021-12-14 14:56:36 +00:00
fieldLabelId="clients:loginTheme"
/>
2021-08-26 12:15:28 +00:00
}
fieldId="loginTheme"
>
<Controller
name="attributes.login_theme"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="loginTheme"
onToggle={setLoginThemeOpen}
2021-08-26 12:15:28 +00:00
onSelect={(_, value) => {
onChange(value.toString());
2021-08-26 12:15:28 +00:00
setLoginThemeOpen(false);
}}
selections={value || t("common:choose")}
variant={SelectVariant.single}
aria-label={t("loginTheme")}
isOpen={loginThemeOpen}
>
2022-02-22 11:22:29 +00:00
{[
<SelectOption key="empty" value="">
{t("common:choose")}
</SelectOption>,
...loginThemes.map((theme) => (
2021-08-26 12:15:28 +00:00
<SelectOption
selected={theme.name === value}
key={theme.name}
value={theme.name}
/>
2022-02-22 11:22:29 +00:00
)),
]}
2021-08-26 12:15:28 +00:00
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("consentRequired")}
labelIcon={
<HelpItem
helpText="clients-help:consentRequired"
2021-12-14 14:56:36 +00:00
fieldLabelId="clients:consentRequired"
/>
}
2021-08-26 12:15:28 +00:00
fieldId="kc-consent"
hasNoPaddingTop
>
<Controller
name="consentRequired"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-consent-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value}
onChange={onChange}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("displayOnClient")}
labelIcon={
<HelpItem
helpText="clients-help:displayOnClient"
2021-12-14 14:56:36 +00:00
fieldLabelId="clients:displayOnClient"
/>
}
2021-08-26 12:15:28 +00:00
fieldId="kc-display-on-client"
hasNoPaddingTop
>
<Controller
name="attributes.display.on.consent.screen"
2021-08-26 12:15:28 +00:00
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-display-on-client-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
isDisabled={!consentRequired}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("consentScreenText")}
labelIcon={
<HelpItem
helpText="clients-help:consentScreenText"
2021-12-14 14:56:36 +00:00
fieldLabelId="clients:consentScreenText"
/>
}
2021-08-26 12:15:28 +00:00
fieldId="kc-consent-screen-text"
>
<TextArea
id="kc-consent-screen-text"
name="attributes.consent.screen.text"
2021-08-26 12:15:28 +00:00
ref={register}
isDisabled={!(consentRequired && displayOnConsentScreen === "true")}
2021-04-06 17:19:15 +00:00
/>
2021-08-26 12:15:28 +00:00
</FormGroup>
2022-02-22 11:22:29 +00:00
</FormAccess>
<FormAccess isHorizontal role="manage-clients">
{protocol === "openid-connect" && (
<>
<FormGroup
label={t("frontchannelLogout")}
labelIcon={
<HelpItem
helpText="clients-help:frontchannelLogout"
fieldLabelId="clients:frontchannelLogout"
/>
}
fieldId="frontchannelLogout"
hasNoPaddingTop
>
<Controller
name="frontchannelLogout"
defaultValue={true}
control={control}
render={({ onChange, value }) => (
<Switch
id="frontchannelLogout"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value.toString() === "true"}
onChange={(value) => onChange(value.toString())}
/>
)}
/>
</FormGroup>
{frontchannelLogout?.toString() === "true" && (
<FormGroup
label={t("frontchannelLogoutUrl")}
fieldId="frontchannelLogoutUrl"
labelIcon={
<HelpItem
helpText="clients-help:frontchannelLogoutUrl"
fieldLabelId="clients:frontchannelLogoutUrl"
/>
}
helperTextInvalid={
errors.attributes?.frontchannel?.logout?.url?.message
}
validated={
errors.attributes?.frontchannel?.logout?.url?.message
? ValidatedOptions.error
: ValidatedOptions.default
}
>
<KeycloakTextInput
type="text"
id="frontchannelLogoutUrl"
name="attributes.frontchannel.logout.url"
ref={register({
validate: (uri) =>
((uri.startsWith("https://") ||
uri.startsWith("http://")) &&
!uri.includes("*")) ||
uri === "" ||
t("frontchannelUrlInvalid").toString(),
})}
validated={
errors.attributes?.frontchannel?.logout?.url?.message
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
</FormGroup>
)}
</>
)}
2022-02-22 11:22:29 +00:00
<FormGroup
label={t("backchannelLogoutUrl")}
fieldId="backchannelLogoutUrl"
labelIcon={
<HelpItem
helpText="clients-help:backchannelLogoutUrl"
fieldLabelId="clients:backchannelLogoutUrl"
/>
}
helperTextInvalid={
errors.attributes?.backchannel?.logout?.url?.message
}
validated={
errors.attributes?.backchannel?.logout?.url?.message
? ValidatedOptions.error
: ValidatedOptions.default
}
>
<KeycloakTextInput
2022-02-22 11:22:29 +00:00
type="text"
id="backchannelLogoutUrl"
name="attributes.backchannel.logout.url"
ref={register({
validate: (uri) =>
((uri.startsWith("https://") || uri.startsWith("http://")) &&
!uri.includes("*")) ||
2022-02-22 11:22:29 +00:00
uri === "" ||
t("backchannelUrlInvalid").toString(),
})}
validated={
errors.attributes?.backchannel?.logout?.url?.message
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
</FormGroup>
<FormGroup
label={t("backchannelLogoutSessionRequired")}
labelIcon={
<HelpItem
helpText="clients-help:backchannelLogoutSessionRequired"
fieldLabelId="clients:backchannelLogoutSessionRequired"
/>
}
fieldId="backchannelLogoutSessionRequired"
hasNoPaddingTop
>
<Controller
name="attributes.backchannel.logout.session.required"
defaultValue="true"
control={control}
render={({ onChange, value }) => (
<Switch
id="backchannelLogoutSessionRequired"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange(value.toString())}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("backchannelLogoutRevokeOfflineSessions")}
labelIcon={
<HelpItem
helpText="clients-help:backchannelLogoutRevokeOfflineSessions"
fieldLabelId="clients:backchannelLogoutRevokeOfflineSessions"
/>
}
fieldId="backchannelLogoutRevokeOfflineSessions"
hasNoPaddingTop
>
<Controller
name="attributes.backchannel.logout.revoke.offline.tokens"
defaultValue="false"
control={control}
render={({ onChange, value }) => (
<Switch
id="backchannelLogoutRevokeOfflineSessions"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange(value.toString())}
/>
)}
/>
</FormGroup>
2021-08-26 12:15:28 +00:00
<SaveReset
className="keycloak__form_actions"
name="settings"
save={save}
reset={reset}
/>
</FormAccess>
</ScrollForm>
);
};