From 334bc6be8caa44e8d5776d1e3cba75cb9cb7ea9b Mon Sep 17 00:00:00 2001 From: pablodanswer Date: Sun, 15 Dec 2024 16:28:45 -0800 Subject: [PATCH] Increase password requirements --- backend/onyx/auth/users.py | 31 +++++++++++++++++++ backend/onyx/configs/constants.py | 4 +++ backend/onyx/main.py | 1 + .../integration/common_utils/managers/user.py | 2 +- web/src/app/auth/login/EmailPasswordForm.tsx | 7 +++-- web/tests/e2e/constants.js | 2 +- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/backend/onyx/auth/users.py b/backend/onyx/auth/users.py index 9aaec5823..b692e8d50 100644 --- a/backend/onyx/auth/users.py +++ b/backend/onyx/auth/users.py @@ -73,6 +73,7 @@ from onyx.configs.constants import AuthType from onyx.configs.constants import DANSWER_API_KEY_DUMMY_EMAIL_DOMAIN from onyx.configs.constants import DANSWER_API_KEY_PREFIX from onyx.configs.constants import MilestoneRecordType +from onyx.configs.constants import PASSWORD_SPECIAL_CHARS from onyx.configs.constants import UNNAMED_KEY_PLACEHOLDER from onyx.db.api_key import fetch_user_for_api_key from onyx.db.auth import get_access_token_db @@ -302,6 +303,36 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): return user + async def validate_password(self, password: str, _: schemas.UC | models.UP) -> None: + # Validate password according to basic security guidelines + if len(password) < 12: + raise exceptions.InvalidPasswordException( + reason="Password must be at least 12 characters long." + ) + if len(password) > 64: + raise exceptions.InvalidPasswordException( + reason="Password must not exceed 64 characters." + ) + if not any(char.isupper() for char in password): + raise exceptions.InvalidPasswordException( + reason="Password must contain at least one uppercase letter." + ) + if not any(char.islower() for char in password): + raise exceptions.InvalidPasswordException( + reason="Password must contain at least one lowercase letter." + ) + if not any(char.isdigit() for char in password): + raise exceptions.InvalidPasswordException( + reason="Password must contain at least one number." + ) + if not any(char in PASSWORD_SPECIAL_CHARS for char in password): + raise exceptions.InvalidPasswordException( + reason="Password must contain at least one special character from the following set: " + f"{PASSWORD_SPECIAL_CHARS}." + ) + + return + async def oauth_callback( self, oauth_name: str, diff --git a/backend/onyx/configs/constants.py b/backend/onyx/configs/constants.py index ab13f2c68..3899ce860 100644 --- a/backend/onyx/configs/constants.py +++ b/backend/onyx/configs/constants.py @@ -173,6 +173,10 @@ class AuthType(str, Enum): CLOUD = "cloud" +# Special characters for password validation +PASSWORD_SPECIAL_CHARS = "!@#$%^&*()_+-=[]{}|;:,.<>?" + + class SessionType(str, Enum): CHAT = "Chat" SEARCH = "Search" diff --git a/backend/onyx/main.py b/backend/onyx/main.py index 288da17a6..de0aca291 100644 --- a/backend/onyx/main.py +++ b/backend/onyx/main.py @@ -243,6 +243,7 @@ def get_application() -> FastAPI: include_router_with_global_prefix_prepended(application, admin_query_router) include_router_with_global_prefix_prepended(application, admin_router) include_router_with_global_prefix_prepended(application, connector_router) + include_router_with_global_prefix_prepended(application, user_router) include_router_with_global_prefix_prepended(application, credential_router) include_router_with_global_prefix_prepended(application, cc_pair_router) include_router_with_global_prefix_prepended(application, folder_router) diff --git a/backend/tests/integration/common_utils/managers/user.py b/backend/tests/integration/common_utils/managers/user.py index d0cad7568..4acfd2795 100644 --- a/backend/tests/integration/common_utils/managers/user.py +++ b/backend/tests/integration/common_utils/managers/user.py @@ -14,7 +14,7 @@ from tests.integration.common_utils.test_models import DATestUser DOMAIN = "test.com" -DEFAULT_PASSWORD = "test" +DEFAULT_PASSWORD = "TestPassword123!" def build_email(name: str) -> str: diff --git a/web/src/app/auth/login/EmailPasswordForm.tsx b/web/src/app/auth/login/EmailPasswordForm.tsx index df474bdca..61b8116db 100644 --- a/web/src/app/auth/login/EmailPasswordForm.tsx +++ b/web/src/app/auth/login/EmailPasswordForm.tsx @@ -9,6 +9,7 @@ import * as Yup from "yup"; import { requestEmailVerification } from "../lib"; import { useState } from "react"; import { Spinner } from "@/components/Spinner"; +import { set } from "lodash"; export function EmailPasswordForm({ isSignup = false, @@ -47,10 +48,12 @@ export function EmailPasswordForm({ ); if (!response.ok) { + setIsWorking(false); const errorDetail = (await response.json()).detail; - let errorMsg = "Unknown error"; - if (errorDetail === "REGISTER_USER_ALREADY_EXISTS") { + if (typeof errorDetail === "object" && errorDetail.reason) { + errorMsg = errorDetail.reason; + } else if (errorDetail === "REGISTER_USER_ALREADY_EXISTS") { errorMsg = "An account already exists with the specified email."; } diff --git a/web/tests/e2e/constants.js b/web/tests/e2e/constants.js index b33de0589..847ebf338 100644 --- a/web/tests/e2e/constants.js +++ b/web/tests/e2e/constants.js @@ -1,4 +1,4 @@ export const TEST_CREDENTIALS = { email: "admin_user@test.com", - password: "test", + password: "TestPassword123!", };