From 2e174cd4e8b353335178af8e8992ec1987092015 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 17 Jan 2023 15:29:42 +0100 Subject: [PATCH] Added extra step to OIDC client wizzard (#4035) --- .../admin-ui/cypress/e2e/clients_test.spec.ts | 13 +- ...ser_fed_ldap_hardcoded_mapper_test.spec.ts | 1 + .../src/clients/add/AccessSettings.tsx | 158 +--------------- .../src/clients/add/LoginSettings.tsx | 178 ++++++++++++++++++ .../src/clients/add/NewClientForm.tsx | 92 +++++---- 5 files changed, 243 insertions(+), 199 deletions(-) create mode 100644 apps/admin-ui/src/clients/add/LoginSettings.tsx diff --git a/apps/admin-ui/cypress/e2e/clients_test.spec.ts b/apps/admin-ui/cypress/e2e/clients_test.spec.ts index 642effd8f6..72d567337e 100644 --- a/apps/admin-ui/cypress/e2e/clients_test.spec.ts +++ b/apps/admin-ui/cypress/e2e/clients_test.spec.ts @@ -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"); diff --git a/apps/admin-ui/cypress/e2e/user_fed_ldap_hardcoded_mapper_test.spec.ts b/apps/admin-ui/cypress/e2e/user_fed_ldap_hardcoded_mapper_test.spec.ts index 311710f961..1b4ff52001 100644 --- a/apps/admin-ui/cypress/e2e/user_fed_ldap_hardcoded_mapper_test.spec.ts +++ b/apps/admin-ui/cypress/e2e/user_fed_ldap_hardcoded_mapper_test.spec.ts @@ -130,6 +130,7 @@ describe("User Fed LDAP mapper tests", () => { .selectClientType("openid-connect") .fillClientData(clientName) .continue() + .continue() .save(); masthead.checkNotificationMessage(clientCreatedSuccess); diff --git a/apps/admin-ui/src/clients/add/AccessSettings.tsx b/apps/admin-ui/src/clients/add/AccessSettings.tsx index 10d38b3dc0..1d141b2d75 100644 --- a/apps/admin-ui/src/clients/add/AccessSettings.tsx +++ b/apps/admin-ui/src/clients/add/AccessSettings.tsx @@ -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(); - 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 ( - {!client.bearerOnly && ( - <> - - } - > - - - - } - > - - - - } - > - - - - } - > - - - {protocol === "saml" && ( - <> - - } - helperText={ - idpInitiatedSsoUrlName !== "" && - t("idpInitiatedSsoUrlNameHelp", { - url: `${environment.authServerUrl}/realms/${realm}/protocol/saml/clients/${idpInitiatedSsoUrlName}`, - }) - } - > - - - - } - > - - - - } - > - - - - )} - {protocol !== "saml" && ( - - } - > - - - )} - - )} + {!client.bearerOnly && } {protocol !== "saml" && ( { + const { t } = useTranslation("clients"); + const { register, watch } = useFormContext(); + const { realm } = useRealm(); + + const idpInitiatedSsoUrlName: string = watch( + "attributes.saml_idp_initiated_sso_url_name" + ); + return ( + <> + + } + > + + + + } + > + + + + } + > + + + + } + > + + + {protocol === "saml" && ( + <> + + } + helperText={ + idpInitiatedSsoUrlName !== "" && + t("idpInitiatedSsoUrlNameHelp", { + url: `${environment.authServerUrl}/realms/${realm}/protocol/saml/clients/${idpInitiatedSsoUrlName}`, + }) + } + > + + + + } + > + + + + } + > + + + + )} + {protocol !== "saml" && ( + + } + > + + + )} + + ); +}; diff --git a/apps/admin-ui/src/clients/add/NewClientForm.tsx b/apps/admin-ui/src/clients/add/NewClientForm.tsx index 451d1cada5..41cbe0840e 100644 --- a/apps/admin-ui/src/clients/add/NewClientForm.tsx +++ b/apps/admin-ui/src/clients/add/NewClientForm.tsx @@ -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,25 +30,32 @@ export default function NewClientForm() { const { adminClient } = useAdminClient(); const navigate = useNavigate(); - const [showCapabilityConfig, setShowCapabilityConfig] = useState(false); - const [client, setClient] = useState({ - protocol: "openid-connect", - clientId: "", - name: "", - description: "", - publicClient: true, - authorizationServicesEnabled: false, - serviceAccountsEnabled: false, - implicitFlowEnabled: false, - directAccessGrantsEnabled: true, - standardFlowEnabled: true, - frontchannelLogout: true, - }); + const [step, setStep] = useState(0); + const { addAlert, addError } = useAlerts(); - const methods = useForm({ defaultValues: client }); - const protocol = methods.watch("protocol"); + const form = useForm({ + defaultValues: { + protocol: "openid-connect", + clientId: "", + name: "", + description: "", + publicClient: true, + authorizationServicesEnabled: false, + serviceAccountsEnabled: false, + implicitFlowEnabled: false, + directAccessGrantsEnabled: true, + standardFlowEnabled: true, + frontchannelLogout: true, + attributes: { + saml_idp_initiated_sso_url_name: "", + }, + }, + }); + 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 (!isFinalStep()) { - setShowCapabilityConfig(true); - } - onNext?.(); + if (!(await trigger())) { + return; } + if (!isFinalStep()) { + 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" /> - + navigate(toClients({ realm }))} navAriaLabel={`${title} steps`} @@ -146,17 +149,26 @@ export default function NewClientForm() { name: t("generalSettings"), component: , }, - ...(showCapabilityConfig + ...(protocol !== "saml" ? [ { id: "capabilityConfig", name: t("capabilityConfig"), - component: ( - - ), + component: , + canJumpTo: step >= 1, }, ] : []), + { + id: "loginSettings", + name: t("loginSettings"), + component: ( + + + + ), + canJumpTo: step >= 1, + }, ]} footer={