import json from fastapi import APIRouter, Depends, HTTPException, Request from app.auth import get_current_agent from app.scopes import has_scope_access, ALL_SCOPES from app.models import get_rule, get_all_rules, get_all_ports, add_audit_log, set_rule router = APIRouter(prefix="/api") async def log_audit(request: Request, agent: str, scope: str): ip_address = request.client.host if request.client else "unknown" await add_audit_log(agent, scope, ip_address) @router.get("/rules/{scope}") async def get_scope_rules(scope: str, request: Request, agent: str = Depends(get_current_agent)): if not has_scope_access(agent, scope): await log_audit(request, agent, f"{scope} (FORBIDDEN)") raise HTTPException(status_code=403, detail="Forbidden") await log_audit(request, agent, scope) rule = await get_rule(scope) if not rule: raise HTTPException(status_code=404, detail="Scope not found") return rule @router.get("/ports") async def get_ports_registry(request: Request, agent: str = Depends(get_current_agent)): if not has_scope_access(agent, "infra"): await log_audit(request, agent, "ports (FORBIDDEN)") raise HTTPException(status_code=403, detail="Forbidden: Requires infra scope") await log_audit(request, agent, "ports") ports = await get_all_ports() return ports @router.get("/search") async def search_rules(q: str, request: Request, agent: str = Depends(get_current_agent)): await log_audit(request, agent, f"search (q={q})") all_rules = await get_all_rules() results = {} for scope in ALL_SCOPES: if has_scope_access(agent, scope): content = all_rules.get(scope, {}) # Basic string matching in JSON serialization if q.lower() in json.dumps(content).lower(): results[scope] = content return results @router.patch("/rules/{scope}") async def patch_scope_rules(scope: str, request: Request, agent: str = Depends(get_current_agent)): if not has_scope_access(agent, scope): await log_audit(request, agent, f"{scope} (WRITE FORBIDDEN)") raise HTTPException(status_code=403, detail="Forbidden") body = await request.json() if not isinstance(body, dict): raise HTTPException(status_code=400, detail="Body must be a JSON object") existing = await get_rule(scope) or {} existing.update(body) await set_rule(scope, existing) await log_audit(request, agent, f"{scope} (WRITE)") return {"status": "ok", "scope": scope, "updated_keys": list(body.keys())} @router.get("/context") async def get_agent_context(request: Request, agent: str = Depends(get_current_agent)): await log_audit(request, agent, "context (READ ALL)") all_rules = await get_all_rules() result = {} for scope in ALL_SCOPES: if has_scope_access(agent, scope): result[scope] = all_rules.get(scope, {}) ports = await get_all_ports() return {"agent": agent, "scopes": result, "ports": ports}