mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
Merge pull request #4476 from logto-io/gao-password-policy-cleanup
chore: legacy password policy cleanup
This commit is contained in:
commit
1680cf5fa4
54 changed files with 10 additions and 250 deletions
|
@ -1,4 +1,3 @@
|
|||
import { passwordRegEx } from '@logto/core-kit';
|
||||
import type { KeyboardEventHandler } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
@ -94,14 +93,6 @@ function ChangePasswordModal() {
|
|||
placeholder={t('profile.password.password')}
|
||||
{...register('newPassword', {
|
||||
required: t('profile.password.required'),
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: t('profile.password.min_length', { min: 8 }),
|
||||
},
|
||||
pattern: {
|
||||
value: passwordRegEx,
|
||||
message: t('errors.password_pattern_error'),
|
||||
},
|
||||
})}
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
import { passwordRegEx } from '@logto/core-kit';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
// Note: password requires a minimum of 8 characters and contains a mix of letters, numbers, and symbols.
|
||||
export const generateRandomPassword = (length = 8) => {
|
||||
// eslint-disable-next-line @silverhand/fp/no-let
|
||||
let generated = nanoid(length);
|
||||
while (!passwordRegEx.test(generated)) {
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
generated = nanoid(length);
|
||||
}
|
||||
|
||||
return generated;
|
||||
};
|
||||
export const generateRandomPassword = () => nanoid(8);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { emailRegEx, passwordRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { emailRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { userInfoSelectFields, jsonObjectGuard } from '@logto/schemas';
|
||||
import { conditional, pick } from '@silverhand/essentials';
|
||||
import { literal, object, string } from 'zod';
|
||||
|
@ -123,7 +123,7 @@ export default function userRoutes<T extends AuthedMeRouter>(
|
|||
|
||||
router.post(
|
||||
'/password',
|
||||
koaGuard({ body: object({ password: string().regex(passwordRegEx) }) }),
|
||||
koaGuard({ body: object({ password: string().min(1) }) }),
|
||||
async (ctx, next) => {
|
||||
const { id: userId } = ctx.auth;
|
||||
const { password } = ctx.guard.body;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { CreateUser, Role, SignInExperience, User } from '@logto/schemas';
|
||||
import { RoleType } from '@logto/schemas';
|
||||
import { createMockUtils, pickDefault } from '@logto/shared/esm';
|
||||
import { removeUndefinedKeys } from '@silverhand/essentials';
|
||||
|
||||
import { mockUser, mockUserResponse } from '#src/__mocks__/index.js';
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
|
@ -84,7 +85,7 @@ const usersLibraries = {
|
|||
insertUser: jest.fn(
|
||||
async (user: CreateUser): Promise<User> => ({
|
||||
...mockUser,
|
||||
...user,
|
||||
...removeUndefinedKeys(user), // No undefined values will be returned from database
|
||||
})
|
||||
),
|
||||
} satisfies Partial<Libraries['users']>;
|
||||
|
@ -125,14 +126,14 @@ describe('adminUserRoutes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('POST /users should throw with invalid input params', async () => {
|
||||
it('POST /users should be ok with simple passwords', async () => {
|
||||
const username = 'MJAtLogto';
|
||||
const name = 'Michael';
|
||||
|
||||
// Invalid input format
|
||||
await expect(
|
||||
userRequest.post('/users').send({ username, password: 'abc', name })
|
||||
).resolves.toHaveProperty('status', 400);
|
||||
).resolves.toHaveProperty('status', 200);
|
||||
});
|
||||
|
||||
it('POST /users should throw if username exists', async () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { emailRegEx, passwordRegEx, phoneRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { emailRegEx, phoneRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { jsonObjectGuard, userInfoSelectFields, userProfileResponseGuard } from '@logto/schemas';
|
||||
import { conditional, pick } from '@silverhand/essentials';
|
||||
import { boolean, literal, object, string } from 'zod';
|
||||
|
@ -100,7 +100,7 @@ export default function adminUserRoutes<T extends AuthedRouter>(
|
|||
primaryPhone: string().regex(phoneRegEx),
|
||||
primaryEmail: string().regex(emailRegEx),
|
||||
username: string().regex(usernameRegEx),
|
||||
password: string().regex(passwordRegEx),
|
||||
password: string().min(1),
|
||||
name: string(),
|
||||
}).partial(),
|
||||
response: userProfileResponseGuard,
|
||||
|
@ -183,7 +183,7 @@ export default function adminUserRoutes<T extends AuthedRouter>(
|
|||
'/users/:userId/password',
|
||||
koaGuard({
|
||||
params: object({ userId: string() }),
|
||||
body: object({ password: string().regex(passwordRegEx) }),
|
||||
body: object({ password: string().min(1) }),
|
||||
response: userProfileResponseGuard,
|
||||
status: [200, 422],
|
||||
}),
|
||||
|
|
|
@ -10,9 +10,6 @@ const error = {
|
|||
username_invalid_charset: 'Benutzername darf nur Buchstaben, Zahlen und Unterstriche enthalten',
|
||||
invalid_email: 'Die Email ist ungültig',
|
||||
invalid_phone: 'Die Telefonnummer ist ungültig',
|
||||
password_min_length: 'Passwort muss mindestens {{min}} Zeichen lang sein',
|
||||
invalid_password:
|
||||
'Passwort erfordert mindestens {{min}} Zeichen und enthält eine Kombination aus Buchstaben, Zahlen und Symbolen.',
|
||||
passwords_do_not_match: 'Passwörter stimmen nicht überein',
|
||||
invalid_passcode: 'Der Bestätigungscode ist ungültig',
|
||||
invalid_connector_auth: 'Die Autorisierung ist ungültig',
|
||||
|
|
|
@ -10,9 +10,6 @@ const error = {
|
|||
username_invalid_charset: 'Username should only contain letters, numbers, or underscores.',
|
||||
invalid_email: 'The email is invalid',
|
||||
invalid_phone: 'The phone number is invalid',
|
||||
password_min_length: 'Password requires a minimum of {{min}} characters',
|
||||
invalid_password:
|
||||
'Password requires a minimum of {{min}} characters and contains a mix of letters, numbers, and symbols.',
|
||||
passwords_do_not_match: 'Your passwords don’t match. Please try again.',
|
||||
invalid_passcode: 'The verification code is invalid',
|
||||
invalid_connector_auth: 'The authorization is invalid',
|
||||
|
|
|
@ -11,9 +11,6 @@ const error = {
|
|||
'El nombre de usuario solo debe contener letras, números o guiones bajos.',
|
||||
invalid_email: 'El correo electrónico no es válido',
|
||||
invalid_phone: 'El número de teléfono no es válido',
|
||||
password_min_length: 'La contraseña requiere un mínimo de {{min}} caracteres',
|
||||
invalid_password:
|
||||
'La contraseña requiere un mínimo de {{min}} caracteres y debe contener una combinación de letras, números y símbolos.',
|
||||
passwords_do_not_match: 'Las contraseñas no coinciden. Por favor intente de nuevo',
|
||||
invalid_passcode: 'El código de verificación no es válido',
|
||||
invalid_connector_auth: 'La autorización no es válida',
|
||||
|
|
|
@ -12,9 +12,6 @@ const error = {
|
|||
"Le nom d'utilisateur ne doit contenir que des lettres, des chiffres ou des caractères de soulignement.",
|
||||
invalid_email: "L'email n'est pas valide",
|
||||
invalid_phone: "Le numéro de téléphone n'est pas valide",
|
||||
password_min_length: 'Le mot de passe doit comporter un minimum de {{min}} caractères.',
|
||||
invalid_password:
|
||||
'Le mot de passe doit contenir au minimum {{min}} caractères et doit inclure une combinaison de lettres, de chiffres et de symboles.',
|
||||
passwords_do_not_match: 'Les mots de passe ne correspondent pas',
|
||||
invalid_passcode: 'Le code est invalide',
|
||||
invalid_connector_auth: "L'autorisation n'est pas valide",
|
||||
|
|
|
@ -10,9 +10,6 @@ const error = {
|
|||
username_invalid_charset: "L'username dovrebbe contenere solo lettere, numeri o underscore.",
|
||||
invalid_email: "L'email non è valida",
|
||||
invalid_phone: 'Il numero di telefono non è valido',
|
||||
password_min_length: 'La password richiede un minimo di {{min}} caratteri',
|
||||
invalid_password:
|
||||
'La password richiede un minimo di {{min}} caratteri e contiene una combinazione di lettere, numeri e simboli.',
|
||||
passwords_do_not_match: 'Le password non corrispondono. Per favore prova di nuovo.',
|
||||
invalid_passcode: 'Il codice di verifica non è valido',
|
||||
invalid_connector_auth: "L'autorizzazione è invalida",
|
||||
|
|
|
@ -11,9 +11,6 @@ const error = {
|
|||
'ユーザー名は文字、数字、またはアンダースコアのみを含める必要があります。',
|
||||
invalid_email: 'メールアドレスが無効です',
|
||||
invalid_phone: '電話番号が無効です',
|
||||
password_min_length: 'パスワードは{{min}}文字以上である必要があります',
|
||||
invalid_password:
|
||||
'パスワードは{{min}}文字以上で、文字、数字、および記号の組み合わせである必要があります。',
|
||||
passwords_do_not_match: 'パスワードが一致しません。もう一度お試しください。',
|
||||
invalid_passcode: '検証コードが無効です',
|
||||
invalid_connector_auth: '認証が無効です',
|
||||
|
|
|
@ -10,8 +10,6 @@ const error = {
|
|||
username_invalid_charset: '사용자 이름은 문자, 숫자, _(밑줄 문자) 로만 이루어져야 해요.',
|
||||
invalid_email: '이메일이 유효하지 않아요.',
|
||||
invalid_phone: '휴대전화번호가 유효하지 않아요.',
|
||||
password_min_length: '비밀번호는 최소 {{min}} 자리로 이루어져야 해요.',
|
||||
invalid_password: '비밀번호는 최소 {{min}}자 이상이며 문자, 숫자 및 기호의 조합이어야 해요.',
|
||||
passwords_do_not_match: '비밀번호가 일치하지 않아요.',
|
||||
invalid_passcode: '비밀번호가 유효하지 않아요.',
|
||||
invalid_connector_auth: '인증이 유효하지 않아요.',
|
||||
|
|
|
@ -11,9 +11,6 @@ const error = {
|
|||
'Nazwa użytkownika powinna zawierać tylko litery, liczby lub podkreślenia.',
|
||||
invalid_email: 'Nieprawidłowy adres e-mail',
|
||||
invalid_phone: 'Nieprawidłowy numer telefonu',
|
||||
password_min_length: 'Hasło wymaga minimum {{min}} znaków',
|
||||
invalid_password:
|
||||
'Hasło wymaga minimum {{min}} znaków i zawiera kombinację liter, cyfr oraz symboli.',
|
||||
passwords_do_not_match: 'Hasła nie pasują do siebie. Proszę spróbuj ponownie.',
|
||||
invalid_passcode: 'Nieprawidłowy kod weryfikacyjny',
|
||||
invalid_connector_auth: 'Nieprawidłowa autoryzacja',
|
||||
|
|
|
@ -10,9 +10,6 @@ const error = {
|
|||
username_invalid_charset: 'O nome de usuário deve conter apenas letras, números ou sublinhados.',
|
||||
invalid_email: 'O e-mail é inválido',
|
||||
invalid_phone: 'O número de telefone é inválido',
|
||||
password_min_length: 'A senha requer um mínimo de {{min}} caracteres',
|
||||
invalid_password:
|
||||
'A senha requer um mínimo de {{min}} caracteres e contém uma mistura de letras, números e símbolos.',
|
||||
passwords_do_not_match: 'Suas senhas não correspondem. Por favor, tente novamente.',
|
||||
invalid_passcode: 'O código de verificação é inválido',
|
||||
invalid_connector_auth: 'A autorização é inválida',
|
||||
|
|
|
@ -11,9 +11,6 @@ const error = {
|
|||
'O nome de utilizador deve conter apenas letras, números ou underscores.',
|
||||
invalid_email: 'O email é inválido',
|
||||
invalid_phone: 'O número de telefone é inválido',
|
||||
password_min_length: 'A password requer um mínimo de {{min}} caracteres',
|
||||
invalid_password:
|
||||
'A senha requer um mínimo de {{min}} caracteres e contém uma mistura de letras, números e símbolos.',
|
||||
passwords_do_not_match: 'As passwords não coincidem',
|
||||
invalid_passcode: 'O código de verificação é inválido.',
|
||||
invalid_connector_auth: 'A autorização é inválida',
|
||||
|
|
|
@ -11,9 +11,6 @@ const error = {
|
|||
'Имя пользователя должно содержать только буквы, цифры или символы подчеркивания',
|
||||
invalid_email: 'Электронная почта указана неправильно',
|
||||
invalid_phone: 'Номер телефона указан неправильно',
|
||||
password_min_length: 'Пароль должен быть минимум {{min}} символов',
|
||||
invalid_password:
|
||||
'Пароль должен содержать минимум {{min}} символов, включая буквы, цифры и символы.',
|
||||
passwords_do_not_match: 'Пароли не совпадают. Пожалуйста, попробуйте еще раз.',
|
||||
invalid_passcode: 'Неправильный код подтверждения',
|
||||
invalid_connector_auth: 'Авторизация недействительна',
|
||||
|
|
|
@ -10,9 +10,6 @@ const error = {
|
|||
username_invalid_charset: 'Kullanıcı adı yalnızca harf,sayı veya alt çizgi içermeli.',
|
||||
invalid_email: 'E-posta adresi geçersiz',
|
||||
invalid_phone: 'Telefon numarası geçersiz',
|
||||
password_min_length: 'Şifre en az {{min}} karakterden oluşmalıdır',
|
||||
invalid_password:
|
||||
'Şifre en az {{min}} karakter gerektirir ve harf, sayı ve sembol karışımı içerir.',
|
||||
passwords_do_not_match: 'Şifreler eşleşmiyor',
|
||||
invalid_passcode: 'Doğrulama kodu geçersiz',
|
||||
invalid_connector_auth: 'Yetki geçersiz',
|
||||
|
|
|
@ -10,8 +10,6 @@ const error = {
|
|||
username_invalid_charset: '用户名只能包含英文字母、数字或下划线。',
|
||||
invalid_email: '无效的邮箱',
|
||||
invalid_phone: '无效的手机号',
|
||||
password_min_length: '密码最少需要 {{min}} 个字符',
|
||||
invalid_password: '密码至少需要 {{min}} 个字符,并包含字母、数字和符号的组合。',
|
||||
passwords_do_not_match: '两次输入的密码不一致,请重试。',
|
||||
invalid_passcode: '无效的验证码',
|
||||
invalid_connector_auth: '登录失败',
|
||||
|
|
|
@ -10,8 +10,6 @@ const error = {
|
|||
username_invalid_charset: '用戶名只能包含英文字母、數字或下劃線。',
|
||||
invalid_email: '無效的電子郵件',
|
||||
invalid_phone: '無效的手機號碼',
|
||||
password_min_length: '密碼最少需要 {{min}} 個字符',
|
||||
invalid_password: '密碼至少需要 {{min}} 個字符,並包含字母、數字和符號的組合。',
|
||||
passwords_do_not_match: '兩次輸入的密碼不一致,請重試。',
|
||||
invalid_passcode: '無效的驗證碼',
|
||||
invalid_connector_auth: '登錄失敗',
|
||||
|
|
|
@ -10,8 +10,6 @@ const error = {
|
|||
username_invalid_charset: '使用者名稱只能包含英文字母、數字或下劃線。',
|
||||
invalid_email: '無效的郵箱',
|
||||
invalid_phone: '無效的手機號',
|
||||
password_min_length: '密碼最少需要 {{min}} 個字符',
|
||||
invalid_password: '密碼至少需要 {{min}} 個字符,並包含字母、數字和符號的組合。',
|
||||
passwords_do_not_match: '兩次輸入的密碼不一致,請重試。',
|
||||
invalid_passcode: '無效的驗證碼',
|
||||
invalid_connector_auth: '登錄失敗',
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Mehr Details',
|
||||
username_pattern_error:
|
||||
'Der Benutzername sollte nur Buchstaben, Zahlen oder Unterstriche enthalten und nicht mit einer Zahl beginnen.',
|
||||
password_pattern_error:
|
||||
'Das Passwort benötigt mindestens {{min}} Zeichen und enthält eine Mischung aus Buchstaben, Zahlen und Symbolen.',
|
||||
email_pattern_error: 'Die E-Mail-Adresse ist ungültig.',
|
||||
phone_pattern_error: 'Die Telefonnummer ist ungültig.',
|
||||
insecure_contexts: 'Unsichere Kontexte (nicht-HTTPS) werden nicht unterstützt.',
|
||||
|
|
|
@ -36,7 +36,6 @@ const profile = {
|
|||
verify_via_password: 'Überprüfen per Passwort',
|
||||
show_password: 'Passwort zeigen',
|
||||
required: 'Passwort ist erforderlich',
|
||||
min_length: 'Das Passwort muss mindestens {{min}} Zeichen lang sein',
|
||||
do_not_match: 'Die Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'More details',
|
||||
username_pattern_error:
|
||||
'Username should only contain letters, numbers, or underscore and should not start with a number.',
|
||||
password_pattern_error:
|
||||
'Password requires a minimum of {{min}} characters and contains a mix of letters, numbers, and symbols.',
|
||||
email_pattern_error: 'The email address is invalid.',
|
||||
phone_pattern_error: 'The phone number is invalid.',
|
||||
insecure_contexts: 'Insecure contexts (non-HTTPS) are not supported.',
|
||||
|
|
|
@ -35,7 +35,6 @@ const profile = {
|
|||
verify_via_password: 'Verify via password',
|
||||
show_password: 'Show password',
|
||||
required: 'Password is required',
|
||||
min_length: 'Password requires a minimum of {{min}} characters',
|
||||
do_not_match: 'Passwords do not match. Please try again.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Más detalles',
|
||||
username_pattern_error:
|
||||
'El nombre de usuario solo debe contener letras, números o guiones bajos y no debe comenzar con un número.',
|
||||
password_pattern_error:
|
||||
'La contraseña requiere un mínimo de {{min}} caracteres y contiene una combinación de letras, números y símbolos.',
|
||||
email_pattern_error: 'La dirección de correo electrónico no es válida.',
|
||||
phone_pattern_error: 'El número de teléfono no es válido.',
|
||||
insecure_contexts: 'Los contextos inseguros (no HTTPS) no son compatibles.',
|
||||
|
|
|
@ -37,7 +37,6 @@ const profile = {
|
|||
verify_via_password: 'Verificar mediante contraseña',
|
||||
show_password: 'Mostrar contraseña',
|
||||
required: 'Se requiere contraseña',
|
||||
min_length: 'La contraseña requiere un mínimo de {{min}} caracteres',
|
||||
do_not_match: 'Las contraseñas no coinciden. Inténtelo de nuevo.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Plus de détails',
|
||||
username_pattern_error:
|
||||
"Le nom d'utilisateur ne doit contenir que des lettres, des chiffres ou des traits de soulignement et ne doit pas commencer par un chiffre.",
|
||||
password_pattern_error:
|
||||
'Le mot de passe nécessite un minimum de {{min}} caractères et contient un mélange de lettres, de chiffres et de symboles.',
|
||||
email_pattern_error: "L'adresse e-mail n'est pas valide.",
|
||||
phone_pattern_error: 'Le numéro de téléphone n’est pas valide.',
|
||||
insecure_contexts: 'Les contextes non sécurisés (non HTTPS) ne sont pas pris en charge.',
|
||||
|
|
|
@ -36,7 +36,6 @@ const profile = {
|
|||
verify_via_password: 'Vérifier via mot de passe',
|
||||
show_password: 'Afficher le mot de passe',
|
||||
required: 'Le mot de passe est obligatoire',
|
||||
min_length: 'Le mot de passe doit comporter au minimum {{min}} caractères',
|
||||
do_not_match: 'Les mots de passe ne correspondent pas. Veuillez réessayer.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Ulteriori dettagli',
|
||||
username_pattern_error:
|
||||
'Il nome utente dovrebbe contenere solo lettere, numeri, o trattini bassi e non dovrebbe iniziare con un numero.',
|
||||
password_pattern_error:
|
||||
'La password richiede un minimo di {{min}} caratteri e contiene una combinazione di lettere, numeri e simboli.',
|
||||
email_pattern_error: "L'indirizzo email non è valido.",
|
||||
phone_pattern_error: 'Il numero di telefono non è valido.',
|
||||
insecure_contexts: 'I contesti non sicuri (non HTTPS) non sono supportati.',
|
||||
|
|
|
@ -35,7 +35,6 @@ const profile = {
|
|||
verify_via_password: 'Verifica tramite password',
|
||||
show_password: 'Mostra password',
|
||||
required: 'La password è obbligatoria',
|
||||
min_length: 'La password deve contenere almeno {{min}} caratteri',
|
||||
do_not_match: 'Le password non corrispondono. Riprova.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: '詳細を見る',
|
||||
username_pattern_error:
|
||||
'ユーザー名には、文字、数字、またはアンダースコアしか含めることができず、数字で始めることはできません。',
|
||||
password_pattern_error:
|
||||
'パスワードには、{{min}}文字以上の文字列と文字、数字、およびシンボルが必要です。',
|
||||
email_pattern_error: 'メールアドレスが無効です。',
|
||||
phone_pattern_error: '電話番号が無効です。',
|
||||
insecure_contexts: '安全でないコンテキスト(ノンHTTP)はサポートされていません。',
|
||||
|
|
|
@ -35,7 +35,6 @@ const profile = {
|
|||
verify_via_password: 'パスワードを使って確認する',
|
||||
show_password: 'パスワードを表示する',
|
||||
required: 'パスワードが必要です',
|
||||
min_length: 'パスワードは{{min}}文字以上である必要があります',
|
||||
do_not_match: 'パスワードが一致しません。もう一度お試しください。',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,7 +13,6 @@ const errors = {
|
|||
more_details: '자세히',
|
||||
username_pattern_error:
|
||||
'아이디는 반드시 문자, 숫자, _ 만으로 이루어져야 하며, 숫자로 시작하면 안 돼요.',
|
||||
password_pattern_error: '비밀번호에는 최소 {{min}}자의 문자, 숫자, 특수문자가 포함되어야 해요.',
|
||||
email_pattern_error: '이메일 형식이 유효하지 않아요.',
|
||||
phone_pattern_error: '전화번호 형식이 유효하지 않아요.',
|
||||
insecure_contexts: '비보안 연결(non-HTTPS)는 지원하지 않아요.',
|
||||
|
|
|
@ -34,7 +34,6 @@ const profile = {
|
|||
verify_via_password: '비밀번호로 인증',
|
||||
show_password: '비밀번호 보이기',
|
||||
required: '비밀번호 필요',
|
||||
min_length: '비밀번호는 최소 {{min}} 글자여야 해요.',
|
||||
do_not_match: '비밀번호가 일치하지 않아요. 다시 시도해 주세요.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Więcej szczegółów',
|
||||
username_pattern_error:
|
||||
'Nazwa użytkownika powinna zawierać tylko litery, cyfry lub znak podkreślenia i nie powinna zaczynać się od cyfry.',
|
||||
password_pattern_error:
|
||||
'Hasło wymaga minimum {{min}} znaków i zawiera kombinację liter, cyfr i symboli.',
|
||||
email_pattern_error: 'Adres e-mail jest nieprawidłowy.',
|
||||
phone_pattern_error: 'Numer telefonu jest nieprawidłowy.',
|
||||
insecure_contexts: 'Nieobsługiwane są niebezpieczne konteksty (non-HTTPS).',
|
||||
|
|
|
@ -35,7 +35,6 @@ const profile = {
|
|||
verify_via_password: 'Zweryfikuj za pomocą hasła',
|
||||
show_password: 'Pokaż hasło',
|
||||
required: 'Hasło jest wymagane',
|
||||
min_length: 'Hasło wymaga co najmniej {{min}} znaków.',
|
||||
do_not_match: 'Hasła nie pasują do siebie. Spróbuj ponownie.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Mais detalhes',
|
||||
username_pattern_error:
|
||||
'O nome de usuário deve conter apenas letras, números ou sublinhado e não deve começar com um número.',
|
||||
password_pattern_error:
|
||||
'A senha requer um mínimo de {{min}} caracteres e contém uma mistura de letras, números e símbolos.',
|
||||
email_pattern_error: 'O endereço de e-mail é inválido.',
|
||||
phone_pattern_error: 'O número de telefone é inválido.',
|
||||
insecure_contexts: 'Contextos inseguros (não-HTTPS) não são suportados.',
|
||||
|
|
|
@ -35,7 +35,6 @@ const profile = {
|
|||
verify_via_password: 'Verificar via senha',
|
||||
show_password: 'Mostrar senha',
|
||||
required: 'Senha obrigatória',
|
||||
min_length: 'A senha requer um mínimo de {{min}} caracteres',
|
||||
do_not_match: 'As senhas não coincidem. Tente novamente.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Mais detalhes',
|
||||
username_pattern_error:
|
||||
'O nome de utilizador deve conter apenas letras, números ou underscores e não deve começar com um número.',
|
||||
password_pattern_error:
|
||||
'A password deve conter pelo menos {{min}} caracteres e ter uma combinação de letras, números e símbolos.',
|
||||
email_pattern_error: 'O endereço de e-mail é inválido.',
|
||||
phone_pattern_error: 'O número de telefone é inválido.',
|
||||
insecure_contexts: 'Contextos inseguros (não HTTPS) não são compatíveis.',
|
||||
|
|
|
@ -36,7 +36,6 @@ const profile = {
|
|||
verify_via_password: 'Verificar através da palavra-passe',
|
||||
show_password: 'Mostrar palavra-passe',
|
||||
required: 'Palavra-passe é obrigatória',
|
||||
min_length: 'A palavra-passe requer um mínimo de {{min}} caracteres',
|
||||
do_not_match: 'As palavras-passe não correspondem. Por favor, tente novamente.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Больше информации',
|
||||
username_pattern_error:
|
||||
'Имя пользователя должно состоять только из букв, цифр или подчеркивания и не должно начинаться с цифры.',
|
||||
password_pattern_error:
|
||||
'Пароль должен содержать не менее {{min}} символов и состоять из букв, цифр и символов.',
|
||||
email_pattern_error: 'Адрес электронной почты недействителен.',
|
||||
phone_pattern_error: 'Номер телефона недействителен.',
|
||||
insecure_contexts: 'Небезопасные контексты (нет HTTPS) не поддерживаются.',
|
||||
|
|
|
@ -36,7 +36,6 @@ const profile = {
|
|||
verify_via_password: 'Проверить через пароль',
|
||||
show_password: 'Показать пароль',
|
||||
required: 'Пароль обязателен',
|
||||
min_length: 'Пароль должен содержать не менее {{min}} символов',
|
||||
do_not_match: 'Пароли не совпадают. Попробуйте еще раз.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -13,8 +13,6 @@ const errors = {
|
|||
more_details: 'Daha çok detay',
|
||||
username_pattern_error:
|
||||
'Kullanıcı adı yalnızca harf, sayı veya alt çizgi içermeli ve bir sayı ile başlamamalıdır.',
|
||||
password_pattern_error:
|
||||
'Şifre en az {{min}} karakter ve harfler, sayılar ve simgelerin bir karışımını içermelidir.',
|
||||
email_pattern_error: 'E-posta adresi geçersiz.',
|
||||
phone_pattern_error: 'Telefon numarası geçersiz.',
|
||||
insecure_contexts: 'Güvenli olmayan bağlamlar (HTTPS olmayan) desteklenmez.',
|
||||
|
|
|
@ -36,7 +36,6 @@ const profile = {
|
|||
verify_via_password: 'Şifre ile doğrula',
|
||||
show_password: 'Şifreyi göster',
|
||||
required: 'Şifre gerekli',
|
||||
min_length: 'Şifre en az {{min}} karakterden oluşmalıdır',
|
||||
do_not_match: 'Şifreler eşleşmiyor. Tekrar deneyin.',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -12,7 +12,6 @@ const errors = {
|
|||
required_field_missing_plural: '至少需要输入一个{{field}}',
|
||||
more_details: '查看详情',
|
||||
username_pattern_error: '用户名只能包含英文字母、数字或下划线,且不以数字开头。',
|
||||
password_pattern_error: '密码至少需要 {{min}} 个字符,且必须包含字母、数字和符号。',
|
||||
email_pattern_error: '邮箱地址无效',
|
||||
phone_pattern_error: '手机号码无效',
|
||||
insecure_contexts: '不支持不安全的上下文(非 HTTPS)。',
|
||||
|
|
|
@ -33,7 +33,6 @@ const profile = {
|
|||
verify_via_password: '通过密码验证',
|
||||
show_password: '显示密码',
|
||||
required: '密码不能为空',
|
||||
min_length: '密码最少需要{{min}}个字符',
|
||||
do_not_match: '密码不匹配,请重新输入。',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -12,7 +12,6 @@ const errors = {
|
|||
required_field_missing_plural: '至少需要輸入一個{{field}}',
|
||||
more_details: '查看詳情',
|
||||
username_pattern_error: '用戶名只能包含英文字母、數字或下劃線,且不以數字開頭。',
|
||||
password_pattern_error: '密碼至少需要 {{min}} 個字符,且必須包含字母、數字和符號。',
|
||||
email_pattern_error: '郵箱地址無效',
|
||||
phone_pattern_error: '手機號碼無效',
|
||||
insecure_contexts: '不支持不安全的上下文(非 HTTPS)。',
|
||||
|
|
|
@ -33,7 +33,6 @@ const profile = {
|
|||
verify_via_password: '通過密碼驗證',
|
||||
show_password: '顯示密碼',
|
||||
required: '密碼不能為空',
|
||||
min_length: '密碼最少需要{{min}}個字符',
|
||||
do_not_match: '密碼不匹配,請重新輸入。',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -12,7 +12,6 @@ const errors = {
|
|||
required_field_missing_plural: '至少需要輸入一個{{field}}',
|
||||
more_details: '查看詳情',
|
||||
username_pattern_error: '用戶名只能包含英文字母、數字或下劃線,且不以數字開頭。',
|
||||
password_pattern_error: '密碼至少需要{{min}}個字符,且必須包含字母、數字和符號。',
|
||||
email_pattern_error: '郵箱地址無效',
|
||||
phone_pattern_error: '手機號碼無效',
|
||||
insecure_contexts: '不支援不安全的上下文(非 HTTPS)。',
|
||||
|
|
|
@ -33,7 +33,6 @@ const profile = {
|
|||
verify_via_password: '通過密碼驗證',
|
||||
show_password: '顯示密碼',
|
||||
required: '密碼不能為空',
|
||||
min_length: '密碼最少需要{{min}}個字符',
|
||||
do_not_match: '密碼不匹配,請重新輸入。',
|
||||
},
|
||||
code: {
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import { passwordRegEx } from './regex.js';
|
||||
|
||||
describe('passwordRegEx', () => {
|
||||
it('should match password with at least 8 chars', () => {
|
||||
expect(passwordRegEx.test('1234ddf')).toBeFalsy();
|
||||
expect(passwordRegEx.test('1234ddf!')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('password should not contains non ASCII visible chars', () => {
|
||||
expect(passwordRegEx.test('a1?aaaaa测试')).toBeFalsy();
|
||||
|
||||
expect(passwordRegEx.test('a1?aaaaa测试')).toBeFalsy();
|
||||
|
||||
expect(passwordRegEx.test('a1?aaaaa🌹')).toBeFalsy();
|
||||
|
||||
expect(passwordRegEx.test('a1?aaaaa')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('password should contains at least 2 of 3 types of chars', () => {
|
||||
const singleTypeChars = ['aaaaaaaa', '11111111', '!@#$%^&*(())'];
|
||||
|
||||
it.each(singleTypeChars)('single typed password format %p should be invalid', (password) => {
|
||||
expect(passwordRegEx.test(password)).toBeFalsy();
|
||||
});
|
||||
|
||||
const doubleTypeChars = [
|
||||
'asdfghj1',
|
||||
'asdfghj$',
|
||||
'1234567@',
|
||||
'1234567a',
|
||||
'!@#$%^&1',
|
||||
'!@#$%^&a',
|
||||
];
|
||||
|
||||
it.each(doubleTypeChars)('double typed password format %p should be valid', (password) => {
|
||||
expect(passwordRegEx.test(password)).toBeTruthy();
|
||||
});
|
||||
|
||||
const tripleTypeChars = ['ASD!@#45', 'a!@#$%123', '1ASDfg654', '*123345GHJ'];
|
||||
|
||||
it.each(tripleTypeChars)('triple typed password format %p should be valid', (password) => {
|
||||
expect(passwordRegEx.test(password)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -7,12 +7,3 @@ export const mobileUriSchemeProtocolRegEx = /^[a-z][\d+_a-z-]*(\.[\d+_a-z-]+)+:$
|
|||
export const hexColorRegEx = /^#[\da-f]{3}([\da-f]{3})?$/i;
|
||||
export const dateRegex = /^\d{4}(-\d{2}){2}/;
|
||||
export const noSpaceRegEx = /^\S+$/;
|
||||
|
||||
const atLeastOneDigitAndOneLetters = /(?=.*\d)(?=.*[A-Za-z])/;
|
||||
const atLeastOneDigitAndOneSpecialChar = /(?=.*\d)(?=.*[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~-])/;
|
||||
const atLeastOneLetterAndOneSpecialChar = /(?=.*[A-Za-z])(?=.*[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~-])/;
|
||||
const allowedChars = /[\w!"#$%&'()*+,./:;<=>?@[\]^`{|}~-]{8,}/;
|
||||
|
||||
export const passwordRegEx = new RegExp(
|
||||
`^(${atLeastOneDigitAndOneLetters.source}|${atLeastOneDigitAndOneSpecialChar.source}|${atLeastOneLetterAndOneSpecialChar.source})${allowedChars.source}$`
|
||||
);
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import { validatePassword } from './form';
|
||||
|
||||
describe('password format', () => {
|
||||
it('password min length should be 8', () => {
|
||||
expect(validatePassword('a1?')).toEqual({ code: 'password_min_length', data: { min: 8 } });
|
||||
expect(validatePassword('aaa123aa')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('password should not contains non ASCII visible chars', () => {
|
||||
expect(validatePassword('a1?aaaaa测试')).toEqual({
|
||||
code: 'invalid_password',
|
||||
data: { min: 8 },
|
||||
});
|
||||
|
||||
expect(validatePassword('a1?aaaaa测试')).toEqual({
|
||||
code: 'invalid_password',
|
||||
data: { min: 8 },
|
||||
});
|
||||
|
||||
expect(validatePassword('a1?aaaaa🌹')).toEqual({
|
||||
code: 'invalid_password',
|
||||
data: { min: 8 },
|
||||
});
|
||||
|
||||
expect(validatePassword('a1?aaaaa')).toBe(undefined);
|
||||
});
|
||||
|
||||
describe('password should contains at least 2 of 3 types of chars', () => {
|
||||
const singleTypeChars = ['aaaaaaaa', '11111111', '!@#$%^&*(())'];
|
||||
|
||||
it.each(singleTypeChars)('single typed password format %p should be invalid', (password) => {
|
||||
expect(validatePassword(password)).toEqual({
|
||||
code: 'invalid_password',
|
||||
data: { min: 8 },
|
||||
});
|
||||
});
|
||||
|
||||
const doubleTypeChars = [
|
||||
'asdfghj1',
|
||||
'asdfghj$',
|
||||
'1234567@',
|
||||
'1234567a',
|
||||
'!@#$%^&1',
|
||||
'!@#$%^&a',
|
||||
];
|
||||
|
||||
it.each(doubleTypeChars)('double typed password format %p should be valid', (password) => {
|
||||
expect(validatePassword(password)).toBe(undefined);
|
||||
});
|
||||
|
||||
const tripleTypeChars = ['ASD!@#45', 'a!@#$%123', '1ASDfg654', '*123345GHJ'];
|
||||
|
||||
it.each(tripleTypeChars)('triple typed password format %p should be valid', (password) => {
|
||||
expect(validatePassword(password)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -10,15 +10,6 @@ import { parseE164Number, parsePhoneNumber } from '@/utils/country-code';
|
|||
|
||||
const { t } = i18next;
|
||||
|
||||
// We validate the password format step by step to provide more detailed error messages at the front end.
|
||||
// The overall password format regex passwordRegEx is defined in the '@logto/core-kit' need to align these two.
|
||||
const specialChars = /[!"#$%&'()*+,./:;<=>?@[\]^_`{|}~-]/;
|
||||
const digits = /\d/;
|
||||
const letters = /[A-Za-z]/;
|
||||
const allowedChars = /^[\w!"#$%&'()*+,./:;<=>?@[\]^`{|}~-]*$/;
|
||||
|
||||
export const passwordMinLength = 8;
|
||||
|
||||
export const validateUsername = (username: string): ErrorType | undefined => {
|
||||
if (!username) {
|
||||
return 'username_required';
|
||||
|
@ -55,25 +46,6 @@ export const validatePhone = (value: string): ErrorType | undefined => {
|
|||
}
|
||||
};
|
||||
|
||||
export const validatePassword = (value: string): ErrorType | undefined => {
|
||||
const hasDigits = digits.test(value);
|
||||
const hasLetters = letters.test(value);
|
||||
const hasSpecialChars = specialChars.test(value);
|
||||
const nonInvalidChars = allowedChars.test(value);
|
||||
|
||||
if (!nonInvalidChars) {
|
||||
return { code: 'invalid_password', data: { min: passwordMinLength } };
|
||||
}
|
||||
|
||||
if (value.length < passwordMinLength) {
|
||||
return { code: 'password_min_length', data: { min: passwordMinLength } };
|
||||
}
|
||||
|
||||
if ((hasDigits ? 1 : 0) + (hasLetters ? 1 : 0) + (hasSpecialChars ? 1 : 0) < 2) {
|
||||
return { code: 'invalid_password', data: { min: passwordMinLength } };
|
||||
}
|
||||
};
|
||||
|
||||
export const validateIdentifierField = (type: IdentifierInputType, value: string) => {
|
||||
switch (type) {
|
||||
case SignInIdentifier.Username: {
|
||||
|
|
Loading…
Add table
Reference in a new issue