1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-30 19:52:20 +01:00

(bug) URL validation regex was wrong

This commit is contained in:
Sylvain 2023-03-13 11:10:36 +01:00
parent ccd3899ebc
commit cd612f4c77
7 changed files with 28 additions and 35 deletions

View File

@ -3,6 +3,7 @@ import { UseFormRegister, FormState } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields'; import { FieldValues } from 'react-hook-form/dist/types/fields';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FabOutputCopy } from '../base/fab-output-copy'; import { FabOutputCopy } from '../base/fab-output-copy';
import ValidationLib from '../../lib/validation';
interface Oauth2FormProps<TFieldValues> { interface Oauth2FormProps<TFieldValues> {
register: UseFormRegister<TFieldValues>, register: UseFormRegister<TFieldValues>,
@ -16,10 +17,6 @@ interface Oauth2FormProps<TFieldValues> {
export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strategyName, formState }: Oauth2FormProps<TFieldValues>) => { export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strategyName, formState }: Oauth2FormProps<TFieldValues>) => {
const { t } = useTranslation('admin'); const { t } = useTranslation('admin');
// regular expression to validate the input fields
const endpointRegex = /^\/?([-._~:?#[\]@!$&'()*+,;=%\w]+\/?)*$/;
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
/** /**
* Build the callback URL, based on the strategy name. * Build the callback URL, based on the strategy name.
*/ */
@ -35,26 +32,26 @@ export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strateg
register={register} register={register}
placeholder="https://sso.example.net..." placeholder="https://sso.example.net..."
label={t('app.admin.authentication.oauth2_form.common_url')} label={t('app.admin.authentication.oauth2_form.common_url')}
rules={{ required: true, pattern: urlRegex }} rules={{ required: true, pattern: ValidationLib.urlRegex }}
formState={formState} /> formState={formState} />
<FormInput id="providable_attributes.authorization_endpoint" <FormInput id="providable_attributes.authorization_endpoint"
register={register} register={register}
placeholder="/oauth2/auth..." placeholder="/oauth2/auth..."
label={t('app.admin.authentication.oauth2_form.authorization_endpoint')} label={t('app.admin.authentication.oauth2_form.authorization_endpoint')}
rules={{ required: true, pattern: endpointRegex }} rules={{ required: true, pattern: ValidationLib.endpointRegex }}
formState={formState} /> formState={formState} />
<FormInput id="providable_attributes.token_endpoint" <FormInput id="providable_attributes.token_endpoint"
register={register} register={register}
placeholder="/oauth2/token..." placeholder="/oauth2/token..."
label={t('app.admin.authentication.oauth2_form.token_acquisition_endpoint')} label={t('app.admin.authentication.oauth2_form.token_acquisition_endpoint')}
rules={{ required: true, pattern: endpointRegex }} rules={{ required: true, pattern: ValidationLib.endpointRegex }}
formState={formState} /> formState={formState} />
<FormInput id="providable_attributes.profile_url" <FormInput id="providable_attributes.profile_url"
register={register} register={register}
placeholder="https://exemple.net/user..." placeholder="https://exemple.net/user..."
label={t('app.admin.authentication.oauth2_form.profile_edition_url')} label={t('app.admin.authentication.oauth2_form.profile_edition_url')}
tooltip={t('app.admin.authentication.oauth2_form.profile_edition_url_help')} tooltip={t('app.admin.authentication.oauth2_form.profile_edition_url_help')}
rules={{ required: true, pattern: urlRegex }} rules={{ required: true, pattern: ValidationLib.urlRegex }}
formState={formState} /> formState={formState} />
<FormInput id="providable_attributes.client_id" <FormInput id="providable_attributes.client_id"
register={register} register={register}

View File

@ -12,6 +12,7 @@ import SsoClient from '../../api/external/sso';
import { FieldPathValue } from 'react-hook-form/dist/types/path'; import { FieldPathValue } from 'react-hook-form/dist/types/path';
import { FormMultiSelect } from '../form/form-multi-select'; import { FormMultiSelect } from '../form/form-multi-select';
import { difference } from 'lodash'; import { difference } from 'lodash';
import ValidationLib from '../../lib/validation';
interface OpenidConnectFormProps<TFieldValues, TContext extends object> { interface OpenidConnectFormProps<TFieldValues, TContext extends object> {
register: UseFormRegister<TFieldValues>, register: UseFormRegister<TFieldValues>,
@ -51,10 +52,6 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
checkForDiscoveryEndpoint({ target: { value: currentFormValues?.issuer } } as React.ChangeEvent<HTMLInputElement>); checkForDiscoveryEndpoint({ target: { value: currentFormValues?.issuer } } as React.ChangeEvent<HTMLInputElement>);
}, []); }, []);
// regular expression to validate the input fields
const endpointRegex = /^\/?([-._~:?#[\]@!$&'()*+,;=%\w]+\/?)*$/;
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
/** /**
* If the discovery endpoint is available, the user will be able to choose to use it or not. * If the discovery endpoint is available, the user will be able to choose to use it or not.
* Otherwise, he will need to end the client configuration manually. * Otherwise, he will need to end the client configuration manually.
@ -109,7 +106,7 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
label={t('app.admin.authentication.openid_connect_form.issuer')} label={t('app.admin.authentication.openid_connect_form.issuer')}
placeholder="https://sso.exemple.com" placeholder="https://sso.exemple.com"
tooltip={t('app.admin.authentication.openid_connect_form.issuer_help')} tooltip={t('app.admin.authentication.openid_connect_form.issuer_help')}
rules={{ required: true, pattern: urlRegex }} rules={{ required: true, pattern: ValidationLib.urlRegex }}
onChange={checkForDiscoveryEndpoint} onChange={checkForDiscoveryEndpoint}
debounce={400} debounce={400}
warning={!discoveryAvailable && { message: t('app.admin.authentication.openid_connect_form.discovery_unavailable') } } warning={!discoveryAvailable && { message: t('app.admin.authentication.openid_connect_form.discovery_unavailable') } }
@ -161,7 +158,7 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
placeholder="https://sso.exemple.com/my-account" placeholder="https://sso.exemple.com/my-account"
label={t('app.admin.authentication.openid_connect_form.profile_edition_url')} label={t('app.admin.authentication.openid_connect_form.profile_edition_url')}
tooltip={t('app.admin.authentication.openid_connect_form.profile_edition_url_help')} tooltip={t('app.admin.authentication.openid_connect_form.profile_edition_url_help')}
rules={{ required: false, pattern: urlRegex }} rules={{ required: false, pattern: ValidationLib.urlRegex }}
formState={formState} /> formState={formState} />
<h4>{t('app.admin.authentication.openid_connect_form.client_options')}</h4> <h4>{t('app.admin.authentication.openid_connect_form.client_options')}</h4>
<FormInput id="providable_attributes.client__identifier" <FormInput id="providable_attributes.client__identifier"
@ -178,31 +175,31 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
<FormInput id="providable_attributes.client__authorization_endpoint" <FormInput id="providable_attributes.client__authorization_endpoint"
label={t('app.admin.authentication.openid_connect_form.client__authorization_endpoint')} label={t('app.admin.authentication.openid_connect_form.client__authorization_endpoint')}
placeholder="/authorize" placeholder="/authorize"
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }} rules={{ required: !currentFormValues?.discovery, pattern: ValidationLib.endpointRegex }}
formState={formState} formState={formState}
register={register} /> register={register} />
<FormInput id="providable_attributes.client__token_endpoint" <FormInput id="providable_attributes.client__token_endpoint"
label={t('app.admin.authentication.openid_connect_form.client__token_endpoint')} label={t('app.admin.authentication.openid_connect_form.client__token_endpoint')}
placeholder="/token" placeholder="/token"
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }} rules={{ required: !currentFormValues?.discovery, pattern: ValidationLib.endpointRegex }}
formState={formState} formState={formState}
register={register} /> register={register} />
<FormInput id="providable_attributes.client__userinfo_endpoint" <FormInput id="providable_attributes.client__userinfo_endpoint"
label={t('app.admin.authentication.openid_connect_form.client__userinfo_endpoint')} label={t('app.admin.authentication.openid_connect_form.client__userinfo_endpoint')}
placeholder="/userinfo" placeholder="/userinfo"
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }} rules={{ required: !currentFormValues?.discovery, pattern: ValidationLib.endpointRegex }}
formState={formState} formState={formState}
register={register} /> register={register} />
{currentFormValues?.client_auth_method === 'jwks' && <FormInput id="providable_attributes.client__jwks_uri" {currentFormValues?.client_auth_method === 'jwks' && <FormInput id="providable_attributes.client__jwks_uri"
label={t('app.admin.authentication.openid_connect_form.client__jwks_uri')} label={t('app.admin.authentication.openid_connect_form.client__jwks_uri')}
rules={{ required: currentFormValues.client_auth_method === 'jwks', pattern: endpointRegex }} rules={{ required: currentFormValues.client_auth_method === 'jwks', pattern: ValidationLib.endpointRegex }}
formState={formState} formState={formState}
placeholder="/jwk" placeholder="/jwk"
register={register} />} register={register} />}
<FormInput id="providable_attributes.client__end_session_endpoint" <FormInput id="providable_attributes.client__end_session_endpoint"
label={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint')} label={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint')}
tooltip={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint_help')} tooltip={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint_help')}
rules={{ pattern: endpointRegex }} rules={{ pattern: ValidationLib.endpointRegex }}
formState={formState} formState={formState}
register={register} /> register={register} />
</div>} </div>}

View File

@ -5,6 +5,7 @@ import { FormSwitch } from '../form/form-switch';
import { FormRichText } from '../form/form-rich-text'; import { FormRichText } from '../form/form-rich-text';
import { FormInput } from '../form/form-input'; import { FormInput } from '../form/form-input';
import { SettingName, SettingValue } from '../../models/setting'; import { SettingName, SettingValue } from '../../models/setting';
import ValidationLib from '../../lib/validation';
export type EditorialKeys = 'active_text_block' | 'text_block' | 'active_cta' | 'cta_label' | 'cta_url'; export type EditorialKeys = 'active_text_block' | 'text_block' | 'active_cta' | 'cta_label' | 'cta_url';
interface EditorialBlockFormProps { interface EditorialBlockFormProps {
@ -15,9 +16,6 @@ interface EditorialBlockFormProps {
keys: Record<EditorialKeys, SettingName> keys: Record<EditorialKeys, SettingName>
} }
// regular expression to validate the input fields
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
/** /**
* Allows to create a formatted text and optional cta button in a form block, to be included in a resource form managed by react-hook-form. * Allows to create a formatted text and optional cta button in a form block, to be included in a resource form managed by react-hook-form.
*/ */
@ -78,7 +76,7 @@ export const EditorialBlockForm: React.FC<EditorialBlockFormProps> = ({ register
formState={formState} formState={formState}
rules={{ rules={{
required: { value: isActiveCta, message: t('app.admin.editorial_block_form.url_is_required') }, required: { value: isActiveCta, message: t('app.admin.editorial_block_form.url_is_required') },
pattern: { value: urlRegex, message: t('app.admin.editorial_block_form.url_must_be_safe') } pattern: { value: ValidationLib.urlRegex, message: t('app.admin.editorial_block_form.url_must_be_safe') }
}} }}
label={t('app.admin.editorial_block_form.cta_url')} /> label={t('app.admin.editorial_block_form.cta_url')} />
</>} </>}

View File

@ -7,6 +7,7 @@ import Icons from '../../../../images/social-icons.svg';
import { FormInput } from '../form/form-input'; import { FormInput } from '../form/form-input';
import { Trash } from 'phosphor-react'; import { Trash } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ValidationLib from '../../lib/validation';
interface EditSocialsProps<TFieldValues> { interface EditSocialsProps<TFieldValues> {
register: UseFormRegister<TFieldValues>, register: UseFormRegister<TFieldValues>,
@ -21,8 +22,6 @@ interface EditSocialsProps<TFieldValues> {
*/ */
export const EditSocials = <TFieldValues extends FieldValues>({ register, setValue, networks, formState, disabled }: EditSocialsProps<TFieldValues>) => { export const EditSocials = <TFieldValues extends FieldValues>({ register, setValue, networks, formState, disabled }: EditSocialsProps<TFieldValues>) => {
const { t } = useTranslation('shared'); const { t } = useTranslation('shared');
// regular expression to validate the input fields
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
const initSelectedNetworks = networks.filter(el => !['', null, undefined].includes(el.url)); const initSelectedNetworks = networks.filter(el => !['', null, undefined].includes(el.url));
const [selectedNetworks, setSelectedNetworks] = useState(initSelectedNetworks); const [selectedNetworks, setSelectedNetworks] = useState(initSelectedNetworks);
@ -72,7 +71,7 @@ export const EditSocials = <TFieldValues extends FieldValues>({ register, setVal
register={register} register={register}
rules= {{ rules= {{
pattern: { pattern: {
value: urlRegex, value: ValidationLib.urlRegex,
message: t('app.shared.edit_socials.website_invalid') message: t('app.shared.edit_socials.website_invalid')
} }
}} }}

View File

@ -12,6 +12,7 @@ import Icons from '../../../../images/social-icons.svg';
import { Trash } from 'phosphor-react'; import { Trash } from 'phosphor-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FabButton } from '../base/fab-button'; import { FabButton } from '../base/fab-button';
import ValidationLib from '../../lib/validation';
declare const Application: IApplication; declare const Application: IApplication;
@ -26,8 +27,6 @@ interface FabSocialsProps {
*/ */
export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, onSuccess }) => { export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, onSuccess }) => {
const { t } = useTranslation('shared'); const { t } = useTranslation('shared');
// regular expression to validate the input fields
const urlRegex = /^(https?:\/\/)([\da-z.-]+)\.([-a-z\d.]{2,30})([/\w .-]*)*\/?$/;
const { handleSubmit, register, setValue, formState } = useForm(); const { handleSubmit, register, setValue, formState } = useForm();
@ -109,7 +108,7 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, o
register={register} register={register}
rules={{ rules={{
pattern: { pattern: {
value: urlRegex, value: ValidationLib.urlRegex,
message: t('app.shared.fab_socials.website_invalid') message: t('app.shared.fab_socials.website_invalid')
} }
}} }}

View File

@ -32,6 +32,7 @@ import { ProfileCustomField } from '../../models/profile-custom-field';
import { SettingName } from '../../models/setting'; import { SettingName } from '../../models/setting';
import SettingAPI from '../../api/setting'; import SettingAPI from '../../api/setting';
import { SelectOption } from '../../models/select'; import { SelectOption } from '../../models/select';
import ValidationLib from '../../lib/validation';
declare const Application: IApplication; declare const Application: IApplication;
@ -55,10 +56,6 @@ interface UserProfileFormProps {
export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size, user, operator, className, onError, onSuccess, showGroupInput, showTermsAndConditionsInput, showTrainingsInput, showTagsInput }) => { export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size, user, operator, className, onError, onSuccess, showGroupInput, showTermsAndConditionsInput, showTrainingsInput, showTagsInput }) => {
const { t } = useTranslation('shared'); const { t } = useTranslation('shared');
// regular expression to validate the input fields
const phoneRegex = /^((00|\+)\d{2,3})?[\d -]{4,14}$/;
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
const { handleSubmit, register, control, formState, setValue, reset } = useForm<User>({ defaultValues: { ...user } }); const { handleSubmit, register, control, formState, setValue, reset } = useForm<User>({ defaultValues: { ...user } });
const output = useWatch<User>({ control }); const output = useWatch<User>({ control });
@ -215,7 +212,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
register={register} register={register}
rules={{ rules={{
pattern: { pattern: {
value: phoneRegex, value: ValidationLib.phoneRegex,
message: t('app.shared.user_profile_form.phone_number_invalid') message: t('app.shared.user_profile_form.phone_number_invalid')
}, },
required: fieldsSettings.get('phone_required') === 'true' required: fieldsSettings.get('phone_required') === 'true'
@ -314,7 +311,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
register={register} register={register}
rules={{ rules={{
pattern: { pattern: {
value: urlRegex, value: ValidationLib.urlRegex,
message: t('app.shared.user_profile_form.website_invalid') message: t('app.shared.user_profile_form.website_invalid')
} }
}} }}

View File

@ -0,0 +1,6 @@
// Provides regular expressions to validate user inputs
export default class ValidationLib {
static urlRegex = /^(https?:\/\/)(([^.]+)\.)+(.{2,30})(\/.*)*\/?$/;
static endpointRegex = /^\/?([-._~:?#[\]@!$&'()*+,;=%\w]+\/?)*$/;
static phoneRegex = /^((00|\+)\d{2,3})?[\d -]{4,14}$/;
}