diff --git a/services/user_service/main.py b/services/user_service/main.py index 3888681..c45e8a0 100644 --- a/services/user_service/main.py +++ b/services/user_service/main.py @@ -82,7 +82,18 @@ async def register_user(user_data: UserCreate, db: AsyncSession = Depends(get_db ) # Create new user - hashed_password = get_password_hash(user_data.password) + try: + hashed_password = get_password_hash(user_data.password) + except ValueError as e: + if "password cannot be longer than 72 bytes" in str(e): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Password is too long. Please use a shorter password (max 70 characters)." + ) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Password validation error: {str(e)}" + ) # Используем phone_number как запасной вариант для phone phone = user_data.phone or user_data.phone_number diff --git a/services/user_service/schemas.py b/services/user_service/schemas.py index fec2e08..3d92a44 100644 --- a/services/user_service/schemas.py +++ b/services/user_service/schemas.py @@ -41,7 +41,16 @@ class UserBase(BaseModel): class UserCreate(UserBase): - password: str = Field(..., min_length=8, max_length=100) + password: str = Field(..., min_length=8, max_length=70, description="Password (will be truncated to 72 bytes for bcrypt compatibility)") + + @field_validator("password") + @classmethod + def validate_password_bytes(cls, v): + """Ensure password doesn't exceed bcrypt's 72-byte limit.""" + password_bytes = v.encode('utf-8') + if len(password_bytes) > 72: + raise ValueError("Password is too long when encoded as UTF-8 (max 72 bytes for bcrypt)") + return v class UserUpdate(BaseModel): @@ -93,7 +102,16 @@ class UserResponse(UserBase): class UserLogin(BaseModel): email: Optional[EmailStr] = None username: Optional[str] = None - password: str + password: str = Field(..., max_length=70, description="Password (will be truncated to 72 bytes for bcrypt compatibility)") + + @field_validator("password") + @classmethod + def validate_password_bytes(cls, v): + """Ensure password doesn't exceed bcrypt's 72-byte limit.""" + password_bytes = v.encode('utf-8') + if len(password_bytes) > 72: + raise ValueError("Password is too long when encoded as UTF-8 (max 72 bytes for bcrypt)") + return v class Token(BaseModel): diff --git a/shared/auth.py b/shared/auth.py index d038fd3..98c3a7b 100644 --- a/shared/auth.py +++ b/shared/auth.py @@ -22,12 +22,20 @@ security = HTTPBearer() def verify_password(plain_password: str, hashed_password: str) -> bool: - """Verify password against hash.""" + """Verify password against hash. Apply same truncation as used during hashing.""" + # Apply same truncation logic as during hashing + password_bytes = plain_password.encode('utf-8') + if len(password_bytes) > 72: + plain_password = password_bytes[:72].decode('utf-8', errors='ignore') return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: - """Get password hash.""" + """Get password hash. Truncate password to 72 bytes if necessary for bcrypt compatibility.""" + # bcrypt has a 72-byte limit, so truncate if necessary + password_bytes = password.encode('utf-8') + if len(password_bytes) > 72: + password = password_bytes[:72].decode('utf-8', errors='ignore') return pwd_context.hash(password)