From 0df1c0ff482a7236af60f845bf0a0e6482306cdc Mon Sep 17 00:00:00 2001 From: Petr Kachanovsky Date: Tue, 14 Jan 2025 10:52:30 +0200 Subject: [PATCH] fix: add validation backend check --- .../13-standardPagesTuning.md | 27 ++++++++++- adminforth/index.ts | 46 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md b/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md index dec295428..ac2abab53 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md @@ -343,7 +343,32 @@ export default { ### Validation -[Documentation in progress] +In cases when column values must follow certain format, you can add `validation` to it. +`validation` is an array of rules, each containing `regExp` that defines a format for a value and `message` that will be displayed in case when entered value does not pass the check. + +```typescript title="./resources/users.ts" +export default { + name: 'users', + columns: [ + ... + { + name: 'email', + required: true, + isUnique: true, + validation: [ + { + regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', + message: 'Email is not valid, must be in format example@test.com', + }, + ], + }, + ], + }, + ... + ], +``` + +> `validation` checks are enforced both on frontend and backend. ### Foreign resources diff --git a/adminforth/index.ts b/adminforth/index.ts index aa6222f67..05ede79c7 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -53,6 +53,36 @@ class AdminForth implements IAdminForth { return await AdminForthAuth.generatePasswordHash(password); }, + applyRegexValidation(value, validation) { + if (validation?.length) { + const validationArray = validation; + for (let i = 0; i < validationArray.length; i++) { + if (validationArray[i].regExp) { + let flags = ''; + if (validationArray[i].caseSensitive) { + flags += 'i'; + } + if (validationArray[i].multiline) { + flags += 'm'; + } + if (validationArray[i].global) { + flags += 'g'; + } + + const regExp = new RegExp(validationArray[i].regExp, flags); + if (value === undefined || value === null) { + value = ''; + } + let valueS = `${value}`; + + if (!regExp.test(valueS)) { + return validationArray[i].message; + } + } + } + } + }, + PASSWORD_VALIDATORS: { UP_LOW_NUM_SPECIAL: { regExp: '^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#\\$%\\^&\\*\\(\\)\\-_=\\+\\[\\]\\{\\}\\|;:\',\\.<>\\/\\?]).+$', @@ -320,6 +350,14 @@ class AdminForth implements IAdminForth { { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra } ): Promise<{ error?: string, createdRecord?: any }> { + // check if record with validation is valid + for (const column of resource.columns.filter((col) => col.name in record && col.validation)) { + const error = AdminForth.Utils.applyRegexValidation(record[column.name], column.validation); + if (error) { + return { error }; + } + } + // check if record with minValue or maxValue is within limits for (const column of resource.columns.filter((col) => col.name in record && ['integer', 'decimal', 'float'].includes(col.type) @@ -397,6 +435,14 @@ class AdminForth implements IAdminForth { { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra } ): Promise<{ error?: string }> { + // check if record with validation is valid + for (const column of resource.columns.filter((col) => col.name in record && col.validation)) { + const error = AdminForth.Utils.applyRegexValidation(record[column.name], column.validation); + if (error) { + return { error }; + } + } + // check if record with minValue or maxValue is within limits for (const column of resource.columns.filter((col) => col.name in record && ['integer', 'decimal', 'float'].includes(col.type)