agromatrix: enforce mentor auth and expose shared-memory review via gateway
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.responses import Response
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal, Optional, Dict, Any, List
|
||||
@@ -10,6 +10,7 @@ import yaml
|
||||
import httpx
|
||||
import logging
|
||||
import hashlib
|
||||
import hmac
|
||||
import time # For latency metrics
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
@@ -888,6 +889,12 @@ CLAN_RUNTIME_CONSENT_EVENT_SCHEMA_PATH = os.getenv(
|
||||
NEO4J_URI = os.getenv("NEO4J_BOLT_URL", "bolt://neo4j:7687")
|
||||
NEO4J_USER = os.getenv("NEO4J_USER", "neo4j")
|
||||
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD", "DaarionNeo4j2026!")
|
||||
AGROMATRIX_REVIEW_AUTH_MODE = os.getenv("AGROMATRIX_REVIEW_AUTH_MODE", "bearer").strip().lower()
|
||||
AGROMATRIX_REVIEW_BEARER_TOKENS = [
|
||||
part.strip()
|
||||
for part in os.getenv("AGROMATRIX_REVIEW_BEARER_TOKENS", "").replace(";", ",").split(",")
|
||||
if part.strip()
|
||||
]
|
||||
|
||||
# HTTP client for backend services
|
||||
http_client: Optional[httpx.AsyncClient] = None
|
||||
@@ -1235,6 +1242,30 @@ class SharedMemoryReviewRequest(BaseModel):
|
||||
note: Optional[str] = None
|
||||
|
||||
|
||||
def _require_agromatrix_review_auth(request: Request) -> None:
|
||||
mode = AGROMATRIX_REVIEW_AUTH_MODE
|
||||
if mode in {"off", "none", "disabled"}:
|
||||
return
|
||||
|
||||
if mode != "bearer":
|
||||
raise HTTPException(status_code=500, detail=f"Unsupported AGROMATRIX_REVIEW_AUTH_MODE={mode}")
|
||||
|
||||
if not AGROMATRIX_REVIEW_BEARER_TOKENS:
|
||||
logger.error("AGROMATRIX_REVIEW_AUTH_MODE=bearer but AGROMATRIX_REVIEW_BEARER_TOKENS is empty")
|
||||
raise HTTPException(status_code=503, detail="Review auth is not configured")
|
||||
|
||||
auth_header = request.headers.get("Authorization", "")
|
||||
if not auth_header.startswith("Bearer "):
|
||||
raise HTTPException(status_code=401, detail="Missing Bearer token")
|
||||
|
||||
token = auth_header[len("Bearer ") :].strip()
|
||||
if not token:
|
||||
raise HTTPException(status_code=401, detail="Empty Bearer token")
|
||||
|
||||
if not any(hmac.compare_digest(token, candidate) for candidate in AGROMATRIX_REVIEW_BEARER_TOKENS):
|
||||
raise HTTPException(status_code=403, detail="Invalid mentor token")
|
||||
|
||||
|
||||
|
||||
|
||||
# =========================================================================
|
||||
@@ -2889,8 +2920,10 @@ async def agromatrix_shared_pending(limit: int = 50):
|
||||
|
||||
|
||||
@app.post("/v1/agromatrix/shared-memory/review")
|
||||
async def agromatrix_shared_review(req: SharedMemoryReviewRequest):
|
||||
async def agromatrix_shared_review(req: SharedMemoryReviewRequest, request: Request):
|
||||
"""Approve or reject a pending shared agronomy memory case."""
|
||||
_require_agromatrix_review_auth(request)
|
||||
|
||||
if not MEMORY_RETRIEVAL_AVAILABLE or not memory_retrieval:
|
||||
raise HTTPException(status_code=503, detail="Memory retrieval not available")
|
||||
if not hasattr(memory_retrieval, "review_shared_pending_case"):
|
||||
|
||||
Reference in New Issue
Block a user