Files
security-alert-center/backend/app/auth/jwt_auth.py
T
PapaTramp 56c469ddbd feat: multi-user UI login with admin and monitor roles
Add sac_users table, JWT roles, settings/users API guarded for admin, Users page in UI, and sac_manage_user.py CLI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 09:27:14 +10:00

71 lines
2.4 KiB
Python

from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from app.config import get_settings
from app.models.user import USER_ROLE_ADMIN
bearer_scheme = HTTPBearer(auto_error=False)
@dataclass(frozen=True)
class CurrentUser:
username: str
role: str
@property
def is_admin(self) -> bool:
return self.role == USER_ROLE_ADMIN
def create_access_token(subject: str, role: str) -> str:
settings = get_settings()
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.jwt_expire_minutes)
payload = {"sub": subject, "role": role, "exp": expire}
return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm)
def _decode_current_user(token: str) -> CurrentUser:
settings = get_settings()
try:
payload = jwt.decode(
token,
settings.jwt_secret,
algorithms=[settings.jwt_algorithm],
)
username = payload.get("sub")
if not username:
raise HTTPException(status_code=401, detail="Invalid token")
role = str(payload.get("role") or USER_ROLE_ADMIN)
return CurrentUser(username=str(username), role=role)
except JWTError as exc:
raise HTTPException(status_code=401, detail="Invalid token") from exc
def verify_access_token(token: str) -> str:
"""Проверка JWT (в т.ч. query-параметр для SSE). Возвращает username."""
return _decode_current_user(token).username
def get_current_user(
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
) -> CurrentUser:
if credentials is None or credentials.scheme.lower() != "bearer":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
)
return _decode_current_user(credentials.credentials)
def require_admin(user: CurrentUser = Depends(get_current_user)) -> CurrentUser:
if not user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin access required",
)
return user