2022-04-06 17:14:23 +02:00
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
import { UseFormRegister, useFieldArray, ArrayPath, useWatch, Path } from 'react-hook-form';
|
2022-04-05 16:56:44 +02:00
|
|
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
|
|
|
import AuthProviderAPI from '../../api/auth-provider';
|
2022-04-20 15:22:07 +02:00
|
|
|
import { AuthenticationProviderMapping, MappingFields, mappingType, ProvidableType } from '../../models/authentication-provider';
|
|
|
|
import { Control, UseFormSetValue } from 'react-hook-form/dist/types/form';
|
2022-04-06 17:14:23 +02:00
|
|
|
import { FormSelect } from '../form/form-select';
|
2022-04-05 16:56:44 +02:00
|
|
|
import { FormInput } from '../form/form-input';
|
2022-04-06 17:14:23 +02:00
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { FabButton } from '../base/fab-button';
|
|
|
|
import { TypeMappingModal } from './type-mapping-modal';
|
2022-04-12 10:02:39 +02:00
|
|
|
import { useImmer } from 'use-immer';
|
2022-04-20 14:12:22 +02:00
|
|
|
import { Oauth2DataMappingForm } from './oauth2-data-mapping-form';
|
|
|
|
import { OpenidConnectDataMappingForm } from './openid-connect-data-mapping-form';
|
2022-04-05 16:56:44 +02:00
|
|
|
|
2022-04-06 17:14:23 +02:00
|
|
|
export interface DataMappingFormProps<TFieldValues, TContext extends object> {
|
2022-04-05 16:56:44 +02:00
|
|
|
register: UseFormRegister<TFieldValues>,
|
2022-04-06 17:14:23 +02:00
|
|
|
control: Control<TFieldValues, TContext>,
|
2022-04-20 14:12:22 +02:00
|
|
|
providerType: ProvidableType,
|
2022-04-20 15:22:07 +02:00
|
|
|
setValue: UseFormSetValue<TFieldValues>,
|
|
|
|
currentFormValues: Array<AuthenticationProviderMapping>,
|
2022-04-05 16:56:44 +02:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:14:23 +02:00
|
|
|
type selectModelFieldOption = { value: string, label: string };
|
|
|
|
|
2022-04-11 16:12:13 +02:00
|
|
|
/**
|
|
|
|
* Partial form to define the mapping of the data between the API of the authentication provider and the application internals.
|
|
|
|
*/
|
2022-04-20 15:22:07 +02:00
|
|
|
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, providerType, setValue, currentFormValues }: DataMappingFormProps<TFieldValues, TContext>) => {
|
2022-04-12 13:54:47 +02:00
|
|
|
const { t } = useTranslation('admin');
|
2022-04-06 17:14:23 +02:00
|
|
|
const [dataMapping, setDataMapping] = useState<MappingFields>(null);
|
2022-04-12 10:02:39 +02:00
|
|
|
const [isOpenTypeMappingModal, updateIsOpenTypeMappingModal] = useImmer<Map<number, boolean>>(new Map());
|
2022-04-06 17:14:23 +02:00
|
|
|
|
|
|
|
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes' as ArrayPath<TFieldValues> });
|
|
|
|
const output = useWatch({ name: 'auth_provider_mappings_attributes' as Path<TFieldValues>, control });
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the list of available models for the data mapping
|
|
|
|
*/
|
|
|
|
const buildModelOptions = (): Array<selectModelFieldOption> => {
|
2022-04-12 10:59:49 +02:00
|
|
|
if (!dataMapping) return [];
|
|
|
|
|
2022-04-06 17:14:23 +02:00
|
|
|
return Object.keys(dataMapping).map(model => {
|
|
|
|
return {
|
|
|
|
label: model,
|
|
|
|
value: model
|
|
|
|
};
|
|
|
|
}) || [];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the list of fields of the current model for the data mapping
|
|
|
|
*/
|
|
|
|
const buildFieldOptions = (formData: Array<TFieldValues>, index: number): Array<selectModelFieldOption> => {
|
2022-04-12 10:59:49 +02:00
|
|
|
if (!dataMapping) return [];
|
|
|
|
|
2022-04-06 17:14:23 +02:00
|
|
|
return dataMapping[getModel(formData, index)]?.map(field => {
|
|
|
|
return {
|
|
|
|
label: field[0],
|
|
|
|
value: field[0]
|
|
|
|
};
|
|
|
|
}) || [];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the name of the modal for the given index, in the current data-mapping form
|
|
|
|
*/
|
|
|
|
const getModel = (formData: Array<TFieldValues>, index: number): string => {
|
|
|
|
return formData ? formData[index]?.local_model : undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the name of the field for the given index, in the current data-mapping form
|
|
|
|
*/
|
|
|
|
const getField = (formData: Array<TFieldValues>, index: number): string => {
|
|
|
|
return formData ? formData[index]?.local_field : undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the type of data expected for the given index, in the current data-mapping form
|
|
|
|
*/
|
2022-04-11 13:19:07 +02:00
|
|
|
const getDataType = (formData: Array<TFieldValues>, index: number): mappingType => {
|
2022-04-06 17:14:23 +02:00
|
|
|
const model = getModel(formData, index);
|
|
|
|
const field = getField(formData, index);
|
2022-04-12 10:59:49 +02:00
|
|
|
if (model && field && dataMapping) {
|
2022-04-06 17:14:23 +02:00
|
|
|
return dataMapping[model]?.find(f => f[0] === field)?.[1];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-04-12 10:02:39 +02:00
|
|
|
* Open/closes the "edit type mapping" modal dialog for the given mapping index
|
2022-04-06 17:14:23 +02:00
|
|
|
*/
|
2022-04-12 10:02:39 +02:00
|
|
|
const toggleTypeMappingModal = (index: number): () => void => {
|
|
|
|
return () => {
|
|
|
|
updateIsOpenTypeMappingModal(draft => draft.set(index, !draft.get(index)));
|
|
|
|
};
|
2022-04-06 17:14:23 +02:00
|
|
|
};
|
2022-04-05 16:56:44 +02:00
|
|
|
|
|
|
|
// fetch the mapping data from the API on mount
|
|
|
|
useEffect(() => {
|
|
|
|
AuthProviderAPI.mappingFields().then((data) => {
|
|
|
|
setDataMapping(data);
|
|
|
|
});
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
2022-04-11 13:19:07 +02:00
|
|
|
<div className="data-mapping-form array-mapping-form">
|
2022-04-12 13:54:47 +02:00
|
|
|
<h4>{t('app.admin.authentication.data_mapping_form.define_the_fields_mapping')}</h4>
|
2022-04-11 16:12:13 +02:00
|
|
|
<div className="mapping-actions">
|
|
|
|
<FabButton
|
|
|
|
icon={<i className="fa fa-plus"/>}
|
|
|
|
onClick={() => append({})}>
|
2022-04-12 13:54:47 +02:00
|
|
|
{t('app.admin.authentication.data_mapping_form.add_a_match')}
|
2022-04-11 16:12:13 +02:00
|
|
|
</FabButton>
|
|
|
|
</div>
|
2022-04-06 17:14:23 +02:00
|
|
|
{fields.map((item, index) => (
|
2022-04-11 13:19:07 +02:00
|
|
|
<div key={item.id} className="mapping-item">
|
2022-04-06 17:14:23 +02:00
|
|
|
<div className="inputs">
|
|
|
|
<FormInput id={`auth_provider_mappings_attributes.${index}.id`} register={register} type="hidden" />
|
|
|
|
<div className="local-data">
|
2022-04-11 13:19:07 +02:00
|
|
|
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_model`}
|
|
|
|
control={control} rules={{ required: true }}
|
2022-04-12 13:54:47 +02:00
|
|
|
options={buildModelOptions()}
|
|
|
|
label={t('app.admin.authentication.data_mapping_form.model')}/>
|
2022-04-11 13:19:07 +02:00
|
|
|
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_field`}
|
|
|
|
options={buildFieldOptions(output, index)}
|
|
|
|
control={control}
|
|
|
|
rules={{ required: true }}
|
2022-04-12 13:54:47 +02:00
|
|
|
label={t('app.admin.authentication.data_mapping_form.field')} />
|
2022-04-06 17:14:23 +02:00
|
|
|
</div>
|
|
|
|
<div className="remote-data">
|
2022-04-20 14:12:22 +02:00
|
|
|
{providerType === 'OAuth2Provider' && <Oauth2DataMappingForm register={register} control={control} index={index} />}
|
2022-04-20 15:22:07 +02:00
|
|
|
{providerType === 'OpenIdConnectProvider' && <OpenidConnectDataMappingForm register={register}
|
|
|
|
index={index}
|
|
|
|
setValue={setValue}
|
|
|
|
currentFormValues={currentFormValues} />}
|
2022-04-06 17:14:23 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="actions">
|
2022-04-12 13:54:47 +02:00
|
|
|
<FabButton icon={<i className="fa fa-random" />}
|
|
|
|
onClick={toggleTypeMappingModal(index)}
|
|
|
|
disabled={getField(output, index) === undefined}
|
|
|
|
tooltip={t('app.admin.authentication.data_mapping_form.data_mapping')} />
|
2022-04-06 17:14:23 +02:00
|
|
|
<FabButton icon={<i className="fa fa-trash" />} onClick={() => remove(index)} className="delete-button" />
|
2022-04-11 13:19:07 +02:00
|
|
|
<TypeMappingModal model={getModel(output, index)}
|
|
|
|
field={getField(output, index)}
|
|
|
|
type={getDataType(output, index)}
|
2022-04-12 10:02:39 +02:00
|
|
|
isOpen={isOpenTypeMappingModal.get(index)}
|
|
|
|
toggleModal={toggleTypeMappingModal(index)}
|
2022-04-11 13:19:07 +02:00
|
|
|
control={control} register={register}
|
|
|
|
fieldMappingId={index} />
|
2022-04-06 17:14:23 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
2022-04-05 16:56:44 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|