from collections.abc import Callable from fastapi import Depends, Header, status from fastapi.security import OAuth2PasswordBearer from jwt import InvalidTokenError from sqlalchemy.orm import Session from app.core.config import settings from app.core.errors import raise_api_error from app.core.security import decode_access_token from app.db.session import get_db from app.services.log_service import LogService from app.services.service_control import ServiceControlService from app.services.server_runtime import ServerRuntimeService from app.services.telegram_service import TelegramService from app.models.user import User oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login", auto_error=False) def get_current_user( db: Session = Depends(get_db), token: str | None = Depends(oauth2_scheme), ) -> User: if not token: raise_api_error( status_code=status.HTTP_401_UNAUTHORIZED, code="unauthorized", message="Authentication required.", ) try: payload = decode_access_token(token) user_id = int(payload["sub"]) token_version = int(payload["ver"]) except (InvalidTokenError, KeyError, ValueError): raise_api_error( status_code=status.HTTP_401_UNAUTHORIZED, code="invalid_token", message="Authentication token is invalid.", ) user = db.get(User, user_id) if user is None: raise_api_error( status_code=status.HTTP_401_UNAUTHORIZED, code="user_not_found", message="Authenticated user does not exist.", ) # Reject tokens from older login sessions after logout or auth-relevant # user updates without introducing per-token server storage. if user.token_version != token_version: raise_api_error( status_code=status.HTTP_401_UNAUTHORIZED, code="invalid_token", message="Authentication token is invalid.", ) if not user.is_active: raise_api_error( status_code=status.HTTP_403_FORBIDDEN, code="user_inactive", message="User account is inactive.", ) return user def require_roles(*allowed_roles: str) -> Callable[[User], User]: def dependency(current_user: User = Depends(get_current_user)) -> User: if current_user.role not in allowed_roles: raise_api_error( status_code=status.HTTP_403_FORBIDDEN, code="forbidden", message="Insufficient permissions.", ) return current_user return dependency def get_server_runtime_service() -> ServerRuntimeService: return ServerRuntimeService() def get_service_control_service() -> ServiceControlService: return ServiceControlService() def get_log_service() -> LogService: return LogService() def get_telegram_service() -> TelegramService: return TelegramService() def require_telegram_bot_secret( bot_secret: str | None = Header(default=None, alias="X-Telegram-Bot-Secret"), ) -> str: if not settings.telegram_bot_api_secret: raise_api_error( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, code="telegram_bot_secret_unavailable", message="Telegram bot integration is not configured.", ) if bot_secret != settings.telegram_bot_api_secret: raise_api_error( status_code=status.HTTP_401_UNAUTHORIZED, code="invalid_bot_secret", message="Telegram bot authentication failed.", ) return bot_secret