Identity providers(mappers): update form fields for all Social mapper types (#1304)

Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jenny 2021-10-06 07:04:17 -04:00 committed by GitHub
parent c71d21c748
commit 8917744c04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 508 additions and 456 deletions

View file

@ -272,6 +272,20 @@ describe("Identity provider test", () => {
masthead.checkNotificationMessage(createMapperSuccessMsg);
});
it("should add Social mapper of type Attribute Importer", () => {
sidebarPage.goToIdentityProviders();
listingPage.goToItemDetails("facebook");
addMapperPage.goToMappersTab();
addMapperPage.addMapper();
addMapperPage.fillSocialMapper("facebook attribute importer");
masthead.checkNotificationMessage(createMapperSuccessMsg);
});
it("should edit Username Template Importer mapper", () => {
sidebarPage.goToIdentityProviders();
@ -293,7 +307,7 @@ describe("Identity provider test", () => {
addMapperPage.goToMappersTab();
listingPage.goToItemDetails("facebook mapper");
listingPage.goToItemDetails("facebook attribute importer");
addMapperPage.editSocialMapper();
});

View file

@ -9,7 +9,10 @@ export default class AddMapperPage {
private mapperRoleInput = "mapper-role-input";
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";
@ -73,14 +76,17 @@ export default class AddMapperPage {
.contains("Attribute Importer")
.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"
cy.findByTestId(this.socialProfileJSONfieldPath).clear();
cy.findByTestId(this.socialProfileJSONfieldPath).type(
"social profile JSON field path"
);
cy.findByTestId(this.userAttributeName).clear();
cy.findByTestId(this.userAttributeName).type("user attribute name");
this.saveNewMapper();
return this;
}
@ -332,16 +338,16 @@ export default class AddMapperPage {
cy.findByTestId("inherit").click();
cy.findByTestId(this.userSessionAttribute).clear();
cy.findByTestId(this.userSessionAttribute).type(
"user session attribute_edited"
);
cy.findByTestId(this.userSessionAttributeValue).clear();
cy.findByTestId(this.socialProfileJSONfieldPath).clear();
cy.findByTestId(this.userSessionAttributeValue).type(
"user session attribute value_edited"
cy.findByTestId(this.socialProfileJSONfieldPath).type(
"social profile JSON field path edited"
);
cy.findByTestId(this.userAttributeName).clear();
cy.findByTestId(this.userAttributeName).type("user attribute name edited");
this.saveNewMapper();
return this;

View file

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Controller, useFieldArray, useForm } from "react-hook-form";
@ -35,6 +35,8 @@ import { convertToFormValues } from "../../util";
import { toIdentityProvider } from "../routes/IdentityProvider";
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
import { AddMapperForm } from "./AddMapperForm";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { groupBy } from "lodash";
export type IdPMapperRepresentationWithAttributes =
IdentityProviderMapperRepresentation & {
@ -54,11 +56,26 @@ export const AddMapper = () => {
const { providerId, alias } = useParams<IdentityProviderAddMapperParams>();
const { id } = useParams<IdentityProviderEditMapperParams>();
const isSAMLorOIDC = providerId === "saml" || providerId === "oidc";
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("advancedAttributeToRole");
const [mapperType, setMapperType] = useState(
isSocialIdP ? "attributeImporter" : "hardcodedRole"
);
const [currentMapper, setCurrentMapper] =
useState<IdentityProviderMapperRepresentation>();
const [roles, setRoles] = useState<RoleRepresentation[]>([]);
@ -207,6 +224,11 @@ export const AddMapper = () => {
const isOIDCUsernameTemplateImporter =
formValues.identityProviderMapper === "oidc-username-idp-mapper";
const isSocialAttributeImporter = useMemo(
() => formValues.identityProviderMapper?.includes("user-attribute-mapper"),
[formValues.identityProviderMapper]
);
const toggleModal = () => {
setRolesModalOpen(!rolesModalOpen);
};
@ -218,10 +240,12 @@ export const AddMapper = () => {
titleKey={
id
? t("editIdPMapper", {
providerId: providerId.toUpperCase(),
providerId:
providerId[0].toUpperCase() + providerId.substring(1),
})
: t("addIdPMapper", {
providerId: providerId.toUpperCase(),
providerId:
providerId[0].toUpperCase() + providerId.substring(1),
})
}
divider
@ -267,13 +291,12 @@ export const AddMapper = () => {
<AddMapperForm
form={form}
id={id}
providerId={providerId}
mapperTypes={mapperTypes}
updateMapperType={setMapperType}
formValues={formValues}
mapperType={mapperType}
isSocialIdP={isSocialIdP}
/>
{isSAMLorOIDC ? (
<>
{(isSAMLAdvancedAttrToRole || isOIDCAdvancedClaimToRole) && (
<>
@ -413,9 +436,7 @@ export const AddMapper = () => {
id="target-dropdown"
placeholderText={t("realm-settings:placeholderText")}
direction="down"
onToggle={() =>
setTargetOptionsOpen(!targetOptionsOpen)
}
onToggle={() => setTargetOptionsOpen(!targetOptionsOpen)}
onSelect={(_, value) => {
onChange(t(`targetOptions.${value}`));
setTargetOptionsOpen(false);
@ -698,9 +719,7 @@ export const AddMapper = () => {
: "kc-user-attribute-name"
}
name={
isOIDCclaimToRole
? "config.claim"
: "config.user-attribute"
isOIDCclaimToRole ? "config.claim" : "config.user-attribute"
}
validated={
errors.name
@ -711,6 +730,79 @@ export const AddMapper = () => {
</FormGroup>
</>
)}
{isSocialAttributeImporter && (
<>
<FormGroup
label={t("socialProfileJSONFieldPath")}
labelIcon={
<HelpItem
id="social-profile-JSON-field-path-help-icon"
helpText="identity-providers-help:socialProfileJSONFieldPath"
forLabel={t("socialProfileJSONFieldPath")}
forID={t(`common:helpLabel`, {
label: t("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
id="user-attribute-name-help-icon"
helpText="identity-providers-help:socialUserAttributeName"
forLabel={t("mapperUserAttributeName")}
forID={t(`common:helpLabel`, {
label: t("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 ||
@ -758,75 +850,6 @@ export const AddMapper = () => {
</FormGroup>
)}
</>
) : (
<>
<FormGroup
label={t("userSessionAttribute")}
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"
isRequired
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={register({ required: true })}
type="text"
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:userSessionAttributeValue"
forLabel={t("userSessionAttributeValue")}
forID={t(`common:helpLabel`, {
label: t("userSessionAttributeValue"),
})}
/>
}
fieldId="kc-user-session-attribute-value"
isRequired
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={register({ required: true })}
type="text"
data-testid="user-session-attribute-value"
id="kc-user-session-attribute-value"
name="config.attribute-value"
validated={
errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
</FormGroup>
</>
)}
<ActionGroup>
<Button
data-testid="new-mapper-save-button"

View file

@ -20,11 +20,11 @@ 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;
isSocialIdP: boolean;
};
export const AddMapperForm = ({
@ -34,6 +34,7 @@ export const AddMapperForm = ({
id,
updateMapperType,
formValues,
isSocialIdP,
}: AddMapperFormProps) => {
const { t } = useTranslation("identity-providers");
@ -129,7 +130,9 @@ export const AddMapperForm = ({
helpText={
formValues.identityProviderMapper ===
"saml-user-attribute-idp-mapper" &&
(providerId === "oidc" || providerId === "keycloak-oidc")
(providerId === "oidc" ||
providerId === "keycloak-oidc" ||
isSocialIdP)
? `identity-providers-help:oidcAttributeImporter`
: `identity-providers-help:${mapperType}`
}
@ -142,7 +145,9 @@ export const AddMapperForm = ({
<Controller
name="identityProviderMapper"
defaultValue={
providerId === "saml"
isSocialIdP
? `${providerId.toLowerCase()}-user-attribute-mapper`
: providerId === "saml"
? "saml-advanced-role-idp-mapper"
: "oidc-advanced-role-idp-mapper"
}

View file

@ -136,6 +136,8 @@ export default {
userAttribute: "Name of user attribute you want to hardcode",
claim:
"Name of claim to search for in token. You can reference nested claims by using a '.', i.e. 'address.locality'. To use dot (.) literally, escape it with backslash. (\\.)",
socialProfileJSONFieldPath:
"Path of field in Social Provider User Profile JSON data to get value from. You can use dot notation for nesting and square brackets for array index. E.g. 'contact.address[0].country'.",
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.",
@ -143,6 +145,7 @@ export default {
"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.",
socialUserAttributeName: "User attribute name to store information.",
attributeValue:
"Value the attribute must have. If the attribute is a list, then the value must be contained in the list.",
attributes:

View file

@ -74,6 +74,7 @@ export default {
claim: "Claim",
claimValue: "Claim Value",
claims: "Claims",
socialProfileJSONFieldPath: "Social Profile JSON Field Path",
mapperAttributeName: "Attribute Name",
mapperUserAttributeName: "User Attribute Name",
mapperAttributeFriendlyName: "Friendly name",