From f763b98c760352355a7e52f99cd50d9f146abd71 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:23:16 +0530 Subject: [PATCH 01/10] chore: added metadata and intl string for password validation Signed-off-by: Dakshesh Jain --- dev/prod/public/config.json | 15 ++++++++++----- dev/prod/src/platform.ts | 20 ++++++++++++++++++++ plugins/login-assets/lang/cs.json | 9 +++++++-- plugins/login-assets/lang/en.json | 7 ++++++- plugins/login-assets/lang/es.json | 9 +++++++-- plugins/login-assets/lang/fr.json | 9 +++++++-- plugins/login-assets/lang/it.json | 7 ++++++- plugins/login-assets/lang/pt.json | 9 +++++++-- plugins/login-assets/lang/ru.json | 7 ++++++- plugins/login-assets/lang/zh.json | 7 ++++++- plugins/login/src/index.ts | 16 ++++++++++++++-- 11 files changed, 96 insertions(+), 19 deletions(-) diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index d2f1c4d3a0..be45f1157b 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -1,14 +1,14 @@ { - "ACCOUNTS_URL":"/account", + "ACCOUNTS_URL": "/account", "COLLABORATOR_URL": "ws://localhost:3078", - "UPLOAD_URL":"/files", + "UPLOAD_URL": "/files", "TELEGRAM_URL": "http://localhost:8086", "GMAIL_URL": "http://localhost:8088", "CALENDAR_URL": "http://localhost:8095", "REKONI_URL": "/rekoni", "GITHUB_APP": "uberflow-dev", "GITHUB_CLIENTID": "Iv1.43f9cac43bd68617", - "GITHUB_URL":"http://localhost:3500", + "GITHUB_URL": "http://localhost:3500", "LAST_NAME_FIRST": "true", "PRINT_URL": "http://localhost:4005", "SIGN_URL": "http://localhost:4006", @@ -17,5 +17,10 @@ "BRANDING_URL": "/branding.json", "VERSION": null, "MODEL_VERSION": null, - "STATS_URL": "http://localhost:4900" -} \ No newline at end of file + "STATS_URL": "http://localhost:4900", + "PASSWORD_MIN_LENGTH": 8, + "PASSWORD_MIN_SPECIAL_CHARS": 1, + "PASSWORD_MIN_DIGITS": 1, + "PASSWORD_MIN_UPPER_CHARS": 1, + "PASSWORD_MIN_LOWER_CHARS": 1 +} diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index cfee6d5396..a558c19b91 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -158,6 +158,11 @@ export interface Config { AI_URL?:string DISABLE_SIGNUP?: string LINK_PREVIEW_URL?: string + PASSWORD_MIN_LENGTH?: string | number + PASSWORD_MIN_SPECIAL_CHARS?: string | number + PASSWORD_MIN_DIGITS?: string | number + PASSWORD_MIN_UPPER_CHARS?: string | number + PASSWORD_MIN_LOWER_CHARS?: string | number // Could be defined for dev environment FRONT_URL?: string PREVIEW_CONFIG?: string @@ -251,6 +256,13 @@ function configureI18n(): void { addStringsLoader(surveyId, async (lang: string) => await import(`@hcengineering/survey-assets/lang/${lang}.json`)) } +function parseNumberOrZero(value?:string|number) :number { + if (typeof value === 'number' ) return value ?? 0; + + const parsed = parseInt(value ?? '0') + return isNaN(parsed) ? 0 : parsed +} + export async function configurePlatform() { setMetadata(platform.metadata.LoadHelper, async (loader) => { for (let i = 0; i < 5; i++) { @@ -306,6 +318,14 @@ export async function configurePlatform() { setMetadata(login.metadata.AccountsUrl, config.ACCOUNTS_URL) setMetadata(login.metadata.DisableSignUp, config.DISABLE_SIGNUP === 'true') + + setMetadata(login.metadata.PasswordValidations, { + MinDigits: parseNumberOrZero(config.PASSWORD_MIN_DIGITS), + MinLength: parseNumberOrZero(config.PASSWORD_MIN_LENGTH), + MinLowerChars: parseNumberOrZero(config.PASSWORD_MIN_LOWER_CHARS), + MinSpecialChars: parseNumberOrZero(config.PASSWORD_MIN_SPECIAL_CHARS), + MinUpperChars: parseNumberOrZero(config.PASSWORD_MIN_UPPER_CHARS) + }) setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) diff --git a/plugins/login-assets/lang/cs.json b/plugins/login-assets/lang/cs.json index b3f3ebf249..66a3b661d5 100644 --- a/plugins/login-assets/lang/cs.json +++ b/plugins/login-assets/lang/cs.json @@ -57,6 +57,11 @@ "Next": "Další", "Skip": "Přeskočit", "SignUpCompleted": "Registrace dokončena", - "StartUsingHuly": "Začněte používat Huly" + "StartUsingHuly": "Začněte používat Huly", + "PasswordMinLength": "{count, plural, =1 {Heslo musí být alespoň # znak dlouhé} other {Heslo musí být alespoň # znaků dlouhé}}", + "PasswordMinSpecialChars": "{count, plural, =1 {Heslo musí obsahovat alespoň # speciální znak} other {Heslo musí obsahovat alespoň # speciálních znaků}}", + "PasswordMinDigits": "{count, plural, =1 {Heslo musí obsahovat alespoň # číslo} other {Heslo musí obsahovat alespoň # čísel}}", + "PasswordMinUpperChars": "{count, plural, =1 {Heslo musí obsahovat alespoň # velké písmeno} other {Heslo musí obsahovat alespoň # velkých písmen}}", + "PasswordMinLowerChars": "{count, plural, =1 {Heslo musí obsahovat alespoň # malé písmeno} other {Heslo musí obsahovat alespoň # malých písmen}}" } -} \ No newline at end of file +} diff --git a/plugins/login-assets/lang/en.json b/plugins/login-assets/lang/en.json index efd8b57ede..be40196013 100644 --- a/plugins/login-assets/lang/en.json +++ b/plugins/login-assets/lang/en.json @@ -57,6 +57,11 @@ "Next": "Next", "Skip": "Skip", "SignUpCompleted": "Sign up completed", - "StartUsingHuly": "Start using Huly" + "StartUsingHuly": "Start using Huly", + "PasswordMinLength": "{count, plural, =1 {Password must be at least # character long} other {Password must be at least # characters long}}", + "PasswordMinSpecialChars": "{count, plural, =1 {Password must contain at least # special character} other {Password must contain at least # special characters}}", + "PasswordMinDigits": "{count, plural, =1 {Password must contain at least # number} other {Password must contain at least # numbers}}", + "PasswordMinUpperChars": "{count, plural, =1 {Password must contain at least # uppercase letter} other {Password must contain at least # uppercase letters}}", + "PasswordMinLowerChars": "{count, plural, =1 {Password must contain at least # lowercase letter} other {Password must contain at least # lowercase letters}}" } } diff --git a/plugins/login-assets/lang/es.json b/plugins/login-assets/lang/es.json index 8dd31dde43..c41aa9df61 100644 --- a/plugins/login-assets/lang/es.json +++ b/plugins/login-assets/lang/es.json @@ -57,6 +57,11 @@ "Next": "Siguiente", "Skip": "Saltar", "SignUpCompleted": "Registro completado", - "StartUsingHuly": "Comienza a usar Huly" + "StartUsingHuly": "Comienza a usar Huly", + "PasswordMinLength": "{count, plural, =1 {La contraseña debe tener al menos # carácter} other {La contraseña debe tener al menos # caracteres}}", + "PasswordMinSpecialChars": "{count, plural, =1 {La contraseña debe contener al menos # carácter especial} other {La contraseña debe contener al menos # caracteres especiales}}", + "PasswordMinDigits": "{count, plural, =1 {La contraseña debe contener al menos # número} other {La contraseña debe contener al menos # números}}", + "PasswordMinUpperChars": "{count, plural, =1 {La contraseña debe contener al menos # letra mayúscula} other {La contraseña debe contener al menos # letras mayúsculas}}", + "PasswordMinLowerChars": "{count, plural, =1 {La contraseña debe contener al menos # letra minúscula} other {La contraseña debe contener al menos # letras minúsculas}}" } -} \ No newline at end of file +} diff --git a/plugins/login-assets/lang/fr.json b/plugins/login-assets/lang/fr.json index b442b854c1..3bce03a9c1 100644 --- a/plugins/login-assets/lang/fr.json +++ b/plugins/login-assets/lang/fr.json @@ -57,6 +57,11 @@ "Next": "Suivant", "Skip": "Passer", "SignUpCompleted": "Inscription terminée", - "StartUsingHuly": "Commencez à utiliser Huly" + "StartUsingHuly": "Commencez à utiliser Huly", + "PasswordMinLength": "{count, plural, =1 {Le mot de passe doit contenir au moins # caractère} other {Le mot de passe doit contenir au moins # caractères}}", + "PasswordMinSpecialChars": "{count, plural, =1 {Le mot de passe doit contenir au moins # caractère spécial} other {Le mot de passe doit contenir au moins # caractères spéciaux}}", + "PasswordMinDigits": "{count, plural, =1 {Le mot de passe doit contenir au moins # chiffre} other {Le mot de passe doit contenir au moins # chiffres}}", + "PasswordMinUpperChars": "{count, plural, =1 {Le mot de passe doit contenir au moins # lettre majuscule} other {Le mot de passe doit contenir au moins # lettres majuscules}}", + "PasswordMinLowerChars": "{count, plural, =1 {Le mot de passe doit contenir au moins # lettre minuscule} other {Le mot de passe doit contenir au moins # lettres minuscules}}" } -} \ No newline at end of file +} diff --git a/plugins/login-assets/lang/it.json b/plugins/login-assets/lang/it.json index 9f59687b01..9081f6e525 100644 --- a/plugins/login-assets/lang/it.json +++ b/plugins/login-assets/lang/it.json @@ -57,6 +57,11 @@ "Next": "Avanti", "Skip": "Salta", "SignUpCompleted": "Registrazione completata", - "StartUsingHuly": "Inizia a usare Huly" + "StartUsingHuly": "Inizia a usare Huly", + "PasswordMinLength": "{count, plural, =1 {La password deve contenere almeno # carattere} other {La password deve contenere almeno # caratteri}}", + "PasswordMinSpecialChars": "{count, plural, =1 {La password deve contenere almeno # carattere speciale} other {La password deve contenere almeno # caratteri speciali}}", + "PasswordMinDigits": "{count, plural, =1 {La password deve contenere almeno # numero} other {La password deve contenere almeno # numeri}}", + "PasswordMinUpperChars": "{count, plural, =1 {La password deve contenere almeno # lettera maiuscola} other {La password deve contenere almeno # lettere maiuscole}}", + "PasswordMinLowerChars": "{count, plural, =1 {La password deve contenere almeno # lettera minuscola} other {La password deve contenere almeno # lettere minuscole}}" } } diff --git a/plugins/login-assets/lang/pt.json b/plugins/login-assets/lang/pt.json index 754b9f81a4..565a8d70d5 100644 --- a/plugins/login-assets/lang/pt.json +++ b/plugins/login-assets/lang/pt.json @@ -57,6 +57,11 @@ "Next": "Seguinte", "Skip": "Saltar", "SignUpCompleted": "Registo concluído", - "StartUsingHuly": "Começar a usar Huly" + "StartUsingHuly": "Começar a usar Huly", + "PasswordMinLength": "{count, plural, =1 {A senha deve ter pelo menos # caractere} other {A senha deve ter pelo menos # caracteres}}", + "PasswordMinSpecialChars": "{count, plural, =1 {A senha deve conter pelo menos # caractere especial} other {A senha deve conter pelo menos # caracteres especiais}}", + "PasswordMinDigits": "{count, plural, =1 {A senha deve conter pelo menos # número} other {A senha deve conter pelo menos # números}}", + "PasswordMinUpperChars": "{count, plural, =1 {A senha deve conter pelo menos # letra maiúscula} other {A senha deve conter pelo menos # letras maiúsculas}}", + "PasswordMinLowerChars": "{count, plural, =1 {A senha deve conter pelo menos # letra minúscula} other {A senha deve conter pelo menos # letras minúsculas}}" } -} \ No newline at end of file +} diff --git a/plugins/login-assets/lang/ru.json b/plugins/login-assets/lang/ru.json index 0d49a61cbf..e25d1ea0ea 100644 --- a/plugins/login-assets/lang/ru.json +++ b/plugins/login-assets/lang/ru.json @@ -57,6 +57,11 @@ "Next": "Дальше", "Skip": "Пропустить", "SignUpCompleted": "Регистрация завершена", - "StartUsingHuly": "Начать использовать Huly" + "StartUsingHuly": "Начать использовать Huly", + "PasswordMinLength": "{count, plural, =1 {Пароль должен содержать минимум # символ} other {Пароль должен содержать минимум # символов}}", + "PasswordMinSpecialChars": "{count, plural, =1 {Пароль должен содержать минимум # специальный символ} other {Пароль должен содержать минимум # специальных символов}}", + "PasswordMinDigits": "{count, plural, =1 {Пароль должен содержать минимум # цифру} other {Пароль должен содержать минимум # цифр}}", + "PasswordMinUpperChars": "{count, plural, =1 {Пароль должен содержать минимум # заглавную букву} other {Пароль должен содержать минимум # заглавных букв}}", + "PasswordMinLowerChars": "{count, plural, =1 {Пароль должен содержать минимум # строчную букву} other {Пароль должен содержать минимум # строчных букв}}" } } diff --git a/plugins/login-assets/lang/zh.json b/plugins/login-assets/lang/zh.json index e5c44c87d6..1ad8b565cf 100644 --- a/plugins/login-assets/lang/zh.json +++ b/plugins/login-assets/lang/zh.json @@ -57,6 +57,11 @@ "Next": "下一个", "Skip": "跳过", "SignUpCompleted": "注册完成", - "StartUsingHuly": "开始使用 Huly" + "StartUsingHuly": "开始使用 Huly", + "PasswordMinLength": "{count, plural, =1 {密码至少需要 # 个字符} other {密码至少需要 # 个字符}}", + "PasswordMinSpecialChars": "{count, plural, =1 {密码至少需要 # 个特殊字符} other {密码至少需要 # 个特殊字符}}", + "PasswordMinDigits": "{count, plural, =1 {密码至少需要 # 个数字} other {密码至少需要 # 个数字}}", + "PasswordMinUpperChars": "{count, plural, =1 {密码至少需要 # 个大写字母} other {密码至少需要 # 个大写字母}}", + "PasswordMinLowerChars": "{count, plural, =1 {密码至少需要 # 个小写字母} other {密码至少需要 # 个小写字母}}" } } diff --git a/plugins/login/src/index.ts b/plugins/login/src/index.ts index 3c79562abb..77b7c0a85a 100644 --- a/plugins/login/src/index.ts +++ b/plugins/login/src/index.ts @@ -72,7 +72,14 @@ export default plugin(loginId, { LoginEndpoint: '' as Metadata, LoginEmail: '' as Metadata, DisableSignUp: '' as Metadata, - TransactorOverride: '' as Metadata + TransactorOverride: '' as Metadata, + PasswordValidations: '' as Metadata<{ + MinLength: number + MinSpecialChars: number + MinDigits: number + MinUpperChars: number + MinLowerChars: number + }> }, component: { LoginApp: '' as AnyComponent, @@ -85,7 +92,12 @@ export default plugin(loginId, { LinkValidHours: '' as IntlString, EmailMask: '' as IntlString, NoLimit: '' as IntlString, - InviteLimit: '' as IntlString + InviteLimit: '' as IntlString, + PasswordMinLength: '' as IntlString<{ min: string }>, + PasswordMinSpecialChars: '' as IntlString<{ min: string }>, + PasswordMinDigits: '' as IntlString<{ min: string }>, + PasswordMinUpperChars: '' as IntlString<{ min: string }>, + PasswordMinLowerChars: '' as IntlString<{ min: string }> }, function: { SendInvite: '' as Resource<(email: string, personId?: Ref, role?: AccountRole) => Promise>, From 7b33ec62d62b0d7e199b46314c66e3cfeb036caf Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:24:18 +0530 Subject: [PATCH 02/10] refactor: moved field type to types file Signed-off-by: Dakshesh Jain --- .../src/components/Form.svelte | 23 +++++--------- plugins/login-resources/src/types.ts | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 plugins/login-resources/src/types.ts diff --git a/plugins/login-resources/src/components/Form.svelte b/plugins/login-resources/src/components/Form.svelte index d040397339..aa6da14f25 100644 --- a/plugins/login-resources/src/components/Form.svelte +++ b/plugins/login-resources/src/components/Form.svelte @@ -22,25 +22,12 @@ import { onMount } from 'svelte' import { BottomAction } from '..' import { makeSequential } from '../mutex' + import type { Field } from '../types' import login from '../plugin' import BottomActionComponent from './BottomAction.svelte' import Providers from './Providers.svelte' import Tabs from './Tabs.svelte' - interface Field { - id?: string - name: string - i18n: IntlString - password?: boolean - optional?: boolean - short?: boolean - rules?: { - rule: RegExp - notMatch: boolean - ruleDescr: IntlString - }[] - } - interface Action { i18n: IntlString func: () => Promise @@ -85,8 +72,12 @@ } if (f.rules !== undefined) { for (const rule of f.rules) { - if (rule.rule.test(v) === rule.notMatch) { - status = new Status(Severity.INFO, rule.ruleDescr, {}) + const isValid = typeof rule.rule === 'function' + ? rule.rule(v) !== rule.notMatch + : rule.rule.test(v) !== rule.notMatch + + if (!isValid) { + status = new Status(Severity.INFO, rule.ruleDescr, rule.ruleDescrParams ?? {}) return false } } diff --git a/plugins/login-resources/src/types.ts b/plugins/login-resources/src/types.ts new file mode 100644 index 0000000000..135d08da7e --- /dev/null +++ b/plugins/login-resources/src/types.ts @@ -0,0 +1,31 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type IntlString } from '@hcengineering/platform' + +export interface Field { + id?: string + name: string + i18n: IntlString + password?: boolean + optional?: boolean + short?: boolean + rules?: Array<{ + rule: RegExp | ((value: string) => boolean) + notMatch: boolean + ruleDescr: IntlString | IntlString> + ruleDescrParams?: Record + }> +} From 2df89faf44c380aece0f5ee0e3b6e8df21270245 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:25:16 +0530 Subject: [PATCH 03/10] feat: made password validation function Signed-off-by: Dakshesh Jain --- plugins/login-resources/src/validations.ts | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 plugins/login-resources/src/validations.ts diff --git a/plugins/login-resources/src/validations.ts b/plugins/login-resources/src/validations.ts new file mode 100644 index 0000000000..f17624ed8e --- /dev/null +++ b/plugins/login-resources/src/validations.ts @@ -0,0 +1,65 @@ +// +// Copyright © 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { + getMetadata +} from '@hcengineering/platform' + +import login from './plugin' +import type { Field } from './types' + +/** + * Generates password validation rules based on metadata. + * + * @returns {Field['rules']} An array of password validation rules. + */ +export function getPasswordValidationRules (): Field['rules'] { + const passwordValidations = getMetadata(login.metadata.PasswordValidations) + + const passwordValidationRules: Field['rules'] = [ + { + rule: (value: string) => value.length >= (passwordValidations?.MinLength ?? 0), + notMatch: false, + ruleDescr: login.string.PasswordMinLength, + ruleDescrParams: { count: passwordValidations?.MinLength } + }, + { + rule: (value: string) => (value.match(/[^a-zA-Z0-9]/g)?.length ?? 0) >= (passwordValidations?.MinSpecialChars ?? 0), + notMatch: false, + ruleDescr: login.string.PasswordMinSpecialChars, + ruleDescrParams: { count: passwordValidations?.MinSpecialChars } + }, + { + rule: (value: string) => (value.match(/[0-9]/g)?.length ?? 0) >= (passwordValidations?.MinDigits ?? 0), + notMatch: false, + ruleDescr: login.string.PasswordMinDigits, + ruleDescrParams: { count: passwordValidations?.MinDigits } + }, + { + rule: (value: string) => (value.match(/[A-Z]/g)?.length ?? 0) >= (passwordValidations?.MinUpperChars ?? 0), + notMatch: false, + ruleDescr: login.string.PasswordMinUpperChars, + ruleDescrParams: { count: passwordValidations?.MinUpperChars } + }, + { + rule: (value: string) => (value.match(/[a-z]/g)?.length ?? 0) >= (passwordValidations?.MinLowerChars ?? 0), + notMatch: false, + ruleDescr: login.string.PasswordMinLowerChars, + ruleDescrParams: { count: passwordValidations?.MinLowerChars } + } + ] + + return passwordValidationRules +} From c218d217196f12f735222b9ee0e79c71fd9af142 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:25:48 +0530 Subject: [PATCH 04/10] feat: using password validation function in signup and restore password Signed-off-by: Dakshesh Jain --- .../login-resources/src/components/PasswordRestore.svelte | 6 ++++-- plugins/login-resources/src/components/SignupForm.svelte | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/login-resources/src/components/PasswordRestore.svelte b/plugins/login-resources/src/components/PasswordRestore.svelte index bed8c838b1..c3562ee30e 100644 --- a/plugins/login-resources/src/components/PasswordRestore.svelte +++ b/plugins/login-resources/src/components/PasswordRestore.svelte @@ -18,11 +18,13 @@ import presentation from '@hcengineering/presentation' import { getCurrentLocation, setMetadataLocalStorage } from '@hcengineering/ui' import login from '../plugin' + import type { Field } from '../types' import { goTo, restorePassword } from '../utils' import Form from './Form.svelte' + import { getPasswordValidationRules } from '../validations' - const fields = [ - { id: 'new-password', name: 'password', i18n: login.string.Password, password: true }, + const fields: Array = [ + { id: 'new-password', name: 'password', i18n: login.string.Password, password: true, rules: getPasswordValidationRules() }, { id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true } ] diff --git a/plugins/login-resources/src/components/SignupForm.svelte b/plugins/login-resources/src/components/SignupForm.svelte index 57268ccaf3..72bcf80051 100644 --- a/plugins/login-resources/src/components/SignupForm.svelte +++ b/plugins/login-resources/src/components/SignupForm.svelte @@ -18,16 +18,18 @@ import presentation from '@hcengineering/presentation' import { setMetadataLocalStorage } from '@hcengineering/ui' import login from '../plugin' + import { getPasswordValidationRules } from '../validations' import { goTo, signUp } from '../utils' import Form from './Form.svelte' + import type { Field } from '../types' export let signUpDisabled = false - const fields = [ + const fields: Array = [ { id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true }, { id: 'family-name', name: 'last', i18n: login.string.LastName, short: true }, { id: 'email', name: 'username', i18n: login.string.Email }, - { id: 'new-password', name: 'password', i18n: login.string.Password, password: true }, + { id: 'new-password', name: 'password', i18n: login.string.Password, password: true, rules: getPasswordValidationRules() }, { id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true } ] From 65b0fb555f23cf8a49aaf00e29bacc034cc0132b Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:37:22 +0530 Subject: [PATCH 05/10] chore: made password related config 0 Signed-off-by: Dakshesh Jain --- dev/prod/public/config.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index be45f1157b..06b7610cc1 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -18,9 +18,9 @@ "VERSION": null, "MODEL_VERSION": null, "STATS_URL": "http://localhost:4900", - "PASSWORD_MIN_LENGTH": 8, - "PASSWORD_MIN_SPECIAL_CHARS": 1, - "PASSWORD_MIN_DIGITS": 1, - "PASSWORD_MIN_UPPER_CHARS": 1, - "PASSWORD_MIN_LOWER_CHARS": 1 + "PASSWORD_MIN_LENGTH": 0, + "PASSWORD_MIN_SPECIAL_CHARS": 0, + "PASSWORD_MIN_DIGITS": 0, + "PASSWORD_MIN_UPPER_CHARS": 0, + "PASSWORD_MIN_LOWER_CHARS": 0 } From cbaaf07dc955d5c7bdca55010127800a225cffc6 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 16:58:16 +0530 Subject: [PATCH 06/10] fix: formatting issue Signed-off-by: Dakshesh Jain --- plugins/login-resources/src/components/Form.svelte | 5 ++--- .../login-resources/src/components/PasswordRestore.svelte | 8 +++++++- plugins/login-resources/src/components/SignupForm.svelte | 8 +++++++- plugins/login-resources/src/validations.ts | 7 +++---- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/login-resources/src/components/Form.svelte b/plugins/login-resources/src/components/Form.svelte index aa6da14f25..ba27a04d98 100644 --- a/plugins/login-resources/src/components/Form.svelte +++ b/plugins/login-resources/src/components/Form.svelte @@ -72,9 +72,8 @@ } if (f.rules !== undefined) { for (const rule of f.rules) { - const isValid = typeof rule.rule === 'function' - ? rule.rule(v) !== rule.notMatch - : rule.rule.test(v) !== rule.notMatch + const isValid = + typeof rule.rule === 'function' ? rule.rule(v) !== rule.notMatch : rule.rule.test(v) !== rule.notMatch if (!isValid) { status = new Status(Severity.INFO, rule.ruleDescr, rule.ruleDescrParams ?? {}) diff --git a/plugins/login-resources/src/components/PasswordRestore.svelte b/plugins/login-resources/src/components/PasswordRestore.svelte index c3562ee30e..fd47f6fdeb 100644 --- a/plugins/login-resources/src/components/PasswordRestore.svelte +++ b/plugins/login-resources/src/components/PasswordRestore.svelte @@ -24,7 +24,13 @@ import { getPasswordValidationRules } from '../validations' const fields: Array = [ - { id: 'new-password', name: 'password', i18n: login.string.Password, password: true, rules: getPasswordValidationRules() }, + { + id: 'new-password', + name: 'password', + i18n: login.string.Password, + password: true, + rules: getPasswordValidationRules() + }, { id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true } ] diff --git a/plugins/login-resources/src/components/SignupForm.svelte b/plugins/login-resources/src/components/SignupForm.svelte index 72bcf80051..3c6bf122d2 100644 --- a/plugins/login-resources/src/components/SignupForm.svelte +++ b/plugins/login-resources/src/components/SignupForm.svelte @@ -29,7 +29,13 @@ { id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true }, { id: 'family-name', name: 'last', i18n: login.string.LastName, short: true }, { id: 'email', name: 'username', i18n: login.string.Email }, - { id: 'new-password', name: 'password', i18n: login.string.Password, password: true, rules: getPasswordValidationRules() }, + { + id: 'new-password', + name: 'password', + i18n: login.string.Password, + password: true, + rules: getPasswordValidationRules() + }, { id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true } ] diff --git a/plugins/login-resources/src/validations.ts b/plugins/login-resources/src/validations.ts index f17624ed8e..7a8d5a8182 100644 --- a/plugins/login-resources/src/validations.ts +++ b/plugins/login-resources/src/validations.ts @@ -13,9 +13,7 @@ // limitations under the License. // -import { - getMetadata -} from '@hcengineering/platform' +import { getMetadata } from '@hcengineering/platform' import login from './plugin' import type { Field } from './types' @@ -36,7 +34,8 @@ export function getPasswordValidationRules (): Field['rules'] { ruleDescrParams: { count: passwordValidations?.MinLength } }, { - rule: (value: string) => (value.match(/[^a-zA-Z0-9]/g)?.length ?? 0) >= (passwordValidations?.MinSpecialChars ?? 0), + rule: (value: string) => + (value.match(/[^a-zA-Z0-9]/g)?.length ?? 0) >= (passwordValidations?.MinSpecialChars ?? 0), notMatch: false, ruleDescr: login.string.PasswordMinSpecialChars, ruleDescrParams: { count: passwordValidations?.MinSpecialChars } From 3af86933412ae778f04a91f61cf2eeda57d301c8 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 11 Jan 2025 17:08:50 +0530 Subject: [PATCH 07/10] fix: inconsistency in types Signed-off-by: Dakshesh Jain --- plugins/login/src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/login/src/index.ts b/plugins/login/src/index.ts index 77b7c0a85a..9369f8dced 100644 --- a/plugins/login/src/index.ts +++ b/plugins/login/src/index.ts @@ -93,11 +93,11 @@ export default plugin(loginId, { EmailMask: '' as IntlString, NoLimit: '' as IntlString, InviteLimit: '' as IntlString, - PasswordMinLength: '' as IntlString<{ min: string }>, - PasswordMinSpecialChars: '' as IntlString<{ min: string }>, - PasswordMinDigits: '' as IntlString<{ min: string }>, - PasswordMinUpperChars: '' as IntlString<{ min: string }>, - PasswordMinLowerChars: '' as IntlString<{ min: string }> + PasswordMinLength: '' as IntlString<{ count: number }>, + PasswordMinSpecialChars: '' as IntlString<{ count: number }>, + PasswordMinDigits: '' as IntlString<{ count: number }>, + PasswordMinUpperChars: '' as IntlString<{ count: number }>, + PasswordMinLowerChars: '' as IntlString<{ count: number }> }, function: { SendInvite: '' as Resource<(email: string, personId?: Ref, role?: AccountRole) => Promise>, From 515952dbdceefde5bb745299e563762006e930be Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 18 Jan 2025 19:58:02 +0530 Subject: [PATCH 08/10] fix: add de translation Signed-off-by: Dakshesh Jain --- plugins/login-assets/lang/de.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/login-assets/lang/de.json b/plugins/login-assets/lang/de.json index 3467b0224c..775fbd2878 100644 --- a/plugins/login-assets/lang/de.json +++ b/plugins/login-assets/lang/de.json @@ -57,6 +57,11 @@ "Next": "Weiter", "Skip": "Überspringen", "SignUpCompleted": "Registrierung abgeschlossen", - "StartUsingHuly": "Starten Sie mit Huly" + "StartUsingHuly": "Starten Sie mit Huly", + "PasswordMinLength": "{count, plural, =1 {Das Passwort muss mindestens # Zeichen lang sein} other {Das Passwort muss mindestens # Zeichen lang sein}}", + "PasswordMinSpecialChars": "{count, plural, =1 {Das Passwort muss mindestens # Sonderzeichen enthalten} other {Das Passwort muss mindestens # Sonderzeichen enthalten}}", + "PasswordMinDigits": "{count, plural, =1 {Das Passwort muss mindestens # Zahl enthalten} other {Das Passwort muss mindestens # Zahlen enthalten}}", + "PasswordMinUpperChars": "{count, plural, =1 {Das Passwort muss mindestens # Großbuchstaben enthalten} other {Das Passwort muss mindestens # Großbuchstaben enthalten}}", + "PasswordMinLowerChars": "{count, plural, =1 {Das Passwort muss mindestens # Kleinbuchstaben enthalten} other {Das Passwort muss mindestens # Kleinbuchstaben enthalten}}" } } From 17efdc8387256ac02bd108835951da60ebd00a6c Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 18 Jan 2025 19:58:25 +0530 Subject: [PATCH 09/10] refactor: replace multi config to single Signed-off-by: Dakshesh Jain --- dev/prod/public/config.json | 6 +---- dev/prod/src/platform.ts | 45 +++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 06b7610cc1..fee334f040 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -18,9 +18,5 @@ "VERSION": null, "MODEL_VERSION": null, "STATS_URL": "http://localhost:4900", - "PASSWORD_MIN_LENGTH": 0, - "PASSWORD_MIN_SPECIAL_CHARS": 0, - "PASSWORD_MIN_DIGITS": 0, - "PASSWORD_MIN_UPPER_CHARS": 0, - "PASSWORD_MIN_LOWER_CHARS": 0 + "PASSWORD_STRICTNESS": "none" } diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index a558c19b91..447e60857f 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -158,11 +158,7 @@ export interface Config { AI_URL?:string DISABLE_SIGNUP?: string LINK_PREVIEW_URL?: string - PASSWORD_MIN_LENGTH?: string | number - PASSWORD_MIN_SPECIAL_CHARS?: string | number - PASSWORD_MIN_DIGITS?: string | number - PASSWORD_MIN_UPPER_CHARS?: string | number - PASSWORD_MIN_LOWER_CHARS?: string | number + PASSWORD_STRICTNESS?: "very_strict" | "strict" | "normal" | "none" // Could be defined for dev environment FRONT_URL?: string PREVIEW_CONFIG?: string @@ -203,6 +199,37 @@ const configs: Record = { 'dev-worker-local': '/config-worker-local.json', } +const PASSWORD_REQUIREMENTS : Record> = { + very_strict: { + MinDigits: 4, + MinLength: 32, + MinLowerChars: 4, + MinSpecialChars: 4, + MinUpperChars: 4 + }, + strict: { + MinDigits: 2, + MinLength: 16, + MinLowerChars: 2, + MinSpecialChars: 2, + MinUpperChars: 2 + }, + normal: { + MinDigits: 1, + MinLength: 8, + MinLowerChars: 1, + MinSpecialChars: 1, + MinUpperChars: 1 + }, + none: { + MinDigits: 0, + MinLength: 0, + MinLowerChars: 0, + MinSpecialChars: 0, + MinUpperChars: 0 + } +} + function configureI18n(): void { //Add localization addStringsLoader(platformId, async (lang: string) => await import(`@hcengineering/platform/lang/${lang}.json`)) @@ -319,13 +346,7 @@ export async function configurePlatform() { setMetadata(login.metadata.AccountsUrl, config.ACCOUNTS_URL) setMetadata(login.metadata.DisableSignUp, config.DISABLE_SIGNUP === 'true') - setMetadata(login.metadata.PasswordValidations, { - MinDigits: parseNumberOrZero(config.PASSWORD_MIN_DIGITS), - MinLength: parseNumberOrZero(config.PASSWORD_MIN_LENGTH), - MinLowerChars: parseNumberOrZero(config.PASSWORD_MIN_LOWER_CHARS), - MinSpecialChars: parseNumberOrZero(config.PASSWORD_MIN_SPECIAL_CHARS), - MinUpperChars: parseNumberOrZero(config.PASSWORD_MIN_UPPER_CHARS) - }) + setMetadata(login.metadata.PasswordValidations, PASSWORD_REQUIREMENTS[config.PASSWORD_STRICTNESS ?? 'none']) setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) From 28541691221875adcc15f6a9617ab5cf5a07cd91 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Sat, 18 Jan 2025 20:14:57 +0530 Subject: [PATCH 10/10] refactor: removed unused function Signed-off-by: Dakshesh Jain --- dev/prod/src/platform.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 447e60857f..12482bbaf6 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -283,13 +283,6 @@ function configureI18n(): void { addStringsLoader(surveyId, async (lang: string) => await import(`@hcengineering/survey-assets/lang/${lang}.json`)) } -function parseNumberOrZero(value?:string|number) :number { - if (typeof value === 'number' ) return value ?? 0; - - const parsed = parseInt(value ?? '0') - return isNaN(parsed) ? 0 : parsed -} - export async function configurePlatform() { setMetadata(platform.metadata.LoadHelper, async (loader) => { for (let i = 0; i < 5; i++) {