1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-01 12:24:28 +01:00

(ui) automatic field mapping configuration for openid

This commit is contained in:
Sylvain 2022-04-20 15:22:07 +02:00
parent 1960c7139f
commit d72de33670
7 changed files with 78 additions and 9 deletions

View File

@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react';
import { UseFormRegister, useFieldArray, ArrayPath, useWatch, Path } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import AuthProviderAPI from '../../api/auth-provider';
import { MappingFields, mappingType, ProvidableType } from '../../models/authentication-provider';
import { Control } from 'react-hook-form/dist/types/form';
import { AuthenticationProviderMapping, MappingFields, mappingType, ProvidableType } from '../../models/authentication-provider';
import { Control, UseFormSetValue } from 'react-hook-form/dist/types/form';
import { FormSelect } from '../form/form-select';
import { FormInput } from '../form/form-input';
import { useTranslation } from 'react-i18next';
@ -17,6 +17,8 @@ export interface DataMappingFormProps<TFieldValues, TContext extends object> {
register: UseFormRegister<TFieldValues>,
control: Control<TFieldValues, TContext>,
providerType: ProvidableType,
setValue: UseFormSetValue<TFieldValues>,
currentFormValues: Array<AuthenticationProviderMapping>,
}
type selectModelFieldOption = { value: string, label: string };
@ -24,7 +26,7 @@ type selectModelFieldOption = { value: string, label: string };
/**
* Partial form to define the mapping of the data between the API of the authentication provider and the application internals.
*/
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, providerType }: DataMappingFormProps<TFieldValues, TContext>) => {
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, providerType, setValue, currentFormValues }: DataMappingFormProps<TFieldValues, TContext>) => {
const { t } = useTranslation('admin');
const [dataMapping, setDataMapping] = useState<MappingFields>(null);
const [isOpenTypeMappingModal, updateIsOpenTypeMappingModal] = useImmer<Map<number, boolean>>(new Map());
@ -128,7 +130,10 @@ export const DataMappingForm = <TFieldValues extends FieldValues, TContext exten
</div>
<div className="remote-data">
{providerType === 'OAuth2Provider' && <Oauth2DataMappingForm register={register} control={control} index={index} />}
{providerType === 'OpenIdConnectProvider' && <OpenidConnectDataMappingForm register={register} index={index} />}
{providerType === 'OpenIdConnectProvider' && <OpenidConnectDataMappingForm register={register}
index={index}
setValue={setValue}
currentFormValues={currentFormValues} />}
</div>
</div>
<div className="actions">

View File

@ -1,18 +1,59 @@
import React from 'react';
import { UseFormRegister } from 'react-hook-form';
import { Path, UseFormRegister } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import { FormInput } from '../form/form-input';
import { HtmlTranslate } from '../base/html-translate';
import { useTranslation } from 'react-i18next';
import { UnpackNestedValue, UseFormSetValue } from 'react-hook-form/dist/types/form';
import { FabButton } from '../base/fab-button';
import { FieldPathValue } from 'react-hook-form/dist/types/path';
import { AuthenticationProviderMapping } from '../../models/authentication-provider';
interface OpenidConnectDataMappingFormProps<TFieldValues> {
register: UseFormRegister<TFieldValues>,
setValue: UseFormSetValue<TFieldValues>,
currentFormValues: Array<AuthenticationProviderMapping>,
index: number,
}
export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({ register, index }: OpenidConnectDataMappingFormProps<TFieldValues>) => {
export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({ register, setValue, currentFormValues, index }: OpenidConnectDataMappingFormProps<TFieldValues>) => {
const { t } = useTranslation('admin');
const standardConfiguration = {
'user.uid': { api_field: 'sub' },
'user.email': { api_field: 'email' },
'user.username': { api_field: 'preferred_username' },
'profile.first_name': { api_field: 'given_name' },
'profile.last_name': { api_field: 'family_name' },
'profile.avatar': { api_field: 'picture' },
'profile.website': { api_field: 'website' },
'profile.gender': { api_field: 'gender', transformation: { true_value: 'male', false_value: 'female' } },
'profile.birthday': { api_field: 'birthdate', transformation: { format: 'iso8601' } },
'profile.phone': { api_field: 'phone_number' },
'profile.address': { api_field: 'address.formatted' }
};
/**
* Set the data mapping according to the standard OpenID Connect specification
*/
const openIdStandardConfiguration = (): void => {
const model = currentFormValues[index]?.local_model;
const field = currentFormValues[index]?.local_field;
const configuration = standardConfiguration[`${model}.${field}`];
setValue(
`auth_provider_mappings_attributes.${index}.api_field` as Path<TFieldValues>,
configuration.api_field as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
if (configuration.transformation) {
Object.keys(configuration.transformation).forEach((key) => {
setValue(
`auth_provider_mappings_attributes.${index}.transformation.${key}` as Path<TFieldValues>,
configuration.transformation[key] as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
});
}
};
return (
<div className="openid-connect-data-mapping-form">
<FormInput id={`auth_provider_mappings_attributes.${index}.api_endpoint`}
@ -31,6 +72,11 @@ export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({
placeholder="claim..."
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_data_mapping_form.api_field_help_html" />}
label={t('app.admin.authentication.openid_connect_data_mapping_form.api_field')} />
<FabButton
icon={<i className="fa fa-magic" />}
className="auto-configure-button"
onClick={openIdStandardConfiguration}
tooltip={t('app.admin.authentication.openid_connect_data_mapping_form.openid_standard_configuration')} />
</div>
);
};

View File

@ -2,7 +2,12 @@ import React, { useCallback, useEffect, useState } from 'react';
import { useForm, SubmitHandler, useWatch } from 'react-hook-form';
import { react2angular } from 'react2angular';
import { debounce as _debounce } from 'lodash';
import { AuthenticationProvider, OpenIdConnectProvider, ProvidableType } from '../../models/authentication-provider';
import {
AuthenticationProvider,
AuthenticationProviderMapping,
OpenIdConnectProvider,
ProvidableType
} from '../../models/authentication-provider';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { FormInput } from '../form/form-input';
@ -108,7 +113,11 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
currentFormValues={output.providable_attributes as OpenIdConnectProvider}
formState={formState}
setValue={setValue} />}
{providableType && providableType !== 'DatabaseProvider' && <DataMappingForm register={register} control={control} providerType={providableType} />}
{providableType && providableType !== 'DatabaseProvider' && <DataMappingForm register={register}
control={control}
providerType={providableType}
setValue={setValue}
currentFormValues={output.auth_provider_mappings_attributes as Array<AuthenticationProviderMapping>} />}
<div className="main-actions">
<FabButton type="submit" className="submit-button">{t('app.admin.authentication.provider_form.save')}</FabButton>
</div>

View File

@ -24,8 +24,9 @@
@import "modules/signup";
@import "modules/stripe";
@import "modules/tour";
@import "modules/authentication-provider/data-mapping-form";
@import "modules/authentication-provider/array-mapping-form";
@import "modules/authentication-provider/data-mapping-form";
@import "modules/authentication-provider/openid-connect-data-mapping-form";
@import "modules/authentication-provider/provider-form";
@import "modules/authentication-provider/type-mapping-modal";
@import "modules/base/fab-modal";

View File

@ -0,0 +1,7 @@
.openid-connect-data-mapping-form {
.auto-configure-button {
align-self: center;
margin-top: 0.8rem;
margin-left: 20px;
}
}

View File

@ -1081,6 +1081,7 @@ en:
openid_connect_data_mapping_form:
api_field: "Userinfo claim"
api_field_help_html: 'Set the field providing the corresponding data through <a href="https://openid.net/specs/openid-connect-core-1_0.html#Claims" target="_blank">the userinfo endpoint</a>.<br> <a href="https://jsonpath.com/" target="_blank">JsonPath</a> syntax is supported. If many fields are selected, the first one will be used.<br> <b>Example</b>: $.data[*].name'
openid_standard_configuration: "Use the OpenID standard configuration"
type_mapping_modal:
data_mapping: "Data mapping"
TYPE_expected: "{TYPE} expected"

View File