[GIT-44] refactor(auth): add PASSWORD_TOO_WEAK error code (#8522)
* refactor(auth): add PASSWORD_TOO_WEAK error code and update related error handling in password change flow * fix(auth): update import to use type for EAuthenticationErrorCodes in security page * Update apps/web/app/(all)/profile/security/page.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update apps/web/app/(all)/[workspaceSlug]/(settings)/settings/account/security/page.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor: updated auth error exception accross zxcvbn usages * fix: improve error handling for password strength validation and update error messages * i18n(ru): update Russian translations for stickies and automation description Added translation for 'stickies' and improved formatting of the automation description in Russian locale. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
bf521b7b03
commit
53b3358a63
11 changed files with 45 additions and 23 deletions
|
|
@ -85,8 +85,8 @@ class Adapter:
|
||||||
results = zxcvbn(self.code)
|
results = zxcvbn(self.code)
|
||||||
if results["score"] < 3:
|
if results["score"] < 3:
|
||||||
raise AuthenticationException(
|
raise AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
|
error_code=AUTHENTICATION_ERROR_CODES["PASSWORD_TOO_WEAK"],
|
||||||
error_message="INVALID_PASSWORD",
|
error_message="PASSWORD_TOO_WEAK",
|
||||||
payload={"email": email},
|
payload={"email": email},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ AUTHENTICATION_ERROR_CODES = {
|
||||||
"USER_ACCOUNT_DEACTIVATED": 5019,
|
"USER_ACCOUNT_DEACTIVATED": 5019,
|
||||||
# Password strength
|
# Password strength
|
||||||
"INVALID_PASSWORD": 5020,
|
"INVALID_PASSWORD": 5020,
|
||||||
|
"PASSWORD_TOO_WEAK": 5021,
|
||||||
"SMTP_NOT_CONFIGURED": 5025,
|
"SMTP_NOT_CONFIGURED": 5025,
|
||||||
# Sign Up
|
# Sign Up
|
||||||
"USER_ALREADY_EXIST": 5030,
|
"USER_ALREADY_EXIST": 5030,
|
||||||
|
|
|
||||||
|
|
@ -145,8 +145,8 @@ class ResetPasswordEndpoint(View):
|
||||||
results = zxcvbn(password)
|
results = zxcvbn(password)
|
||||||
if results["score"] < 3:
|
if results["score"] < 3:
|
||||||
exc = AuthenticationException(
|
exc = AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
|
error_code=AUTHENTICATION_ERROR_CODES["PASSWORD_TOO_WEAK"],
|
||||||
error_message="INVALID_PASSWORD",
|
error_message="PASSWORD_TOO_WEAK",
|
||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_app=True),
|
base_host(request=request, is_app=True),
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,8 @@ class ChangePasswordEndpoint(APIView):
|
||||||
results = zxcvbn(new_password)
|
results = zxcvbn(new_password)
|
||||||
if results["score"] < 3:
|
if results["score"] < 3:
|
||||||
exc = AuthenticationException(
|
exc = AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES["INVALID_NEW_PASSWORD"],
|
error_code=AUTHENTICATION_ERROR_CODES["PASSWORD_TOO_WEAK"],
|
||||||
error_message="INVALID_NEW_PASSWORD",
|
error_message="PASSWORD_TOO_WEAK",
|
||||||
)
|
)
|
||||||
return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST)
|
return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,8 @@ class ResetPasswordSpaceEndpoint(View):
|
||||||
results = zxcvbn(password)
|
results = zxcvbn(password)
|
||||||
if results["score"] < 3:
|
if results["score"] < 3:
|
||||||
exc = AuthenticationException(
|
exc = AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
|
error_code=AUTHENTICATION_ERROR_CODES["PASSWORD_TOO_WEAK"],
|
||||||
error_message="INVALID_PASSWORD",
|
error_message="PASSWORD_TOO_WEAK",
|
||||||
)
|
)
|
||||||
url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" # noqa: E501
|
url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" # noqa: E501
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,8 @@ class InstanceAdminSignUpEndpoint(View):
|
||||||
results = zxcvbn(password)
|
results = zxcvbn(password)
|
||||||
if results["score"] < 3:
|
if results["score"] < 3:
|
||||||
exc = AuthenticationException(
|
exc = AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES["INVALID_ADMIN_PASSWORD"],
|
error_code=AUTHENTICATION_ERROR_CODES["PASSWORD_TOO_WEAK"],
|
||||||
error_message="INVALID_ADMIN_PASSWORD",
|
error_message="PASSWORD_TOO_WEAK",
|
||||||
payload={
|
payload={
|
||||||
"email": email,
|
"email": email,
|
||||||
"first_name": first_name,
|
"first_name": first_name,
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ import { getPasswordStrength } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { ProfileSettingsHeading } from "@/components/settings/profile/heading";
|
import { ProfileSettingsHeading } from "@/components/settings/profile/heading";
|
||||||
// helpers
|
// helpers
|
||||||
import { authErrorHandler } from "@/helpers/authentication.helper";
|
import { authErrorHandler, EAuthenticationErrorCodes, passwordErrors } from "@/helpers/authentication.helper";
|
||||||
import type { EAuthenticationErrorCodes } from "@/helpers/authentication.helper";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store/user";
|
import { useUser } from "@/hooks/store/user";
|
||||||
// services
|
// services
|
||||||
|
|
@ -58,6 +57,7 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
watch,
|
watch,
|
||||||
|
setError,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
reset,
|
reset,
|
||||||
} = useForm<FormValues>({ defaultValues });
|
} = useForm<FormValues>({ defaultValues });
|
||||||
|
|
@ -93,11 +93,9 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
|
||||||
message: t("auth.common.password.toast.change_password.success.message"),
|
message: t("auth.common.password.toast.change_password.success.message"),
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
let errorInfo = undefined;
|
const err = error as Error & { error_code?: string };
|
||||||
if (error instanceof Error) {
|
const code = err.error_code?.toString();
|
||||||
const code = "error_code" in error ? error.error_code?.toString() : undefined;
|
const errorInfo = code ? authErrorHandler(code as EAuthenticationErrorCodes) : undefined;
|
||||||
errorInfo = code ? authErrorHandler(code as EAuthenticationErrorCodes) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
|
|
@ -105,6 +103,13 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
|
||||||
message:
|
message:
|
||||||
typeof errorInfo?.message === "string" ? errorInfo.message : t("auth.common.password.toast.error.message"),
|
typeof errorInfo?.message === "string" ? errorInfo.message : t("auth.common.password.toast.error.message"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (code && passwordErrors.includes(code as EAuthenticationErrorCodes)) {
|
||||||
|
setError("new_password", {
|
||||||
|
type: "manual",
|
||||||
|
message: errorInfo?.message?.toString() || t("auth.common.password.toast.error.message"),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -204,6 +209,9 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{passwordSupport}
|
{passwordSupport}
|
||||||
|
{errors.new_password && (
|
||||||
|
<span className="text-11 text-danger-primary">{errors.new_password.message}</span>
|
||||||
|
)}
|
||||||
{isNewPasswordSameAsOldPassword && !isPasswordInputFocused && (
|
{isNewPasswordSameAsOldPassword && !isPasswordInputFocused && (
|
||||||
<span className="text-11 text-danger-primary">
|
<span className="text-11 text-danger-primary">
|
||||||
{t("new_password_must_be_different_from_old_password")}
|
{t("new_password_must_be_different_from_old_password")}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ export enum EAuthenticationErrorCodes {
|
||||||
USER_ACCOUNT_DEACTIVATED = "5019",
|
USER_ACCOUNT_DEACTIVATED = "5019",
|
||||||
// Password strength
|
// Password strength
|
||||||
INVALID_PASSWORD = "5020",
|
INVALID_PASSWORD = "5020",
|
||||||
|
PASSWORD_TOO_WEAK = "5021",
|
||||||
SMTP_NOT_CONFIGURED = "5025",
|
SMTP_NOT_CONFIGURED = "5025",
|
||||||
// Sign Up
|
// Sign Up
|
||||||
USER_ALREADY_EXIST = "5030",
|
USER_ALREADY_EXIST = "5030",
|
||||||
|
|
@ -107,6 +108,7 @@ export type TAuthErrorInfo = {
|
||||||
message: ReactNode;
|
message: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: move all error messages to translation files
|
||||||
const errorCodeMessages: {
|
const errorCodeMessages: {
|
||||||
[key in EAuthenticationErrorCodes]: { title: string; message: (email?: string) => ReactNode };
|
[key in EAuthenticationErrorCodes]: { title: string; message: (email?: string) => ReactNode };
|
||||||
} = {
|
} = {
|
||||||
|
|
@ -143,6 +145,10 @@ const errorCodeMessages: {
|
||||||
title: `Invalid password`,
|
title: `Invalid password`,
|
||||||
message: () => `Invalid password. Please try again.`,
|
message: () => `Invalid password. Please try again.`,
|
||||||
},
|
},
|
||||||
|
[EAuthenticationErrorCodes.PASSWORD_TOO_WEAK]: {
|
||||||
|
title: `Password too weak`,
|
||||||
|
message: () => `Please use a stronger password.`,
|
||||||
|
},
|
||||||
[EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED]: {
|
[EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED]: {
|
||||||
title: `SMTP not configured`,
|
title: `SMTP not configured`,
|
||||||
message: () => `SMTP not configured. Please contact your administrator.`,
|
message: () => `SMTP not configured. Please contact your administrator.`,
|
||||||
|
|
@ -418,6 +424,7 @@ export const authErrorHandler = (errorCode: EAuthenticationErrorCodes, email?: s
|
||||||
EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST,
|
EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST,
|
||||||
EAuthenticationErrorCodes.ADMIN_USER_DEACTIVATED,
|
EAuthenticationErrorCodes.ADMIN_USER_DEACTIVATED,
|
||||||
EAuthenticationErrorCodes.RATE_LIMIT_EXCEEDED,
|
EAuthenticationErrorCodes.RATE_LIMIT_EXCEEDED,
|
||||||
|
EAuthenticationErrorCodes.PASSWORD_TOO_WEAK,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (bannerAlertErrorCodes.includes(errorCode))
|
if (bannerAlertErrorCodes.includes(errorCode))
|
||||||
|
|
@ -430,3 +437,8 @@ export const authErrorHandler = (errorCode: EAuthenticationErrorCodes, email?: s
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const passwordErrors = [
|
||||||
|
EAuthenticationErrorCodes.PASSWORD_TOO_WEAK,
|
||||||
|
EAuthenticationErrorCodes.INVALID_NEW_PASSWORD,
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ export enum EAuthErrorCodes {
|
||||||
USER_ACCOUNT_DEACTIVATED = "5019",
|
USER_ACCOUNT_DEACTIVATED = "5019",
|
||||||
// Password strength
|
// Password strength
|
||||||
INVALID_PASSWORD = "5020",
|
INVALID_PASSWORD = "5020",
|
||||||
|
PASSWORD_TOO_WEAK = "5021",
|
||||||
SMTP_NOT_CONFIGURED = "5025",
|
SMTP_NOT_CONFIGURED = "5025",
|
||||||
// Sign Up
|
// Sign Up
|
||||||
USER_ALREADY_EXIST = "5030",
|
USER_ALREADY_EXIST = "5030",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ export default {
|
||||||
favorites: "Избранное",
|
favorites: "Избранное",
|
||||||
pro: "Pro",
|
pro: "Pro",
|
||||||
upgrade: "Обновить",
|
upgrade: "Обновить",
|
||||||
|
stickies: "Стикеры",
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
common: {
|
common: {
|
||||||
|
|
@ -2002,8 +2003,7 @@ export default {
|
||||||
automations: {
|
automations: {
|
||||||
label: "Автоматизация",
|
label: "Автоматизация",
|
||||||
heading: "Автоматизация",
|
heading: "Автоматизация",
|
||||||
description:
|
description: "Настройте автоматические действия для оптимизации рабочего процесса и сокращения ручных задач.",
|
||||||
"Настройте автоматические действия для оптимизации рабочего процесса и сокращения ручных задач.",
|
|
||||||
"auto-archive": {
|
"auto-archive": {
|
||||||
title: "Автоархивация закрытых рабочих элементов",
|
title: "Автоархивация закрытых рабочих элементов",
|
||||||
description: "Plane будет автоматически архивировать рабочие элементы, которые были завершены или отменены.",
|
description: "Plane будет автоматически архивировать рабочие элементы, которые были завершены или отменены.",
|
||||||
|
|
@ -2921,8 +2921,4 @@ export default {
|
||||||
enter_number_of_projects: "Введите количество проектов",
|
enter_number_of_projects: "Введите количество проектов",
|
||||||
pin: "Закрепить",
|
pin: "Закрепить",
|
||||||
unpin: "Открепить",
|
unpin: "Открепить",
|
||||||
sidebar: {
|
|
||||||
stickies: "Стикеры",
|
|
||||||
your_work: "Ваша работа",
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,10 @@ const errorCodeMessages: {
|
||||||
title: `Invalid password`,
|
title: `Invalid password`,
|
||||||
message: () => `Invalid password. Please try again.`,
|
message: () => `Invalid password. Please try again.`,
|
||||||
},
|
},
|
||||||
|
[EAuthErrorCodes.PASSWORD_TOO_WEAK]: {
|
||||||
|
title: `Password too weak`,
|
||||||
|
message: () => `Please use a stronger password.`,
|
||||||
|
},
|
||||||
[EAuthErrorCodes.SMTP_NOT_CONFIGURED]: {
|
[EAuthErrorCodes.SMTP_NOT_CONFIGURED]: {
|
||||||
title: `SMTP not configured`,
|
title: `SMTP not configured`,
|
||||||
message: () => `SMTP not configured. Please contact your administrator.`,
|
message: () => `SMTP not configured. Please contact your administrator.`,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue