Start integrating the onboarding flow with backend 1
This commit is contained in:
@@ -7,7 +7,7 @@ FIXED VERSION - Consistent password hashing using passlib
|
||||
import re
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, Dict, Any, List
|
||||
import redis.asyncio as redis
|
||||
from fastapi import HTTPException, status
|
||||
import structlog
|
||||
@@ -36,6 +36,9 @@ class SecurityManager:
|
||||
if len(password) < settings.PASSWORD_MIN_LENGTH:
|
||||
return False
|
||||
|
||||
if len(password) > 128: # Max length from Pydantic schema
|
||||
return False
|
||||
|
||||
if settings.PASSWORD_REQUIRE_UPPERCASE and not re.search(r'[A-Z]', password):
|
||||
return False
|
||||
|
||||
@@ -50,6 +53,31 @@ class SecurityManager:
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_password_validation_errors(password: str) -> List[str]:
|
||||
"""Get detailed password validation errors for better UX"""
|
||||
errors = []
|
||||
|
||||
if len(password) < settings.PASSWORD_MIN_LENGTH:
|
||||
errors.append(f"Password must be at least {settings.PASSWORD_MIN_LENGTH} characters long")
|
||||
|
||||
if len(password) > 128:
|
||||
errors.append("Password cannot exceed 128 characters")
|
||||
|
||||
if settings.PASSWORD_REQUIRE_UPPERCASE and not re.search(r'[A-Z]', password):
|
||||
errors.append("Password must contain at least one uppercase letter")
|
||||
|
||||
if settings.PASSWORD_REQUIRE_LOWERCASE and not re.search(r'[a-z]', password):
|
||||
errors.append("Password must contain at least one lowercase letter")
|
||||
|
||||
if settings.PASSWORD_REQUIRE_NUMBERS and not re.search(r'\d', password):
|
||||
errors.append("Password must contain at least one number")
|
||||
|
||||
if settings.PASSWORD_REQUIRE_SYMBOLS and not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
||||
errors.append("Password must contain at least one symbol (!@#$%^&*(),.?\":{}|<>)")
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def hash_password(password: str) -> str:
|
||||
"""Hash password using passlib bcrypt - FIXED"""
|
||||
|
||||
@@ -50,6 +50,10 @@ class EnhancedAuthService:
|
||||
if existing_user:
|
||||
raise DuplicateRecordError("User with this email already exists")
|
||||
|
||||
# Validate password strength
|
||||
if not SecurityManager.validate_password(user_data.password):
|
||||
raise ValueError("Password does not meet security requirements")
|
||||
|
||||
# Create user data
|
||||
user_role = user_data.role if user_data.role else "user"
|
||||
hashed_password = SecurityManager.hash_password(user_data.password)
|
||||
@@ -446,6 +450,13 @@ class EnhancedAuthService:
|
||||
detail="Invalid old password"
|
||||
)
|
||||
|
||||
# Validate new password strength
|
||||
if not SecurityManager.validate_password(new_password):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="New password does not meet security requirements"
|
||||
)
|
||||
|
||||
# Hash new password and update
|
||||
new_hashed_password = SecurityManager.hash_password(new_password)
|
||||
await user_repo.update(user_id, {"hashed_password": new_hashed_password})
|
||||
|
||||
Reference in New Issue
Block a user