From bbc0bcf8b2b8e6cfe906a9c015c8ca17d66120e8 Mon Sep 17 00:00:00 2001 From: Jenny <32821331+jenny-s51@users.noreply.github.com> Date: Wed, 2 Mar 2022 16:59:37 -0500 Subject: [PATCH] Add new capability configs and tests (#2164) * add checkbox fields, more tests unskip test fix beforeEach add front channel logout configs set default to true set default to true fix console warning and conditionally render url field show frontchannel settings under logout settings if OIDC client PR feedback from Jon * fix clients test * resolve console warning * addCapabilityConfigs --- cypress/integration/clients_test.spec.ts | 39 +++++++++- .../manage/clients/CreateClientPage.ts | 52 ++++++++++++-- src/clients/ClientSettings.tsx | 72 ++++++++++++++++++- src/clients/add/CapabilityConfig.tsx | 71 +++++++++++++++--- src/clients/add/NewClientForm.tsx | 7 +- src/clients/add/capability-config.css | 4 ++ src/clients/help.ts | 6 ++ src/clients/messages.ts | 4 ++ 8 files changed, 232 insertions(+), 23 deletions(-) diff --git a/cypress/integration/clients_test.spec.ts b/cypress/integration/clients_test.spec.ts index 909c6f32eb..a9a08b6778 100644 --- a/cypress/integration/clients_test.spec.ts +++ b/cypress/integration/clients_test.spec.ts @@ -212,6 +212,32 @@ describe("Clients test", () => { sidebarPage.goToClients(); }); + it("Should cancel creating client", () => { + listingPage.goToCreateItem(); + + createClientPage.continue().checkClientIdRequiredMessage(); + + createClientPage + .fillClientData("") + .selectClientType("openid-connect") + .cancel(); + + cy.url().should("not.include", "/add-client"); + }); + + it("Should navigate to previous using 'back' button", () => { + listingPage.goToCreateItem(); + + createClientPage.continue().checkClientIdRequiredMessage(); + + createClientPage + .fillClientData("test_client") + .selectClientType("openid-connect") + .continue() + .back() + .checkGeneralSettingsStepActive(); + }); + it("Should fail creating client", () => { listingPage.goToCreateItem(); @@ -223,7 +249,7 @@ describe("Clients test", () => { .continue() .checkClientIdRequiredMessage(); - createClientPage.fillClientData("account").continue().continue(); + createClientPage.fillClientData("account").continue().save(); // The error should inform about duplicated name/id masthead.checkNotificationMessage( @@ -241,7 +267,14 @@ describe("Clients test", () => { .selectClientType("openid-connect") .fillClientData(itemId) .continue() - .continue(); + .switchClientAuthentication() + .clickDirectAccess() + .clickImplicitFlow() + .clickOAuthDeviceAuthorizationGrant() + .clickOidcCibaGrant() + .clickServiceAccountRoles() + .clickStandardFlow() + .save(); masthead.checkNotificationMessage("Client created successfully"); @@ -338,7 +371,7 @@ describe("Clients test", () => { .selectClientType("openid-connect") .fillClientData(client) .continue() - .continue(); + .save(); advancedTab.goToAdvancedTab(); }); diff --git a/cypress/support/pages/admin_console/manage/clients/CreateClientPage.ts b/cypress/support/pages/admin_console/manage/clients/CreateClientPage.ts index 3451d64da4..12634dc742 100644 --- a/cypress/support/pages/admin_console/manage/clients/CreateClientPage.ts +++ b/cypress/support/pages/admin_console/manage/clients/CreateClientPage.ts @@ -10,15 +10,21 @@ export default class CreateClientPage { '[for="kc-always-display-in-console-switch"] .pf-c-switch__toggle'; frontchannelLogoutSwitch = '[for="kc-frontchannelLogout-switch"] .pf-c-switch__toggle'; - clientAuthenticationSwitch = '[for="kc-authentication"] .pf-c-switch__toggle'; + clientAuthenticationSwitch = + '[for="kc-authentication-switch"] > .pf-c-switch__toggle'; + clientAuthorizationSwitch = + '[for="kc-authorization-switch"] > .pf-c-switch__toggle'; standardFlowChkBx = "#kc-flow-standard"; directAccessChkBx = "#kc-flow-direct"; implicitFlowChkBx = "#kc-flow-implicit"; + oidcCibaGrantChkBx = "#kc-oidc-ciba-grant"; + deviceAuthGrantChkBx = "#kc-oauth-device-authorization-grant"; serviceAccountRolesChkBx = "#kc-flow-service-account"; - continueBtn = ".pf-c-wizard__footer .pf-m-primary"; - backBtn = ".pf-c-wizard__footer .pf-m-secondary"; - cancelBtn = ".pf-c-wizard__footer .pf-m-link"; + saveBtn = "save"; + continueBtn = "next"; + backBtn = "back"; + cancelBtn = "cancel"; //#region General Settings selectClientType(clientType: string) { @@ -72,6 +78,14 @@ export default class CreateClientPage { return this; } + + checkGeneralSettingsStepActive() { + cy.get(".pf-c-wizard__nav-link") + .contains("General Settings") + .should("have.class", "pf-m-current"); + + return this; + } //#endregion //#region Capability config @@ -81,6 +95,12 @@ export default class CreateClientPage { return this; } + switchClientAuthorization() { + cy.get(this.clientAuthorizationSwitch).click(); + + return this; + } + clickStandardFlow() { cy.get(this.standardFlowChkBx).click(); @@ -104,22 +124,40 @@ export default class CreateClientPage { return this; } + + clickOAuthDeviceAuthorizationGrant() { + cy.get(this.deviceAuthGrantChkBx).click(); + + return this; + } + + clickOidcCibaGrant() { + cy.get(this.oidcCibaGrantChkBx).click(); + + return this; + } //#endregion + save() { + cy.findByTestId(this.saveBtn).click(); + + return this; + } + continue() { - cy.get(this.continueBtn).click(); + cy.findByTestId(this.continueBtn).click(); return this; } back() { - cy.get(this.backBtn).click(); + cy.findByTestId(this.backBtn).click(); return this; } cancel() { - cy.get(this.cancelBtn).click(); + cy.findByTestId(this.cancelBtn).click(); return this; } diff --git a/src/clients/ClientSettings.tsx b/src/clients/ClientSettings.tsx index 6fcc70072d..527fa49d71 100644 --- a/src/clients/ClientSettings.tsx +++ b/src/clients/ClientSettings.tsx @@ -47,6 +47,7 @@ export const ClientSettings = ({ "attributes.display.on.consent.screen" ); const protocol = watch("protocol"); + const frontchannelLogout = watch("frontchannelLogout"); const sections = useMemo(() => { let result = ["generalSettings"]; @@ -289,6 +290,75 @@ export const ClientSettings = ({ + {protocol === "openid-connect" && ( + <> + + } + fieldId="frontchannelLogout" + hasNoPaddingTop + > + ( + onChange(value.toString())} + /> + )} + /> + + {frontchannelLogout?.toString() === "true" && ( + + } + helperTextInvalid={ + errors.attributes?.frontchannel?.logout?.url?.message + } + validated={ + errors.attributes?.frontchannel?.logout?.url?.message + ? ValidatedOptions.error + : ValidatedOptions.default + } + > + + ((uri.startsWith("https://") || + uri.startsWith("http://")) && + !uri.includes("*")) || + uri === "" || + t("frontchannelUrlInvalid").toString(), + })} + validated={ + errors.attributes?.frontchannel?.logout?.url?.message + ? ValidatedOptions.error + : ValidatedOptions.default + } + /> + + )} + + )} ((uri.startsWith("https://") || uri.startsWith("http://")) && - uri.indexOf("*") === -1) || + !uri.includes("*")) || uri === "" || t("backchannelUrlInvalid").toString(), })} diff --git a/src/clients/add/CapabilityConfig.tsx b/src/clients/add/CapabilityConfig.tsx index faccdc3aa5..45dd31e182 100644 --- a/src/clients/add/CapabilityConfig.tsx +++ b/src/clients/add/CapabilityConfig.tsx @@ -69,6 +69,7 @@ export const CapabilityConfig = ({ if (!value) { setValue("authorizationServicesEnabled", false); setValue("serviceAccountsEnabled", false); + setValue("attributes.oidc.ciba.grant.enabled", false); } }} /> @@ -114,7 +115,7 @@ export const CapabilityConfig = ({ label={t("authenticationFlow")} fieldId="kc-flow" > - + ( @@ -173,7 +174,7 @@ export const CapabilityConfig = ({ label={t("implicitFlow")} id="kc-flow-implicit" name="implicitFlowEnabled" - isChecked={value} + isChecked={value.toString() === "true"} onChange={onChange} /> + + ( + + + + + )} + /> + + + ( + + + + + )} + /> + @@ -233,15 +282,15 @@ export const CapabilityConfig = ({ ( onChange("" + value)} + isChecked={value} + onChange={onChange} /> )} /> @@ -260,15 +309,15 @@ export const CapabilityConfig = ({ ( onChange("" + value)} + isChecked={value} + onChange={onChange} /> )} /> diff --git a/src/clients/add/NewClientForm.tsx b/src/clients/add/NewClientForm.tsx index 8322de4f88..0948da402e 100644 --- a/src/clients/add/NewClientForm.tsx +++ b/src/clients/add/NewClientForm.tsx @@ -48,6 +48,7 @@ export default function NewClientForm() { const newClient = await adminClient.clients.create({ ...convertFormValuesToObject(client), clientId: client.clientId?.trim(), + frontchannelLogout: true, }); addAlert(t("createSuccess"), AlertVariant.success); history.push( @@ -93,6 +94,9 @@ export default function NewClientForm() { <> - diff --git a/src/clients/add/capability-config.css b/src/clients/add/capability-config.css index 46545f21c1..43a4391ed5 100644 --- a/src/clients/add/capability-config.css +++ b/src/clients/add/capability-config.css @@ -3,3 +3,7 @@ --pf-c-form--m-horizontal__group-label--md--GridColumnWidth: 10rem; } } + +div#authenticationFlowGrid > .pf-l-grid__item { + padding-bottom: var(--pf-global--spacer--lg); +} diff --git a/src/clients/help.ts b/src/clients/help.ts index cc8b6f1ee2..1b22511e9a 100644 --- a/src/clients/help.ts +++ b/src/clients/help.ts @@ -18,6 +18,10 @@ export default { "This enables standard OpenID Connect redirect based authentication with authorization code. In terms of OpenID Connect or OAuth2 specifications, this enables support of 'Authorization Code Flow' for this client.", implicitFlow: "This enables support for OpenID Connect redirect based authentication without authorization code. In terms of OpenID Connect or OAuth2 specifications, this enables support of 'Implicit Flow' for this client.", + oauthDeviceAuthorizationGrant: + "This enables support for OAuth 2.0 Device Authorization Grant, which means that client is an application on device that has limited input capabilities or lack a suitable browser.", + oidcCibaGrant: + "This enables support for OIDC CIBA Grant, which means that the user is authenticated via some external authentication device instead of the user's browser.", rootURL: "Root URL appended to relative URLs", validRedirectURIs: "Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed such as 'http://example.com/*'. Relative path can be specified too such as /my/relative/path/*. Relative paths are relative to the client root URL, or if none is specified the auth server root URL is used. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request.", @@ -173,6 +177,8 @@ export default { "URL to send the HTTP ARTIFACT messages to. You can leave this blank if you are using a different binding. This value should be set when forcing ARTIFACT binding together with IdP initiated login.", frontchannelLogout: "When true, logout requires a browser redirect to client. When false, server performs a background invocation for logout.", + frontchannelLogoutUrl: + "URL that will cause the client to log itself out when a logout request is sent to this realm (via end_session_endpoint). If not provided, it defaults to the base url.", backchannelLogoutUrl: "URL that will cause the client to log itself out when a logout request is sent to this realm (via end_session_endpoint). If omitted, no logout request will be sent to the client is this case.", backchannelLogoutSessionRequired: diff --git a/src/clients/messages.ts b/src/clients/messages.ts index 7adf25d7a1..2160870e96 100644 --- a/src/clients/messages.ts +++ b/src/clients/messages.ts @@ -348,6 +348,8 @@ export default { standardFlow: "Standard flow", directAccess: "Direct access grants", serviceAccount: "Service accounts roles", + oauthDeviceAuthorizationGrant: "OAuth 2.0 Device Authorization Grant", + oidcCibaGrant: "OIDC CIBA Grant", enableServiceAccount: "Enable service account roles", assignRolesTo: "Assign roles to {{client}} account", searchByRoleName: "Search by role name", @@ -367,6 +369,8 @@ export default { backchannelLogoutRevokeOfflineSessions: "Backchannel logout revoke offline sessions", frontchannelLogout: "Front channel logout", + frontchannelLogoutUrl: "Front-channel logout URL", + frontchannelUrlInvalid: "Front-channel logout URL is not a valid URL", accessSettings: "Access settings", rootUrl: "Root URL", validRedirectUri: "Valid redirect URIs",