/** * 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; };