Merge pull request #535 from mfrances17/save-mapper

Add/edit/delete LDAP mappers
This commit is contained in:
mfrances17 2021-04-20 16:53:55 -04:00 committed by GitHub
commit f3511d0be1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1385 additions and 1302 deletions

View file

@ -143,9 +143,9 @@ export const MappingDetails = () => {
} else { } else {
await adminClient.clientScopes.addProtocolMapper({ id }, map); await adminClient.clientScopes.addProtocolMapper({ id }, map);
} }
addAlert(t(`mapping${key}Success`), AlertVariant.success); addAlert(t(`common:mapping${key}Success`), AlertVariant.success);
} catch (error) { } catch (error) {
addAlert(t(`mapping${key}Error`, { error }), AlertVariant.danger); addAlert(t(`common:mapping${key}Error`, { error }), AlertVariant.danger);
} }
}; };

View file

@ -18,8 +18,6 @@
"deletedSuccess": "The client scope has been deleted", "deletedSuccess": "The client scope has been deleted",
"deleteError": "Could not delete client scope: {{error}}", "deleteError": "Could not delete client scope: {{error}}",
"includeInTokenScope": "Include in token scope", "includeInTokenScope": "Include in token scope",
"mappingUpdatedSuccess": "Mapping successfully updated",
"mappingUpdatedError": "Could not update mapping: '{{error}}'",
"realmRolePrefix": "Realm role prefix", "realmRolePrefix": "Realm role prefix",
"multiValued": "Multivalued", "multiValued": "Multivalued",
"tokenClaimName": "Token claim name", "tokenClaimName": "Token claim name",

View file

@ -109,11 +109,14 @@
"id": "ID", "id": "ID",
"addMapper": "Add mapper", "addMapper": "Add mapper",
"createNewMapper": "Create new mapper",
"searchForMapper": "Search for mapper", "searchForMapper": "Search for mapper",
"mapperType": "Mapper type", "mapperType": "Mapper type",
"mappingDeletedSuccess": "Mapping successfully deleted", "mappingDeletedSuccess": "Mapping successfully deleted",
"mappingDeletedError": "Could not delete mapping: '{{error}}'", "mappingDeletedError": "Could not delete mapping: '{{error}}'",
"mappingDetails": "Mapper details", "mappingDetails": "Mapper details",
"mappingUpdatedSuccess": "Mapping successfully updated",
"mappingUpdatedError": "Could not update mapping: '{{error}}'",
"mappingCreatedSuccess": "Mapping successfully created", "mappingCreatedSuccess": "Mapping successfully created",
"mappingCreatedError": "Could not create mapping: '{{error}}'", "mappingCreatedError": "Could not create mapping: '{{error}}'",
"deleteMappingTitle": "Delete mapping?", "deleteMappingTitle": "Delete mapping?",

View file

