From 999b502d44f9aa43c67aa0582807fde905dcfaaf Mon Sep 17 00:00:00 2001
From: Jenny <32821331+jenny-s51@users.noreply.github.com>
Date: Tue, 23 Nov 2021 09:59:04 -0500
Subject: [PATCH] Realm settings(Client policies): Update client-scopes
condition to match new design (#1575)
---
.../integration/realm_settings_test.spec.ts | 28 +-
.../realm_settings/RealmSettingsPage.ts | 73 ++++-
src/client-scopes/ClientScopesSection.tsx | 6 +-
src/clients/ClientDetails.tsx | 1 +
src/clients/messages.ts | 6 +-
src/clients/scopes/AddScopeDialog.tsx | 281 +++++++++++++++---
src/clients/scopes/ClientScopes.tsx | 10 +-
src/clients/scopes/client-scopes.css | 4 +
src/clients/utils.ts | 4 +-
.../dynamic/MultivaluedScopesComponent.tsx | 129 ++++++++
.../NewClientPolicyCondition.tsx | 33 +-
src/realm-settings/NewClientPolicyForm.tsx | 4 +-
src/realm-settings/RealmSettingsSection.css | 13 +
13 files changed, 523 insertions(+), 69 deletions(-)
create mode 100644 src/components/dynamic/MultivaluedScopesComponent.tsx
diff --git a/cypress/integration/realm_settings_test.spec.ts b/cypress/integration/realm_settings_test.spec.ts
index d89fe90e5e..631f82a1d9 100644
--- a/cypress/integration/realm_settings_test.spec.ts
+++ b/cypress/integration/realm_settings_test.spec.ts
@@ -118,7 +118,7 @@ describe("Realm settings tests", () => {
masthead.checkNotificationMessage("Realm successfully updated");
});
- it.skip("Go to login tab", () => {
+ it("Go to login tab", () => {
sidebarPage.goToRealmSettings();
cy.findByTestId("rs-login-tab").click();
realmSettingsPage.toggleSwitch(realmSettingsPage.userRegSwitch);
@@ -126,7 +126,7 @@ describe("Realm settings tests", () => {
realmSettingsPage.toggleSwitch(realmSettingsPage.rememberMeSwitch);
});
- it.skip("Check login tab values", () => {
+ it("Check login tab values", () => {
sidebarPage.goToRealmSettings();
cy.findByTestId("rs-login-tab").click();
@@ -605,20 +605,32 @@ describe("Realm settings tests", () => {
realmSettingsPage.shouldCancelAddingCondition();
});
- it("Should add a new condition to a client profile", () => {
- realmSettingsPage.shouldAddCondition();
+ it("Should add a new client-roles condition to a client profile", () => {
+ realmSettingsPage.shouldAddClientRolesCondition();
});
- it("Should edit the condition of a client profile", () => {
- realmSettingsPage.shouldEditCondition();
+ it("Should add a new client-scopes condition to a client profile", () => {
+ realmSettingsPage.shouldAddClientScopesCondition();
+ });
+
+ it("Should edit the client-roles condition of a client profile", () => {
+ realmSettingsPage.shouldEditClientRolesCondition();
+ });
+
+ it("Should edit the client-scopes condition of a client profile", () => {
+ realmSettingsPage.shouldEditClientScopesCondition();
});
it("Should cancel deleting condition from a client profile", () => {
realmSettingsPage.shouldCancelDeletingCondition();
});
- it("Should delete condition from a client profile", () => {
- realmSettingsPage.shouldDeleteCondition();
+ it("Should delete client-roles condition from a client profile", () => {
+ realmSettingsPage.shouldDeleteClientRolesCondition();
+ });
+
+ it("Should delete client-scopes condition from a client profile", () => {
+ realmSettingsPage.shouldDeleteClientScopesCondition();
});
it("Check cancelling the client policy deletion", () => {
diff --git a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts
index bd7f3137b1..4bb10b33a9 100644
--- a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts
+++ b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts
@@ -197,6 +197,8 @@ export default class RealmSettingsPage {
private addConditionCancelBtn = "addCondition-cancelBtn";
private addConditionSaveBtn = "addCondition-saveBtn";
private conditionTypeLink = "condition-type-link";
+ private clientRolesConditionLink = "client-roles-condition-link";
+ private clientScopesConditionLink = "client-scopes-condition-link";
private eventListenersFormLabel = ".pf-c-form__label-text";
private eventListenersDrpDwn = ".pf-c-select.kc_eventListeners_select";
private eventListenersSaveBtn = "saveEventListenerBtn";
@@ -208,6 +210,9 @@ export default class RealmSettingsPage {
".pf-c-button.pf-c-select__toggle-button.pf-m-plain";
private eventListenerRemove = '[data-ouia-component-id="Remove"]';
private roleSelect = ".pf-c-select.kc-role-select";
+ private selectScopeButton = "select-scope-button";
+ private deleteClientRolesCondition = "delete-client-roles-condition";
+ private deleteClientScopesCondition = "delete-client-scopes-condition";
selectLoginThemeType(themeType: string) {
cy.get(this.selectLoginTheme).click();
@@ -966,7 +971,7 @@ export default class RealmSettingsPage {
);
}
- shouldAddCondition() {
+ shouldAddClientRolesCondition() {
cy.get(this.clientPolicy).click();
cy.findByTestId(this.addCondition).click();
cy.get(this.addConditionDrpDwn).click();
@@ -989,10 +994,37 @@ export default class RealmSettingsPage {
cy.get('ul[class*="pf-c-data-list"]').should("have.text", "client-roles");
}
- shouldEditCondition() {
+ addClientScopes() {
+ cy.findByTestId(this.selectScopeButton).click();
+ cy.get(".pf-c-table__check > input[name=checkrow0]").click();
+ cy.get(".pf-c-table__check > input[name=checkrow1]").click();
+ cy.get(".pf-c-table__check > input[name=checkrow2]").click();
+
+ cy.findByTestId("modalConfirm").contains("Add").click();
+ }
+
+ shouldAddClientScopesCondition() {
+ cy.get(this.clientPolicy).click();
+ cy.findByTestId(this.addCondition).click();
+ cy.get(this.addConditionDrpDwn).click();
+ cy.findByTestId(this.addConditionDrpDwnOption)
+ .contains("client-scopes")
+ .click();
+
+ this.addClientScopes();
+
+ cy.findByTestId(this.addConditionSaveBtn).click();
+ cy.get(this.alertMessage).should(
+ "be.visible",
+ "Success! Condition created successfully"
+ );
+ cy.get('ul[class*="pf-c-data-list"]').contains("client-scopes");
+ }
+
+ shouldEditClientRolesCondition() {
cy.get(this.clientPolicy).click();
- cy.findByTestId(this.conditionTypeLink).contains("client-roles").click();
+ cy.findByTestId(this.clientRolesConditionLink).click();
cy.get(this.roleSelect).click();
cy.get(this.roleSelect).contains("create-client").click();
@@ -1006,26 +1038,53 @@ export default class RealmSettingsPage {
);
}
+ shouldEditClientScopesCondition() {
+ cy.get(this.clientPolicy).click();
+
+ cy.findByTestId(this.clientScopesConditionLink).click();
+
+ cy.wait(200);
+
+ this.addClientScopes();
+
+ cy.findByTestId(this.addConditionSaveBtn).click();
+ cy.get(this.alertMessage).should(
+ "be.visible",
+ "Success! Condition updated successfully"
+ );
+ }
+
shouldCancelDeletingCondition() {
cy.get(this.clientPolicy).click();
- cy.get('svg[class*="kc-conditionType-trash-icon"]').click();
+ cy.findByTestId(this.deleteClientRolesCondition).click();
cy.get(this.deleteDialogTitle).contains("Delete condition?");
cy.get(this.deleteDialogBodyText).contains(
"This action will permanently delete client-roles. This cannot be undone."
);
cy.findByTestId("modalConfirm").contains("Delete");
cy.get(this.deleteDialogCancelBtn).contains("Cancel").click();
- cy.get('ul[class*="pf-c-data-list"]').should("have.text", "client-roles");
+ cy.get('ul[class*="pf-c-data-list"]').contains("client-roles");
}
- shouldDeleteCondition() {
+ shouldDeleteClientRolesCondition() {
cy.get(this.clientPolicy).click();
- cy.get('svg[class*="kc-conditionType-trash-icon"]').click();
+ cy.findByTestId(this.deleteClientRolesCondition).click();
cy.get(this.deleteDialogTitle).contains("Delete condition?");
cy.get(this.deleteDialogBodyText).contains(
"This action will permanently delete client-roles. This cannot be undone."
);
cy.findByTestId("modalConfirm").contains("Delete").click();
+ cy.get('ul[class*="pf-c-data-list"]').contains("client-scopes");
+ }
+
+ shouldDeleteClientScopesCondition() {
+ cy.get(this.clientPolicy).click();
+ cy.findByTestId(this.deleteClientScopesCondition).click();
+ cy.get(this.deleteDialogTitle).contains("Delete condition?");
+ cy.get(this.deleteDialogBodyText).contains(
+ "This action will permanently delete client-scopes. This cannot be undone."
+ );
+ cy.findByTestId("modalConfirm").contains("Delete").click();
cy.get('h6[class*="kc-emptyConditions"]').should(
"have.text",
"No conditions configured"
diff --git a/src/client-scopes/ClientScopesSection.tsx b/src/client-scopes/ClientScopesSection.tsx
index 7de0fc594e..342ded148f 100644
--- a/src/client-scopes/ClientScopesSection.tsx
+++ b/src/client-scopes/ClientScopesSection.tsx
@@ -19,7 +19,7 @@ import { useAlerts } from "../components/alert/Alerts";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { useRealm } from "../context/realm-context/RealmContext";
-import { upperCaseFormatter, emptyFormatter } from "../util";
+import { emptyFormatter } from "../util";
import {
CellDropdown,
ClientScope,
@@ -44,6 +44,7 @@ import {
typeFilter,
} from "./details/SearchFilter";
import type { Row } from "../clients/scopes/ClientScopes";
+import { getProtocolName } from "../clients/utils";
export default function ClientScopesSection() {
const { realm } = useRealm();
@@ -267,7 +268,8 @@ export default function ClientScopesSection() {
{
name: "protocol",
displayKey: "client-scopes:protocol",
- cellFormatters: [upperCaseFormatter()],
+ cellRenderer: (client) =>
+ getProtocolName(t, client.protocol ?? "openid-connect"),
transforms: [cellWidth(15)],
},
{
diff --git a/src/clients/ClientDetails.tsx b/src/clients/ClientDetails.tsx
index a06e8fb9c0..cde0c902fa 100644
--- a/src/clients/ClientDetails.tsx
+++ b/src/clients/ClientDetails.tsx
@@ -458,6 +458,7 @@ export default function ClientDetails() {
title={{t("setup")}}
>
diff --git a/src/clients/messages.ts b/src/clients/messages.ts
index 52c888b428..3fcc3fb193 100644
--- a/src/clients/messages.ts
+++ b/src/clients/messages.ts
@@ -1,9 +1,11 @@
export default {
clients: {
- protocol: {
+ protocolTypes: {
openIdConnect: "OpenID Connect",
saml: "SAML",
+ all: "All",
},
+ protocol: "Protocol",
clientType: "Client type",
clientAuthorization: "Authorization",
implicitFlow: "Implicit flow",
@@ -27,7 +29,7 @@ export default {
"You haven't created any roles for this client. Create a role to get started.",
clientScopes: "Client scopes",
addClientScope: "Add client scope",
- addClientScopesTo: "Add client scopes to {{clientId}}",
+ addClientScopesTo: "Add client scopes to {{clientName}}",
clientScopeRemoveSuccess: "Scope mapping successfully removed",
clientScopeRemoveError: "Could not remove the scope mapping {{error}}",
clientScopeSuccess: "Scope mapping successfully updated",
diff --git a/src/clients/scopes/AddScopeDialog.tsx b/src/clients/scopes/AddScopeDialog.tsx
index 8c8025806c..afe0fecfe9 100644
--- a/src/clients/scopes/AddScopeDialog.tsx
+++ b/src/clients/scopes/AddScopeDialog.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
Button,
@@ -8,8 +8,17 @@ import {
Modal,
ModalVariant,
DropdownDirection,
+ DropdownItem,
+ Select,
+ SelectOption,
+ SelectVariant,
+ SelectDirection,
} from "@patternfly/react-core";
-import { CaretUpIcon } from "@patternfly/react-icons";
+import {
+ CaretDownIcon,
+ CaretUpIcon,
+ FilterIcon,
+} from "@patternfly/react-icons";
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
import {
@@ -19,27 +28,65 @@ import {
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import "./client-scopes.css";
+import { getProtocolName } from "../utils";
export type AddScopeDialogProps = {
clientScopes: ClientScopeRepresentation[];
+ clientName?: string;
open: boolean;
toggleDialog: () => void;
onAdd: (
- scopes: { scope: ClientScopeRepresentation; type: ClientScopeType }[]
+ scopes: { scope: ClientScopeRepresentation; type?: ClientScopeType }[]
) => void;
+ isClientScopesConditionType?: boolean;
};
+enum FilterType {
+ Name = "Name",
+ Protocol = "Protocol",
+}
+
+enum ProtocolType {
+ All = "All",
+ SAML = "SAML",
+ OpenIDConnect = "OpenID Connect",
+}
+
export const AddScopeDialog = ({
clientScopes,
+ clientName,
open,
toggleDialog,
onAdd,
+ isClientScopesConditionType,
}: AddScopeDialogProps) => {
const { t } = useTranslation("clients");
const [addToggle, setAddToggle] = useState(false);
const [rows, setRows] = useState([]);
+ const [filterType, setFilterType] = useState(FilterType.Name);
+ const [protocolType, setProtocolType] = useState(ProtocolType.All);
+ const [key, setKey] = useState(0);
+ const refresh = () => setKey(key + 1);
- const loader = () => Promise.resolve(clientScopes);
+ const [isFilterTypeDropdownOpen, setIsFilterTypeDropdownOpen] =
+ useState(false);
+
+ const [isProtocolTypeDropdownOpen, setIsProtocolTypeDropdownOpen] =
+ useState(false);
+
+ useEffect(() => {
+ refresh();
+ }, [filterType, protocolType]);
+
+ const loader = async () => {
+ if (protocolType === ProtocolType.OpenIDConnect) {
+ return clientScopes.filter((item) => item.protocol === "openid-connect");
+ } else if (protocolType === ProtocolType.SAML) {
+ return clientScopes.filter((item) => item.protocol === "saml");
+ }
+
+ return clientScopes;
+ };
const action = (scope: ClientScopeType) => {
const scopes = rows.map((row) => {
@@ -50,55 +97,213 @@ export const AddScopeDialog = ({
toggleDialog();
};
+ const onFilterTypeDropdownToggle = () => {
+ setIsFilterTypeDropdownOpen(!isFilterTypeDropdownOpen);
+ };
+
+ const onProtocolTypeDropdownToggle = () => {
+ setIsProtocolTypeDropdownOpen(!isProtocolTypeDropdownOpen);
+ };
+
+ const onFilterTypeDropdownSelect = (filterType: string) => {
+ if (filterType === FilterType.Name) {
+ setFilterType(FilterType.Protocol);
+ } else if (filterType === FilterType.Protocol) {
+ setFilterType(FilterType.Name);
+ }
+
+ setIsFilterTypeDropdownOpen(!isFilterTypeDropdownOpen);
+ };
+
+ const onProtocolTypeDropdownSelect = (protocolType: string) => {
+ if (protocolType === ProtocolType.SAML) {
+ setProtocolType(ProtocolType.SAML);
+ } else if (protocolType === ProtocolType.OpenIDConnect) {
+ setProtocolType(ProtocolType.OpenIDConnect);
+ } else if (protocolType === ProtocolType.All) {
+ setProtocolType(ProtocolType.All);
+ }
+
+ setIsProtocolTypeDropdownOpen(!isProtocolTypeDropdownOpen);
+ };
+
+ const protocolTypeOptions = [
+
+ {t("protocolTypes.saml")}
+ ,
+
+ {t("protocolTypes.openIdConnect")}
+ ,
+
+ {t("protocolTypes.all")}
+ ,
+ ];
+
return (
setAddToggle(!addToggle)}
- isPrimary
- toggleIndicator={CaretUpIcon}
- id="add-scope-toggle"
- >
- {t("common:add")}
-
- }
- dropdownItems={clientScopeTypesDropdown(t, action)}
- />,
- ,
- ]}
+ actions={
+ isClientScopesConditionType
+ ? [
+ ,
+ ,
+ ]
+ : [
+ setAddToggle(!addToggle)}
+ isPrimary
+ toggleIndicator={CaretUpIcon}
+ id="add-scope-toggle"
+ >
+ {t("common:add")}
+
+ }
+ dropdownItems={clientScopeTypesDropdown(t, action)}
+ />,
+ ,
+ ]
+ }
>
{
+ onFilterTypeDropdownSelect(filterType);
+ }}
+ data-testid="filter-type-dropdown"
+ toggle={
+ }
+ >
+ {filterType}
+
+ }
+ isOpen={isFilterTypeDropdownOpen}
+ dropdownItems={[
+
+ {filterType === FilterType.Name
+ ? t("protocol")
+ : t("common:name")}
+ ,
+ ]}
+ />
+ }
+ key={key}
+ toolbarItem={
+ filterType === FilterType.Protocol && (
+ <>
+ {
+ onFilterTypeDropdownSelect(filterType);
+ }}
+ data-testid="filter-type-dropdown"
+ toggle={
+ }
+ >
+ {filterType}
+
+ }
+ isOpen={isFilterTypeDropdownOpen}
+ dropdownItems={[
+
+ {t("common:name")}
+ ,
+ ]}
+ />
+
+ >
+ )
+ }
canSelectAll
onSelect={(rows) => setRows(rows)}
columns={[
{
name: "name",
},
+ {
+ name: "protocol",
+ displayKey: "clients:protocol",
+ cellRenderer: (client) =>
+ getProtocolName(t, client.protocol ?? "openid-connect"),
+ },
{
name: "description",
},
diff --git a/src/clients/scopes/ClientScopes.tsx b/src/clients/scopes/ClientScopes.tsx
index 8e659eb787..659fa2a4e6 100644
--- a/src/clients/scopes/ClientScopes.tsx
+++ b/src/clients/scopes/ClientScopes.tsx
@@ -38,6 +38,7 @@ import { ChangeTypeDropdown } from "../../client-scopes/ChangeTypeDropdown";
export type ClientScopesProps = {
clientId: string;
protocol: string;
+ clientName: string;
};
export type Row = ClientScopeRepresentation & {
@@ -45,7 +46,11 @@ export type Row = ClientScopeRepresentation & {
description?: string;
};
-export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
+export const ClientScopes = ({
+ clientId,
+ protocol,
+ clientName,
+}: ClientScopesProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
@@ -135,6 +140,7 @@ export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
{rest && (
setAddDialogOpen(!addDialogOpen)}
onAdd={async (scopes) => {
@@ -146,7 +152,7 @@ export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
adminClient,
clientId,
scope.scope,
- scope.type
+ scope.type!
)
)
);
diff --git a/src/clients/scopes/client-scopes.css b/src/clients/scopes/client-scopes.css
index 4286158636..48ebef95fc 100644
--- a/src/clients/scopes/client-scopes.css
+++ b/src/clients/scopes/client-scopes.css
@@ -5,4 +5,8 @@
.keycloak__client-scopes-add__add-dropdown {
margin-right: var(--pf-global--spacer--md);
+}
+
+.kc-protocolType-select {
+ max-width: 25%;
}
\ No newline at end of file
diff --git a/src/clients/utils.ts b/src/clients/utils.ts
index 3a48759ca6..6c18ee724c 100644
--- a/src/clients/utils.ts
+++ b/src/clients/utils.ts
@@ -12,9 +12,9 @@ export const isRealmClient = (client: ClientRepresentation) => !client.protocol;
export const getProtocolName = (t: TFunction<"clients">, protocol: string) => {
switch (protocol) {
case "openid-connect":
- return t("clients:protocol:openIdConnect");
+ return t("clients:protocolTypes:openIdConnect");
case "saml":
- return t("clients:protocol:saml");
+ return t("clients:protocolTypes:saml");
}
return protocol;
diff --git a/src/components/dynamic/MultivaluedScopesComponent.tsx b/src/components/dynamic/MultivaluedScopesComponent.tsx
new file mode 100644
index 0000000000..d0ce381916
--- /dev/null
+++ b/src/components/dynamic/MultivaluedScopesComponent.tsx
@@ -0,0 +1,129 @@
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { Controller, useFormContext } from "react-hook-form";
+import {
+ Button,
+ Chip,
+ ChipGroup,
+ FormGroup,
+ TextInput,
+} from "@patternfly/react-core";
+
+import { HelpItem } from "../help-enabler/HelpItem";
+import type { ComponentProps } from "./components";
+import { AddScopeDialog } from "../../clients/scopes/AddScopeDialog";
+import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
+import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
+import { useParams } from "react-router";
+import type { EditClientPolicyConditionParams } from "../../realm-settings/routes/EditCondition";
+
+export const MultivaluedScopesComponent = ({
+ defaultValue,
+ name,
+}: ComponentProps) => {
+ const { t } = useTranslation("dynamic");
+ const { control } = useFormContext();
+ const { conditionName } = useParams();
+ const adminClient = useAdminClient();
+ const [open, setOpen] = useState(false);
+ const [clientScopes, setClientScopes] = useState(
+ []
+ );
+
+ useFetch(
+ () => adminClient.clientScopes.find(),
+ (clientScopes) => {
+ setClientScopes(clientScopes);
+ },
+ []
+ );
+
+ const toggleModal = () => {
+ setOpen(!open);
+ };
+
+ return (
+
+ }
+ fieldId={name!}
+ >
+ {
+ return (
+ <>
+ {open && (
+ !value.includes(scope.name!)
+ )}
+ isClientScopesConditionType
+ open={open}
+ toggleDialog={() => setOpen(!open)}
+ onAdd={(scopes) => {
+ onChange([
+ ...value,
+ ...scopes
+ .map((scope) => scope.scope)
+ .map((item) => item.name!),
+ ]);
+ }}
+ />
+ )}
+ {value.length === 0 && !conditionName && (
+
+ )}
+ {
+ onChange([]);
+ }}
+ >
+ {value.map((currentChip: string) => (
+ {
+ onChange(
+ value.filter((item: string) => item !== currentChip)
+ );
+ }}
+ >
+ {currentChip}
+
+ ))}
+
+
+ >
+ );
+ }}
+ />
+
+ );
+};
diff --git a/src/realm-settings/NewClientPolicyCondition.tsx b/src/realm-settings/NewClientPolicyCondition.tsx
index c10fc836a8..6a2ee6eefe 100644
--- a/src/realm-settings/NewClientPolicyCondition.tsx
+++ b/src/realm-settings/NewClientPolicyCondition.tsx
@@ -34,12 +34,12 @@ import {
convertToMultiline,
toValue,
} from "../components/multi-line-input/MultiLineInput";
-import { MultivaluedRoleComponent } from "../components/dynamic/MultivaluedRoleComponent";
import {
COMPONENTS,
isValidComponentType,
} from "../components/dynamic/components";
-
+import { MultivaluedScopesComponent } from "../components/dynamic/MultivaluedScopesComponent";
+import { MultivaluedRoleComponent } from "../components/dynamic/MultivaluedRoleComponent";
export type ItemType = { value: string };
type ConfigProperty = ConfigPropertyRepresentation & {
@@ -54,6 +54,7 @@ export default function NewClientPolicyCondition() {
const [openConditionType, setOpenConditionType] = useState(false);
const [policies, setPolicies] = useState([]);
+
const [condition, setCondition] = useState<
ClientPolicyConditionRepresentation[]
>([]);
@@ -87,9 +88,15 @@ export default function NewClientPolicyCondition() {
Object.entries(condition.configuration!).map(([key, value]) => {
const formKey = `config.${key}`;
+
const property = properties.find((p) => p.name === key);
- if (property?.type === "MultivaluedString") {
+ if (
+ property?.type === "MultivaluedString" &&
+ property.name !== "scopes"
+ ) {
form.setValue(formKey, convertToMultiline(value));
+ } else if (property?.name === "client-scopes") {
+ form.setValue("config.scopes", value);
} else {
form.setValue(formKey, value);
}
@@ -98,8 +105,10 @@ export default function NewClientPolicyCondition() {
useFetch(
() => adminClient.clientPolicies.listPolicies(),
+
(policies) => {
setPolicies(policies.policies ?? []);
+
if (conditionName) {
const currentPolicy = policies.policies?.find(
(item) => item.name === policyName
@@ -124,13 +133,14 @@ export default function NewClientPolicyCondition() {
const save = async (configPolicy: ConfigProperty) => {
const configValues = configPolicy.config;
- const writeConfig = () =>
- conditionProperties.reduce((r: any, p) => {
- p.type === "MultivaluedString"
+ const writeConfig = () => {
+ return conditionProperties.reduce((r: any, p) => {
+ p.type === "MultivaluedString" && p.name !== "scopes"
? (r[p.name!] = toValue(configValues[p.name!]))
: (r[p.name!] = configValues[p.name!]);
return r;
}, {});
+ };
const updatedPolicies = policies.map((policy) => {
if (policy.name !== policyName) {
@@ -284,6 +294,17 @@ export default function NewClientPolicyCondition() {
conditionName === "client-roles")
) {
return ;
+ } else if (
+ property.name === "scopes" &&
+ (conditionType === "client-scopes" ||
+ conditionName === "client-scopes")
+ ) {
+ return (
+
+ );
} else if (isValidComponentType(componentType)) {
const Component = COMPONENTS[componentType];
return ;
diff --git a/src/realm-settings/NewClientPolicyForm.tsx b/src/realm-settings/NewClientPolicyForm.tsx
index 273300f70f..a669224b57 100644
--- a/src/realm-settings/NewClientPolicyForm.tsx
+++ b/src/realm-settings/NewClientPolicyForm.tsx
@@ -551,7 +551,7 @@ export default function NewClientPolicyForm() {
0 ? (
{
toggleDeleteConditionDialog();
setConditionToDelete({
diff --git a/src/realm-settings/RealmSettingsSection.css b/src/realm-settings/RealmSettingsSection.css
index 9708734e60..a70ce217fb 100644
--- a/src/realm-settings/RealmSettingsSection.css
+++ b/src/realm-settings/RealmSettingsSection.css
@@ -252,3 +252,16 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template
.kc_eventListeners_select {
width: 35rem;
}
+
+input#kc-scopes {
+ width: 630px;
+
+ margin-right: 24px;
+}
+
+.kc-client-scopes-chip-group {
+ margin-right: var(--pf-global--spacer--2xl);
+ max-width: 585px;
+ min-width: 585px;
+ padding-left: none;
+}