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:
parent
1960c7139f
commit
d72de33670
@ -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">
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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";
|
||||
|
@ -0,0 +1,7 @@
|
||||
.openid-connect-data-mapping-form {
|
||||
.auto-configure-button {
|
||||
align-self: center;
|
||||
margin-top: 0.8rem;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
0
doc/sso_open_id_connect.md
Normal file
0
doc/sso_open_id_connect.md
Normal file
Loading…
Reference in New Issue
Block a user