feat: service-level role gating for privacy-sensitive services (Option A)
All checks were successful
CI / lint-and-test (push) Successful in 25s
All checks were successful
CI / lint-and-test (push) Successful in 25s
- Add PRIVACY_SERVICES and PRIVACY_SERVICE_ROLES config variables
- Add user_can_access_privacy_services(claims) helper in auth.py
- /api/events filters out privacy services for users without required roles
- /api/filter-options excludes privacy services from dropdown options
- /api/ask excludes privacy services from NLQ queries
- /api/events/{id}/explain returns 403 for privacy events if unauthorized
- Teams added to default noisy service exclusion (frontend + backend)
- Update .env.example with privacy config documentation
- Add tests for event filtering, filter-options exclusion, and explain 403
This commit is contained in:
@@ -8,6 +8,8 @@ from config import (
|
||||
AUTH_CLIENT_ID,
|
||||
AUTH_ENABLED,
|
||||
AUTH_TENANT_ID,
|
||||
PRIVACY_SERVICE_ROLES,
|
||||
PRIVACY_SERVICES,
|
||||
)
|
||||
from fastapi import Header, HTTPException
|
||||
from jwt import ExpiredSignatureError, InvalidTokenError, decode
|
||||
@@ -82,6 +84,14 @@ def _decode_token(token: str, jwks):
|
||||
raise HTTPException(status_code=401, detail=f"Invalid token ({type(exc).__name__})") from None
|
||||
|
||||
|
||||
def user_can_access_privacy_services(claims: dict) -> bool:
|
||||
"""Check if the user has roles that grant access to privacy-sensitive services."""
|
||||
if not PRIVACY_SERVICES or not PRIVACY_SERVICE_ROLES:
|
||||
return True
|
||||
user_roles = set(claims.get("roles", []) or claims.get("role", []) or [])
|
||||
return bool(user_roles.intersection(PRIVACY_SERVICE_ROLES))
|
||||
|
||||
|
||||
def require_auth(authorization: str | None = Header(None)):
|
||||
if not AUTH_ENABLED:
|
||||
return {"sub": "anonymous"}
|
||||
|
||||
Reference in New Issue
Block a user