mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-27 21:54:27 +01:00
(wip)(ui) data mapping configuration interface
This commit is contained in:
parent
7beb1466bf
commit
acf5001b37
@ -1,4 +1,4 @@
|
|||||||
import { AuthenticationProvider } from '../models/authentication-provider';
|
import { AuthenticationProvider, MappingFields } from '../models/authentication-provider';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import apiClient from './clients/api-client';
|
import apiClient from './clients/api-client';
|
||||||
|
|
||||||
@ -27,5 +27,8 @@ export default class AuthProviderAPI {
|
|||||||
await apiClient.delete(`/api/auth_providers/${id}`);
|
await apiClient.delete(`/api/auth_providers/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async mappingFields(): Promise<>
|
static async mappingFields (): Promise<MappingFields> {
|
||||||
|
const res: AxiosResponse<MappingFields> = await apiClient.get('/api/auth_providers/mapping_fields');
|
||||||
|
return res?.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
|
import AuthProviderAPI from '../../api/auth-provider';
|
||||||
|
import { MappingFields } from '../../models/authentication-provider';
|
||||||
|
import { FormInput } from '../form/form-input';
|
||||||
|
|
||||||
|
export interface DataMappingFormProps<TFieldValues> {
|
||||||
|
register: UseFormRegister<TFieldValues>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DataMappingForm = <TFieldValues extends FieldValues>({ register }: DataMappingFormProps<TFieldValues>) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [dataMapping, setDataMapping] = React.useState<MappingFields>(null);
|
||||||
|
|
||||||
|
// fetch the mapping data from the API on mount
|
||||||
|
useEffect(() => {
|
||||||
|
AuthProviderAPI.mappingFields().then((data) => {
|
||||||
|
setDataMapping(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="data-mapping-form">
|
||||||
|
<FormInput id="local_model" register={register} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormInput } from '../form/form-input';
|
||||||
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
interface Oauth2FormProps<TFieldValues> {
|
||||||
|
register: UseFormRegister<TFieldValues>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial form to fill the OAuth2 settings for a new/existing authentication provider.
|
||||||
|
*/
|
||||||
|
export const Oauth2Form = <TFieldValues extends FieldValues>({ register }: Oauth2FormProps<TFieldValues>) => {
|
||||||
|
const { t } = useTranslation('shared');
|
||||||
|
|
||||||
|
// regular expression to validate the the input fields
|
||||||
|
const endpointRegex = /^\/?([-._~:?#[\]@!$&'()*+,;=%\w]+\/?)*$/;
|
||||||
|
const urlRegex = /^(https?:\/\/)([\da-z.-]+)\.([-a-z0-9.]{2,30})([/\w .-]*)*\/?$/;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="oauth2-form">
|
||||||
|
<hr/>
|
||||||
|
<FormInput id="base_url"
|
||||||
|
register={register}
|
||||||
|
placeholder="https://sso.example.net..."
|
||||||
|
label={t('app.shared.oauth2.common_url')}
|
||||||
|
rules={{ required: true, pattern: urlRegex }} />
|
||||||
|
<FormInput id="authorization_endpoint"
|
||||||
|
register={register}
|
||||||
|
placeholder="/oauth2/auth..."
|
||||||
|
label={t('app.shared.oauth2.authorization_endpoint')}
|
||||||
|
rules={{ required: true, pattern: endpointRegex }} />
|
||||||
|
<FormInput id="token_endpoint"
|
||||||
|
register={register}
|
||||||
|
placeholder="/oauth2/token..."
|
||||||
|
label={t('app.shared.oauth2.token_acquisition_endpoint')}
|
||||||
|
rules={{ required: true, pattern: endpointRegex }} />
|
||||||
|
<FormInput id="profile_url"
|
||||||
|
register={register}
|
||||||
|
placeholder="https://exemple.net/user..."
|
||||||
|
label={t('app.shared.oauth2.profil_edition_url')}
|
||||||
|
rules={{ required: true, pattern: urlRegex }} />
|
||||||
|
<FormInput id="client_id"
|
||||||
|
register={register}
|
||||||
|
label={t('app.shared.oauth2.client_identifier')}
|
||||||
|
rules={{ required: true }} />
|
||||||
|
<FormInput id="client_secret"
|
||||||
|
register={register}
|
||||||
|
label={t('app.shared.oauth2.client_secret')}
|
||||||
|
rules={{ required: true }} />
|
||||||
|
<FormInput id="scopes" register={register}
|
||||||
|
placeholder="profile,email..."
|
||||||
|
label={t('app.shared.oauth2.scopes')} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -7,6 +7,7 @@ import { IApplication } from '../../models/application';
|
|||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FormSelect } from '../form/form-select';
|
import { FormSelect } from '../form/form-select';
|
||||||
|
import { Oauth2Form } from './oauth2-form';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -26,10 +27,16 @@ interface ProviderFormProps {
|
|||||||
|
|
||||||
type selectProvidableTypeOption = { value: string, label: string };
|
type selectProvidableTypeOption = { value: string, label: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form to create or update an authentication provider.
|
||||||
|
*/
|
||||||
export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, onError, onSuccess }) => {
|
export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, onError, onSuccess }) => {
|
||||||
const { handleSubmit, register, control } = useForm<AuthenticationProvider>({ defaultValues: { ...provider } });
|
const { handleSubmit, register, control } = useForm<AuthenticationProvider>({ defaultValues: { ...provider } });
|
||||||
const { t } = useTranslation('shared');
|
const { t } = useTranslation('shared');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback triggered when the form is submitted: process with the provider creation or update.
|
||||||
|
*/
|
||||||
const onSubmit: SubmitHandler<AuthenticationProvider> = (data: AuthenticationProvider) => {
|
const onSubmit: SubmitHandler<AuthenticationProvider> = (data: AuthenticationProvider) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
onSuccess('Provider created successfully');
|
onSuccess('Provider created successfully');
|
||||||
@ -38,6 +45,9 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the list of available authentication methods to match with react-select requirements.
|
||||||
|
*/
|
||||||
const buildProvidableTypeOptions = (): Array<selectProvidableTypeOption> => {
|
const buildProvidableTypeOptions = (): Array<selectProvidableTypeOption> => {
|
||||||
return Object.keys(METHODS).map((method: string) => {
|
return Object.keys(METHODS).map((method: string) => {
|
||||||
return { value: method, label: t(`app.shared.authentication.${METHODS[method]}`) };
|
return { value: method, label: t(`app.shared.authentication.${METHODS[method]}`) };
|
||||||
@ -48,6 +58,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
|
|||||||
<form className="provider-form" onSubmit={handleSubmit(onSubmit)}>
|
<form className="provider-form" onSubmit={handleSubmit(onSubmit)}>
|
||||||
<FormInput id="name" register={register} readOnly={action === 'update'} rules={{ required: true }} label={t('app.shared.authentication.name')} />
|
<FormInput id="name" register={register} readOnly={action === 'update'} rules={{ required: true }} label={t('app.shared.authentication.name')} />
|
||||||
<FormSelect id="providable_type" control={control} options={buildProvidableTypeOptions()} label={t('app.shared.authentication.authentication_type')} rules={{ required: true }} />
|
<FormSelect id="providable_type" control={control} options={buildProvidableTypeOptions()} label={t('app.shared.authentication.authentication_type')} rules={{ required: true }} />
|
||||||
|
{provider?.providable_type === 'OAuth2Provider' && <Oauth2Form register={register} />}
|
||||||
<input type={'submit'} />
|
<input type={'submit'} />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -34,13 +34,12 @@ export const FormSelect = <TFieldValues extends FieldValues, TContext extends ob
|
|||||||
{label && <div className="form-select-header">
|
{label && <div className="form-select-header">
|
||||||
<p>{label}</p>
|
<p>{label}</p>
|
||||||
</div>}
|
</div>}
|
||||||
<div className="form-select-field">
|
<div>
|
||||||
<Controller name={id as FieldPath<TFieldValues>}
|
<Controller name={id as FieldPath<TFieldValues>}
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue={valueDefault as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>}
|
defaultValue={valueDefault as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>}
|
||||||
render={({ field: { onChange, value, ref } }) =>
|
render={({ field: { onChange, value, ref } }) =>
|
||||||
<Select inputRef={ref}
|
<Select inputRef={ref}
|
||||||
className="form-select-field-input"
|
|
||||||
value={options.find(c => c.value === value)}
|
value={options.find(c => c.value === value)}
|
||||||
onChange={val => onChange(val.value)}
|
onChange={val => onChange(val.value)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
@ -8,6 +8,8 @@ export interface AuthenticationProvider {
|
|||||||
providable_attributes?: OAuth2Provider | OpenIdConnectProvider
|
providable_attributes?: OAuth2Provider | OpenIdConnectProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type mappingType = 'string' | 'text' | 'date' | 'integer' | 'boolean';
|
||||||
|
|
||||||
export interface AuthenticationProviderMapping {
|
export interface AuthenticationProviderMapping {
|
||||||
id?: number,
|
id?: number,
|
||||||
local_model: 'user' | 'profile',
|
local_model: 'user' | 'profile',
|
||||||
@ -16,7 +18,7 @@ export interface AuthenticationProviderMapping {
|
|||||||
api_endpoint: string,
|
api_endpoint: string,
|
||||||
api_data_type: 'json',
|
api_data_type: 'json',
|
||||||
transformation: {
|
transformation: {
|
||||||
type: 'string' | 'text' | 'date' | 'integer' | 'boolean',
|
type: mappingType,
|
||||||
format: 'iso8601' | 'rfc2822' | 'rfc3339' | 'timestamp-s' | 'timestamp-ms',
|
format: 'iso8601' | 'rfc2822' | 'rfc3339' | 'timestamp-s' | 'timestamp-ms',
|
||||||
true_value: string,
|
true_value: string,
|
||||||
false_value: string,
|
false_value: string,
|
||||||
@ -66,3 +68,8 @@ export interface OpenIdConnectProvider {
|
|||||||
client__end_session_endpoint?: string,
|
client__end_session_endpoint?: string,
|
||||||
profile_url?: string
|
profile_url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MappingFields {
|
||||||
|
user: Array<[string, mappingType]>,
|
||||||
|
profile: Array<[string, mappingType]>
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user