Identity Providers(Mappers): Add create/edit functionality for mappers of type Username Template Importer (#1248)
This commit is contained in:
parent
bd8fc558d5
commit
4d5c8f3f18
6 changed files with 387 additions and 87 deletions
|
@ -133,7 +133,7 @@ describe("Identity provider test", () => {
|
|||
|
||||
addMapperPage.goToMappersTab();
|
||||
|
||||
addMapperPage.clickAdd();
|
||||
addMapperPage.emptyStateAddMapper();
|
||||
|
||||
addMapperPage.fillSocialMapper("facebook mapper");
|
||||
|
||||
|
@ -149,13 +149,43 @@ describe("Identity provider test", () => {
|
|||
|
||||
addMapperPage.goToMappersTab();
|
||||
|
||||
addMapperPage.clickAdd();
|
||||
addMapperPage.emptyStateAddMapper();
|
||||
|
||||
addMapperPage.fillSAMLorOIDCMapper("SAML mapper");
|
||||
|
||||
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||
});
|
||||
|
||||
it("should add SAML mapper of type Username Template Importer", () => {
|
||||
sidebarPage.goToIdentityProviders();
|
||||
|
||||
listingPage.goToItemDetails("saml");
|
||||
|
||||
addMapperPage.goToMappersTab();
|
||||
|
||||
addMapperPage.addMapper();
|
||||
|
||||
addMapperPage.addUsernameTemplateImporterMapper(
|
||||
"SAML Username Template Importer Mapper"
|
||||
);
|
||||
|
||||
masthead.checkNotificationMessage(createMapperSuccessMsg);
|
||||
});
|
||||
|
||||
it("should edit Username Template Importer mapper", () => {
|
||||
sidebarPage.goToIdentityProviders();
|
||||
|
||||
listingPage.goToItemDetails("saml");
|
||||
|
||||
addMapperPage.goToMappersTab();
|
||||
|
||||
listingPage.goToItemDetails("SAML Username Template Importer Mapper");
|
||||
|
||||
addMapperPage.editUsernameTemplateImporterMapper();
|
||||
|
||||
masthead.checkNotificationMessage(saveMapperSuccessMsg);
|
||||
});
|
||||
|
||||
it("should edit facebook mapper", () => {
|
||||
sidebarPage.goToIdentityProviders();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ export default class AddMapperPage {
|
|||
private noMappersAddMapperButton = "no-mappers-empty-action";
|
||||
private idpMapperSelectToggle = "#identityProviderMapper";
|
||||
private idpMapperSelect = "idp-mapper-select";
|
||||
private addMapperButton = "#add-mapper-button";
|
||||
|
||||
private mapperNameInput = "#kc-name";
|
||||
private mapperRoleInput = "mapper-role-input";
|
||||
|
@ -13,6 +14,9 @@ export default class AddMapperPage {
|
|||
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";
|
||||
|
@ -22,11 +26,16 @@ export default class AddMapperPage {
|
|||
return this;
|
||||
}
|
||||
|
||||
clickAdd() {
|
||||
emptyStateAddMapper() {
|
||||
cy.findByTestId(this.noMappersAddMapperButton).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
addMapper() {
|
||||
cy.get(this.addMapperButton).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
clickCreateDropdown() {
|
||||
cy.contains("Add provider").click();
|
||||
return this;
|
||||
|
@ -94,7 +103,7 @@ export default class AddMapperPage {
|
|||
cy.get(this.idpMapperSelectToggle).click();
|
||||
|
||||
cy.findByTestId(this.idpMapperSelect)
|
||||
.contains("Hardcoded User Session Attribute")
|
||||
.contains("Advanced Attribute To Role")
|
||||
.click();
|
||||
|
||||
cy.get(this.attributesKeyInput).clear();
|
||||
|
@ -114,6 +123,49 @@ export default class AddMapperPage {
|
|||
return this;
|
||||
}
|
||||
|
||||
addUsernameTemplateImporterMapper(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("Username Template Importer")
|
||||
.click();
|
||||
|
||||
cy.findByTestId(this.template).clear();
|
||||
cy.findByTestId(this.template).type("Template");
|
||||
|
||||
cy.get(this.target).click();
|
||||
|
||||
cy.get(this.targetDropdown).contains("LOCAL").click();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
editUsernameTemplateImporterMapper() {
|
||||
cy.get(this.syncmodeSelectToggle).click();
|
||||
|
||||
cy.findByTestId("legacy").click();
|
||||
|
||||
cy.findByTestId(this.template).type("_edited");
|
||||
|
||||
cy.get(this.target).click();
|
||||
|
||||
cy.get(this.targetDropdown).contains("BROKER_ID").click();
|
||||
|
||||
this.saveNewMapper();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
editSocialMapper() {
|
||||
cy.get(this.syncmodeSelectToggle).click();
|
||||
|
||||
|
|
|
@ -147,6 +147,10 @@ export const AddMapper = () => {
|
|||
);
|
||||
}
|
||||
|
||||
if (mapper.config?.attribute) {
|
||||
form.setValue("config.attributes", value.attribute);
|
||||
}
|
||||
|
||||
if (mapper.config?.attributes) {
|
||||
form.setValue("config.attributes", JSON.parse(value.attributes));
|
||||
}
|
||||
|
@ -164,6 +168,8 @@ export const AddMapper = () => {
|
|||
|
||||
const syncModes = ["inherit", "import", "legacy", "force"];
|
||||
const [syncModeOpen, setSyncModeOpen] = useState(false);
|
||||
const targetOptions = ["local", "brokerId", "brokerUsername"];
|
||||
const [targetOptionsOpen, setTargetOptionsOpen] = useState(false);
|
||||
const [mapperTypeOpen, setMapperTypeOpen] = useState(false);
|
||||
const [selectedRole, setSelectedRole] = useState<RoleRepresentation[]>([]);
|
||||
|
||||
|
@ -171,6 +177,8 @@ export const AddMapper = () => {
|
|||
setRolesModalOpen(!rolesModalOpen);
|
||||
};
|
||||
|
||||
const formValues = form.getValues();
|
||||
|
||||
return (
|
||||
<PageSection variant="light">
|
||||
<ViewHeader
|
||||
|
@ -371,93 +379,279 @@ export const AddMapper = () => {
|
|||
</FormGroup>
|
||||
{isSAMLorOIDC ? (
|
||||
<>
|
||||
{" "}
|
||||
<FormGroup
|
||||
label={t("common:attributes")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:attributes"
|
||||
forLabel={t("attributes")}
|
||||
forID={t(`common:helpLabel`, { label: t("attributes") })}
|
||||
/>
|
||||
}
|
||||
fieldId="kc-gui-order"
|
||||
>
|
||||
<AttributesForm
|
||||
form={form}
|
||||
inConfig
|
||||
array={{ fields, append, remove }}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("regexAttributeValues")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:regexAttributeValues"
|
||||
forLabel={t("regexAttributeValues")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("regexAttributeValues"),
|
||||
})}
|
||||
/>
|
||||
}
|
||||
fieldId="regexAttributeValues"
|
||||
>
|
||||
<Controller
|
||||
name="config.are-attribute-values-regex"
|
||||
control={control}
|
||||
defaultValue="false"
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
id="regexAttributeValues"
|
||||
data-testid="regex-attribute-values-switch"
|
||||
label={t("common:on")}
|
||||
labelOff={t("common:off")}
|
||||
isChecked={value === "true"}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
{formValues.identityProviderMapper ===
|
||||
"saml-advanced-role-idp-mapper" && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("common:attributes")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:attributes"
|
||||
forLabel={t("attributes")}
|
||||
forID={t(`common:helpLabel`, { label: t("attributes") })}
|
||||
/>
|
||||
}
|
||||
fieldId="kc-gui-order"
|
||||
>
|
||||
<AttributesForm
|
||||
form={form}
|
||||
inConfig
|
||||
array={{ fields, append, remove }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("common:role")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
id="name-help-icon"
|
||||
helpText="identity-providers-help:role"
|
||||
forLabel={t("identity-providers-help:role")}
|
||||
forID={t(`identity-providers:helpLabel`, {
|
||||
label: t("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"
|
||||
value={selectedRole[0]?.name}
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("regexAttributeValues")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="identity-providers-help:regexAttributeValues"
|
||||
forLabel={t("regexAttributeValues")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("regexAttributeValues"),
|
||||
})}
|
||||
/>
|
||||
}
|
||||
fieldId="regexAttributeValues"
|
||||
>
|
||||
<Controller
|
||||
name="config.are-attribute-values-regex"
|
||||
control={control}
|
||||
defaultValue="false"
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
id="regexAttributeValues"
|
||||
data-testid="regex-attribute-values-switch"
|
||||
label={t("common:on")}
|
||||
labelOff={t("common:off")}
|
||||
isChecked={value === "true"}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
{formValues.identityProviderMapper ===
|
||||
"saml-username-idp-mapper" && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("template")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
id="target-help-icon"
|
||||
helpText="identity-providers-help:template"
|
||||
forLabel={t("template")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("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
|
||||
id="user-session-attribute-help-icon"
|
||||
helpText="identity-providers-help:target"
|
||||
forLabel={t("target")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("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>
|
||||
</>
|
||||
)}
|
||||
{[
|
||||
"saml-advanced-role-idp-mapper",
|
||||
"oidc-hardcoded-role-idp-mapper",
|
||||
"saml-role-idp-mapper",
|
||||
].includes(formValues.identityProviderMapper!) && (
|
||||
<FormGroup
|
||||
label={t("common:role")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
id="name-help-icon"
|
||||
helpText="identity-providers-help:role"
|
||||
forLabel={t("identity-providers-help:role")}
|
||||
forID={t(`identity-providers:helpLabel`, {
|
||||
label: t("role"),
|
||||
})}
|
||||
/>
|
||||
}
|
||||
fieldId="kc-role"
|
||||
validated={
|
||||
errors.config?.role
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
data-testid="select-role-button"
|
||||
onClick={() => toggleModal()}
|
||||
helperTextInvalid={t("common:required")}
|
||||
>
|
||||
{t("selectRole")}
|
||||
</Button>
|
||||
</FormGroup>{" "}
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
id="kc-role"
|
||||
data-testid="mapper-role-input"
|
||||
name="config.role"
|
||||
value={selectedRole[0]?.name}
|
||||
validated={
|
||||
errors.config?.role
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
data-testid="select-role-button"
|
||||
onClick={() => toggleModal()}
|
||||
>
|
||||
{t("selectRole")}
|
||||
</Button>
|
||||
</FormGroup>
|
||||
)}
|
||||
{[
|
||||
"hardcoded-user-session-attribute-idp-mapper",
|
||||
"hardcoded-attribute-idp-mapper",
|
||||
].includes(formValues.identityProviderMapper!) && (
|
||||
<>
|
||||
<FormGroup
|
||||
label={
|
||||
formValues.identityProviderMapper ===
|
||||
"hardcoded-user-session-attribute-idp-mapper"
|
||||
? t("userSessionAttribute")
|
||||
: t("userAttribute")
|
||||
}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
id="user-session-attribute-help-icon"
|
||||
helpText="identity-providers-help:userSessionAttribute"
|
||||
forLabel={t("userSessionAttribute")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("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="user-session-attribute"
|
||||
name="config.attribute"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("userSessionAttributeValue")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
id="user-session-attribute-value-help-icon"
|
||||
helpText="identity-providers-help:userAttributeValue"
|
||||
forLabel={t("userSessionAttributeValue")}
|
||||
forID={t(`common:helpLabel`, {
|
||||
label: t("userSessionAttributeValue"),
|
||||
})}
|
||||
/>
|
||||
}
|
||||
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="user-session-attribute-value"
|
||||
id="kc-user-session-attribute-value"
|
||||
name="config.attribute-value"
|
||||
validated={
|
||||
errors.name
|
||||
? ValidatedOptions.error
|
||||
: ValidatedOptions.default
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -527,6 +721,7 @@ export const AddMapper = () => {
|
|||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ActionGroup>
|
||||
<Button
|
||||
data-testid="new-mapper-save-button"
|
||||
|
|
|
@ -328,7 +328,7 @@ export const DetailSettings = () => {
|
|||
providerId: provider.providerId!,
|
||||
tab: "mappers",
|
||||
})}
|
||||
datatest-id="add-mapper-button"
|
||||
id="add-mapper-button"
|
||||
>
|
||||
{t("addMapper")}
|
||||
</Link>
|
||||
|
|
|
@ -128,12 +128,26 @@ export default {
|
|||
"When user is imported from provider, hardcode a value to a specific user attribute.",
|
||||
samlAttributeToRole:
|
||||
"If an attribute exists, grant the user the specified realm or client role.",
|
||||
template:
|
||||
"Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${CLAIM.sub}'. ALIAS is the provider alias. CLAIM.<NAME> references an ID or Access token claim. The substitution can be converted to upper or lower case by appending |uppercase or |lowercase to the substituted value, e.g. '${CLAIM.sub | lowercase}",
|
||||
target:
|
||||
"Destination field for the mapper. LOCAL (default) means that the changes are applied to the username stored in local database upon user import. BROKER_ID and BROKER_USERNAME means that the changes are stored into the ID or username used for federation user lookup, respectively.",
|
||||
userSessionAttribute: "Name of user session attribute you want to hardcode",
|
||||
userAttribute: "Name of user attribute you want to hardcode",
|
||||
|
||||
userAttributeValue: "Value you want to hardcode",
|
||||
attributeName:
|
||||
"Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.",
|
||||
friendlyName:
|
||||
"Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.",
|
||||
userAttributeName:
|
||||
"User attribute name to store SAML attribute. Use email, lastName, and firstName to map to those predefined user properties.",
|
||||
attributeValue:
|
||||
"Value the attribute must have. If the attribute is a list, then the value must be contained in the list.",
|
||||
attributes:
|
||||
"Name and (regex) value of the attributes to search for in token. The configured name of an attribute is searched in SAML attribute name and attribute friendly name fields. Every given attribute description must be met to set the role. If the attribute is an array, then the value must be contained in the array. If an attribute can be found several times, then one match is sufficient.",
|
||||
regexAttributeValues:
|
||||
"If enabled attribute values are interpreted as regular expressions.",
|
||||
role: "Role to grant to user if all attributes are present. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference a client role the syntax is clientname.clientrole, i.e. myclient.myrole",
|
||||
userSessionAttribute: "Name of user session attribute you want to hardcode",
|
||||
userSessionAttributeValue: "Value you want to hardcode",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -166,7 +166,16 @@ export default {
|
|||
mapperCreateError: "Error creating mapper.",
|
||||
mapperSaveSuccess: "Mapper saved successfully.",
|
||||
mapperSaveError: "Error saving mapper: {{error}}",
|
||||
userAttribute: "User Attribute",
|
||||
userAttributeValue: "User Attribute Value",
|
||||
userSessionAttribute: "User Session Attribute",
|
||||
userSessionAttributeValue: "User Session Attribute Value",
|
||||
template: "Template",
|
||||
target: "Target",
|
||||
targetOptions: {
|
||||
local: "LOCAL",
|
||||
brokerId: "BROKER_ID",
|
||||
brokerUsername: "BROKER_USERNAME",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue