374 lines
16 KiB
TypeScript
374 lines
16 KiB
TypeScript
/**
|
||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||
* SPDX-License-Identifier: AGPL-3.0-only
|
||
* See the LICENSE file for details.
|
||
*/
|
||
|
||
import type { ReactNode } from "react";
|
||
// plane imports
|
||
import type { TAuthErrorInfo } from "@plane/constants";
|
||
import { E_PASSWORD_STRENGTH, EErrorAlertType, EAuthErrorCodes } from "@plane/constants";
|
||
import zxcvbn from "zxcvbn";
|
||
|
||
/**
|
||
* @description Password strength levels
|
||
*/
|
||
export enum PasswordStrength {
|
||
EMPTY = "empty",
|
||
WEAK = "weak",
|
||
FAIR = "fair",
|
||
GOOD = "good",
|
||
STRONG = "strong",
|
||
}
|
||
|
||
/**
|
||
* Calculate password strength based on various criteria
|
||
*/
|
||
export const getPasswordStrength = (password: string): E_PASSWORD_STRENGTH => {
|
||
if (!password || password === "" || password.length <= 0) {
|
||
return E_PASSWORD_STRENGTH.EMPTY;
|
||
}
|
||
|
||
if (password.length < 8) {
|
||
return E_PASSWORD_STRENGTH.LENGTH_NOT_VALID;
|
||
}
|
||
|
||
// Check all criteria
|
||
const hasUpperCase = /[A-Z]/.test(password);
|
||
const hasLowerCase = /[a-z]/.test(password);
|
||
const hasDigit = /[0-9]/.test(password);
|
||
const hasSpecialChar = /[!@#$%^&*()\-_+=\[\]{}|;:'",.<>?/]/.test(password);
|
||
const isComplexEnough = hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar;
|
||
const isStrongEnough = zxcvbn(password).score >= 3;
|
||
|
||
if (isComplexEnough && isStrongEnough) {
|
||
return E_PASSWORD_STRENGTH.STRENGTH_VALID;
|
||
}
|
||
|
||
return E_PASSWORD_STRENGTH.STRENGTH_NOT_VALID;
|
||
};
|
||
|
||
export type PasswordCriteria = {
|
||
key: string;
|
||
label: string;
|
||
isValid: boolean;
|
||
};
|
||
|
||
/**
|
||
* Get password criteria for validation display
|
||
*/
|
||
export const getPasswordCriteria = (password: string): PasswordCriteria[] => [
|
||
{
|
||
key: "length",
|
||
label: "Минимум 8 символов",
|
||
isValid: password.length >= 8,
|
||
},
|
||
{
|
||
key: "uppercase",
|
||
label: "Минимум 1 заглавная буква",
|
||
isValid: /[A-Z]/.test(password),
|
||
},
|
||
{
|
||
key: "lowercase",
|
||
label: "Минимум 1 строчная буква",
|
||
isValid: /[a-z]/.test(password),
|
||
},
|
||
{
|
||
key: "number",
|
||
label: "Минимум 1 цифра",
|
||
isValid: /[0-9]/.test(password),
|
||
},
|
||
{
|
||
key: "special",
|
||
label: "Минимум 1 спецсимвол",
|
||
isValid: /[!@#$%^&*()\-_+=\[\]{}|;:'",.<>?/]/.test(password),
|
||
},
|
||
{
|
||
key: "predictable",
|
||
label: "Избегайте простых и предсказуемых шаблонов",
|
||
isValid: password.length >= 8 ? zxcvbn(password).score >= 3 : false,
|
||
},
|
||
];
|
||
|
||
// Error code messages
|
||
const errorCodeMessages: {
|
||
[key in EAuthErrorCodes]: { title: string; message: (email?: string) => ReactNode };
|
||
} = {
|
||
// global
|
||
[EAuthErrorCodes.INSTANCE_NOT_CONFIGURED]: {
|
||
title: `Инстанс не настроен`,
|
||
message: () => `Инстанс не настроен. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.SIGNUP_DISABLED]: {
|
||
title: `Регистрация отключена`,
|
||
message: () => `Регистрация отключена. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_PASSWORD]: {
|
||
title: `Неверный пароль`,
|
||
message: () => `Неверный пароль. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.PASSWORD_TOO_WEAK]: {
|
||
title: `Слишком простой пароль`,
|
||
message: () =>
|
||
`Пароль должен содержать заглавные и строчные буквы, цифру, спецсимвол и не быть предсказуемым.`,
|
||
},
|
||
[EAuthErrorCodes.SMTP_NOT_CONFIGURED]: {
|
||
title: `SMTP не настроен`,
|
||
message: () => `SMTP не настроен. Обратитесь к администратору.`,
|
||
},
|
||
// email check in both sign up and sign in
|
||
[EAuthErrorCodes.INVALID_EMAIL]: {
|
||
title: `Некорректный e-mail`,
|
||
message: () => `Некорректный e-mail. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.EMAIL_REQUIRED]: {
|
||
title: `Нужен e-mail`,
|
||
message: () => `Укажите e-mail и попробуйте снова.`,
|
||
},
|
||
// sign up
|
||
[EAuthErrorCodes.USER_ALREADY_EXIST]: {
|
||
title: `Аккаунт уже существует`,
|
||
message: () => `Аккаунт уже зарегистрирован. Выполните вход.`,
|
||
},
|
||
[EAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP]: {
|
||
title: `Нужны e-mail и пароль`,
|
||
message: () => `Укажите e-mail и пароль, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_UP]: {
|
||
title: `Не удалось выполнить вход`,
|
||
message: () => `Не удалось выполнить вход. Проверьте данные и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_EMAIL_SIGN_UP]: {
|
||
title: `Некорректный e-mail`,
|
||
message: () => `Некорректный e-mail. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED]: {
|
||
title: `Нужны e-mail и код`,
|
||
message: () => `Укажите e-mail и код подтверждения, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP]: {
|
||
title: `Некорректный e-mail`,
|
||
message: () => `Некорректный e-mail. Попробуйте снова.`,
|
||
},
|
||
// sign in
|
||
[EAuthErrorCodes.USER_ACCOUNT_DEACTIVATED]: {
|
||
title: `Аккаунт деактивирован`,
|
||
message: () => `Аккаунт деактивирован. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.USER_DOES_NOT_EXIST]: {
|
||
title: `Аккаунт не найден`,
|
||
message: () => `Аккаунт не найден. Создайте новый для начала работы.`,
|
||
},
|
||
[EAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN]: {
|
||
title: `Нужны e-mail и пароль`,
|
||
message: () => `Укажите e-mail и пароль, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]: {
|
||
title: `Не удалось выполнить вход`,
|
||
message: () => `Не удалось выполнить вход. Проверьте данные и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_EMAIL_SIGN_IN]: {
|
||
title: `Некорректный e-mail`,
|
||
message: () => `Некорректный e-mail. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED]: {
|
||
title: `Нужны e-mail и код`,
|
||
message: () => `Укажите e-mail и код подтверждения, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN]: {
|
||
title: `Некорректный e-mail`,
|
||
message: () => `Некорректный e-mail. Попробуйте снова.`,
|
||
},
|
||
// Both Sign in and Sign up
|
||
[EAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_IN]: {
|
||
title: `Неверный код подтверждения`,
|
||
message: () => `Неверный код подтверждения. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_UP]: {
|
||
title: `Неверный код подтверждения`,
|
||
message: () => `Неверный код подтверждения. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN]: {
|
||
title: `Код подтверждения истёк`,
|
||
message: () => `Код подтверждения истёк. Запросите новый и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_UP]: {
|
||
title: `Код подтверждения истёк`,
|
||
message: () => `Код подтверждения истёк. Запросите новый и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN]: {
|
||
title: `Лимит попыток исчерпан`,
|
||
message: () => `Лимит попыток ввода кода исчерпан. Запросите новый код.`,
|
||
},
|
||
[EAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP]: {
|
||
title: `Лимит попыток исчерпан`,
|
||
message: () => `Лимит попыток ввода кода исчерпан. Запросите новый код.`,
|
||
},
|
||
// Oauth
|
||
[EAuthErrorCodes.OAUTH_NOT_CONFIGURED]: {
|
||
title: `OAuth не настроен`,
|
||
message: () => `OAuth не настроен. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.GOOGLE_NOT_CONFIGURED]: {
|
||
title: `Google OAuth не настроен`,
|
||
message: () => `Google OAuth не настроен. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.GITHUB_NOT_CONFIGURED]: {
|
||
title: `GitHub OAuth не настроен`,
|
||
message: () => `GitHub OAuth не настроен. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.GITLAB_NOT_CONFIGURED]: {
|
||
title: `GitLab OAuth не настроен`,
|
||
message: () => `GitLab OAuth не настроен. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR]: {
|
||
title: `Ошибка Google OAuth`,
|
||
message: () => `Не удалось авторизоваться через Google. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR]: {
|
||
title: `Ошибка GitHub OAuth`,
|
||
message: () => `Не удалось авторизоваться через GitHub. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR]: {
|
||
title: `Ошибка GitLab OAuth`,
|
||
message: () => `Не удалось авторизоваться через GitLab. Попробуйте снова.`,
|
||
},
|
||
// Reset Password
|
||
[EAuthErrorCodes.INVALID_PASSWORD_TOKEN]: {
|
||
title: `Некорректный токен`,
|
||
message: () => `Некорректный токен восстановления. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.EXPIRED_PASSWORD_TOKEN]: {
|
||
title: `Токен истёк`,
|
||
message: () => `Токен восстановления истёк. Запросите новый.`,
|
||
},
|
||
// Change password
|
||
[EAuthErrorCodes.MISSING_PASSWORD]: {
|
||
title: `Нужен пароль`,
|
||
message: () => `Укажите пароль и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INCORRECT_OLD_PASSWORD]: {
|
||
title: `Неверный старый пароль`,
|
||
message: () => `Старый пароль указан неверно. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_NEW_PASSWORD]: {
|
||
title: `Некорректный новый пароль`,
|
||
message: () => `Новый пароль не соответствует требованиям.`,
|
||
},
|
||
// set password
|
||
[EAuthErrorCodes.PASSWORD_ALREADY_SET]: {
|
||
title: `Пароль уже установлен`,
|
||
message: () => `Пароль уже установлен. Выполните вход.`,
|
||
},
|
||
// admin
|
||
[EAuthErrorCodes.ADMIN_ALREADY_EXIST]: {
|
||
title: `Администратор уже существует`,
|
||
message: () => `Администратор уже существует. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: {
|
||
title: `Нужны e-mail, пароль и имя`,
|
||
message: () => `Укажите e-mail, пароль и имя, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_ADMIN_EMAIL]: {
|
||
title: `Некорректный e-mail администратора`,
|
||
message: () => `Некорректный e-mail администратора. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.INVALID_ADMIN_PASSWORD]: {
|
||
title: `Некорректный пароль администратора`,
|
||
message: () => `Некорректный пароль администратора. Попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: {
|
||
title: `Нужны e-mail и пароль`,
|
||
message: () => `Укажите e-mail и пароль, затем попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.ADMIN_AUTHENTICATION_FAILED]: {
|
||
title: `Не удалось выполнить вход`,
|
||
message: () => `Не удалось выполнить вход. Проверьте данные и попробуйте снова.`,
|
||
},
|
||
[EAuthErrorCodes.ADMIN_USER_ALREADY_EXIST]: {
|
||
title: `Администратор уже существует`,
|
||
message: () => `Администратор уже существует. Выполните вход.`,
|
||
},
|
||
[EAuthErrorCodes.ADMIN_USER_DOES_NOT_EXIST]: {
|
||
title: `Администратор не найден`,
|
||
message: () => `Администратор не найден. Выполните вход.`,
|
||
},
|
||
[EAuthErrorCodes.MAGIC_LINK_LOGIN_DISABLED]: {
|
||
title: `Вход по magic link отключён`,
|
||
message: () => `Вход по magic link отключён. Используйте пароль.`,
|
||
},
|
||
[EAuthErrorCodes.PASSWORD_LOGIN_DISABLED]: {
|
||
title: `Вход по паролю отключён`,
|
||
message: () => `Вход по паролю отключён. Используйте magic link.`,
|
||
},
|
||
[EAuthErrorCodes.ADMIN_USER_DEACTIVATED]: {
|
||
title: `Администратор деактивирован`,
|
||
message: () => `Аккаунт администратора деактивирован. Обратитесь к администратору.`,
|
||
},
|
||
[EAuthErrorCodes.RATE_LIMIT_EXCEEDED]: {
|
||
title: `Слишком много запросов`,
|
||
message: () => `Слишком много запросов. Повторите попытку позже.`,
|
||
},
|
||
};
|
||
|
||
// Error handler
|
||
export const authErrorHandler = (errorCode: EAuthErrorCodes, email?: string): TAuthErrorInfo | undefined => {
|
||
const bannerAlertErrorCodes = [
|
||
EAuthErrorCodes.INSTANCE_NOT_CONFIGURED,
|
||
EAuthErrorCodes.INVALID_EMAIL,
|
||
EAuthErrorCodes.EMAIL_REQUIRED,
|
||
EAuthErrorCodes.SIGNUP_DISABLED,
|
||
EAuthErrorCodes.INVALID_PASSWORD,
|
||
EAuthErrorCodes.SMTP_NOT_CONFIGURED,
|
||
EAuthErrorCodes.USER_ALREADY_EXIST,
|
||
EAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_UP,
|
||
EAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP,
|
||
EAuthErrorCodes.INVALID_EMAIL_SIGN_UP,
|
||
EAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP,
|
||
EAuthErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED,
|
||
EAuthErrorCodes.USER_DOES_NOT_EXIST,
|
||
EAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_IN,
|
||
EAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN,
|
||
EAuthErrorCodes.INVALID_EMAIL_SIGN_IN,
|
||
EAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN,
|
||
EAuthErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED,
|
||
EAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_IN,
|
||
EAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_UP,
|
||
EAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN,
|
||
EAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_UP,
|
||
EAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN,
|
||
EAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP,
|
||
EAuthErrorCodes.OAUTH_NOT_CONFIGURED,
|
||
EAuthErrorCodes.GOOGLE_NOT_CONFIGURED,
|
||
EAuthErrorCodes.GITHUB_NOT_CONFIGURED,
|
||
EAuthErrorCodes.GITLAB_NOT_CONFIGURED,
|
||
EAuthErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR,
|
||
EAuthErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR,
|
||
EAuthErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR,
|
||
EAuthErrorCodes.INVALID_PASSWORD_TOKEN,
|
||
EAuthErrorCodes.EXPIRED_PASSWORD_TOKEN,
|
||
EAuthErrorCodes.INCORRECT_OLD_PASSWORD,
|
||
EAuthErrorCodes.INVALID_NEW_PASSWORD,
|
||
EAuthErrorCodes.PASSWORD_ALREADY_SET,
|
||
EAuthErrorCodes.ADMIN_ALREADY_EXIST,
|
||
EAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME,
|
||
EAuthErrorCodes.INVALID_ADMIN_EMAIL,
|
||
EAuthErrorCodes.INVALID_ADMIN_PASSWORD,
|
||
EAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD,
|
||
EAuthErrorCodes.ADMIN_AUTHENTICATION_FAILED,
|
||
EAuthErrorCodes.ADMIN_USER_ALREADY_EXIST,
|
||
EAuthErrorCodes.ADMIN_USER_DOES_NOT_EXIST,
|
||
EAuthErrorCodes.USER_ACCOUNT_DEACTIVATED,
|
||
];
|
||
|
||
if (bannerAlertErrorCodes.includes(errorCode))
|
||
return {
|
||
type: EErrorAlertType.BANNER_ALERT,
|
||
code: errorCode,
|
||
title: errorCodeMessages[errorCode]?.title || "Ошибка",
|
||
message: errorCodeMessages[errorCode]?.message(email) || "Что-то пошло не так. Попробуйте снова.",
|
||
};
|
||
|
||
return undefined;
|
||
};
|