-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsecurity.py
More file actions
75 lines (65 loc) · 3.48 KB
/
security.py
File metadata and controls
75 lines (65 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# ════════════════════════════════════════════════
# security.py — Hybrid Audio API
# v5.1 NDF — Internal API Key Validation + Sonic-3 Alignment
# Author: José Daniel Soto
# ════════════════════════════════════════════════
"""
Minimal, NDF-safe security layer.
• Validates INTERNAL_API_KEY for protected routes
• Respects MODE (DEV/PROD)
• Fail-open when in DEV AND no key set (local development)
• Fail-closed in PROD or when key is defined
• Zero impact on Sonic-3, caching, rotation, or CLI paths
• Safe import for all routes (no side effects)
"""
import os
from fastapi import Header, HTTPException, status
# ────────────────────────────────────────────────
# Load configuration (fully .env-driven)
# ────────────────────────────────────────────────
INTERNAL_API_KEY = os.getenv("INTERNAL_API_KEY", "")
MODE = os.getenv("MODE", "DEV").upper()
FAIL_OPEN = (MODE == "DEV" and not INTERNAL_API_KEY)
FAIL_CLOSED = not FAIL_OPEN
# ────────────────────────────────────────────────
# Internal API-Key Validator
# ────────────────────────────────────────────────
async def verify_internal_key(x_internal_api_key: str = Header(None)) -> None:
"""
Ensures the presence of X-Internal-API-Key for protected endpoints.
Behavior:
• DEV + no INTERNAL_API_KEY → fail-open (no verification)
• Otherwise → must match INTERNAL_API_KEY
"""
if FAIL_OPEN:
return
if not x_internal_api_key:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing X-Internal-API-Key header",
)
if x_internal_api_key != INTERNAL_API_KEY:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid API key",
)
# ────────────────────────────────────────────────
# Security Status Summary (for /health/security)
# ────────────────────────────────────────────────
def summarize_security() -> dict:
return {
"mode": MODE,
"internal_key_set": bool(INTERNAL_API_KEY),
"fail_open": FAIL_OPEN,
"fail_closed": FAIL_CLOSED,
"header_required": FAIL_CLOSED,
}
# ════════════════════════════════════════════════
# Usage Example (FastAPI)
# ════════════════════════════════════════════════
# from fastapi import Depends
# from security import verify_internal_key
#
# @router.get("/internal/secure", dependencies=[Depends(verify_internal_key)])
# async def secure_route():
# return {"ok": True, "message": "Authorized"}