Added extra step to OIDC client wizzard (#4035)
This commit is contained in:
parent
158f471bea
commit
2e174cd4e8
5 changed files with 243 additions and 199 deletions
|
@ -262,6 +262,7 @@ describe("Clients test", () => {
|
|||
.fillClientData(clientId)
|
||||
.continue()
|
||||
.checkCapabilityConfigElements()
|
||||
.continue()
|
||||
.save();
|
||||
|
||||
commonPage
|
||||
|
@ -304,7 +305,7 @@ describe("Clients test", () => {
|
|||
.continue()
|
||||
.checkClientIdRequiredMessage();
|
||||
|
||||
createClientPage.fillClientData("account").continue().save();
|
||||
createClientPage.fillClientData("account").continue().continue().save();
|
||||
|
||||
// The error should inform about duplicated name/id
|
||||
commonPage
|
||||
|
@ -335,6 +336,7 @@ describe("Clients test", () => {
|
|||
.clickOidcCibaGrant()
|
||||
.clickServiceAccountRoles()
|
||||
.clickStandardFlow()
|
||||
.continue()
|
||||
.save();
|
||||
|
||||
commonPage
|
||||
|
@ -448,7 +450,11 @@ describe("Clients test", () => {
|
|||
commonPage.sidebar().goToClients();
|
||||
commonPage.tableToolbarUtils().createClient();
|
||||
|
||||
createClientPage.fillClientData(identicalClientId).continue().save();
|
||||
createClientPage
|
||||
.fillClientData(identicalClientId)
|
||||
.continue()
|
||||
.continue()
|
||||
.save();
|
||||
|
||||
commonPage.masthead().closeAllAlertMessages();
|
||||
commonPage.sidebar().goToClients();
|
||||
|
@ -493,6 +499,7 @@ describe("Clients test", () => {
|
|||
.selectClientType("openid-connect")
|
||||
.fillClientData(client)
|
||||
.continue()
|
||||
.continue()
|
||||
.save();
|
||||
commonPage
|
||||
.masthead()
|
||||
|
@ -705,7 +712,7 @@ describe("Clients test", () => {
|
|||
|
||||
commonPage.sidebar().waitForPageLoad();
|
||||
|
||||
createClientPage.save();
|
||||
createClientPage.continue().save();
|
||||
commonPage
|
||||
.masthead()
|
||||
.checkNotificationMessage("Client created successfully");
|
||||
|
|
|
@ -130,6 +130,7 @@ describe("User Fed LDAP mapper tests", () => {
|
|||
.selectClientType("openid-connect")
|
||||
.fillClientData(clientName)
|
||||
.continue()
|
||||
.continue()
|
||||
.save();
|
||||
|
||||
masthead.checkNotificationMessage(clientCreatedSuccess);
|
||||
|
|
|
@ -5,14 +5,11 @@ 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 { MultiLineInput } from "../../components/multi-line-input/hook-form-v7/MultiLineInput";
|
||||
import { useAccess } from "../../context/access/Access";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import environment from "../../environment";
|
||||
import { convertAttributeNameToForm } from "../../util";
|
||||
import { SaveReset } from "../advanced/SaveReset";
|
||||
import { FormFields } from "../ClientDetails";
|
||||
import type { ClientSettingsProps } from "../ClientSettings";
|
||||
import { LoginSettings } from "./LoginSettings";
|
||||
|
||||
export const AccessSettings = ({
|
||||
client,
|
||||
|
@ -21,15 +18,11 @@ export const AccessSettings = ({
|
|||
}: ClientSettingsProps) => {
|
||||
const { t } = useTranslation("clients");
|
||||
const { register, watch } = useFormContext<FormFields>();
|
||||
const { realm } = useRealm();
|
||||
|
||||
const { hasAccess } = useAccess();
|
||||
const isManager = hasAccess("manage-clients") || client.access?.configure;
|
||||
|
||||
const protocol = watch("protocol");
|
||||
const idpInitiatedSsoUrlName: string = watch(
|
||||
"attributes.saml_idp_initiated_sso_url_name"
|
||||
);
|
||||
|
||||
return (
|
||||
<FormAccess
|
||||
|
@ -37,154 +30,7 @@ export const AccessSettings = ({
|
|||
fineGrainedAccess={client.access?.configure}
|
||||
role="manage-clients"
|
||||
>
|
||||
{!client.bearerOnly && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("rootUrl")}
|
||||
fieldId="kc-root-url"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:rootURL"
|
||||
fieldLabelId="clients:rootUrl"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="kc-root-url"
|
||||
type="url"
|
||||
{...register("rootUrl")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("homeURL")}
|
||||
fieldId="kc-home-url"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:homeURL"
|
||||
fieldLabelId="clients:homeURL"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="kc-home-url"
|
||||
type="url"
|
||||
{...register("baseUrl")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("validRedirectUri")}
|
||||
fieldId="kc-redirect"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:validRedirectURIs"
|
||||
fieldLabelId="clients:validRedirectUri"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiLineInput
|
||||
name="redirectUris"
|
||||
aria-label={t("validRedirectUri")}
|
||||
addButtonLabel="clients:addRedirectUri"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("validPostLogoutRedirectUri")}
|
||||
fieldId="kc-postLogoutRedirect"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:validPostLogoutRedirectURIs"
|
||||
fieldLabelId="clients:validPostLogoutRedirectUri"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiLineInput
|
||||
name={convertAttributeNameToForm(
|
||||
"attributes.post.logout.redirect.uris"
|
||||
)}
|
||||
aria-label={t("validPostLogoutRedirectUri")}
|
||||
addButtonLabel="clients:addPostLogoutRedirectUri"
|
||||
stringify
|
||||
/>
|
||||
</FormGroup>
|
||||
{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
|
||||
id="idpInitiatedSsoUrlName"
|
||||
data-testid="idpInitiatedSsoUrlName"
|
||||
{...register("attributes.saml_idp_initiated_sso_url_name")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("idpInitiatedSsoRelayState")}
|
||||
fieldId="idpInitiatedSsoRelayState"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:idpInitiatedSsoRelayState"
|
||||
fieldLabelId="clients:idpInitiatedSsoRelayState"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="idpInitiatedSsoRelayState"
|
||||
data-testid="idpInitiatedSsoRelayState"
|
||||
{...register("attributes.saml_idp_initiated_sso_relay_state")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("masterSamlProcessingUrl")}
|
||||
fieldId="masterSamlProcessingUrl"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:masterSamlProcessingUrl"
|
||||
fieldLabelId="clients:masterSamlProcessingUrl"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="masterSamlProcessingUrl"
|
||||
type="url"
|
||||
data-testid="masterSamlProcessingUrl"
|
||||
{...register("adminUrl")}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!client.bearerOnly && <LoginSettings protocol={protocol} />}
|
||||
{protocol !== "saml" && (
|
||||
<FormGroup
|
||||
label={t("adminURL")}
|
||||
|
|
178
apps/admin-ui/src/clients/add/LoginSettings.tsx
Normal file
178
apps/admin-ui/src/clients/add/LoginSettings.tsx
Normal file
|
@ -0,0 +1,178 @@
|
|||
import { FormGroup } from "@patternfly/react-core";
|
||||
import { useFormContext } from "react-hook-form-v7";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
|
||||
import { MultiLineInput } from "../../components/multi-line-input/hook-form-v7/MultiLineInput";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import environment from "../../environment";
|
||||
import { convertAttributeNameToForm } from "../../util";
|
||||
import { FormFields } from "../ClientDetails";
|
||||
|
||||
type LoginSettingsProps = {
|
||||
protocol?: string;
|
||||
};
|
||||
|
||||
export const LoginSettings = ({
|
||||
protocol = "openid-connect",
|
||||
}: LoginSettingsProps) => {
|
||||
const { t } = useTranslation("clients");
|
||||
const { register, watch } = useFormContext<FormFields>();
|
||||
const { realm } = useRealm();
|
||||
|
||||
const idpInitiatedSsoUrlName: string = watch(
|
||||
"attributes.saml_idp_initiated_sso_url_name"
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("rootUrl")}
|
||||
fieldId="kc-root-url"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:rootURL"
|
||||
fieldLabelId="clients:rootUrl"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="kc-root-url"
|
||||
type="url"
|
||||
{...register("rootUrl")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("homeURL")}
|
||||
fieldId="kc-home-url"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:homeURL"
|
||||
fieldLabelId="clients:homeURL"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="kc-home-url"
|
||||
type="url"
|
||||
{...register("baseUrl")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("validRedirectUri")}
|
||||
fieldId="kc-redirect"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:validRedirectURIs"
|
||||
fieldLabelId="clients:validRedirectUri"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiLineInput
|
||||
id="kc-redirect"
|
||||
name="redirectUris"
|
||||
aria-label={t("validRedirectUri")}
|
||||
addButtonLabel="clients:addRedirectUri"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("validPostLogoutRedirectUri")}
|
||||
fieldId="kc-postLogoutRedirect"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:validPostLogoutRedirectURIs"
|
||||
fieldLabelId="clients:validPostLogoutRedirectUri"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiLineInput
|
||||
id="kc-postLogoutRedirect"
|
||||
name={convertAttributeNameToForm(
|
||||
"attributes.post.logout.redirect.uris"
|
||||
)}
|
||||
aria-label={t("validPostLogoutRedirectUri")}
|
||||
addButtonLabel="clients:addPostLogoutRedirectUri"
|
||||
stringify
|
||||
/>
|
||||
</FormGroup>
|
||||
{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
|
||||
id="idpInitiatedSsoUrlName"
|
||||
data-testid="idpInitiatedSsoUrlName"
|
||||
{...register("attributes.saml_idp_initiated_sso_url_name")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("idpInitiatedSsoRelayState")}
|
||||
fieldId="idpInitiatedSsoRelayState"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:idpInitiatedSsoRelayState"
|
||||
fieldLabelId="clients:idpInitiatedSsoRelayState"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="idpInitiatedSsoRelayState"
|
||||
data-testid="idpInitiatedSsoRelayState"
|
||||
{...register("attributes.saml_idp_initiated_sso_relay_state")}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("masterSamlProcessingUrl")}
|
||||
fieldId="masterSamlProcessingUrl"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:masterSamlProcessingUrl"
|
||||
fieldLabelId="clients:masterSamlProcessingUrl"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<KeycloakTextInput
|
||||
id="masterSamlProcessingUrl"
|
||||
type="url"
|
||||
data-testid="masterSamlProcessingUrl"
|
||||
{...register("adminUrl")}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
{protocol !== "saml" && (
|
||||
<FormGroup
|
||||
label={t("webOrigins")}
|
||||
fieldId="kc-web-origins"
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="clients-help:webOrigins"
|
||||
fieldLabelId="clients:webOrigins"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MultiLineInput
|
||||
id="kc-web-origins"
|
||||
name="webOrigins"
|
||||
aria-label={t("webOrigins")}
|
||||
addButtonLabel="clients:addWebOrigins"
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
||||
import {
|
||||
AlertVariant,
|
||||
Button,
|
||||
|
@ -11,7 +10,9 @@ import { useState } from "react";
|
|||
import { FormProvider, useForm } from "react-hook-form-v7";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom-v5-compat";
|
||||
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
|
@ -21,6 +22,7 @@ import { toClient } from "../routes/Client";
|
|||
import { toClients } from "../routes/Clients";
|
||||
import { CapabilityConfig } from "./CapabilityConfig";
|
||||
import { GeneralSettings } from "./GeneralSettings";
|
||||
import { LoginSettings } from "./LoginSettings";
|
||||
|
||||
export default function NewClientForm() {
|
||||
const { t } = useTranslation("clients");
|
||||
|
@ -28,8 +30,11 @@ export default function NewClientForm() {
|
|||
const { adminClient } = useAdminClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [showCapabilityConfig, setShowCapabilityConfig] = useState(false);
|
||||
const [client, setClient] = useState<ClientRepresentation>({
|
||||
const [step, setStep] = useState(0);
|
||||
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const form = useForm<FormFields>({
|
||||
defaultValues: {
|
||||
protocol: "openid-connect",
|
||||
clientId: "",
|
||||
name: "",
|
||||
|
@ -41,12 +46,16 @@ export default function NewClientForm() {
|
|||
directAccessGrantsEnabled: true,
|
||||
standardFlowEnabled: true,
|
||||
frontchannelLogout: true,
|
||||
attributes: {
|
||||
saml_idp_initiated_sso_url_name: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const methods = useForm<FormFields>({ defaultValues: client });
|
||||
const protocol = methods.watch("protocol");
|
||||
const { getValues, watch, trigger } = form;
|
||||
const protocol = watch("protocol");
|
||||
|
||||
const save = async () => {
|
||||
const client = convertFormValuesToObject(getValues());
|
||||
try {
|
||||
const newClient = await adminClient.clients.create({
|
||||
...client,
|
||||
|
@ -60,35 +69,29 @@ export default function NewClientForm() {
|
|||
};
|
||||
|
||||
const forward = async (onNext?: () => void) => {
|
||||
if (await methods.trigger()) {
|
||||
setClient({
|
||||
...client,
|
||||
...convertFormValuesToObject(methods.getValues()),
|
||||
});
|
||||
if (!(await trigger())) {
|
||||
return;
|
||||
}
|
||||
if (!isFinalStep()) {
|
||||
setShowCapabilityConfig(true);
|
||||
setStep(step + 1);
|
||||
}
|
||||
onNext?.();
|
||||
}
|
||||
};
|
||||
|
||||
const isFinalStep = () =>
|
||||
showCapabilityConfig || protocol !== "openid-connect";
|
||||
protocol === "openid-connect" ? step === 2 : step === 1;
|
||||
|
||||
const back = () => {
|
||||
setClient({ ...client, ...convertFormValuesToObject(methods.getValues()) });
|
||||
methods.reset({
|
||||
...client,
|
||||
...convertFormValuesToObject(methods.getValues()),
|
||||
});
|
||||
setShowCapabilityConfig(false);
|
||||
setStep(step - 1);
|
||||
};
|
||||
|
||||
const onGoToStep = (newStep: { id?: string | number }) => {
|
||||
if (newStep.id === "generalSettings") {
|
||||
back();
|
||||
setStep(0);
|
||||
} else if (newStep.id === "capabilityConfig") {
|
||||
setStep(1);
|
||||
} else {
|
||||
forward();
|
||||
setStep(2);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -135,7 +138,7 @@ export default function NewClientForm() {
|
|||
subKey="clients:clientsExplain"
|
||||
/>
|
||||
<PageSection variant="light">
|
||||
<FormProvider {...methods}>
|
||||
<FormProvider {...form}>
|
||||
<Wizard
|
||||
onClose={() => navigate(toClients({ realm }))}
|
||||
navAriaLabel={`${title} steps`}
|
||||
|
@ -146,17 +149,26 @@ export default function NewClientForm() {
|
|||
name: t("generalSettings"),
|
||||
component: <GeneralSettings />,
|
||||
},
|
||||
...(showCapabilityConfig
|
||||
...(protocol !== "saml"
|
||||
? [
|
||||
{
|
||||
id: "capabilityConfig",
|
||||
name: t("capabilityConfig"),
|
||||
component: (
|
||||
<CapabilityConfig protocol={client.protocol} />
|
||||
),
|
||||
component: <CapabilityConfig protocol={protocol} />,
|
||||
canJumpTo: step >= 1,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
id: "loginSettings",
|
||||
name: t("loginSettings"),
|
||||
component: (
|
||||
<FormAccess isHorizontal role="manage-clients">
|
||||
<LoginSettings protocol={protocol} />
|
||||
</FormAccess>
|
||||
),
|
||||
canJumpTo: step >= 1,
|
||||
},
|
||||
]}
|
||||
footer={<Footer />}
|
||||
onSave={save}
|
||||
|
|
Loading…
Reference in a new issue