klg-asutk-app/backend/app/api/deps.py
Yuriy aa052763f6 Безопасность и качество: 8 исправлений + обновления
- .env.example: полный шаблон, защита секретов
- .gitignore: явное исключение .env.* и секретов
- layout.tsx: XSS — заменён dangerouslySetInnerHTML на next/script для SW
- ESLint: no-console error (allow warn/error), ignore scripts/
- scripts/remove-console-logs.js: очистка console.log без glob
- backend/routes/modules: README с планом рефакторинга крупных файлов
- SECURITY.md: гид по секретам, XSS, CORS, auth, линту
- .husky/pre-commit: запуск npm run lint

+ прочие правки приложения и бэкенда

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:29:16 +03:00

75 lines
2.1 KiB
Python

"""
FastAPI dependencies — auth, DB, roles.
Supports both DEV mode and Keycloak OIDC.
"""
import os
from fastapi import Depends, HTTPException, Header
from sqlalchemy.orm import Session
from app.db.session import SessionLocal
from app.api.oidc import verify_oidc_token, extract_user_from_claims
ENABLE_DEV_AUTH = os.getenv("ENABLE_DEV_AUTH", "true").lower() == "true"
DEV_TOKEN = os.getenv("DEV_TOKEN", "dev")
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Dev user fallback
DEV_USER = {
"id": "dev-user-001",
"email": "admin@klg.refly.ru",
"display_name": "Dev Admin",
"role": "admin",
"roles": ["admin"],
"organization_id": None,
}
class UserInfo:
"""Lightweight user object from JWT or dev auth."""
def __init__(self, data: dict):
self.id = data.get("id", "")
self.email = data.get("email", "")
self.display_name = data.get("display_name", "")
self.role = data.get("role", "operator_user")
self.roles = data.get("roles", [])
self.organization_id = data.get("organization_id")
def get_current_user(authorization: str = Header(default="")) -> UserInfo:
"""Extract user from Authorization header. Supports DEV and OIDC modes."""
token = authorization.replace("Bearer ", "").strip()
# DEV mode
if ENABLE_DEV_AUTH and token == DEV_TOKEN:
return UserInfo(DEV_USER)
# OIDC mode
if token:
claims = verify_oidc_token(token)
if claims:
return UserInfo(extract_user_from_claims(claims))
# No valid auth
if not ENABLE_DEV_AUTH:
raise HTTPException(status_code=401, detail="Not authenticated")
# Fallback to dev for convenience
return UserInfo(DEV_USER)
def require_roles(*allowed_roles: str):
"""Dependency that checks user has one of allowed roles."""
def checker(user: UserInfo = Depends(get_current_user)):
if user.role in allowed_roles or "admin" in user.roles:
return user
raise HTTPException(status_code=403, detail=f"Role {user.role} not in {allowed_roles}")
return checker