Add ruff and mypy to dev dependencies with configuration in pyproject.toml. Add a lint CI job that runs ruff check, ruff format --check, and mypy. Auto-fix import sorting and formatting across the codebase. Exclude alembic/versions from linting (auto-generated migrations). Ignore B008 (FastAPI Depends pattern) and RUF001 (unicode in user-facing strings). 21 ruff lint errors and 50 mypy errors remain for manual review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
36 lines
1.2 KiB
Python
36 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.auth.security import PasswordService
|
|
from app.db.models import User
|
|
|
|
|
|
class AuthService:
|
|
def __init__(self, password_service: PasswordService) -> None:
|
|
self._password_service = password_service
|
|
|
|
async def authenticate_user(
|
|
self,
|
|
db_session: AsyncSession,
|
|
*,
|
|
email: str,
|
|
password: str,
|
|
) -> User | None:
|
|
normalized_email = email.strip().lower()
|
|
if not normalized_email or not password:
|
|
return None
|
|
result = await db_session.execute(select(User).where(User.email == normalized_email))
|
|
user = result.scalar_one_or_none()
|
|
if user is None or not user.is_active:
|
|
return None
|
|
if not self._password_service.verify_password(user.password_hash, password):
|
|
return None
|
|
return user
|
|
|
|
def hash_password(self, password: str) -> str:
|
|
return self._password_service.hash_password(password)
|
|
|
|
def verify_password(self, *, password_hash: str, password: str) -> bool:
|
|
return self._password_service.verify_password(password_hash, password)
|