agromatrix: enforce mentor auth and expose shared-memory review via gateway

This commit is contained in:
NODA1 System
2026-02-21 13:17:59 +01:00
parent 68ac8fa355
commit f44e920486
4 changed files with 152 additions and 7 deletions

View File

@@ -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"):