feat: implement Phase 2 stabilization
Some checks failed
CI / lint-and-test (push) Has been cancelled
Some checks failed
CI / lint-and-test (push) Has been cancelled
- Cache Graph API tokens with expiry-aware reuse in graph/auth.py - Add tenacity-based retry/backoff wrapper (utils/http.py) and apply to all Graph/source API calls - Add Pydantic request/response models (models/api.py) and FastAPI query constraints - Add unit tests for event_model, auth and integration tests for API endpoints - Configure ruff linter/formatter in pyproject.toml - Add GitHub Actions CI pipeline (.github/workflows/ci.yml) - Add requirements-dev.txt with pytest, mongomock, httpx, ruff - Clean up typing imports and fix ruff linting across codebase
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
import time
|
||||
import structlog
|
||||
from typing import Optional, Set
|
||||
|
||||
import requests
|
||||
from fastapi import Depends, HTTPException, Header
|
||||
from jose import jwt
|
||||
from jose.jwk import construct
|
||||
|
||||
import structlog
|
||||
from config import (
|
||||
AUTH_ALLOWED_GROUPS,
|
||||
AUTH_ALLOWED_ROLES,
|
||||
AUTH_CLIENT_ID,
|
||||
AUTH_ENABLED,
|
||||
AUTH_TENANT_ID,
|
||||
AUTH_CLIENT_ID,
|
||||
AUTH_ALLOWED_ROLES,
|
||||
AUTH_ALLOWED_GROUPS,
|
||||
)
|
||||
from fastapi import Header, HTTPException
|
||||
from jose import jwt
|
||||
from jose.jwk import construct
|
||||
|
||||
JWKS_CACHE = {"exp": 0, "keys": []}
|
||||
logger = structlog.get_logger("aoc.auth")
|
||||
@@ -35,16 +33,15 @@ def _get_jwks():
|
||||
return keys
|
||||
|
||||
|
||||
def _allowed(claims: dict, allowed_roles: Set[str], allowed_groups: Set[str]) -> bool:
|
||||
def _allowed(claims: dict, allowed_roles: set[str], allowed_groups: set[str]) -> bool:
|
||||
if not allowed_roles and not allowed_groups:
|
||||
return True
|
||||
roles = set(claims.get("roles", []) or claims.get("role", []) or [])
|
||||
groups = set(claims.get("groups", []) or [])
|
||||
if allowed_roles and roles.intersection(allowed_roles):
|
||||
return True
|
||||
if allowed_groups and groups.intersection(allowed_groups):
|
||||
return True
|
||||
return False
|
||||
return bool(
|
||||
(allowed_roles and roles.intersection(allowed_roles))
|
||||
or (allowed_groups and groups.intersection(allowed_groups))
|
||||
)
|
||||
|
||||
|
||||
def _decode_token(token: str, jwks):
|
||||
@@ -72,10 +69,10 @@ def _decode_token(token: str, jwks):
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.warning("Token verification failed", error=str(exc))
|
||||
raise HTTPException(status_code=401, detail="Invalid token")
|
||||
raise HTTPException(status_code=401, detail="Invalid token") from None
|
||||
|
||||
|
||||
def require_auth(authorization: Optional[str] = Header(None)):
|
||||
def require_auth(authorization: str | None = Header(None)):
|
||||
if not AUTH_ENABLED:
|
||||
return {"sub": "anonymous"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user