@ -29,8 +29,8 @@ import { RoleMappingForm } from "./client-scopes/add/RoleMappingForm";
import { RealmRoleTabs } from "./realm-roles/RealmRoleTabs"; import { RealmRoleTabs } from "./realm-roles/RealmRoleTabs";
import { SearchGroups } from "./groups/SearchGroups"; import { SearchGroups } from "./groups/SearchGroups";
import { CreateInitialAccessToken } from "./clients/initial-access/CreateInitialAccessToken"; import { CreateInitialAccessToken } from "./clients/initial-access/CreateInitialAccessToken";
import { LdapMappingDetails } from "./user-federation/ldap/mappers/LdapMappingDetails";
import { RealmSettingsTabs } from "./realm-settings/RealmSettingsTabs"; import { RealmSettingsTabs } from "./realm-settings/RealmSettingsTabs";
import { LdapMapperDetails } from "./user-federation/ldap/mappers/LdapMapperDetails";
export type RouteDef = BreadcrumbsRoute & { export type RouteDef = BreadcrumbsRoute & {
access: AccessType; access: AccessType;
@ -226,6 +226,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: null, breadcrumb: null,
access: "view-realm", access: "view-realm",
}, },
{
path: "/:realm/user-federation/ldap/new",
component: UserFederationLdapSettings,
breadcrumb: t("common:settings"),
access: "view-realm",
},
{ {
path: "/:realm/user-federation/ldap/:id/:tab?", path: "/:realm/user-federation/ldap/:id/:tab?",
component: UserFederationLdapSettings, component: UserFederationLdapSettings,
@ -234,16 +240,10 @@ export const routes: RoutesFn = (t: TFunction) => [
}, },
{ {
path: "/:realm/user-federation/ldap/:id/:tab/:mapperId", path: "/:realm/user-federation/ldap/:id/:tab/:mapperId",
component: LdapMappingDetails, component: LdapMapperDetails,
breadcrumb: t("common:mappingDetails"), breadcrumb: t("common:mappingDetails"),
access: "view-realm", access: "view-realm",
}, },
{
path: "/:realm/user-federation/ldap/new",
component: UserFederationLdapSettings,
breadcrumb: t("common:settings"),
access: "view-realm",
},
{ {
path: "/:realm/", path: "/:realm/",
component: DashboardSection, component: DashboardSection,

View file

@ -123,7 +123,7 @@ const LdapSettingsHeader = ({
return ( return (
<> <>
<DisableConfirm /> <DisableConfirm />
{id === "new" ? ( {!id ? (
<ViewHeader titleKey="LDAP" subKey="" /> <ViewHeader titleKey="LDAP" subKey="" />
) : ( ) : (
<ViewHeader <ViewHeader
@ -178,7 +178,7 @@ export const UserFederationLdapSettings = () => {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
if (id !== "new") { if (id) {
const fetchedComponent = await adminClient.components.findOne({ id }); const fetchedComponent = await adminClient.components.findOne({ id });
if (fetchedComponent) { if (fetchedComponent) {
setupForm(fetchedComponent); setupForm(fetchedComponent);
@ -211,22 +211,17 @@ export const UserFederationLdapSettings = () => {
const save = async (component: ComponentRepresentation) => { const save = async (component: ComponentRepresentation) => {
try { try {
if (id) { if (!id) {
if (id === "new") {
await adminClient.components.create(component); await adminClient.components.create(component);
history.push(`/${realm}/user-federation`); history.push(`/${realm}/user-federation`);
} else { } else {
await adminClient.components.update({ id }, component); await adminClient.components.update({ id }, component);
} }
}
setupForm(component as ComponentRepresentation); setupForm(component as ComponentRepresentation);
addAlert( addAlert(t(id ? "saveSuccess" : "createSuccess"), AlertVariant.success);
t(id === "new" ? "createSuccess" : "saveSuccess"),
AlertVariant.success
);
} catch (error) { } catch (error) {
addAlert( addAlert(
`${t(id === "new" ? "createError" : "saveError")} '${error}'`, `${t(id ? "saveError" : "createError")} '${error}'`,
AlertVariant.danger AlertVariant.danger
); );
} }
@ -326,6 +321,7 @@ export const UserFederationLdapSettings = () => {
</ActionGroup> </ActionGroup>
</Form> </Form>
</Tab> </Tab>
{id && (
<Tab <Tab
id="mappers" id="mappers"
eventKey="mappers" eventKey="mappers"
@ -333,6 +329,7 @@ export const UserFederationLdapSettings = () => {
> >
<LdapMapperList /> <LdapMapperList />
</Tab> </Tab>
)}
</KeycloakTabs> </KeycloakTabs>
</PageSection> </PageSection>
</> </>

View file

@ -0,0 +1,394 @@
import React, { useState, useEffect } from "react";
import {
ActionGroup,
AlertVariant,
Button,
Form,
FormGroup,
PageSection,
Select,
SelectOption,
SelectVariant,
TextInput,
} from "@patternfly/react-core";
import { convertFormValuesToObject, convertToFormValues } from "../../../util";
import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation";
import { useAdminClient } from "../../../context/auth/AdminClient";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { useHistory, useParams } from "react-router-dom";
import { Controller, useForm, useWatch } from "react-hook-form";
import { useAlerts } from "../../../components/alert/Alerts";
import { useTranslation } from "react-i18next";
import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { LdapMapperUserAttribute } from "./LdapMapperUserAttribute";
import { LdapMapperMsadUserAccount } from "./LdapMapperMsadUserAccount";
import { LdapMapperFullNameAttribute } from "./LdapMapperFullNameAttribute";
import { LdapMapperHardcodedLdapRole } from "./LdapMapperHardcodedLdapRole";
import { LdapMapperHardcodedLdapGroup } from "./LdapMapperHardcodedLdapGroup";
import { LdapMapperHardcodedLdapAttribute } from "./LdapMapperHardcodedLdapAttribute";
import { LdapMapperHardcodedAttribute } from "./LdapMapperHardcodedAttribute";
import { LdapMapperRoleGroup } from "./LdapMapperRoleGroup";
import { useRealm } from "../../../context/realm-context/RealmContext";
export const LdapMapperDetails = () => {
const form = useForm<ComponentRepresentation>();
const [mapping, setMapping] = useState<ComponentRepresentation>();
const adminClient = useAdminClient();
const { id, mapperId } = useParams<{ id: string; mapperId: string }>();
const history = useHistory();
const { realm } = useRealm();
const { t } = useTranslation("user-federation");
const helpText = useTranslation("user-federation-help").t;
const { addAlert } = useAlerts();
const [isMapperDropdownOpen, setIsMapperDropdownOpen] = useState(false);
useEffect(() => {
(async () => {
if (mapperId !== "new") {
if (mapperId) {
const fetchedMapper = await adminClient.components.findOne({
id: mapperId,
});
if (fetchedMapper) {
setMapping(fetchedMapper);
setupForm(fetchedMapper);
}
}
}
})();
}, []);
const setupForm = (mapper: ComponentRepresentation) => {
Object.entries(mapper).map((entry) => {
if (entry[0] === "config") {
convertToFormValues(entry[1], "config", form.setValue);
} else {
form.setValue(entry[0], entry[1]);
}
});
};
const save = async (mapper: ComponentRepresentation) => {
let config = {};
if (mapper.config !== undefined) {
config = convertFormValuesToObject(mapper.config);
}
const map = { ...mapper, config };
try {
if (mapperId) {
if (mapperId === "new") {
await adminClient.components.create(map);
history.push(
`/${realm}/user-federation/ldap/${mapper!.parentId}/mappers`
);
} else {
await adminClient.components.update({ id: mapperId }, map);
}
}
setupForm(map as ComponentRepresentation);
addAlert(
t(
mapperId === "new"
? "common:mappingCreatedSuccess"
: "common:mappingUpdatedSuccess"
),
AlertVariant.success
);
} catch (error) {
addAlert(
`${t(
mapperId === "new"
? "common:mappingCreatedError"
: "common:mappingUpdatedError"
)} '${error}'`,
AlertVariant.danger
);
}
};
const mapperType = useWatch({
control: form.control,
name: "providerId",
});
const isNew = mapperId === "new";
return (
<>
<ViewHeader
titleKey={mapping ? mapping.name! : t("common:createNewMapper")}
subKey=""
/>
<PageSection variant="light" isFilled>
<FormAccess role="manage-realm" isHorizontal>
{!isNew && (
<FormGroup label={t("common:id")} fieldId="kc-ldap-mapper-id">
<TextInput
isDisabled
type="text"
id="kc-ldap-mapper-id"
data-testid="ldap-mapper-id"
name="id"
ref={form.register}
/>
</FormGroup>
)}
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText={helpText("nameHelp")}
forLabel={t("common:name")}
forID="kc-ldap-mapper-name"
/>
}
fieldId="kc-ldap-mapper-name"
isRequired
>
<TextInput
isDisabled={!isNew}
isRequired
type="text"
id="kc-ldap-mapper-name"
data-testid="ldap-mapper-name"
name="name"
ref={form.register}
/>
<TextInput
hidden
defaultValue={isNew ? id : mapping ? mapping.parentId : ""}
type="text"
id="kc-ldap-parentId"
data-testid="ldap-parentId"
name="parentId"
ref={form.register}
/>
<TextInput
hidden
defaultValue="org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
type="text"
id="kc-ldap-provider-type"
data-testid="ldap-provider-type"
name="providerType"
ref={form.register}
/>
</FormGroup>
{!isNew ? (
<FormGroup
label={t("common:mapperType")}
labelIcon={
<HelpItem
helpText={helpText("mapperTypeHelp")}
forLabel={t("common:mapperType")}
forID="kc-ldap-mapper-type"
/>
}
fieldId="kc-ldap-mapper-type"
isRequired
>
<TextInput
isDisabled={!isNew}
isRequired
type="text"
id="kc-ldap-mapper-type"
data-testid="ldap-mapper-type"
name="providerId"
ref={form.register}
/>
</FormGroup>
) : (
<FormGroup
label={t("common:mapperType")}
labelIcon={
<HelpItem
helpText={helpText("mapperTypeHelp")}
forLabel={t("common:mapperType")}
forID="kc-providerId"
/>
}
fieldId="kc-providerId"
isRequired
>
<Controller
name="providerId"
defaultValue=" "
control={form.control}
render={({ onChange, value }) => (
<Select
toggleId="kc-providerId"
required
onToggle={() =>
setIsMapperDropdownOpen(!isMapperDropdownOpen)
}
isOpen={isMapperDropdownOpen}
onSelect={(_, value) => {
onChange(value as string);
setIsMapperDropdownOpen(false);
}}
selections={value}
variant={SelectVariant.single}
>
<SelectOption
key={0}
value="msad-user-account-control-mapper"
/>
<SelectOption
key={1}
value="msad-lds-user-account-control-mapper"
/>
<SelectOption key={2} value="group-ldap-mapper" />
<SelectOption key={3} value="user-attribute-ldap-mapper" />
<SelectOption key={4} value="role-ldap-mapper" />
<SelectOption key={5} value="hardcoded-attribute-mapper" />
<SelectOption key={6} value="hardcoded-ldap-role-mapper" />
<SelectOption key={7} value="certificate-ldap-mapper" />
<SelectOption key={8} value="full-name-ldap-mapper" />
<SelectOption key={9} value="hardcoded-ldap-group-mapper" />
<SelectOption
key={10}
value="hardcoded-ldap-attribute-mapper"
/>
</Select>
)}
></Controller>
</FormGroup>
)}
{/* When loading existing mappers, load forms based on providerId aka mapper type */}
{mapping
? (mapping.providerId! === "certificate-ldap-mapper" ||
mapping.providerId! === "user-attribute-ldap-mapper") && (
<LdapMapperUserAttribute
form={form}
mapperType={mapping?.providerId}
/>
)
: ""}
{mapping
? mapping.providerId! === "msad-user-account-control-mapper" && (
<LdapMapperMsadUserAccount form={form} />
)
: ""}
{/* msad-lds-user-account-control-mapper does not need a component
because it is just id, name, and mapper type*/}
{mapping
? mapping.providerId! === "full-name-ldap-mapper" && (
<LdapMapperFullNameAttribute form={form} />
)
: ""}
{mapping
? mapping.providerId! === "hardcoded-ldap-role-mapper" && (
<LdapMapperHardcodedLdapRole form={form} />
)
: ""}
{mapping
? mapping.providerId! === "hardcoded-ldap-group-mapper" && (
<LdapMapperHardcodedLdapGroup form={form} />
)
: ""}
{mapping
? mapping.providerId! === "hardcoded-ldap-attribute-mapper" && (
<LdapMapperHardcodedLdapAttribute form={form} />
)
: ""}
{mapping
? mapping.providerId! === "hardcoded-attribute-mapper" && (
<LdapMapperHardcodedAttribute form={form} />
)
: ""}
{mapping
? (mapping.providerId! === "role-ldap-mapper" ||
mapping.providerId! === "group-ldap-mapper") && (
<LdapMapperRoleGroup form={form} type={mapping.providerId} />
)
: ""}
{/* When creating new mappers, load forms based on dropdown selection */}
{isNew && mapperType
? mapperType === "certificate-ldap-mapper" && (
<LdapMapperUserAttribute form={form} mapperType={mapperType} />
)
: ""}
{isNew && mapperType
? mapperType === "user-attribute-ldap-mapper" && (
<LdapMapperUserAttribute form={form} mapperType={mapperType} />
)
: ""}
{isNew && mapperType
? mapperType === "msad-user-account-control-mapper" && (
<LdapMapperMsadUserAccount form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "full-name-ldap-mapper" && (
<LdapMapperFullNameAttribute form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "hardcoded-ldap-role-mapper" && (
<LdapMapperHardcodedLdapRole form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "hardcoded-ldap-group-mapper" && (
<LdapMapperHardcodedLdapGroup form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "hardcoded-ldap-attribute-mapper" && (
<LdapMapperHardcodedLdapAttribute form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "hardcoded-attribute-mapper" && (
<LdapMapperHardcodedAttribute form={form} />
)
: ""}
{isNew && mapperType
? mapperType === "role-ldap-mapper" && (
<LdapMapperRoleGroup form={form} type={mapperType} />
)
: ""}
{isNew && mapperType
? mapperType === "group-ldap-mapper" && (
<LdapMapperRoleGroup form={form} type={mapperType} />
)
: ""}
</FormAccess>
<Form onSubmit={form.handleSubmit(() => save(form.getValues()))}>
<ActionGroup>
<Button
isDisabled={!form.formState.isDirty}
variant="primary"
type="submit"
data-testid="ldap-save"
>
{t("common:save")}
</Button>
<Button
variant="link"
onClick={() =>
isNew
? history.goBack()
: history.push(
`/${realm}/user-federation/ldap/${
mapping!.parentId
}/mappers`
)
}
data-testid="ldap-cancel"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</Form>
</PageSection>
</>
);
};

View file

@ -2,9 +2,7 @@ import { FormGroup, Switch, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { Controller, UseFormMethods } from "react-hook-form"; import { Controller, UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperFullNameAttributeProps = { export type LdapMapperFullNameAttributeProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,9 +16,6 @@ export const LdapMapperFullNameAttribute = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("ldapFullNameAttribute")} label={t("ldapFullNameAttribute")}
labelIcon={ labelIcon={
@ -38,7 +33,7 @@ export const LdapMapperFullNameAttribute = ({
type="text" type="text"
id="kc-full-name-attribute" id="kc-full-name-attribute"
data-testid="full-name-attribute" data-testid="full-name-attribute"
name="config.ldap-full-name-attribute" name="config.ldap-full-name-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -98,7 +93,6 @@ export const LdapMapperFullNameAttribute = ({
)} )}
></Controller> ></Controller>
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -2,9 +2,7 @@ import { FormGroup, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { UseFormMethods } from "react-hook-form"; import { UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperHardcodedAttributeProps = { export type LdapMapperHardcodedAttributeProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,8 +16,6 @@ export const LdapMapperHardcodedAttribute = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("userModelAttributeName")} label={t("userModelAttributeName")}
labelIcon={ labelIcon={
@ -37,7 +33,7 @@ export const LdapMapperHardcodedAttribute = ({
type="text" type="text"
id="kc-user-model-attribute" id="kc-user-model-attribute"
data-testid="user-model-attribute" data-testid="user-model-attribute"
name="config.user-model-attribute" name="config.user-model-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -58,11 +54,10 @@ export const LdapMapperHardcodedAttribute = ({
type="text" type="text"
id="kc-attribute-value" id="kc-attribute-value"
data-testid="attribute-value" data-testid="attribute-value"
name="config.attribute-value" name="config.attribute-value[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -2,9 +2,7 @@ import { FormGroup, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { UseFormMethods } from "react-hook-form"; import { UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperHardcodedLdapAttributeProps = { export type LdapMapperHardcodedLdapAttributeProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,9 +16,6 @@ export const LdapMapperHardcodedLdapAttribute = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("ldapAttributeName")} label={t("ldapAttributeName")}
labelIcon={ labelIcon={
@ -38,7 +33,7 @@ export const LdapMapperHardcodedLdapAttribute = ({
type="text" type="text"
id="kc-ldap-attribute-name" id="kc-ldap-attribute-name"
data-testid="ldap-attribute-name" data-testid="ldap-attribute-name"
name="config.ldap-attribute-name" name="config.ldap-attribute-name[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -59,11 +54,10 @@ export const LdapMapperHardcodedLdapAttribute = ({
type="text" type="text"
id="kc-ldap-attribute-value" id="kc-ldap-attribute-value"
data-testid="ldap-attribute-value" data-testid="ldap-attribute-value"
name="config.ldap-attribute-value" name="config.ldap-attribute-value[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -2,9 +2,7 @@ import { FormGroup, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { UseFormMethods } from "react-hook-form"; import { UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperHardcodedLdapGroupProps = { export type LdapMapperHardcodedLdapGroupProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,8 +16,6 @@ export const LdapMapperHardcodedLdapGroup = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("group")} label={t("group")}
labelIcon={ labelIcon={
@ -37,11 +33,10 @@ export const LdapMapperHardcodedLdapGroup = ({
type="text" type="text"
id="kc-group" id="kc-group"
data-testid="group" data-testid="group"
name="config.group" name="config.group[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -2,9 +2,7 @@ import { FormGroup, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { UseFormMethods } from "react-hook-form"; import { UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperHardcodedLdapRoleProps = { export type LdapMapperHardcodedLdapRoleProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,8 +16,6 @@ export const LdapMapperHardcodedLdapRole = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("common:role")} label={t("common:role")}
labelIcon={ labelIcon={
@ -37,11 +33,10 @@ export const LdapMapperHardcodedLdapRole = ({
type="text" type="text"
id="kc-role" id="kc-role"
data-testid="role" data-testid="role"
name="config.role" name="config.role[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -1,74 +1,76 @@
import React, { useState, useEffect } from "react"; import React, { useState } from "react";
import { Link, useHistory, useParams, useRouteMatch } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { AlertVariant, Button, ToolbarItem } from "@patternfly/react-core"; import {
AlertVariant,
Button,
ButtonVariant,
ToolbarItem,
} from "@patternfly/react-core";
import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation";
import { useErrorHandler } from "react-error-boundary";
import { KeycloakDataTable } from "../../../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../../../components/table-toolbar/KeycloakDataTable";
import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState";
import { useAlerts } from "../../../components/alert/Alerts"; import { useAlerts } from "../../../components/alert/Alerts";
import { import { useAdminClient } from "../../../context/auth/AdminClient";
useAdminClient, import { useConfirmDialog } from "../../../components/confirm-dialog/ConfirmDialog";
asyncStateFetch,
} from "../../../context/auth/AdminClient";
import { Link, useParams, useRouteMatch } from "react-router-dom";
export const LdapMapperList = () => { export const LdapMapperList = () => {
const [mappers, setMappers] = useState<ComponentRepresentation[]>(); const history = useHistory();
const { t } = useTranslation("user-federation"); const { t } = useTranslation("user-federation");
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const { addAlert } = useAlerts(); const { addAlert } = useAlerts();
const { url } = useRouteMatch(); const { url } = useRouteMatch();
const handleError = useErrorHandler();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
const refresh = () => setKey(new Date().getTime());
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
useEffect(() => { const [selectedMapper, setSelectedMapper] = useState<
return asyncStateFetch( ComponentRepresentation
() => { >();
const loader = async () => {
const testParams: { const testParams: {
[name: string]: string | number; [name: string]: string | number;
} = { } = {
parent: id, parent: id,
type: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", type: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper",
}; };
return adminClient.components.find(testParams);
},
(mappers) => {
setMappers(mappers);
// TODO: remove after debugging
console.log("LdapMapperList - setMappers being set with:");
console.log(mappers);
},
handleError
);
}, [key]);
if (!mappers) { const mappersList = (await adminClient.components.find(testParams)).map(
return ( (mapper) => {
<>
<ListEmptyState
message={t("common:emptyMappers")}
instructions={t("common:emptyMappersInstructions")}
primaryActionText={t("common:emptyPrimaryAction")}
/>
</>
);
}
const loader = async () =>
Promise.resolve(
(mappers || []).map((mapper) => {
return { return {
...mapper, ...mapper,
name: mapper.name, name: mapper.name,
type: mapper.providerId, type: mapper.providerId,
} as ComponentRepresentation; } as ComponentRepresentation;
}) }
); );
return mappersList;
};
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: t("common:deleteMappingTitle", { mapperId: selectedMapper?.id }),
messageKey: "common:deleteMappingConfirm",
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.components.del({
id: selectedMapper!.id!,
});
refresh();
addAlert(t("common:mappingDeletedSuccess"), AlertVariant.success);
setSelectedMapper(undefined);
} catch (error) {
addAlert(
t("common:mappingDeletedError", { error }),
AlertVariant.danger
);
}
},
});
const getUrl = (url: string) => { const getUrl = (url: string) => {
if (url.indexOf("/mappers") === -1) { if (url.indexOf("/mappers") === -1) {
@ -85,6 +87,7 @@ export const LdapMapperList = () => {
return ( return (
<> <>
<DeleteConfirm />
<KeycloakDataTable <KeycloakDataTable
key={key} key={key}
loader={loader} loader={loader}
@ -95,13 +98,7 @@ export const LdapMapperList = () => {
<Button <Button
data-testid="createMapperBtn" data-testid="createMapperBtn"
variant="primary" variant="primary"
// onClick={handleModalToggle} onClick={() => history.push(`${url}/new`)}
onClick={() =>
addAlert(
t("Add functionality not implemented yet!"),
AlertVariant.success
)
}
> >
{t("common:addMapper")} {t("common:addMapper")}
</Button> </Button>
@ -110,20 +107,9 @@ export const LdapMapperList = () => {
actions={[ actions={[
{ {
title: t("common:delete"), title: t("common:delete"),
onRowClick: async (mapper) => { onRowClick: (mapper) => {
try { setSelectedMapper(mapper);
addAlert( toggleDeleteDialog();
// t("common:mappingDeletedError"),
"Delete functionality not implemented yet!",
AlertVariant.success
);
} catch (error) {
addAlert(
t("common:mappingDeletedError", { error }),
AlertVariant.danger
);
}
return true;
}, },
}, },
]} ]}

View file

@ -1,20 +0,0 @@
import React from "react";
import { UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperMsadLdsUserAccountProps = {
form: UseFormMethods;
};
export const LdapMapperMsadLdsUserAccount = ({
form,
}: LdapMapperMsadLdsUserAccountProps) => {
return (
<>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
</FormAccess>
</>
);
};

View file

@ -2,9 +2,7 @@ import { FormGroup, Switch } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { Controller, UseFormMethods } from "react-hook-form"; import { Controller, UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperMsadUserAccountProps = { export type LdapMapperMsadUserAccountProps = {
form: UseFormMethods; form: UseFormMethods;
@ -18,8 +16,6 @@ export const LdapMapperMsadUserAccount = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("passwordPolicyHintsEnabled")} label={t("passwordPolicyHintsEnabled")}
labelIcon={ labelIcon={
@ -48,7 +44,6 @@ export const LdapMapperMsadUserAccount = ({
)} )}
></Controller> ></Controller>
</FormGroup> </FormGroup>
</FormAccess>
</> </>
); );
}; };

View file

@ -9,9 +9,7 @@ import {
import React, { useState } from "react"; import React, { useState } from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { Controller, UseFormMethods } from "react-hook-form"; import { Controller, UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperRoleGroupProps = { export type LdapMapperRoleGroupProps = {
form: UseFormMethods; form: UseFormMethods;
@ -42,8 +40,6 @@ export const LdapMapperRoleGroup = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={isRole ? t("ldapRolesDn") : t("ldapGroupsDn")} label={isRole ? t("ldapRolesDn") : t("ldapGroupsDn")}
labelIcon={ labelIcon={
@ -65,7 +61,7 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-ldap-dn" id="kc-ldap-dn"
data-testid="ldap-dn" data-testid="ldap-dn"
name={isRole ? "config.roles-dn" : "config.groups-dn"} name={isRole ? "config.roles-dn[0]" : "config.groups-dn[0]"}
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -81,7 +77,7 @@ export const LdapMapperRoleGroup = ({
: helpText("groupNameLdapAttributeHelp") : helpText("groupNameLdapAttributeHelp")
} }
forLabel={ forLabel={
isRole ? t("roleNameLdapAttribute") : t("roleNameLdapAttribute") isRole ? t("roleNameLdapAttribute") : t("groupNameLdapAttribute")
} }
forID="kc-name-attribute" forID="kc-name-attribute"
/> />
@ -96,8 +92,8 @@ export const LdapMapperRoleGroup = ({
data-testid="name-attribute" data-testid="name-attribute"
name={ name={
isRole isRole
? "config.role-name-ldap-attribute" ? "config.role-name-ldap-attribute[0]"
: "config.group-name-ldap-attribute" : "config.group-name-ldap-attribute[0]"
} }
ref={form.register} ref={form.register}
/> />
@ -111,9 +107,7 @@ export const LdapMapperRoleGroup = ({
? helpText("roleObjectClassesHelp") ? helpText("roleObjectClassesHelp")
: helpText("groupObjectClassesHelp") : helpText("groupObjectClassesHelp")
} }
forLabel={ forLabel={isRole ? t("roleObjectClasses") : t("groupObjectClasses")}
isRole ? t("roleObjectClasses") : t("groupObjectClasses")
}
forID="kc-object-classes" forID="kc-object-classes"
/> />
} }
@ -127,8 +121,8 @@ export const LdapMapperRoleGroup = ({
data-testid="object-classes" data-testid="object-classes"
name={ name={
isRole isRole
? "config.role-object-classes" ? "config.role-object-classes[0]"
: "config.group-object-classes" : "config.group-object-classes[0]"
} }
ref={form.register} ref={form.register}
/> />
@ -210,7 +204,7 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-membership-ldap-attribute" id="kc-membership-ldap-attribute"
data-testid="membership-ldap-attribute" data-testid="membership-ldap-attribute"
name="config.membership-ldap-attribute" name="config.membership-ldap-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -227,7 +221,7 @@ export const LdapMapperRoleGroup = ({
> >
<Controller <Controller
name="config.membership-attribute-type[0]" name="config.membership-attribute-type[0]"
defaultValue="" defaultValue="DN"
control={form.control} control={form.control}
render={({ onChange, value }) => ( render={({ onChange, value }) => (
<Select <Select
@ -270,7 +264,7 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-membership-user-ldap-attribute" id="kc-membership-user-ldap-attribute"
data-testid="membership-user-ldap-attribute" data-testid="membership-user-ldap-attribute"
name="config.membership-user-ldap-attribute" name="config.membership-user-ldap-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -291,7 +285,11 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-ldap-filter" id="kc-ldap-filter"
data-testid="ldap-filter" data-testid="ldap-filter"
name="config.ldap-filter" name={
isRole
? "config.roles-ldap-filter[0]"
: "config.groups-ldap-filter[0]"
}
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -308,7 +306,7 @@ export const LdapMapperRoleGroup = ({
> >
<Controller <Controller
name="config.mode[0]" name="config.mode[0]"
defaultValue="" defaultValue="READ_ONLY"
control={form.control} control={form.control}
render={({ onChange, value }) => ( render={({ onChange, value }) => (
<Select <Select
@ -364,7 +362,7 @@ export const LdapMapperRoleGroup = ({
? "config.user-roles-retrieve-strategy[0]" ? "config.user-roles-retrieve-strategy[0]"
: "config.user-groups-retrieve-strategy[0]" : "config.user-groups-retrieve-strategy[0]"
} }
defaultValue="" defaultValue="LOAD_ROLES_BY_MEMBER_ATTRIBUTE"
control={form.control} control={form.control}
render={({ onChange, value }) => ( render={({ onChange, value }) => (
<Select <Select
@ -416,7 +414,7 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-member-of-attribute" id="kc-member-of-attribute"
data-testid="member-of-attribute" data-testid="member-of-attribute"
name="config.memberof-ldap-attribute" name="config.memberof-ldap-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -510,7 +508,7 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-mapped-attributes" id="kc-mapped-attributes"
data-testid="mapped-attributes" data-testid="mapped-attributes"
name="config.mapped-group-attributes" name="config.mapped-group-attributes[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -559,13 +557,12 @@ export const LdapMapperRoleGroup = ({
type="text" type="text"
id="kc-path" id="kc-path"
data-testid="path" data-testid="path"
name="config.groups-path" name="config.groups-path[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
</> </>
)} )}
</FormAccess>
</> </>
); );
}; };

View file

@ -2,9 +2,7 @@ import { FormGroup, Switch, TextInput } from "@patternfly/react-core";
import React from "react"; import React from "react";
import { HelpItem } from "../../../components/help-enabler/HelpItem"; import { HelpItem } from "../../../components/help-enabler/HelpItem";
import { Controller, UseFormMethods } from "react-hook-form"; import { Controller, UseFormMethods } from "react-hook-form";
import { FormAccess } from "../../../components/form-access/FormAccess";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LdapMapperGeneral } from "./shared/LdapMapperGeneral";
export type LdapMapperUserAttributeProps = { export type LdapMapperUserAttributeProps = {
form: UseFormMethods; form: UseFormMethods;
@ -20,8 +18,6 @@ export const LdapMapperUserAttribute = ({
return ( return (
<> <>
<FormAccess role="manage-realm" isHorizontal>
<LdapMapperGeneral form={form} />
<FormGroup <FormGroup
label={t("userModelAttribute")} label={t("userModelAttribute")}
labelIcon={ labelIcon={
@ -39,7 +35,7 @@ export const LdapMapperUserAttribute = ({
type="text" type="text"
id="kc-user-model-attribute" id="kc-user-model-attribute"
data-testid="user-model-attribute" data-testid="user-model-attribute"
name="config.user-model-attribute" name="config.user-model-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -60,7 +56,7 @@ export const LdapMapperUserAttribute = ({
type="text" type="text"
id="kc-ldap-attribute" id="kc-ldap-attribute"
data-testid="ldap-attribute" data-testid="ldap-attribute"
name="config.ldap-attribute" name="config.ldap-attribute[0]"
ref={form.register} ref={form.register}
/> />
</FormGroup> </FormGroup>
@ -210,7 +206,6 @@ export const LdapMapperUserAttribute = ({
) : ( ) : (
<></> <></>
)} )}
</FormAccess>
</> </>
); );
}; };

View file

@ -1,164 +0,0 @@
import React, { useState, useEffect } from "react";
import {
ActionGroup,
AlertVariant,
Button,
Form,
PageSection,
} from "@patternfly/react-core";
import { convertToFormValues } from "../../../util";
import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation";
import { useAdminClient } from "../../../context/auth/AdminClient";
import { ViewHeader } from "../../../components/view-header/ViewHeader";
import { useHistory, useParams } from "react-router-dom";
import { useForm } from "react-hook-form";
import { useAlerts } from "../../../components/alert/Alerts";
import { useTranslation } from "react-i18next";
import { LdapMapperUserAttribute } from "./LdapMapperUserAttribute";
import { LdapMapperMsadUserAccount } from "./LdapMapperMsadUserAccount";
import { LdapMapperMsadLdsUserAccount } from "./LdapMapperMsadLdsUserAccount";
import { LdapMapperFullNameAttribute } from "./LdapMapperFullNameAttribute";
import { LdapMapperHardcodedLdapRole } from "./LdapMapperHardcodedLdapRole";
import { LdapMapperHardcodedLdapGroup } from "./LdapMapperHardcodedLdapGroup";
import { LdapMapperHardcodedLdapAttribute } from "./LdapMapperHardcodedLdapAttribute";
import { LdapMapperHardcodedAttribute } from "./LdapMapperHardcodedAttribute";
import { LdapMapperRoleGroup } from "./LdapMapperRoleGroup";
import { useRealm } from "../../../context/realm-context/RealmContext";
export const LdapMappingDetails = () => {
const form = useForm<ComponentRepresentation>();
const [mapper, setMapper] = useState<ComponentRepresentation>();
const adminClient = useAdminClient();
const { mapperId } = useParams<{ mapperId: string }>();
const history = useHistory();
const { realm } = useRealm();
const id = mapperId;
const { t } = useTranslation("user-federation");
const { addAlert } = useAlerts();
useEffect(() => {
(async () => {
if (mapperId) {
const fetchedMapper = await adminClient.components.findOne({ id });
if (fetchedMapper) {
// TODO: remove after adding all mapper types
console.log("LdapMappingDetails: id used in findOne(id) call::");
console.log(id);
console.log("LdapMappingDetails: data returned from findOne(id):");
console.log(fetchedMapper);
setMapper(fetchedMapper);
setupForm(fetchedMapper);
}
}
})();
}, []);
const setupForm = (mapper: ComponentRepresentation) => {
Object.entries(mapper).map((entry) => {
if (entry[0] === "config") {
convertToFormValues(entry[1], "config", form.setValue);
} else {
form.setValue(entry[0], entry[1]);
}
});
};
const save = () => {
addAlert(
t(
id === "new"
? "Create functionality not implemented yet!"
: "Save functionality not implemented yet!"
),
AlertVariant.success
);
history.push(`/${realm}/user-federation`);
};
return (
<>
<ViewHeader titleKey={mapper ? mapper.name! : ""} subKey="" />
<PageSection variant="light" isFilled>
{mapper
? (mapper.providerId! === "certificate-ldap-mapper" ||
mapper.providerId! === "user-attribute-ldap-mapper") && (
<LdapMapperUserAttribute
form={form}
mapperType={mapper?.providerId}
/>
)
: ""}
{mapper
? mapper.providerId! === "msad-user-account-control-mapper" && (
<LdapMapperMsadUserAccount form={form} />
)
: ""}
{mapper
? mapper.providerId! === "msad-lds-user-account-control-mapper" && (
<LdapMapperMsadLdsUserAccount form={form} />
)
: ""}
{mapper
? mapper.providerId! === "full-name-ldap-mapper" && (
<LdapMapperFullNameAttribute form={form} />
)
: ""}
{mapper
? mapper.providerId! === "hardcoded-ldap-role-mapper" && (
<LdapMapperHardcodedLdapRole form={form} />
)
: ""}
{mapper
? mapper.providerId! === "hardcoded-ldap-group-mapper" && (
<LdapMapperHardcodedLdapGroup form={form} />
)
: ""}
{mapper
? mapper.providerId! === "hardcoded-ldap-attribute-mapper" && (
<LdapMapperHardcodedLdapAttribute form={form} />
)
: ""}
{mapper
? mapper.providerId! === "hardcoded-attribute-mapper" && (
<LdapMapperHardcodedAttribute form={form} />
)
: ""}
{mapper
? (mapper.providerId! === "role-ldap-mapper" ||
mapper.providerId! === "group-ldap-mapper") && (
<LdapMapperRoleGroup form={form} type={mapper.providerId} />
)
: ""}
<Form onSubmit={form.handleSubmit(save)}>
<ActionGroup>
<Button
isDisabled={!form.formState.isDirty}
variant="primary"
type="submit"
data-testid="ldap-save"
>
{t("common:save")}
</Button>
<Button
variant="link"
onClick={() =>
history.push(
`/${realm}/user-federation/ldap/${mapper!.parentId}/mappers`
)
}
data-testid="ldap-cancel"
>
{t("common:cancel")}
</Button>
</ActionGroup>
</Form>
</PageSection>
</>
);
};

View file

@ -1,71 +0,0 @@
import { FormGroup, TextInput } from "@patternfly/react-core";
import React from "react";
import { HelpItem } from "../../../../components/help-enabler/HelpItem";
import { UseFormMethods } from "react-hook-form";
import { useTranslation } from "react-i18next";
export type LdapMapperGeneralProps = {
form: UseFormMethods;
};
export const LdapMapperGeneral = ({ form }: LdapMapperGeneralProps) => {
const { t } = useTranslation("user-federation");
const helpText = useTranslation("user-federation-help").t;
return (
<>
<FormGroup label={t("common:id")} fieldId="kc-ldap-mapper-id">
<TextInput
isRequired
type="text"
id="kc-ldap-mapper-id"
data-testid="ldap-mapper-id"
name="id"
ref={form.register}
/>
</FormGroup>
<FormGroup
label={t("common:name")}
labelIcon={
<HelpItem
helpText={helpText("nameHelp")}
forLabel={t("common:name")}
forID="kc-ldap-mapper-name"
/>
}
fieldId="kc-ldap-mapper-name"
isRequired
>
<TextInput
isRequired
type="text"
id="kc-ldap-mapper-name"
data-testid="ldap-mapper-name"
name="name"
ref={form.register}
/>
</FormGroup>
<FormGroup
label={t("common:mapperType")}
labelIcon={
<HelpItem
helpText={helpText("mapperTypeHelp")}
forLabel={t("common:mapperType")}
forID="kc-ldap-mapper-type"
/>
}
fieldId="kc-ldap-mapper-type"
isRequired
>
<TextInput
isRequired
type="text"
id="kc-ldap-mapper-type"
data-testid="ldap-mapper-type"
name="providerId"
ref={form.register}
/>
</FormGroup>
</>
);
};