Identity providers(mappers): update "create" form fields for all SAML mapper types (#1282)
This commit is contained in:
parent
ed0949a44f
commit
3b677c6e8e
5 changed files with 586 additions and 175 deletions
|
@ -142,7 +142,7 @@ describe("Identity provider test", () => {
|
||||||
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add SAML mapper", () => {
|
it("should add SAML mapper of type Advanced Attribute to Role", () => {
|
||||||
sidebarPage.goToIdentityProviders();
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
listingPage.goToItemDetails("saml");
|
listingPage.goToItemDetails("saml");
|
||||||
|
@ -151,7 +151,7 @@ describe("Identity provider test", () => {
|
||||||
|
|
||||||
addMapperPage.emptyStateAddMapper();
|
addMapperPage.emptyStateAddMapper();
|
||||||
|
|
||||||
addMapperPage.fillSAMLorOIDCMapper("SAML mapper");
|
addMapperPage.addAdvancedAttrToRoleMapper("SAML mapper");
|
||||||
|
|
||||||
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
});
|
});
|
||||||
|
@ -172,6 +172,78 @@ describe("Identity provider test", () => {
|
||||||
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add SAML mapper of type Hardcoded User Session Attribute", () => {
|
||||||
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
listingPage.goToItemDetails("saml");
|
||||||
|
|
||||||
|
addMapperPage.goToMappersTab();
|
||||||
|
|
||||||
|
addMapperPage.addMapper();
|
||||||
|
|
||||||
|
addMapperPage.addHardcodedUserSessionAttrMapper(
|
||||||
|
"Hardcoded User Session Attribute"
|
||||||
|
);
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add SAML mapper of type Attribute Importer", () => {
|
||||||
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
listingPage.goToItemDetails("saml");
|
||||||
|
|
||||||
|
addMapperPage.goToMappersTab();
|
||||||
|
|
||||||
|
addMapperPage.addMapper();
|
||||||
|
|
||||||
|
addMapperPage.addAttrImporterMapper("Attribute Importer");
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add SAML mapper of type Hardcoded Role", () => {
|
||||||
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
listingPage.goToItemDetails("saml");
|
||||||
|
|
||||||
|
addMapperPage.goToMappersTab();
|
||||||
|
|
||||||
|
addMapperPage.addMapper();
|
||||||
|
|
||||||
|
addMapperPage.addHardcodedRoleMapper("Hardcoded Role");
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add SAML mapper of type Hardcoded Attribute", () => {
|
||||||
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
listingPage.goToItemDetails("saml");
|
||||||
|
|
||||||
|
addMapperPage.goToMappersTab();
|
||||||
|
|
||||||
|
addMapperPage.addMapper();
|
||||||
|
|
||||||
|
addMapperPage.addHardcodedAttrMapper("Hardcoded Attribute");
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add SAML mapper of type SAML Attribute To Role", () => {
|
||||||
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
listingPage.goToItemDetails("saml");
|
||||||
|
|
||||||
|
addMapperPage.goToMappersTab();
|
||||||
|
|
||||||
|
addMapperPage.addMapper();
|
||||||
|
|
||||||
|
addMapperPage.addSAMLAttributeToRoleMapper("SAML Attribute To Role");
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||||
|
});
|
||||||
|
|
||||||
it("should edit Username Template Importer mapper", () => {
|
it("should edit Username Template Importer mapper", () => {
|
||||||
sidebarPage.goToIdentityProviders();
|
sidebarPage.goToIdentityProviders();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,12 @@ export default class AddMapperPage {
|
||||||
|
|
||||||
private mapperNameInput = "#kc-name";
|
private mapperNameInput = "#kc-name";
|
||||||
private mapperRoleInput = "mapper-role-input";
|
private mapperRoleInput = "mapper-role-input";
|
||||||
|
private attributeName = "attribute-name";
|
||||||
|
private attributeFriendlyName = "attribute-friendly-name";
|
||||||
|
private attributeValue = "attribute-value";
|
||||||
|
private userAttribute = "user-attribute";
|
||||||
|
private userAttributeName = "user-attribute-name";
|
||||||
|
private userAttributeValue = "user-attribute-value";
|
||||||
private userSessionAttribute = "user-session-attribute";
|
private userSessionAttribute = "user-session-attribute";
|
||||||
private userSessionAttributeValue = "user-session-attribute-value";
|
private userSessionAttributeValue = "user-session-attribute-value";
|
||||||
private newMapperSaveButton = "new-mapper-save-button";
|
private newMapperSaveButton = "new-mapper-save-button";
|
||||||
|
@ -91,7 +97,7 @@ export default class AddMapperPage {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
fillSAMLorOIDCMapper(name: string) {
|
addAdvancedAttrToRoleMapper(name: string) {
|
||||||
cy.get(this.mapperNameInput).clear();
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
cy.get(this.mapperNameInput).clear().type(name);
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
@ -150,6 +156,135 @@ export default class AddMapperPage {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addHardcodedUserSessionAttrMapper(name: string) {
|
||||||
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
|
||||||
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId("inherit").click();
|
||||||
|
|
||||||
|
cy.get(this.idpMapperSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.idpMapperSelect)
|
||||||
|
.contains("Hardcoded User Session Attribute")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.userSessionAttribute).clear();
|
||||||
|
cy.findByTestId(this.userSessionAttribute).type("user session attribute");
|
||||||
|
|
||||||
|
cy.findByTestId(this.userSessionAttributeValue).clear();
|
||||||
|
cy.findByTestId(this.userSessionAttributeValue).type(
|
||||||
|
"user session attribute value"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.saveNewMapper();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAttrImporterMapper(name: string) {
|
||||||
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
|
||||||
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId("inherit").click();
|
||||||
|
|
||||||
|
cy.get(this.idpMapperSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.idpMapperSelect)
|
||||||
|
.contains("Attribute Importer")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.attributeName).clear();
|
||||||
|
cy.findByTestId(this.attributeName).type("attribute name");
|
||||||
|
|
||||||
|
cy.findByTestId(this.attributeFriendlyName).clear();
|
||||||
|
cy.findByTestId(this.attributeFriendlyName).type("friendly name");
|
||||||
|
|
||||||
|
cy.findByTestId(this.userAttributeName).clear();
|
||||||
|
cy.findByTestId(this.userAttributeName).type("user attribute name");
|
||||||
|
|
||||||
|
this.saveNewMapper();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHardcodedRoleMapper(name: string) {
|
||||||
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
|
||||||
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId("inherit").click();
|
||||||
|
|
||||||
|
cy.get(this.idpMapperSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.idpMapperSelect).contains("Hardcoded Role").click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.mapperRoleInput).clear();
|
||||||
|
cy.findByTestId(this.mapperRoleInput).type("admin");
|
||||||
|
|
||||||
|
this.saveNewMapper();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHardcodedAttrMapper(name: string) {
|
||||||
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
|
||||||
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId("inherit").click();
|
||||||
|
|
||||||
|
cy.get(this.idpMapperSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.idpMapperSelect)
|
||||||
|
.contains("Hardcoded Attribute")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.userAttribute).clear();
|
||||||
|
cy.findByTestId(this.userAttribute).type("user session attribute");
|
||||||
|
|
||||||
|
cy.findByTestId(this.userAttributeValue).clear();
|
||||||
|
cy.findByTestId(this.userAttributeValue).type(
|
||||||
|
"user session attribute value"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.saveNewMapper();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSAMLAttributeToRoleMapper(name: string) {
|
||||||
|
cy.get(this.mapperNameInput).clear();
|
||||||
|
|
||||||
|
cy.get(this.mapperNameInput).clear().type(name);
|
||||||
|
|
||||||
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId("inherit").click();
|
||||||
|
|
||||||
|
cy.get(this.idpMapperSelectToggle).click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.idpMapperSelect)
|
||||||
|
.contains("SAML Attribute To Role")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.findByTestId(this.mapperRoleInput).clear();
|
||||||
|
cy.findByTestId(this.mapperRoleInput).type("admin");
|
||||||
|
|
||||||
|
this.saveNewMapper();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
editUsernameTemplateImporterMapper() {
|
editUsernameTemplateImporterMapper() {
|
||||||
cy.get(this.syncmodeSelectToggle).click();
|
cy.get(this.syncmodeSelectToggle).click();
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import {
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
import type { IdentityProviderAddMapperParams } from "../routes/AddMapper";
|
import type { IdentityProviderAddMapperParams } from "../routes/AddMapper";
|
||||||
import _ from "lodash";
|
|
||||||
import { AssociatedRolesModal } from "../../realm-roles/AssociatedRolesModal";
|
import { AssociatedRolesModal } from "../../realm-roles/AssociatedRolesModal";
|
||||||
import type { RoleRepresentation } from "../../model/role-model";
|
import type { RoleRepresentation } from "../../model/role-model";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
@ -35,8 +34,9 @@ import type { IdentityProviderEditMapperParams } from "../routes/EditMapper";
|
||||||
import { convertToFormValues } from "../../util";
|
import { convertToFormValues } from "../../util";
|
||||||
import { toIdentityProvider } from "../routes/IdentityProvider";
|
import { toIdentityProvider } from "../routes/IdentityProvider";
|
||||||
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
|
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
|
||||||
|
import { AddMapperForm } from "./AddMapperForm";
|
||||||
|
|
||||||
type IdPMapperRepresentationWithAttributes =
|
export type IdPMapperRepresentationWithAttributes =
|
||||||
IdentityProviderMapperRepresentation & {
|
IdentityProviderMapperRepresentation & {
|
||||||
attributes: KeyValueType[];
|
attributes: KeyValueType[];
|
||||||
};
|
};
|
||||||
|
@ -166,19 +166,39 @@ export const AddMapper = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncModes = ["inherit", "import", "legacy", "force"];
|
|
||||||
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
|
||||||
const targetOptions = ["local", "brokerId", "brokerUsername"];
|
const targetOptions = ["local", "brokerId", "brokerUsername"];
|
||||||
const [targetOptionsOpen, setTargetOptionsOpen] = useState(false);
|
const [targetOptionsOpen, setTargetOptionsOpen] = useState(false);
|
||||||
const [mapperTypeOpen, setMapperTypeOpen] = useState(false);
|
|
||||||
const [selectedRole, setSelectedRole] = useState<RoleRepresentation[]>([]);
|
const [selectedRole, setSelectedRole] = useState<RoleRepresentation[]>([]);
|
||||||
|
|
||||||
|
const formValues = form.getValues();
|
||||||
|
|
||||||
|
const isAdvancedAttrToRole =
|
||||||
|
formValues.identityProviderMapper === "saml-advanced-role-idp-mapper";
|
||||||
|
|
||||||
|
const isAttributeImporter =
|
||||||
|
formValues.identityProviderMapper === "saml-user-attribute-idp-mapper";
|
||||||
|
|
||||||
|
const isHardcodedAttribute =
|
||||||
|
form.getValues().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 isUsernameTemplateImporter =
|
||||||
|
formValues.identityProviderMapper === "saml-username-idp-mapper";
|
||||||
|
|
||||||
const toggleModal = () => {
|
const toggleModal = () => {
|
||||||
setRolesModalOpen(!rolesModalOpen);
|
setRolesModalOpen(!rolesModalOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formValues = form.getValues();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
|
@ -232,155 +252,18 @@ export const AddMapper = () => {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
<FormGroup
|
<AddMapperForm
|
||||||
label={t("common:name")}
|
form={form}
|
||||||
labelIcon={
|
id={id}
|
||||||
<HelpItem
|
providerId={providerId}
|
||||||
id="name-help-icon"
|
mapperTypes={mapperTypes}
|
||||||
helpText="identity-providers-help:addIdpMapperName"
|
updateMapperType={setMapperType}
|
||||||
forLabel={t("common:name")}
|
formValues={formValues}
|
||||||
forID={t(`common:helpLabel`, { label: t("common:name") })}
|
mapperType={mapperType}
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
fieldId="kc-name"
|
|
||||||
isRequired
|
|
||||||
validated={
|
|
||||||
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
|
||||||
}
|
|
||||||
helperTextInvalid={t("common:required")}
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
ref={register({ required: true })}
|
|
||||||
type="text"
|
|
||||||
datatest-id="name-input"
|
|
||||||
id="kc-name"
|
|
||||||
name="name"
|
|
||||||
isDisabled={!!id}
|
|
||||||
validated={
|
|
||||||
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("syncModeOverride")}
|
|
||||||
isRequired
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText="identity-providers-help:syncModeOverride"
|
|
||||||
forLabel={t("syncModeOverride")}
|
|
||||||
forID={t(`common:helpLabel`, { label: t("syncModeOverride") })}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="syncMode"
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="config.syncMode"
|
|
||||||
defaultValue={syncModes[0]}
|
|
||||||
control={control}
|
|
||||||
render={({ onChange, value }) => (
|
|
||||||
<Select
|
|
||||||
toggleId="syncMode"
|
|
||||||
datatest-id="syncmode-select"
|
|
||||||
required
|
|
||||||
direction="down"
|
|
||||||
onToggle={() => setSyncModeOpen(!syncModeOpen)}
|
|
||||||
onSelect={(_, value) => {
|
|
||||||
onChange(value.toString().toUpperCase());
|
|
||||||
setSyncModeOpen(false);
|
|
||||||
}}
|
|
||||||
selections={t(`syncModes.${value.toLowerCase()}`)}
|
|
||||||
variant={SelectVariant.single}
|
|
||||||
aria-label={t("syncMode")}
|
|
||||||
isOpen={syncModeOpen}
|
|
||||||
>
|
|
||||||
{syncModes.map((option) => (
|
|
||||||
<SelectOption
|
|
||||||
selected={option === value}
|
|
||||||
key={option}
|
|
||||||
data-testid={option}
|
|
||||||
value={option.toUpperCase()}
|
|
||||||
>
|
|
||||||
{t(`syncModes.${option}`)}
|
|
||||||
</SelectOption>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={t("mapperType")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={
|
|
||||||
mapperType === "attributeImporter" &&
|
|
||||||
(providerId === "oidc" || providerId === "keycloak-oidc")
|
|
||||||
? `identity-providers-help:oidcAttributeImporter`
|
|
||||||
: `identity-providers-help:${mapperType}`
|
|
||||||
}
|
|
||||||
forLabel={t("mapperType")}
|
|
||||||
forID={t(`common:helpLabel`, { label: t("mapperType") })}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="identityProviderMapper"
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="identityProviderMapper"
|
|
||||||
defaultValue={
|
|
||||||
providerId === "saml"
|
|
||||||
? "saml-advanced-role-idp-mapper"
|
|
||||||
: "oidc-advanced-role-idp-mapper"
|
|
||||||
}
|
|
||||||
control={control}
|
|
||||||
render={({ onChange, value }) => (
|
|
||||||
<Select
|
|
||||||
toggleId="identityProviderMapper"
|
|
||||||
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()
|
|
||||||
);
|
|
||||||
|
|
||||||
setMapperType(_.camelCase(value.toString()));
|
|
||||||
onChange(theMapper?.id);
|
|
||||||
setMapperTypeOpen(false);
|
|
||||||
}}
|
|
||||||
selections={
|
|
||||||
mapperTypes &&
|
|
||||||
Object.values(mapperTypes).find(
|
|
||||||
(item) => item.id?.toLowerCase() === value
|
|
||||||
)?.name
|
|
||||||
}
|
|
||||||
variant={SelectVariant.single}
|
|
||||||
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>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
{isSAMLorOIDC ? (
|
{isSAMLorOIDC ? (
|
||||||
<>
|
<>
|
||||||
{formValues.identityProviderMapper ===
|
{isAdvancedAttrToRole && (
|
||||||
"saml-advanced-role-idp-mapper" && (
|
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("common:attributes")}
|
label={t("common:attributes")}
|
||||||
|
@ -430,8 +313,7 @@ export const AddMapper = () => {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{formValues.identityProviderMapper ===
|
{isUsernameTemplateImporter && (
|
||||||
"saml-username-idp-mapper" && (
|
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("template")}
|
label={t("template")}
|
||||||
|
@ -526,11 +408,9 @@ export const AddMapper = () => {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{[
|
{(isAdvancedAttrToRole ||
|
||||||
"saml-advanced-role-idp-mapper",
|
isHardcodedRole ||
|
||||||
"oidc-hardcoded-role-idp-mapper",
|
isSAMLAttributeToRole) && (
|
||||||
"saml-role-idp-mapper",
|
|
||||||
].includes(formValues.identityProviderMapper!) && (
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("common:role")}
|
label={t("common:role")}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
|
@ -572,15 +452,11 @@ export const AddMapper = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
{[
|
{(isHardcodedAttribute || isHardcodedUserSessionAttribute) && (
|
||||||
"hardcoded-user-session-attribute-idp-mapper",
|
|
||||||
"hardcoded-attribute-idp-mapper",
|
|
||||||
].includes(formValues.identityProviderMapper!) && (
|
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={
|
label={
|
||||||
formValues.identityProviderMapper ===
|
isHardcodedUserSessionAttribute
|
||||||
"hardcoded-user-session-attribute-idp-mapper"
|
|
||||||
? t("userSessionAttribute")
|
? t("userSessionAttribute")
|
||||||
: t("userAttribute")
|
: t("userAttribute")
|
||||||
}
|
}
|
||||||
|
@ -607,7 +483,11 @@ export const AddMapper = () => {
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={currentMapper?.config.attribute}
|
defaultValue={currentMapper?.config.attribute}
|
||||||
id="kc-attribute"
|
id="kc-attribute"
|
||||||
data-testid="user-session-attribute"
|
data-testid={
|
||||||
|
isHardcodedUserSessionAttribute
|
||||||
|
? "user-session-attribute"
|
||||||
|
: "user-attribute"
|
||||||
|
}
|
||||||
name="config.attribute"
|
name="config.attribute"
|
||||||
validated={
|
validated={
|
||||||
errors.name
|
errors.name
|
||||||
|
@ -617,14 +497,24 @@ export const AddMapper = () => {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("userSessionAttributeValue")}
|
label={
|
||||||
|
isHardcodedUserSessionAttribute
|
||||||
|
? t("userSessionAttributeValue")
|
||||||
|
: t("userAttributeValue")
|
||||||
|
}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
<HelpItem
|
<HelpItem
|
||||||
id="user-session-attribute-value-help-icon"
|
id="user-session-attribute-value-help-icon"
|
||||||
helpText="identity-providers-help:userAttributeValue"
|
helpText="identity-providers-help:userAttributeValue"
|
||||||
forLabel={t("userSessionAttributeValue")}
|
forLabel={
|
||||||
|
isHardcodedUserSessionAttribute
|
||||||
|
? t("userSessionAttributeValue")
|
||||||
|
: t("userAttributeValue")
|
||||||
|
}
|
||||||
forID={t(`common:helpLabel`, {
|
forID={t(`common:helpLabel`, {
|
||||||
label: t("userSessionAttributeValue"),
|
label: isHardcodedUserSessionAttribute
|
||||||
|
? t("userSessionAttributeValue")
|
||||||
|
: t("userAttributeValue"),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -640,7 +530,11 @@ export const AddMapper = () => {
|
||||||
ref={register()}
|
ref={register()}
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={currentMapper?.config["attribute-value"]}
|
defaultValue={currentMapper?.config["attribute-value"]}
|
||||||
data-testid="user-session-attribute-value"
|
data-testid={
|
||||||
|
isHardcodedUserSessionAttribute
|
||||||
|
? "user-session-attribute-value"
|
||||||
|
: "user-attribute-value"
|
||||||
|
}
|
||||||
id="kc-user-session-attribute-value"
|
id="kc-user-session-attribute-value"
|
||||||
name="config.attribute-value"
|
name="config.attribute-value"
|
||||||
validated={
|
validated={
|
||||||
|
@ -652,6 +546,114 @@ export const AddMapper = () => {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{isAttributeImporter && (
|
||||||
|
<>
|
||||||
|
<FormGroup
|
||||||
|
label={t("mapperAttributeName")}
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
id="user-session-attribute-help-icon"
|
||||||
|
helpText="identity-providers-help:attributeName"
|
||||||
|
forLabel={t("mapperAttributeName")}
|
||||||
|
forID={t(`common:helpLabel`, {
|
||||||
|
label: t("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
|
||||||
|
id="mapper-attribute-friendly-name"
|
||||||
|
helpText="identity-providers-help:friendlyName"
|
||||||
|
forLabel={t("mapperAttributeFriendlyName")}
|
||||||
|
forID={t(`common:helpLabel`, {
|
||||||
|
label: t("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("mapperUserAttributeName")}
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
id="user-attribute-name-help-icon"
|
||||||
|
helpText="identity-providers-help:userAttributeName"
|
||||||
|
forLabel={t("mapperUserAttributeName")}
|
||||||
|
forID={t(`common:helpLabel`, {
|
||||||
|
label: t("mapperUserAttributeName"),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
fieldId="kc-user-attribute-name"
|
||||||
|
validated={
|
||||||
|
errors.name
|
||||||
|
? ValidatedOptions.error
|
||||||
|
: ValidatedOptions.default
|
||||||
|
}
|
||||||
|
helperTextInvalid={t("common:required")}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
ref={register()}
|
||||||
|
type="text"
|
||||||
|
defaultValue={currentMapper?.config["attribute-value"]}
|
||||||
|
data-testid="user-attribute-name"
|
||||||
|
id="kc-user-attribute-name"
|
||||||
|
name="config.attribute-value"
|
||||||
|
validated={
|
||||||
|
errors.name
|
||||||
|
? ValidatedOptions.error
|
||||||
|
: ValidatedOptions.default
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
198
src/identity-providers/add/AddMapperForm.tsx
Normal file
198
src/identity-providers/add/AddMapperForm.tsx
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Controller, UseFormMethods } from "react-hook-form";
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
Select,
|
||||||
|
SelectOption,
|
||||||
|
SelectVariant,
|
||||||
|
TextInput,
|
||||||
|
ValidatedOptions,
|
||||||
|
} 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";
|
||||||
|
|
||||||
|
type AddMapperFormProps = {
|
||||||
|
mapperTypes?: Record<string, IdentityProviderMapperRepresentation>;
|
||||||
|
mapperType: string;
|
||||||
|
providerId: string;
|
||||||
|
id: string;
|
||||||
|
updateMapperType: (mapperType: string) => void;
|
||||||
|
form: UseFormMethods<IdPMapperRepresentationWithAttributes>;
|
||||||
|
formValues: IdPMapperRepresentationWithAttributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddMapperForm = ({
|
||||||
|
mapperTypes,
|
||||||
|
mapperType,
|
||||||
|
form,
|
||||||
|
id,
|
||||||
|
updateMapperType,
|
||||||
|
formValues,
|
||||||
|
}: AddMapperFormProps) => {
|
||||||
|
const { t } = useTranslation("identity-providers");
|
||||||
|
|
||||||
|
const { control, register, errors } = form;
|
||||||
|
|
||||||
|
const [mapperTypeOpen, setMapperTypeOpen] = useState(false);
|
||||||
|
|
||||||
|
const syncModes = ["inherit", "import", "legacy", "force"];
|
||||||
|
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
||||||
|
const { providerId } = useParams<IdentityProviderAddMapperParams>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormGroup
|
||||||
|
label={t("common:name")}
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
id="name-help-icon"
|
||||||
|
helpText="identity-providers-help:addIdpMapperName"
|
||||||
|
forLabel={t("common:name")}
|
||||||
|
forID={t(`common:helpLabel`, { label: t("common:name") })}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
fieldId="kc-name"
|
||||||
|
isRequired
|
||||||
|
validated={
|
||||||
|
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
||||||
|
}
|
||||||
|
helperTextInvalid={t("common:required")}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
ref={register({ required: true })}
|
||||||
|
type="text"
|
||||||
|
datatest-id="name-input"
|
||||||
|
id="kc-name"
|
||||||
|
name="name"
|
||||||
|
isDisabled={!!id}
|
||||||
|
validated={
|
||||||
|
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={t("syncModeOverride")}
|
||||||
|
isRequired
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
helpText="identity-providers-help:syncModeOverride"
|
||||||
|
forLabel={t("syncModeOverride")}
|
||||||
|
forID={t(`common:helpLabel`, { label: t("syncModeOverride") })}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
fieldId="syncMode"
|
||||||
|
>
|
||||||
|
<Controller
|
||||||
|
name="config.syncMode"
|
||||||
|
defaultValue={syncModes[0]}
|
||||||
|
control={control}
|
||||||
|
render={({ onChange, value }) => (
|
||||||
|
<Select
|
||||||
|
toggleId="syncMode"
|
||||||
|
datatest-id="syncmode-select"
|
||||||
|
required
|
||||||
|
direction="down"
|
||||||
|
onToggle={() => setSyncModeOpen(!syncModeOpen)}
|
||||||
|
onSelect={(_, value) => {
|
||||||
|
onChange(value.toString().toUpperCase());
|
||||||
|
setSyncModeOpen(false);
|
||||||
|
}}
|
||||||
|
selections={t(`syncModes.${value.toLowerCase()}`)}
|
||||||
|
variant={SelectVariant.single}
|
||||||
|
aria-label={t("syncMode")}
|
||||||
|
isOpen={syncModeOpen}
|
||||||
|
>
|
||||||
|
{syncModes.map((option) => (
|
||||||
|
<SelectOption
|
||||||
|
selected={option === value}
|
||||||
|
key={option}
|
||||||
|
data-testid={option}
|
||||||
|
value={option.toUpperCase()}
|
||||||
|
>
|
||||||
|
{t(`syncModes.${option}`)}
|
||||||
|
</SelectOption>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={t("mapperType")}
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
helpText={
|
||||||
|
formValues.identityProviderMapper ===
|
||||||
|
"saml-user-attribute-idp-mapper" &&
|
||||||
|
(providerId === "oidc" || providerId === "keycloak-oidc")
|
||||||
|
? `identity-providers-help:oidcAttributeImporter`
|
||||||
|
: `identity-providers-help:${mapperType}`
|
||||||
|
}
|
||||||
|
forLabel={t("mapperType")}
|
||||||
|
forID={t(`common:helpLabel`, { label: t("mapperType") })}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
fieldId="identityProviderMapper"
|
||||||
|
>
|
||||||
|
<Controller
|
||||||
|
name="identityProviderMapper"
|
||||||
|
defaultValue={
|
||||||
|
providerId === "saml"
|
||||||
|
? "saml-advanced-role-idp-mapper"
|
||||||
|
: "oidc-advanced-role-idp-mapper"
|
||||||
|
}
|
||||||
|
control={control}
|
||||||
|
render={({ onChange, value }) => (
|
||||||
|
<Select
|
||||||
|
toggleId="identityProviderMapper"
|
||||||
|
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);
|
||||||
|
setMapperTypeOpen(false);
|
||||||
|
}}
|
||||||
|
selections={
|
||||||
|
mapperTypes &&
|
||||||
|
Object.values(mapperTypes).find(
|
||||||
|
(item) => item.id?.toLowerCase() === value
|
||||||
|
)?.name
|
||||||
|
}
|
||||||
|
variant={SelectVariant.single}
|
||||||
|
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>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -71,6 +71,9 @@ export default {
|
||||||
subjectNameId: "Subject NameID",
|
subjectNameId: "Subject NameID",
|
||||||
attributeName: "Attribute [Name]",
|
attributeName: "Attribute [Name]",
|
||||||
attributeFriendlyName: "Attribute [Friendly Name]",
|
attributeFriendlyName: "Attribute [Friendly Name]",
|
||||||
|
mapperAttributeName: "Attribute Name",
|
||||||
|
mapperUserAttributeName: "User Attribute Name",
|
||||||
|
mapperAttributeFriendlyName: "Friendly name",
|
||||||
httpPostBindingResponse: "HTTP-POST binding response",
|
httpPostBindingResponse: "HTTP-POST binding response",
|
||||||
httpPostBindingAuthnRequest: "HTTP-POST binding for AuthnRequest",
|
httpPostBindingAuthnRequest: "HTTP-POST binding for AuthnRequest",
|
||||||
httpPostBindingLogout: "HTTP-POST binding logout",
|
httpPostBindingLogout: "HTTP-POST binding logout",
|
||||||
|
@ -167,6 +170,7 @@ export default {
|
||||||
mapperSaveSuccess: "Mapper saved successfully.",
|
mapperSaveSuccess: "Mapper saved successfully.",
|
||||||
mapperSaveError: "Error saving mapper: {{error}}",
|
mapperSaveError: "Error saving mapper: {{error}}",
|
||||||
userAttribute: "User Attribute",
|
userAttribute: "User Attribute",
|
||||||
|
attributeValue: "Attribute Value",
|
||||||
userAttributeValue: "User Attribute Value",
|
userAttributeValue: "User Attribute Value",
|
||||||
userSessionAttribute: "User Session Attribute",
|
userSessionAttribute: "User Session Attribute",
|
||||||
userSessionAttributeValue: "User Session Attribute Value",
|
userSessionAttributeValue: "User Session Attribute Value",
|
||||||
|
|
Loading…
Reference in a new issue