Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,91 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ..db.session import get_session
|
||||
from ..db.models import Policy
|
||||
from ..services.policy_engine import evaluate_policy
|
||||
from ..services.action_registry import action_capability
|
||||
from ..services.audit_logger import log_event
|
||||
|
||||
router = APIRouter(prefix='/api/policies')
|
||||
DECISIONS = {'allowed', 'approval_required', 'blocked'}
|
||||
RISKS = {'low', 'medium', 'high', 'critical'}
|
||||
|
||||
|
||||
class PolicyUpdate(BaseModel):
|
||||
voice: str
|
||||
manual: str
|
||||
risk: str
|
||||
reason: str
|
||||
|
||||
|
||||
@router.get('')
|
||||
def list_policies(session: Session = Depends(get_session)):
|
||||
return session.exec(select(Policy).order_by(Policy.action, Policy.target)).all()
|
||||
|
||||
|
||||
@router.patch('/{policy_id}')
|
||||
def update_policy(policy_id: str, payload: PolicyUpdate, session: Session = Depends(get_session)):
|
||||
policy = session.get(Policy, policy_id)
|
||||
if not policy:
|
||||
raise HTTPException(status_code=404, detail='Policy not found.')
|
||||
if payload.voice not in DECISIONS or payload.manual not in DECISIONS or payload.risk not in RISKS:
|
||||
raise HTTPException(status_code=422, detail='Invalid policy decision or risk value.')
|
||||
if not payload.reason.strip():
|
||||
raise HTTPException(status_code=422, detail='A policy reason is required.')
|
||||
|
||||
capability = action_capability(policy.action)
|
||||
if capability == 'destructive' and (payload.voice != 'blocked' or payload.manual != 'blocked'):
|
||||
raise HTTPException(status_code=409, detail='Destructive actions remain blocked by the platform guardrail.')
|
||||
|
||||
before = {'voice': policy.voice, 'manual': policy.manual, 'risk': policy.risk, 'reason': policy.reason}
|
||||
policy.voice = payload.voice
|
||||
policy.manual = payload.manual
|
||||
policy.risk = 'critical' if capability == 'destructive' else payload.risk
|
||||
policy.reason = payload.reason.strip()
|
||||
session.add(policy)
|
||||
session.commit()
|
||||
session.refresh(policy)
|
||||
after = {'voice': policy.voice, 'manual': policy.manual, 'risk': policy.risk, 'reason': policy.reason}
|
||||
log_event(
|
||||
session,
|
||||
source='manual',
|
||||
identity='operator',
|
||||
action_id='policy.update',
|
||||
target=policy.id,
|
||||
target_type='policy',
|
||||
risk='medium',
|
||||
policy_decision='allowed',
|
||||
reason='Operator updated policy decisions within enforced platform guardrails.',
|
||||
result='success',
|
||||
before_state=before,
|
||||
after_state=after,
|
||||
redacted_metadata={'protectedGuardrail': capability == 'destructive', 'policyAction': policy.action},
|
||||
)
|
||||
return {
|
||||
'id': policy.id,
|
||||
'action': policy.action,
|
||||
'target': policy.target,
|
||||
'voice': policy.voice,
|
||||
'manual': policy.manual,
|
||||
'risk': policy.risk,
|
||||
'reason': policy.reason,
|
||||
}
|
||||
|
||||
|
||||
@router.post('/evaluate')
|
||||
def evaluate(payload: dict, session: Session = Depends(get_session)):
|
||||
action = str(payload.get('action', '')).strip()
|
||||
target = str(payload.get('target', '')).strip()
|
||||
source = str(payload.get('source', 'manual')).strip() or 'manual'
|
||||
result = evaluate_policy(session, action=action, target=target, source=source)
|
||||
|
||||
return {
|
||||
'decision': result['decision'],
|
||||
'reason': result['reason'],
|
||||
'risk': result['risk'],
|
||||
'policyId': result['policy_id'],
|
||||
}
|
||||
Reference in new issue
Block a user