Changed the idP mapper form to use the new Dynamic forms (#1641)
This commit is contained in:
parent
920ac1dfe3
commit
67d2c8617f
13 changed files with 300 additions and 829 deletions
|
@ -128,7 +128,7 @@ describe("Identity provider test", () => {
|
|||
masthead.checkNotificationMessage(createMapperSuccessMsg, true);
|
||||
});
|
||||
|
||||
it.skip("should edit Username Template Importer mapper", () => {
|
||||
it("should edit Username Template Importer mapper", () => {
|
||||
sidebarPage.goToIdentityProviders();
|
||||
listingPage.goToItemDetails(samlProviderName);
|
||||
addMapperPage.goToMappersTab();
|
||||
|
|
|
@ -7,28 +7,26 @@ export default class AddMapperPage {
|
|||
|
||||
private mapperNameInput = "#kc-name";
|
||||
private mapperRoleInput = "mapper-role-input";
|
||||
private attributeName = "attribute-name";
|
||||
private attributeFriendlyName = "attribute-friendly-name";
|
||||
private attribute = "user.attribute";
|
||||
private attributeName = "attribute.name";
|
||||
private attributeFriendlyName = "attribute.friendly.name";
|
||||
private attributeValue = "attribute-value";
|
||||
private claimInput = "claim";
|
||||
private claimValueInput = "claim-value-input";
|
||||
private socialProfileJSONfieldPath = "social-profile-JSON-field-path";
|
||||
private userAttribute = "user-attribute";
|
||||
private userAttributeName = "user-attribute-name";
|
||||
private userAttributeValue = "user-attribute-value";
|
||||
private userSessionAttribute = "user-session-attribute";
|
||||
private userSessionAttributeValue = "user-session-attribute-value";
|
||||
private socialProfileJSONfieldPath = "jsonField";
|
||||
private userAttribute = "attribute";
|
||||
private userAttributeName = "userAttribute";
|
||||
private userAttributeValue = "attribute.value";
|
||||
private userSessionAttribute = "attribute";
|
||||
private userSessionAttributeValue = "attribute.value";
|
||||
private newMapperSaveButton = "new-mapper-save-button";
|
||||
private regexAttributeValuesSwitch = "regex-values-switch";
|
||||
private regexAttributeValuesSwitch = "are.attribute.values.regex";
|
||||
private syncmodeSelectToggle = "#syncMode";
|
||||
private attributesKeyInput = 'input[name="config.attributes[0].key"]';
|
||||
private attributesValueInput = 'input[name="config.attributes[0].value"]';
|
||||
private template = "template";
|
||||
private target = "#target";
|
||||
private targetDropdown = "#target-dropdown";
|
||||
private selectRoleButton = "select-role-button";
|
||||
private radio = "[type=radio]";
|
||||
private addAssociatedRolesModalButton = "add-associated-roles-button";
|
||||
|
||||
goToMappersTab() {
|
||||
cy.findByTestId(this.mappersTab).click();
|
||||
|
@ -91,14 +89,16 @@ export default class AddMapperPage {
|
|||
}
|
||||
|
||||
addRoleToMapperForm() {
|
||||
const load = "/auth/admin/realms/master/roles";
|
||||
cy.intercept(load).as("load");
|
||||
|
||||
cy.get(this.radio).eq(0).check();
|
||||
|
||||
cy.findByTestId(this.addAssociatedRolesModalButton).contains("Add").click();
|
||||
|
||||
cy.findByTestId(this.mapperRoleInput).should("have.value", "admin");
|
||||
cy.get("#group-role-select-typeahead")
|
||||
.click()
|
||||
.get(".pf-c-select__menu-item")
|
||||
.first()
|
||||
.click();
|
||||
cy.get("#role-role-select-typeahead")
|
||||
.click()
|
||||
.get(".pf-c-select__menu-item")
|
||||
.first()
|
||||
.click();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ export default class AddMapperPage {
|
|||
cy.get(this.idpMapperSelectToggle).click();
|
||||
|
||||
cy.findByTestId(this.idpMapperSelect)
|
||||
.contains("Advanced Attribute To Role")
|
||||
.contains("Advanced Attribute to Role")
|
||||
.click();
|
||||
|
||||
cy.get(this.attributesKeyInput).clear();
|
||||
|
@ -126,8 +126,6 @@ export default class AddMapperPage {
|
|||
|
||||
this.toggleSwitch(this.regexAttributeValuesSwitch);
|
||||
|
||||
cy.findByTestId(this.selectRoleButton).click();
|
||||
|
||||
this.addRoleToMapperForm();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
@ -153,9 +151,7 @@ export default class AddMapperPage {
|
|||
cy.findByTestId(this.template).clear();
|
||||
cy.findByTestId(this.template).type("Template");
|
||||
|
||||
cy.get(this.target).click();
|
||||
|
||||
cy.get(this.targetDropdown).contains("LOCAL").click();
|
||||
cy.get(this.target).click().parent().contains("BROKER_ID").click();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -211,8 +207,7 @@ export default class AddMapperPage {
|
|||
cy.findByTestId(this.attributeFriendlyName).clear();
|
||||
cy.findByTestId(this.attributeFriendlyName).type("attribute friendly name");
|
||||
|
||||
cy.findByTestId(this.userAttributeName).clear();
|
||||
cy.findByTestId(this.userAttributeName).type("user attribute name");
|
||||
cy.findByTestId(this.attribute).clear().type("user attribute name");
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -234,11 +229,8 @@ export default class AddMapperPage {
|
|||
.contains("Attribute Importer")
|
||||
.click();
|
||||
|
||||
cy.findByTestId(this.claimInput).clear();
|
||||
cy.findByTestId(this.claimInput).type("claim");
|
||||
|
||||
cy.findByTestId(this.userAttributeName).clear();
|
||||
cy.findByTestId(this.userAttributeName).type("user attribute name");
|
||||
cy.findByTestId(this.claimInput).clear().type("claim");
|
||||
cy.findByTestId(this.attribute).clear().type("user attribute name");
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -258,9 +250,7 @@ export default class AddMapperPage {
|
|||
|
||||
cy.findByTestId(this.idpMapperSelect).contains("Hardcoded Role").click();
|
||||
|
||||
cy.findByTestId(this.mapperRoleInput).clear();
|
||||
cy.findByTestId(this.mapperRoleInput).type("admin");
|
||||
|
||||
this.addRoleToMapperForm();
|
||||
this.saveNewMapper();
|
||||
|
||||
return this;
|
||||
|
@ -281,13 +271,11 @@ export default class AddMapperPage {
|
|||
.contains("Hardcoded Attribute")
|
||||
.click();
|
||||
|
||||
cy.findByTestId(this.userAttribute).clear();
|
||||
cy.findByTestId(this.userAttribute).type("user session attribute");
|
||||
cy.findByTestId(this.userAttribute).clear().type("user session attribute");
|
||||
|
||||
cy.findByTestId(this.userAttributeValue).clear();
|
||||
cy.findByTestId(this.userAttributeValue).type(
|
||||
"user session attribute value"
|
||||
);
|
||||
cy.findByTestId(this.userAttributeValue)
|
||||
.clear()
|
||||
.type("user session attribute value");
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -306,11 +294,10 @@ export default class AddMapperPage {
|
|||
cy.get(this.idpMapperSelectToggle).click();
|
||||
|
||||
cy.findByTestId(this.idpMapperSelect)
|
||||
.contains("SAML Attribute To Role")
|
||||
.contains("SAML Attribute to Role")
|
||||
.click();
|
||||
|
||||
cy.findByTestId(this.mapperRoleInput).clear();
|
||||
cy.findByTestId(this.mapperRoleInput).type("admin");
|
||||
this.addRoleToMapperForm();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -324,9 +311,7 @@ export default class AddMapperPage {
|
|||
|
||||
cy.findByTestId(this.template).type("_edited");
|
||||
|
||||
cy.get(this.target).click();
|
||||
|
||||
cy.get(this.targetDropdown).contains("BROKER_ID").click();
|
||||
cy.get(this.target).click().parent().contains("BROKER_USERNAME").click();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
|
@ -408,19 +393,14 @@ export default class AddMapperPage {
|
|||
|
||||
cy.get(this.idpMapperSelectToggle).click();
|
||||
|
||||
cy.findByTestId(this.idpMapperSelect).contains("Claim To Role").click();
|
||||
cy.findByTestId(this.idpMapperSelect).contains("Claim to Role").click();
|
||||
|
||||
cy.get(this.attributesKeyInput).clear();
|
||||
cy.get(this.attributesKeyInput).type("key");
|
||||
cy.findByTestId("attribute-key-input").clear().type("key");
|
||||
cy.findByTestId("attribute-value-input").clear().type("value");
|
||||
|
||||
cy.get(this.attributesValueInput).clear();
|
||||
cy.get(this.attributesValueInput).type("value");
|
||||
|
||||
this.toggleSwitch(this.regexAttributeValuesSwitch);
|
||||
|
||||
cy.findByTestId(this.mapperRoleInput).clear();
|
||||
cy.findByTestId(this.mapperRoleInput).type("admin");
|
||||
this.toggleSwitch("are.claim.values.regex");
|
||||
|
||||
this.addRoleToMapperForm();
|
||||
this.saveNewMapper();
|
||||
|
||||
return this;
|
||||
|
|
|
@ -30,8 +30,6 @@ export default {
|
|||
addMapperExplain:
|
||||
"If you want more fine-grain control, you can create protocol mapper on this client",
|
||||
realmRoles: "Realm roles",
|
||||
clientRoles: "Client roles",
|
||||
selectASourceOfRoles: "Select a source of roles",
|
||||
newRoleName: "New role name",
|
||||
searchClientByName: "Search client by name",
|
||||
clients: "Clients",
|
||||
|
@ -50,8 +48,6 @@ export default {
|
|||
predefinedMappingDescription:
|
||||
"Choose one of the predefined mappings from this table",
|
||||
mappingTable: "Table with predefined mapping",
|
||||
roleGroup: "Use a realm role from:",
|
||||
clientGroup: "Use a client role from:",
|
||||
scope: "Scope",
|
||||
roleMappingUpdatedSuccess: "Role mapping updated",
|
||||
roleMappingUpdatedError: "Could not update role mapping {{error}}",
|
||||
|
|
|
@ -27,7 +27,7 @@ export const BooleanComponent = ({
|
|||
<Controller
|
||||
name={`config.${name}`}
|
||||
data-testid={name}
|
||||
defaultValue={defaultValue}
|
||||
defaultValue={defaultValue || false}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
|
@ -36,6 +36,7 @@ export const BooleanComponent = ({
|
|||
labelOff={t("common:off")}
|
||||
isChecked={value === "true" || value === true}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
data-testid={name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
75
src/components/dynamic/GroupComponent.tsx
Normal file
75
src/components/dynamic/GroupComponent.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
ChipGroup,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import type { ComponentProps } from "./components";
|
||||
import { HelpItem } from "../help-enabler/HelpItem";
|
||||
import { GroupPickerDialog } from "../group/GroupPickerDialog";
|
||||
|
||||
export const GroupComponent = ({ name, label, helpText }: ComponentProps) => {
|
||||
const { t } = useTranslation("dynamic");
|
||||
const [open, setOpen] = useState(false);
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={`config.${name}`}
|
||||
defaultValue=""
|
||||
typeAheadAriaLabel={t("selectGroup")}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<>
|
||||
{open && (
|
||||
<GroupPickerDialog
|
||||
type="selectOne"
|
||||
text={{
|
||||
title: "dynamic:selectGroup",
|
||||
ok: "common:select",
|
||||
}}
|
||||
onConfirm={(groups) => {
|
||||
onChange(groups[0].path);
|
||||
setOpen(false);
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
filterGroups={value}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormGroup
|
||||
label={t(label!)}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={t(helpText!)}
|
||||
fieldLabelId={`dynamic:${label}`}
|
||||
/>
|
||||
}
|
||||
fieldId={name!}
|
||||
>
|
||||
<InputGroup>
|
||||
<ChipGroup>
|
||||
{value && (
|
||||
<Chip onClick={() => onChange(undefined)}>{value}</Chip>
|
||||
)}
|
||||
</ChipGroup>
|
||||
<Button
|
||||
id="kc-join-groups-button"
|
||||
onClick={() => setOpen(!open)}
|
||||
variant="secondary"
|
||||
data-testid="join-groups-button"
|
||||
>
|
||||
{t("selectGroup")}
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
23
src/components/dynamic/MapComponent.tsx
Normal file
23
src/components/dynamic/MapComponent.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormGroup } from "@patternfly/react-core";
|
||||
|
||||
import type { ComponentProps } from "./components";
|
||||
import { HelpItem } from "../help-enabler/HelpItem";
|
||||
import { AttributeInput } from "../attribute-input/AttributeInput";
|
||||
|
||||
export const MapComponent = ({ name, label, helpText }: ComponentProps) => {
|
||||
const { t } = useTranslation("dynamic");
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
label={t(label!)}
|
||||
labelIcon={
|
||||
<HelpItem helpText={t(helpText!)} fieldLabelId={`dynamic:${label}`} />
|
||||
}
|
||||
fieldId={name!}
|
||||
>
|
||||
<AttributeInput name={`config.${name}`} />
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
|
@ -19,12 +19,17 @@ import { useRealm } from "../../context/realm-context/RealmContext";
|
|||
import { HelpItem } from "../help-enabler/HelpItem";
|
||||
import type { ComponentProps } from "./components";
|
||||
|
||||
const RealmClient = (realm: string): ClientRepresentation => ({
|
||||
name: "realmRoles",
|
||||
clientId: realm,
|
||||
});
|
||||
|
||||
export const RoleComponent = ({ name, label, helpText }: ComponentProps) => {
|
||||
const { t } = useTranslation("dynamic");
|
||||
|
||||
const adminClient = useAdminClient();
|
||||
const { realm } = useRealm();
|
||||
const { control, errors } = useFormContext();
|
||||
const { control, errors, getValues } = useFormContext();
|
||||
|
||||
const [roleOpen, setRoleOpen] = useState(false);
|
||||
const [clientsOpen, setClientsOpen] = useState(false);
|
||||
|
@ -51,30 +56,29 @@ export const RoleComponent = ({ name, label, helpText }: ComponentProps) => {
|
|||
(await adminClient.clients.listRoles({ id: client.id! })).length > 0
|
||||
);
|
||||
|
||||
filteredClients.map(
|
||||
(client) =>
|
||||
(client.toString = function () {
|
||||
return this.clientId!;
|
||||
})
|
||||
);
|
||||
return filteredClients;
|
||||
},
|
||||
(filteredClients) => setClients(filteredClients),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const value = getValues(fieldName);
|
||||
const [client, role] = value?.includes(".")
|
||||
? value.split(".")
|
||||
: ["", value || ""];
|
||||
if (client) {
|
||||
setSelectedClient(clients?.find((c) => c.clientId === client));
|
||||
} else {
|
||||
setSelectedClient(RealmClient(realm));
|
||||
}
|
||||
setSelectedRole({ name: role });
|
||||
}, [clients, getValues]);
|
||||
|
||||
const createSelectGroup = (clients: ClientRepresentation[]) => {
|
||||
return [
|
||||
<SelectGroup key="role" label={t("roleGroup")}>
|
||||
<SelectOption
|
||||
key="realmRoles"
|
||||
value={
|
||||
{
|
||||
name: "realmRoles",
|
||||
toString: () => t("realmRoles"),
|
||||
} as ClientRepresentation
|
||||
}
|
||||
>
|
||||
<SelectOption key="realmRoles" value={RealmClient(realm)}>
|
||||
{realm}
|
||||
</SelectOption>
|
||||
</SelectGroup>,
|
||||
|
@ -133,75 +137,76 @@ export const RoleComponent = ({ name, label, helpText }: ComponentProps) => {
|
|||
name={fieldName}
|
||||
defaultValue=""
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ onChange, value }) => {
|
||||
const [client, role] = value?.split(".") || ["", ""];
|
||||
return (
|
||||
<Split hasGutter>
|
||||
<SplitItem>
|
||||
{clients && (
|
||||
<Select
|
||||
toggleId={name!}
|
||||
data-testid={name}
|
||||
onToggle={() => setClientsOpen(!clientsOpen)}
|
||||
isOpen={clientsOpen}
|
||||
variant={SelectVariant.typeahead}
|
||||
typeAheadAriaLabel={t("selectASourceOfRoles")}
|
||||
placeholderText={t("selectASourceOfRoles")}
|
||||
isGrouped
|
||||
onFilter={(evt) => {
|
||||
const textInput = evt?.target.value || "";
|
||||
if (textInput === "") {
|
||||
return createSelectGroup(clients);
|
||||
} else {
|
||||
return createSelectGroup(
|
||||
clients.filter((client) =>
|
||||
client
|
||||
.name!.toLowerCase()
|
||||
.includes(textInput.toLowerCase())
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
selections={selectedClient || client}
|
||||
onClear={() => onClear(onChange)}
|
||||
onSelect={(_, value) => {
|
||||
setSelectedClient(value as ClientRepresentation);
|
||||
setClientsOpen(false);
|
||||
}}
|
||||
>
|
||||
{createSelectGroup(clients)}
|
||||
</Select>
|
||||
)}
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
render={({ onChange }) => (
|
||||
<Split hasGutter>
|
||||
<SplitItem>
|
||||
{clients && (
|
||||
<Select
|
||||
onToggle={(isExpanded) => setRoleOpen(isExpanded)}
|
||||
isOpen={roleOpen}
|
||||
toggleId={`group-${name}`}
|
||||
onToggle={() => setClientsOpen(!clientsOpen)}
|
||||
isOpen={clientsOpen}
|
||||
variant={SelectVariant.typeahead}
|
||||
placeholderText={
|
||||
selectedClient?.name !== "realmRoles"
|
||||
? t("clientRoles")
|
||||
: t("selectARole")
|
||||
}
|
||||
isDisabled={!selectedClient && !role}
|
||||
typeAheadAriaLabel={t("selectARole")}
|
||||
selections={selectedRole?.name || role}
|
||||
onSelect={(_, value) => {
|
||||
const role = value as RoleRepresentation;
|
||||
setSelectedRole(role);
|
||||
onChange(`${selectedClient?.clientId}.${role.name}`);
|
||||
setRoleOpen(false);
|
||||
typeAheadAriaLabel={t("selectASourceOfRoles")}
|
||||
placeholderText={t("selectASourceOfRoles")}
|
||||
isGrouped
|
||||
onFilter={(evt) => {
|
||||
const textInput = evt?.target.value || "";
|
||||
if (textInput === "") {
|
||||
return createSelectGroup(clients);
|
||||
} else {
|
||||
return createSelectGroup(
|
||||
clients.filter((client) =>
|
||||
client
|
||||
.name!.toLowerCase()
|
||||
.includes(textInput.toLowerCase())
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
maxHeight={200}
|
||||
selections={selectedClient?.clientId}
|
||||
onClear={() => onClear(onChange)}
|
||||
onSelect={(_, value) => {
|
||||
onClear(onChange);
|
||||
setSelectedClient(value as ClientRepresentation);
|
||||
setClientsOpen(false);
|
||||
}}
|
||||
>
|
||||
{roleSelectOptions()}
|
||||
{createSelectGroup(clients)}
|
||||
</Select>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
);
|
||||
}}
|
||||
)}
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
<Select
|
||||
toggleId={`role-${name}`}
|
||||
onToggle={(isExpanded) => setRoleOpen(isExpanded)}
|
||||
isOpen={roleOpen}
|
||||
variant={SelectVariant.typeahead}
|
||||
placeholderText={
|
||||
selectedClient?.name !== "realmRoles"
|
||||
? t("clientRoles")
|
||||
: t("selectARole")
|
||||
}
|
||||
isDisabled={!selectedClient}
|
||||
typeAheadAriaLabel={t("selectARole")}
|
||||
selections={selectedRole?.name}
|
||||
onSelect={(_, value) => {
|
||||
const role = value as RoleRepresentation;
|
||||
setSelectedRole(role);
|
||||
onChange(
|
||||
selectedClient?.name === "realmRoles"
|
||||
? role.name
|
||||
: `${selectedClient?.clientId}.${role.name}`
|
||||
);
|
||||
setRoleOpen(false);
|
||||
}}
|
||||
maxHeight={200}
|
||||
onClear={() => onClear(onChange)}
|
||||
>
|
||||
{roleSelectOptions()}
|
||||
</Select>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
|
|
|
@ -5,10 +5,12 @@ import { StringComponent } from "./StringComponent";
|
|||
import { BooleanComponent } from "./BooleanComponent";
|
||||
import { ListComponent } from "./ListComponent";
|
||||
import { RoleComponent } from "./RoleComponent";
|
||||
import { MapComponent } from "./MapComponent";
|
||||
import { ScriptComponent } from "./ScriptComponent";
|
||||
import { ClientSelectComponent } from "./ClientSelectComponent";
|
||||
import { MultiValuedStringComponent } from "./MultivaluedStringComponent";
|
||||
import { MultiValuedListComponent } from "./MultivaluedListComponent";
|
||||
import { GroupComponent } from "./GroupComponent";
|
||||
|
||||
export type ComponentProps = Omit<ConfigPropertyRepresentation, "type">;
|
||||
const ComponentTypes = [
|
||||
|
@ -17,6 +19,8 @@ const ComponentTypes = [
|
|||
"List",
|
||||
"Role",
|
||||
"Script",
|
||||
"Map",
|
||||
"Group",
|
||||
"MultivaluedList",
|
||||
"ClientList",
|
||||
"MultivaluedString",
|
||||
|
@ -32,6 +36,8 @@ export const COMPONENTS: {
|
|||
List: ListComponent,
|
||||
Role: RoleComponent,
|
||||
Script: ScriptComponent,
|
||||
Map: MapComponent,
|
||||
Group: GroupComponent,
|
||||
ClientList: ClientSelectComponent,
|
||||
MultivaluedList: MultiValuedListComponent,
|
||||
MultivaluedString: MultiValuedStringComponent,
|
||||
|
|
|
@ -2,6 +2,11 @@ export default {
|
|||
dynamic: {
|
||||
addMultivaluedLabel: "Add {{fieldLabel}}",
|
||||
selectARole: "Select a role",
|
||||
selectASourceOfRoles: "Select a source of roles",
|
||||
clientRoles: "Client roles",
|
||||
roleGroup: "Use a realm role from:",
|
||||
clientGroup: "Use a client role from:",
|
||||
selectGroup: "Select group",
|
||||
usermodel: {
|
||||
prop: {
|
||||
label: "Property",
|
||||
|
|
|
@ -1,31 +1,23 @@
|
|||
import React, { useMemo, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Link, useHistory, useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
FormGroup,
|
||||
PageSection,
|
||||
Select,
|
||||
SelectOption,
|
||||
SelectVariant,
|
||||
Switch,
|
||||
TextInput,
|
||||
ValidatedOptions,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||
import { AttributeInput } from "../../components/attribute-input/AttributeInput";
|
||||
import type { AttributeForm } from "../../components/attribute-form/AttributeForm";
|
||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||
import type { IdentityProviderAddMapperParams } from "../routes/AddMapper";
|
||||
import { AssociatedRolesModal } from "../../realm-roles/AssociatedRolesModal";
|
||||
import type { RoleRepresentation } from "../../model/role-model";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import {
|
||||
|
@ -35,9 +27,11 @@ import {
|
|||
import { convertFormValuesToObject, convertToFormValues } from "../../util";
|
||||
import { toIdentityProvider } from "../routes/IdentityProvider";
|
||||
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
|
||||
import type { IdentityProviderMapperTypeRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperTypeRepresentation";
|
||||
import { AddMapperForm } from "./AddMapperForm";
|
||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||
import { groupBy } from "lodash";
|
||||
import { DynamicComponents } from "../../components/dynamic/DynamicComponents";
|
||||
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
|
||||
import type { AttributeForm } from "../../components/attribute-form/AttributeForm";
|
||||
|
||||
export type IdPMapperRepresentationWithAttributes =
|
||||
IdentityProviderMapperRepresentation & AttributeForm;
|
||||
|
@ -49,8 +43,10 @@ export type Role = RoleRepresentation & {
|
|||
export default function AddMapper() {
|
||||
const { t } = useTranslation("identity-providers");
|
||||
|
||||
const form = useForm<IdPMapperRepresentationWithAttributes>();
|
||||
const { handleSubmit, control, register, errors } = form;
|
||||
const form = useForm<IdPMapperRepresentationWithAttributes>({
|
||||
shouldUnregister: false,
|
||||
});
|
||||
const { handleSubmit, register, errors } = form;
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const history = useHistory();
|
||||
|
||||
|
@ -60,56 +56,38 @@ export default function AddMapper() {
|
|||
const { providerId, alias } = useParams<IdentityProviderAddMapperParams>();
|
||||
const { id } = useParams<IdentityProviderEditMapperParams>();
|
||||
|
||||
const serverInfo = useServerInfo();
|
||||
const identityProviders = useMemo(
|
||||
() => groupBy(serverInfo.identityProviders, "groupName"),
|
||||
[serverInfo]
|
||||
);
|
||||
|
||||
const isSocialIdP = useMemo(
|
||||
() =>
|
||||
identityProviders["Social"]
|
||||
.map((item) => item.id)
|
||||
.includes(providerId.toLowerCase()),
|
||||
[identityProviders, providerId]
|
||||
);
|
||||
|
||||
const [mapperTypes, setMapperTypes] =
|
||||
useState<Record<string, IdentityProviderMapperRepresentation>>();
|
||||
const [mapperType, setMapperType] = useState(
|
||||
isSocialIdP
|
||||
? "attributeImporter"
|
||||
: providerId === "saml"
|
||||
? "advancedAttributeToRole"
|
||||
: "hardcodedUserSessionAttribute"
|
||||
);
|
||||
useState<Record<string, IdentityProviderMapperTypeRepresentation>>();
|
||||
const [mapperType, setMapperType] = useState<string>();
|
||||
|
||||
const [currentMapper, setCurrentMapper] =
|
||||
useState<IdentityProviderMapperRepresentation>();
|
||||
|
||||
const [rolesModalOpen, setRolesModalOpen] = useState(false);
|
||||
|
||||
const save = async (idpMapper: IdentityProviderMapperRepresentation) => {
|
||||
const attributes = JSON.stringify(idpMapper.config?.attributes ?? []);
|
||||
const mapper = convertFormValuesToObject(idpMapper);
|
||||
const mapper = convertFormValuesToObject(
|
||||
idpMapper
|
||||
) as IdentityProviderMapperRepresentation;
|
||||
const attributes = JSON.stringify(idpMapper.config.attributes ?? []);
|
||||
const claims = JSON.stringify(idpMapper.config.claims ?? []);
|
||||
|
||||
const identityProviderMapper = {
|
||||
...mapper,
|
||||
config: {
|
||||
...mapper.config,
|
||||
attributes,
|
||||
claims,
|
||||
},
|
||||
identityProviderAlias: alias!,
|
||||
};
|
||||
|
||||
if (id) {
|
||||
const updatedMapper = {
|
||||
...mapper,
|
||||
config: {
|
||||
attributes,
|
||||
},
|
||||
identityProviderAlias: alias!,
|
||||
id: id,
|
||||
name: currentMapper?.name!,
|
||||
};
|
||||
try {
|
||||
await adminClient.identityProviders.updateMapper(
|
||||
{
|
||||
id: id!,
|
||||
alias: alias!,
|
||||
},
|
||||
updatedMapper
|
||||
{ ...identityProviderMapper, name: currentMapper?.name! }
|
||||
);
|
||||
addAlert(t("mapperSaveSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
|
@ -118,13 +96,7 @@ export default function AddMapper() {
|
|||
} else {
|
||||
try {
|
||||
const createdMapper = await adminClient.identityProviders.createMapper({
|
||||
identityProviderMapper: {
|
||||
...mapper,
|
||||
identityProviderAlias: alias,
|
||||
config: {
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
identityProviderMapper,
|
||||
alias: alias!,
|
||||
});
|
||||
|
||||
|
@ -153,6 +125,9 @@ export default function AddMapper() {
|
|||
if (mapper) {
|
||||
setCurrentMapper(mapper);
|
||||
setupForm(mapper);
|
||||
setMapperType(mapper.identityProviderMapper!);
|
||||
} else {
|
||||
setMapperType(Object.keys(mapperTypes)[0]);
|
||||
}
|
||||
|
||||
setMapperTypes(mapperTypes);
|
||||
|
@ -161,58 +136,14 @@ export default function AddMapper() {
|
|||
);
|
||||
|
||||
const setupForm = (mapper: IdentityProviderMapperRepresentation) => {
|
||||
form.reset();
|
||||
convertToFormValues(mapper, form.setValue);
|
||||
form.setValue("config.attributes", JSON.parse(mapper.config.attributes));
|
||||
form.setValue("config.claims", JSON.parse(mapper.config.claims));
|
||||
};
|
||||
|
||||
const targetOptions = ["local", "brokerId", "brokerUsername"];
|
||||
const [targetOptionsOpen, setTargetOptionsOpen] = useState(false);
|
||||
const [selectedRole, setSelectedRole] = useState<Role[]>([]);
|
||||
|
||||
const formValues = form.getValues();
|
||||
|
||||
const isSAMLAdvancedAttrToRole =
|
||||
formValues.identityProviderMapper === "saml-advanced-role-idp-mapper";
|
||||
|
||||
const isOIDCclaimToRole =
|
||||
formValues.identityProviderMapper === "oidc-role-idp-mapper";
|
||||
|
||||
const isOIDCAdvancedClaimToRole =
|
||||
formValues.identityProviderMapper === "oidc-advanced-role-idp-mapper";
|
||||
|
||||
const isSAMLAttributeImporter =
|
||||
formValues.identityProviderMapper === "saml-user-attribute-idp-mapper";
|
||||
|
||||
const isOIDCAttributeImporter =
|
||||
formValues.identityProviderMapper === "oidc-user-attribute-idp-mapper";
|
||||
|
||||
const isHardcodedAttribute =
|
||||
formValues.identityProviderMapper === "hardcoded-attribute-idp-mapper";
|
||||
|
||||
const isHardcodedRole =
|
||||
formValues.identityProviderMapper === "oidc-hardcoded-role-idp-mapper";
|
||||
|
||||
const isHardcodedUserSessionAttribute =
|
||||
formValues.identityProviderMapper ===
|
||||
"hardcoded-user-session-attribute-idp-mapper";
|
||||
|
||||
const isSAMLAttributeToRole =
|
||||
formValues.identityProviderMapper === "saml-role-idp-mapper";
|
||||
|
||||
const isSAMLUsernameTemplateImporter =
|
||||
formValues.identityProviderMapper === "saml-username-idp-mapper";
|
||||
|
||||
const isOIDCUsernameTemplateImporter =
|
||||
formValues.identityProviderMapper === "oidc-username-idp-mapper";
|
||||
|
||||
const isSocialAttributeImporter = useMemo(
|
||||
() => formValues.identityProviderMapper?.includes("user-attribute-mapper"),
|
||||
[formValues.identityProviderMapper]
|
||||
);
|
||||
const toggleModal = () => {
|
||||
setRolesModalOpen(!rolesModalOpen);
|
||||
};
|
||||
if (!mapperTypes) {
|
||||
return <KeycloakSpinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<PageSection variant="light">
|
||||
|
@ -231,16 +162,6 @@ export default function AddMapper() {
|
|||
}
|
||||
divider
|
||||
/>
|
||||
{rolesModalOpen && (
|
||||
<AssociatedRolesModal
|
||||
id={id}
|
||||
onConfirm={(role) => setSelectedRole(role)}
|
||||
omitComposites
|
||||
isRadio
|
||||
isMapperId
|
||||
toggleDialog={toggleModal}
|
||||
/>
|
||||
)}
|
||||
<FormAccess
|
||||
role="manage-identity-providers"
|
||||
isHorizontal
|
||||
|
@ -275,511 +196,15 @@ export default function AddMapper() {
|
|||
id={id}
|
||||
mapperTypes={mapperTypes}
|
||||
updateMapperType={setMapperType}
|
||||
formValues={formValues}
|
||||
mapperType={mapperType}
|
||||
isSocialIdP={isSocialIdP}
|
||||
mapperType={mapperType!}
|
||||
/>
|
||||
<>
|
||||
{(isSAMLAdvancedAttrToRole || isOIDCAdvancedClaimToRole) && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={
|
||||
isSAMLAdvancedAttrToRole
|
||||
? t("common:attributes")
|
||||
: t("claims")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={
|
||||
isSAMLAdvancedAttrToRole
|
||||
? "identity-providers-help:attributes"
|
||||
: "identity-providers-help:claims"
|
||||
}
|
||||
fieldLabelId={
|
||||
isSAMLAdvancedAttrToRole ? "attributes" : "claims"
|
||||
}
|
||||
/>
|
||||
}
|
||||
fieldId="kc-gui-order"
|
||||
>
|
||||
<FormProvider {...form}>
|
||||
<AttributeInput name="config.attributes" />
|
||||
</FormProvider>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={
|
||||
isSAMLAdvancedAttrToRole
|
||||
? t("regexAttributeValues")
|
||||
: t("regexClaimValues")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:regexAttributeValues"
|
||||
fieldLabelId="identity-providers:regexAttributeValues"
|
||||
/>
|
||||
}
|
||||
fieldId="regexAttributeValues"
|
||||
>
|
||||
<Controller
|
||||
name={
|
||||
isOIDCAdvancedClaimToRole
|
||||
? "config.are.claim.values.regex"
|
||||
: "config.are.attribute.values.regex"
|
||||
}
|
||||
control={control}
|
||||
defaultValue="false"
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
id="regexValues"
|
||||
data-testid="regex-values-switch"
|
||||
label={t("common:on")}
|
||||
labelOff={t("common:off")}
|
||||
isChecked={value === "true"}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
<FormProvider {...form}>
|
||||
{mapperType && mapperTypes[mapperType].properties && (
|
||||
<DynamicComponents
|
||||
properties={mapperTypes[mapperType].properties!}
|
||||
/>
|
||||
)}
|
||||
{(isSAMLUsernameTemplateImporter ||
|
||||
isOIDCUsernameTemplateImporter) && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("template")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:template"
|
||||
fieldLabelId="identity-providers:template"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-user-session-attribute"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
id="kc-template"
|
||||
data-testid="template"
|
||||
name="config.template"
|
||||
defaultValue={currentMapper?.config.template}
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("target")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:target"
|
||||
fieldLabelId="identity-providers:target"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-target"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<Controller
|
||||
name="config.target"
|
||||
defaultValue={currentMapper?.config.target}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="target"
|
||||
datatest-id="target-select"
|
||||
id="target-dropdown"
|
||||
placeholderText={t("realm-settings:placeholderText")}
|
||||
direction="down"
|
||||
onToggle={() => setTargetOptionsOpen(!targetOptionsOpen)}
|
||||
onSelect={(_, value) => {
|
||||
onChange(t(`targetOptions.${value}`));
|
||||
setTargetOptionsOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
aria-label={t("target")}
|
||||
isOpen={targetOptionsOpen}
|
||||
>
|
||||
{targetOptions.map((option) => (
|
||||
<SelectOption
|
||||
selected={option === value}
|
||||
key={option}
|
||||
data-testid={option}
|
||||
value={option}
|
||||
>
|
||||
{t(`targetOptions.${option}`)}
|
||||
</SelectOption>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isHardcodedAttribute || isHardcodedUserSessionAttribute) && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={
|
||||
isHardcodedUserSessionAttribute
|
||||
? t("userSessionAttribute")
|
||||
: t("userAttribute")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:userSessionAttribute"
|
||||
fieldLabelId="identity-providers:userSessionAttribute"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-user-session-attribute"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config.attribute}
|
||||
id="kc-attribute"
|
||||
data-testid={
|
||||
isHardcodedUserSessionAttribute
|
||||
? "user-session-attribute"
|
||||
: "user-attribute"
|
||||
}
|
||||
name="config.attribute"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={
|
||||
isHardcodedUserSessionAttribute
|
||||
? t("userSessionAttributeValue")
|
||||
: t("userAttributeValue")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:userAttributeValue"
|
||||
fieldLabelId={
|
||||
isHardcodedUserSessionAttribute
|
||||
? "identity-providers:userSessionAttributeValue"
|
||||
: "identity-providers:userAttributeValue"
|
||||
}
|
||||
/>
|
||||
}
|
||||
fieldId="kc-user-session-attribute-value"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config["attribute.value"]}
|
||||
data-testid={
|
||||
isHardcodedUserSessionAttribute
|
||||
? "user-session-attribute-value"
|
||||
: "user-attribute-value"
|
||||
}
|
||||
id="kc-user-session-attribute-value"
|
||||
name="config.attribute.value"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
{(isSAMLAttributeImporter ||
|
||||
isOIDCAttributeImporter ||
|
||||
isOIDCclaimToRole) && (
|
||||
<>
|
||||
{isSAMLAttributeImporter ? (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("mapperAttributeName")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:attributeName"
|
||||
fieldLabelId="identity-providers:mapperAttributeName"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-attribute-name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config["attribute.name"]}
|
||||
id="kc-attribute-name"
|
||||
data-testid="attribute-name"
|
||||
name="config.attribute.name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("mapperAttributeFriendlyName")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:friendlyName"
|
||||
fieldLabelId="identity-providers:mapperAttributeFriendlyName"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-friendly-name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={
|
||||
currentMapper?.config["attribute.friendly.name"]
|
||||
}
|
||||
data-testid="attribute-friendly-name"
|
||||
id="kc-attribute-friendly-name"
|
||||
name="config.attribute.friendly.name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
) : (
|
||||
<FormGroup
|
||||
label={t("claim")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:claim"
|
||||
fieldLabelId="identity-providers:claim"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-friendly-name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config["claim"]}
|
||||
data-testid="claim"
|
||||
id="kc-claim"
|
||||
name={"config.claim"}
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup
|
||||
label={
|
||||
isOIDCclaimToRole
|
||||
? t("claimValue")
|
||||
: t("mapperUserAttributeName")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={
|
||||
isOIDCclaimToRole
|
||||
? "identity-providers-help:claimValue"
|
||||
: "identity-providers-help:userAttributeName"
|
||||
}
|
||||
fieldLabelId="identity-providers:mapperUserAttributeName"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-user-attribute-name"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={
|
||||
isOIDCclaimToRole
|
||||
? currentMapper?.config["claim.value"]
|
||||
: currentMapper?.config["attribute.value"]
|
||||
}
|
||||
data-testid={
|
||||
isOIDCclaimToRole ? "claim.value" : "user-attribute-name"
|
||||
}
|
||||
id={
|
||||
isOIDCclaimToRole
|
||||
? "kc-claim-value"
|
||||
: "kc-user-attribute-name"
|
||||
}
|
||||
name={
|
||||
isOIDCclaimToRole ? "config.claim" : "config.user.attribute"
|
||||
}
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSocialAttributeImporter && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("socialProfileJSONFieldPath")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:socialProfileJSONFieldPath"
|
||||
fieldLabelId="identity-providers:socialProfileJSONFieldPath"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-social-profile-JSON-field-path"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config.attribute}
|
||||
id="kc-social-profile-JSON-field-path"
|
||||
data-testid={"social-profile-JSON-field-path"}
|
||||
name="config.jsonField"
|
||||
validated={
|
||||
errors.config?.role
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("mapperUserAttributeName")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:socialUserAttributeName"
|
||||
fieldLabelId="identity-providers:mapperUserAttributeName"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-user-session-attribute-value"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
defaultValue={currentMapper?.config.userAttribute}
|
||||
data-testid={"user-attribute-name"}
|
||||
id="kc-user-session-attribute-name"
|
||||
name="config.userAttribute"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
{(isSAMLAdvancedAttrToRole ||
|
||||
isHardcodedRole ||
|
||||
isSAMLAttributeToRole ||
|
||||
isOIDCAdvancedClaimToRole ||
|
||||
isOIDCclaimToRole) && (
|
||||
<FormGroup
|
||||
label={t("common:role")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:role"
|
||||
fieldLabelId="role"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-role"
|
||||
validated={
|
||||
errors.config?.role
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
id="kc-role"
|
||||
data-testid="mapper-role-input"
|
||||
name="config.role"
|
||||
isDisabled={!!id}
|
||||
value={
|
||||
selectedRole[0]?.clientRole
|
||||
? `${selectedRole[0].clientId}.${selectedRole[0]?.name}`
|
||||
: selectedRole[0]?.name
|
||||
}
|
||||
validated={
|
||||
errors.config?.role
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
data-testid="select-role-button"
|
||||
onClick={() => toggleModal()}
|
||||
isDisabled={!!id}
|
||||
>
|
||||
{t("selectRole")}
|
||||
</Button>
|
||||
</FormGroup>
|
||||
)}
|
||||
</>
|
||||
</FormProvider>
|
||||
<ActionGroup>
|
||||
<Button
|
||||
data-testid="new-mapper-save-button"
|
||||
|
|
|
@ -11,20 +11,16 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import _ from "lodash";
|
||||
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
|
||||
import type { IdentityProviderAddMapperParams } from "../routes/AddMapper";
|
||||
import { useParams } from "react-router-dom";
|
||||
import type { IdPMapperRepresentationWithAttributes } from "./AddMapper";
|
||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||
|
||||
type AddMapperFormProps = {
|
||||
mapperTypes?: Record<string, IdentityProviderMapperRepresentation>;
|
||||
mapperTypes: Record<string, IdentityProviderMapperRepresentation>;
|
||||
mapperType: string;
|
||||
id: string;
|
||||
updateMapperType: (mapperType: string) => void;
|
||||
form: UseFormMethods<IdPMapperRepresentationWithAttributes>;
|
||||
formValues: IdPMapperRepresentationWithAttributes;
|
||||
isSocialIdP: boolean;
|
||||
};
|
||||
|
||||
export const AddMapperForm = ({
|
||||
|
@ -33,8 +29,6 @@ export const AddMapperForm = ({
|
|||
form,
|
||||
id,
|
||||
updateMapperType,
|
||||
formValues,
|
||||
isSocialIdP,
|
||||
}: AddMapperFormProps) => {
|
||||
const { t } = useTranslation("identity-providers");
|
||||
|
||||
|
@ -44,7 +38,10 @@ export const AddMapperForm = ({
|
|||
|
||||
const syncModes = ["inherit", "import", "legacy", "force"];
|
||||
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
||||
const { providerId } = useParams<IdentityProviderAddMapperParams>();
|
||||
const serverInfo = useServerInfo();
|
||||
const mapper = serverInfo.componentTypes?.[
|
||||
"org.keycloak.broker.provider.IdentityProviderMapper"
|
||||
].find((p) => p.id === mapperType);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -124,15 +121,7 @@ export const AddMapperForm = ({
|
|||
label={t("mapperType")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={
|
||||
formValues.identityProviderMapper ===
|
||||
"saml-user-attribute-idp-mapper" &&
|
||||
(providerId === "oidc" ||
|
||||
providerId === "keycloak-oidc" ||
|
||||
isSocialIdP)
|
||||
? `identity-providers-help:oidcAttributeImporter`
|
||||
: `identity-providers-help:${mapperType}`
|
||||
}
|
||||
helpText={mapper?.helpText}
|
||||
fieldLabelId="identity-providers:mapperType"
|
||||
/>
|
||||
}
|
||||
|
@ -140,13 +129,7 @@ export const AddMapperForm = ({
|
|||
>
|
||||
<Controller
|
||||
name="identityProviderMapper"
|
||||
defaultValue={
|
||||
isSocialIdP
|
||||
? `${providerId.toLowerCase()}-user-attribute-mapper`
|
||||
: providerId === "saml"
|
||||
? "saml-advanced-role-idp-mapper"
|
||||
: "hardcoded-user-session-attribute-idp-mapper"
|
||||
}
|
||||
defaultValue={Object.keys(mapperTypes)[0]}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
|
@ -154,23 +137,13 @@ export const AddMapperForm = ({
|
|||
data-testid="idp-mapper-select"
|
||||
isDisabled={!!id}
|
||||
required
|
||||
direction="down"
|
||||
onToggle={() => setMapperTypeOpen(!mapperTypeOpen)}
|
||||
onSelect={(e, value) => {
|
||||
const theMapper =
|
||||
mapperTypes &&
|
||||
Object.values(mapperTypes).find(
|
||||
(item) =>
|
||||
item.name?.toLowerCase() ===
|
||||
value.toString().toLowerCase()
|
||||
);
|
||||
|
||||
updateMapperType(_.camelCase(value.toString()));
|
||||
onChange(theMapper?.id);
|
||||
onSelect={(_, value) => {
|
||||
updateMapperType(value.toString());
|
||||
onChange(value.toString());
|
||||
setMapperTypeOpen(false);
|
||||
}}
|
||||
selections={
|
||||
mapperTypes &&
|
||||
Object.values(mapperTypes).find(
|
||||
(item) => item.id?.toLowerCase() === value
|
||||
)?.name
|
||||
|
@ -179,17 +152,16 @@ export const AddMapperForm = ({
|
|||
aria-label={t("syncMode")}
|
||||
isOpen={mapperTypeOpen}
|
||||
>
|
||||
{mapperTypes &&
|
||||
Object.values(mapperTypes).map((option) => (
|
||||
<SelectOption
|
||||
selected={option === value}
|
||||
datatest-id={option.id}
|
||||
key={option.name}
|
||||
value={option.name?.toUpperCase()}
|
||||
>
|
||||
{t(`mapperTypes.${_.camelCase(option.name)}`)}
|
||||
</SelectOption>
|
||||
))}
|
||||
{Object.values(mapperTypes).map((option) => (
|
||||
<SelectOption
|
||||
selected={option === value}
|
||||
datatest-id={option.id}
|
||||
key={option.name}
|
||||
value={option.id}
|
||||
>
|
||||
{option.name}
|
||||
</SelectOption>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -157,18 +157,6 @@ export default {
|
|||
legacy: "Legacy",
|
||||
force: "Force",
|
||||
},
|
||||
mapperTypes: {
|
||||
advancedAttributeToRole: "Advanced Attribute To Role",
|
||||
advancedClaimToRole: "Advanced Claim To Role",
|
||||
externalRoleToRole: "External Role To Role",
|
||||
claimToRole: "Claim To Role",
|
||||
usernameTemplateImporter: "Username Template Importer",
|
||||
hardcodedUserSessionAttribute: "Hardcoded User Session Attribute",
|
||||
attributeImporter: "Attribute Importer",
|
||||
hardcodedRole: "Hardcoded Role",
|
||||
hardcodedAttribute: "Hardcoded Attribute",
|
||||
samlAttributeToRole: "SAML Attribute To Role",
|
||||
},
|
||||
syncModeOverride: "Sync mode override",
|
||||
mapperType: "Mapper type",
|
||||
regexAttributeValues: "Regex Attribute Values",
|
||||
|
|
|
@ -27,8 +27,6 @@ export type AssociatedRolesModalProps = {
|
|||
toggleDialog: () => void;
|
||||
onConfirm: (newReps: RoleRepresentation[]) => void;
|
||||
omitComposites?: boolean;
|
||||
isRadio?: boolean;
|
||||
isMapperId?: boolean;
|
||||
};
|
||||
|
||||
type FilterType = "roles" | "clients";
|
||||
|
@ -38,8 +36,6 @@ export const AssociatedRolesModal = ({
|
|||
toggleDialog,
|
||||
onConfirm,
|
||||
omitComposites,
|
||||
isRadio,
|
||||
isMapperId,
|
||||
}: AssociatedRolesModalProps) => {
|
||||
const { t } = useTranslation("roles");
|
||||
const [name, setName] = useState("");
|
||||
|
@ -112,7 +108,7 @@ export const AssociatedRolesModal = ({
|
|||
useFetch(
|
||||
async () => {
|
||||
const [role, compositeRoles] = await Promise.all([
|
||||
!isMapperId ? adminClient.roles.findOneById({ id }) : undefined,
|
||||
adminClient.roles.findOneById({ id }),
|
||||
!omitComposites ? adminClient.roles.getCompositeRoles({ id }) : [],
|
||||
]);
|
||||
|
||||
|
@ -181,7 +177,6 @@ export const AssociatedRolesModal = ({
|
|||
loader={filterType === "roles" ? loader : clientRolesLoader}
|
||||
ariaLabelKey="roles:roleList"
|
||||
searchPlaceholderKey="roles:searchFor"
|
||||
isRadio={isRadio}
|
||||
isPaginated={filterType === "roles"}
|
||||
isRowDisabled={(r) => compositeRoles.some((o) => o.name === r.name)}
|
||||
searchTypeComponent={
|
||||
|
|
Loading…
Reference in a new issue