This commit is contained in:
@@ -134,36 +134,65 @@ async def register_user(user_data: UserCreate, db: AsyncSession = Depends(get_db
|
||||
@app.post("/api/v1/auth/login", response_model=Token)
|
||||
async def login(user_credentials: UserLogin, db: AsyncSession = Depends(get_db)):
|
||||
"""Authenticate user and return token"""
|
||||
print(f"Login attempt: email={user_credentials.email}, username={user_credentials.username}")
|
||||
|
||||
# Проверка валидности входных данных
|
||||
if not user_credentials.email and not user_credentials.username:
|
||||
print("Error: Neither email nor username provided")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Either email or username must be provided",
|
||||
)
|
||||
|
||||
if not user_credentials.password:
|
||||
print("Error: Password not provided")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Password is required",
|
||||
)
|
||||
|
||||
# Определяем, по какому полю ищем пользователя
|
||||
user = None
|
||||
if user_credentials.email:
|
||||
result = await db.execute(select(User).filter(User.email == user_credentials.email))
|
||||
user = result.scalars().first()
|
||||
elif user_credentials.username:
|
||||
result = await db.execute(select(User).filter(User.username == user_credentials.username))
|
||||
user = result.scalars().first()
|
||||
else:
|
||||
try:
|
||||
if user_credentials.email:
|
||||
print(f"Looking up user by email: {user_credentials.email}")
|
||||
result = await db.execute(select(User).filter(User.email == user_credentials.email))
|
||||
user = result.scalars().first()
|
||||
elif user_credentials.username:
|
||||
print(f"Looking up user by username: {user_credentials.username}")
|
||||
result = await db.execute(select(User).filter(User.username == user_credentials.username))
|
||||
user = result.scalars().first()
|
||||
except Exception as e:
|
||||
print(f"Database error during user lookup: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Either email or username must be provided",
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Database error during authentication",
|
||||
)
|
||||
|
||||
# Проверяем наличие пользователя и правильность пароля
|
||||
if not user:
|
||||
print(f"User not found: email={user_credentials.email}, username={user_credentials.username}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
)
|
||||
|
||||
print(f"User found: id={user.id}, email={user.email}")
|
||||
|
||||
# Проверка пароля
|
||||
try:
|
||||
if not verify_password(user_credentials.password, str(user.password_hash)):
|
||||
password_valid = verify_password(user_credentials.password, str(user.password_hash))
|
||||
print(f"Password verification result: {password_valid}")
|
||||
if not password_valid:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
)
|
||||
except Exception:
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
# Если произошла ошибка при проверке пароля, то считаем, что пароль неверный
|
||||
print(f"Password verification error: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
@@ -172,21 +201,25 @@ async def login(user_credentials: UserLogin, db: AsyncSession = Depends(get_db))
|
||||
# Проверка активности аккаунта
|
||||
try:
|
||||
is_active = bool(user.is_active)
|
||||
print(f"User active status: {is_active}")
|
||||
if not is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Account is inactive",
|
||||
)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
# Если произошла ошибка при проверке активности, считаем аккаунт активным
|
||||
print(f"Error checking user active status: {str(e)}")
|
||||
pass
|
||||
|
||||
print("Creating access token...")
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": str(user.id), "email": user.email},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
|
||||
print("Login successful")
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
|
||||
@@ -108,10 +108,21 @@ class UserLogin(BaseModel):
|
||||
@classmethod
|
||||
def validate_password_bytes(cls, v):
|
||||
"""Ensure password doesn't exceed bcrypt's 72-byte limit."""
|
||||
if not v or len(v.strip()) == 0:
|
||||
raise ValueError("Password cannot be empty")
|
||||
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
|
||||
|
||||
@field_validator("username")
|
||||
@classmethod
|
||||
def validate_login_fields(cls, v, info):
|
||||
"""Ensure at least email or username is provided."""
|
||||
email = info.data.get('email')
|
||||
if not email and not v:
|
||||
raise ValueError("Either email or username must be provided")
|
||||
return v
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user