diff --git a/app/frontend/src/javascript/components/authentication-provider/boolean-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/boolean-mapping-form.tsx index 3ee843db2..362764855 100644 --- a/app/frontend/src/javascript/components/authentication-provider/boolean-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/boolean-mapping-form.tsx @@ -13,19 +13,19 @@ export interface BooleanMappingFormProps { * Partial form to map an internal boolean field to an external API providing a string value. */ export const BooleanMappingForm = ({ register, fieldMappingId }: BooleanMappingFormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); return (
-

{t('app.shared.authentication.mappings')}

+

{t('app.admin.authentication.boolean_mapping_form.mappings')}

+ label={t('app.admin.authentication.boolean_mapping_form.true_value')} /> + label={t('app.admin.authentication.boolean_mapping_form.false_value')} />
); }; diff --git a/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx index 1b0333a04..6bb27b61d 100644 --- a/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx @@ -23,7 +23,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 = ({ register, control }: DataMappingFormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); const [dataMapping, setDataMapping] = useState(null); const [isOpenTypeMappingModal, updateIsOpenTypeMappingModal] = useImmer>(new Map()); @@ -101,12 +101,12 @@ export const DataMappingForm = -

{t('app.shared.oauth2.define_the_fields_mapping')}

+

{t('app.admin.authentication.data_mapping_form.define_the_fields_mapping')}

} onClick={() => append({})}> - {t('app.shared.oauth2.add_a_match')} + {t('app.admin.authentication.data_mapping_form.add_a_match')}
{fields.map((item, index) => ( @@ -116,33 +116,37 @@ export const DataMappingForm = + options={buildModelOptions()} + label={t('app.admin.authentication.data_mapping_form.model')}/> + label={t('app.admin.authentication.data_mapping_form.field')} />
+ label={t('app.admin.authentication.data_mapping_form.api_endpoint_url')} /> + label={t('app.admin.authentication.data_mapping_form.api_type')} /> } - label={t('app.shared.oauth2.api_fields')} /> + tooltip={} + label={t('app.admin.authentication.data_mapping_form.api_field')} />
- } onClick={toggleTypeMappingModal(index)} disabled={getField(output, index) === undefined} tooltip={t('app.shared.authentication.data_mapping')} /> + } + onClick={toggleTypeMappingModal(index)} + disabled={getField(output, index) === undefined} + tooltip={t('app.admin.authentication.data_mapping_form.data_mapping')} /> } onClick={() => remove(index)} className="delete-button" /> { * Partial form for mapping an internal date field to an external API. */ export const DateMappingForm = ({ control, fieldMappingId }: DateMappingFormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); // available date formats const dateFormats = [ @@ -41,12 +41,12 @@ export const DateMappingForm = -

{t('app.shared.authentication.input_format')}

+

{t('app.admin.authentication.date_mapping_form.input_format')}

+ label={t('app.admin.authentication.date_mapping_form.date_format')} />
); }; diff --git a/app/frontend/src/javascript/components/authentication-provider/integer-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/integer-mapping-form.tsx index e1a071aa1..a74a69da0 100644 --- a/app/frontend/src/javascript/components/authentication-provider/integer-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/integer-mapping-form.tsx @@ -16,13 +16,13 @@ export interface IntegerMappingFormProps * Partial for to map an internal integer field to an external API providing a string value. */ export const IntegerMappingForm = ({ register, control, fieldMappingId }: IntegerMappingFormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath }); return (
-

{t('app.shared.authentication.mappings')}

+

{t('app.admin.authentication.integer_mapping_form.mappings')}

} @@ -34,12 +34,12 @@ export const IntegerMappingForm = + label={t('app.admin.authentication.integer_mapping_form.mapping_from')} /> + label={t('app.admin.authentication.integer_mapping_form.mapping_to')} />
} onClick={() => remove(index)} className="delete-button" /> diff --git a/app/frontend/src/javascript/components/authentication-provider/oauth2-form.tsx b/app/frontend/src/javascript/components/authentication-provider/oauth2-form.tsx index 28c473f36..8098e05b0 100644 --- a/app/frontend/src/javascript/components/authentication-provider/oauth2-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/oauth2-form.tsx @@ -12,7 +12,7 @@ interface Oauth2FormProps { * Partial form to fill the OAuth2 settings for a new/existing authentication provider. */ export const Oauth2Form = ({ register }: Oauth2FormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); // regular expression to validate the the input fields const endpointRegex = /^\/?([-._~:?#[\]@!$&'()*+,;=%\w]+\/?)*$/; @@ -24,34 +24,34 @@ export const Oauth2Form = ({ register }: Oauth + label={t('app.admin.authentication.oauth2_form.scopes')} />
); }; diff --git a/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx b/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx index dedbc9f93..7bc686033 100644 --- a/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx @@ -36,14 +36,14 @@ type selectProvidableTypeOption = { value: string, label: string }; export const ProviderForm: React.FC = ({ action, provider, onError, onSuccess }) => { const { handleSubmit, register, control } = useForm({ defaultValues: { ...provider } }); const [providableType, setProvidableType] = useState(provider?.providable_type); - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); /** * Callback triggered when the form is submitted: process with the provider creation or update. */ const onSubmit: SubmitHandler = (data: AuthenticationProvider) => { AuthProviderAPI[action](data).then(() => { - onSuccess(t(`app.shared.authentication.${action}_success`)); + onSuccess(t(`app.admin.authentication.provider_form.${action}_success`)); }).catch(error => { onError(error); }); @@ -54,7 +54,7 @@ export const ProviderForm: React.FC = ({ action, provider, on */ const buildProvidableTypeOptions = (): Array => { return Object.keys(METHODS).map((method: string) => { - return { value: method, label: t(`app.shared.authentication.${METHODS[method]}`) }; + return { value: method, label: t(`app.admin.authentication.provider_form.methods.${METHODS[method]}`) }; }); }; @@ -72,17 +72,18 @@ export const ProviderForm: React.FC = ({ action, provider, on register={register} readOnly={action === 'update'} rules={{ required: true }} - label={t('app.shared.authentication.name')} /> + label={t('app.admin.authentication.provider_form.name')} /> {providableType === 'OAuth2Provider' && } {providableType && providableType !== 'DatabaseProvider' && }
- {t('app.shared.authentication.save')} + {t('app.admin.authentication.provider_form.save')}
); diff --git a/app/frontend/src/javascript/components/authentication-provider/string-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/string-mapping-form.tsx index 48a2d9b8c..edd527513 100644 --- a/app/frontend/src/javascript/components/authentication-provider/string-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/string-mapping-form.tsx @@ -16,13 +16,13 @@ export interface StringMappingFormProps { * Partial form to map an internal string field to an external API. */ export const StringMappingForm = ({ register, control, fieldMappingId }: StringMappingFormProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath }); return (
-

{t('app.shared.authentication.mappings')}

+

{t('app.admin.authentication.string_mapping_form.mappings')}

} @@ -34,11 +34,11 @@ export const StringMappingForm = + label={t('app.admin.authentication.string_mapping_form.mapping_from')} /> + label={t('app.admin.authentication.string_mapping_form.mapping_to')} />
} onClick={() => remove(index)} className="delete-button" /> diff --git a/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx b/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx index 75f51294c..6b3e19d76 100644 --- a/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx @@ -28,16 +28,16 @@ interface TypeMappingModalProps { * This component is intended to be used in a react-hook-form context. */ export const TypeMappingModal = ({ model, field, type, isOpen, toggleModal, register, control, fieldMappingId }:TypeMappingModalProps) => { - const { t } = useTranslation('shared'); + const { t } = useTranslation('admin'); return ( } onConfirm={toggleModal}> - {model} > {field} ({t('app.shared.authentication.TYPE_expected', { TYPE: t(`app.shared.authentication.types.${type}`) })}) + {model} > {field} ({t('app.admin.authentication.type_mapping_modal.TYPE_expected', { TYPE: t(`app.admin.authentication.type_mapping_modal.types.${type}`) })}) {type === 'integer' && } {type === 'boolean' && } {type === 'date' && } diff --git a/app/frontend/src/javascript/components/form/form-select.tsx b/app/frontend/src/javascript/components/form/form-select.tsx index 1d9865c50..b8eaf9b52 100644 --- a/app/frontend/src/javascript/components/form/form-select.tsx +++ b/app/frontend/src/javascript/components/form/form-select.tsx @@ -15,6 +15,7 @@ interface FormSelectProps e className?: string, placeholder?: string, disabled?: boolean, + readOnly?: boolean, } /** @@ -26,7 +27,7 @@ type selectOption = { value: TOptionValue, label: string }; /** * This component is a wrapper for react-select to use with react-hook-form */ -export const FormSelect = ({ id, label, className, control, placeholder, options, valueDefault, error, rules, disabled, onChange }: FormSelectProps) => { +export const FormSelect = ({ id, label, className, control, placeholder, options, valueDefault, error, rules, disabled, onChange, readOnly }: FormSelectProps) => { const classNames = ` form-select form-item ${className || ''} ${error && error[id] ? 'is-incorrect' : ''} @@ -62,6 +63,7 @@ export const FormSelect = } />
diff --git a/app/frontend/src/javascript/controllers/admin/authentications.js b/app/frontend/src/javascript/controllers/admin/authentications.js index 2c95a09a4..1558f5729 100644 --- a/app/frontend/src/javascript/controllers/admin/authentications.js +++ b/app/frontend/src/javascript/controllers/admin/authentications.js @@ -113,6 +113,7 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s */ $scope.onSuccess = function (message) { growl.success(message); + $scope.cancel(); }; /** diff --git a/app/frontend/src/stylesheets/modules/form/form-item.scss b/app/frontend/src/stylesheets/modules/form/form-item.scss index bd6402f65..8557fc7a3 100644 --- a/app/frontend/src/stylesheets/modules/form/form-item.scss +++ b/app/frontend/src/stylesheets/modules/form/form-item.scss @@ -29,8 +29,8 @@ position: absolute; right: 0; top: 0; - background-color: black; - color: white; + background-color: white; + color: var(--gray-hard); border-radius: 8px; padding: 1rem; font-size: 12px; diff --git a/app/validators/database_provider_validator.rb b/app/validators/database_provider_validator.rb index 36b9a7d50..d49de8b8d 100644 --- a/app/validators/database_provider_validator.rb +++ b/app/validators/database_provider_validator.rb @@ -5,6 +5,6 @@ class DatabaseProviderValidator < ActiveModel::Validator def validate(record) return if DatabaseProvider.count.zero? - record.errors[:id] << I18n.t('app.admin.authentication_new.a_local_database_provider_already_exists_unable_to_create_another') + record.errors[:id] << I18n.t('authentication_providers.local_database_provider_already_exists') end end diff --git a/app/validators/o_auth2_provider_validator.rb b/app/validators/o_auth2_provider_validator.rb index 8b857bca2..bf4b77743 100644 --- a/app/validators/o_auth2_provider_validator.rb +++ b/app/validators/o_auth2_provider_validator.rb @@ -9,6 +9,6 @@ class OAuth2ProviderValidator < ActiveModel::Validator mapping.local_model == 'user' && mapping.local_field == 'uid' end - record.errors.add(:uid, I18n.t('app.admin.authentication_new.it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider')) + record.errors.add(:uid, I18n.t('authentication_providers.matching_between_User_uid_and_API_required')) end end diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index f4c55c638..648c84525 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -1052,26 +1052,64 @@ en: birth_date: "Date of birth" address: "Address" phone_number: "Phone number" - #add a new authentication provider (SSO) + #authentication providers (SSO) components + authentication: + boolean_mapping_form: + mappings: "Mappings" + true_value: "True value" + false_value: "False value" + date_mapping_form: + input_format: "Input format" + date_format: "Date format" + integer_mapping_form: + mappings: "Mappings" + mapping_from: "From" + mapping_to: "To" + string_mapping_form: + mappings: "Mappings" + mapping_from: "From" + mapping_to: "To" + data_mapping_form: + define_the_fields_mapping: "Define the fields mapping" + add_a_match: "Add a match" + model: "Model" + field: "Field" + api_endpoint_url: "API endpoint or URL" + api_type: "API type" + api_field: "API field" + api_field_help_html: 'JsonPath syntax is supported.
If many fields are selected, the first one will be used.
Example: $.data[*].name"' + data_mapping: "Data mapping" + type_mapping_modal: + data_mapping: "Data mapping" + TYPE_expected: "{TYPE} expected" + types: + integer: "integer" + string: "string" + text: "text" + date: "date" + boolean: "boolean" + oauth2_form: + common_url: "Server root URL" + authorization_endpoint: "Authorization endpoint" + token_acquisition_endpoint: "Token acquisition endpoint" + profil_edition_url: "Profil edition URL" + client_identifier: "Client identifier" + client_secret: "Client secret" + scopes: "Scopes" + provider_form: + name: "Name" + authentication_type: "Authentication type" + save: "Save" + methods: + local_database: "Local database" + o_auth2: "OAuth 2.0" + openid_connect: "OpenID Connect" + #create a new authentication provider (SSO) authentication_new: - local_database: "Local Database" - o_auth2: "OAuth 2.0" add_a_new_authentication_provider: "Add a new authentication provider" - a_local_database_provider_already_exists_unable_to_create_another: "A \"Local Database\" provider already exists. Unable to create another." - local_provider_successfully_saved: "Local provider successfully saved." - it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider: "It is required to set the matching between User.uid and the API to add this provider." - security_issue_detected: "Security issue detected" - beware_the_oauth2_authenticatoin_provider_you_are_about_to_add_isnt_using_HTTPS: "Beware: the OAuth 2 provider you are about to add isn't using HTTPS." - this_is_a_serious_security_issue_on_internet_and_should_never_be_used_except_for_testing_purposes: "This is a serious security issue on internet and should never be used except for testing purposes." - do_you_really_want_to_continue: "Do you really want to continue?" - unsecured_oauth2_provider_successfully_added: "Unsecured OAuth 2.0 provider successfully added." - oauth2_provider_successfully_added: "OAuth 2.0 provider successfully added." #edit an authentication provider (SSO) authentication_edit: provider: "Provider:" - it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider: "It is required to set the matching between User.uid and the API to add this provider." - provider_successfully_updated: "Provider successfully updated." - an_error_occurred_unable_to_update_the_provider: "An error occurred: unable to update the provider." #statistics tables statistics: statistics: "Statistics" diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index a03080022..c7d1fa3b1 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -247,57 +247,6 @@ en: group_is_required: "Group is required." trainings: "Trainings" tags: "Tags" - #partial form to edit/create an authentication provider (SSO) - authentication: - name: "Name" - provider_name_is_required: "Provider name is required." - authentication_type: "Authentication type" - local_database: "Local database" - o_auth2: "OAuth 2.0" - authentication_type_is_required: "Authentication type is required." - data_mapping: "Data mapping" - expected_data_type: "Expected data type" - TYPE_expected: "{TYPE} expected" - input_format: "Input format" - mappings: "Mappings" - mapping_from: "From" - mapping_to: "To" - true_value: "True value" - false_value: "False value" - date_format: "Date format" - save: "Save" - types: - integer: "integer" - string: "string" - text: "text" - date: "date" - boolean: "boolean" - #edition/creation form of an OAuth2 authentication provider - oauth2: - common_url: "Server root URL" - common_url_is_required: "Common URL is required." - provided_url_is_not_a_valid_url: "Provided URL is not a valid URL." - authorization_endpoint: "Authorization endpoint" - oauth2_authorization_endpoint_is_required: "OAuth 2.0 authorization endpoint is required." - provided_endpoint_is_not_valid: "Provided endpoint is not valid." - token_acquisition_endpoint: "Token acquisition endpoint" - oauth2_token_acquisition_endpoint_is_required: "OAuth 2.0 token acquisition endpoint is required." - profil_edition_url: "Profil edition URL" - profile_edition_url_is_required: "Profile edition URL is required." - client_identifier: "Client identifier" - oauth2_client_identifier_is_required: "OAuth 2.0 client identifier is required." - obtain_it_when_registering_with_your_provider: "Obtain it when registering with your provider." - client_secret: "Client secret" - oauth2_client_secret_is_required: "OAuth 2.0 client secret is required." - scopes: "Scopes" - define_the_fields_mapping: "Define the fields mapping" - add_a_match: "Add a match" - model: "Model" - field: "Field" - api_endpoint_url: "API endpoint URL" - api_type: "API type" - api_fields: "API fields" - api_field_help_html: 'JsonPath syntax is supported.
If many fields are selected, the first one will be used.
Example: $.data[*].name"' #machine/training slot modification modal confirm_modify_slot_modal: change_the_slot: "Change the slot" diff --git a/config/locales/en.yml b/config/locales/en.yml index be7505591..4b17e8a70 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -62,6 +62,10 @@ en: your_authentication_code_is_not_valid: "Your authentication code is not valid." current_authentication_method_no_code: "The current authentication method does not require any migration code" requested_account_does_not_exists: "The requested account does not exist" + #SSO external authentication + authentication_providers: + local_database_provider_already_exists: 'A "Local Database" provider already exists. Unable to create another.' + matching_between_User_uid_and_API_required: "It is required to set the matching between User.uid and the API to add this provider." #PDF invoices generation invoices: refund_invoice_reference: "Refund invoice reference: %{REF}"