Files
intercept/utils/authz.py
2026-02-20 17:02:16 +00:00

75 lines
2.1 KiB
Python

"""Authorization helpers for role-based and arming-gated operations."""
from __future__ import annotations
from functools import wraps
from typing import Any, Callable
from flask import jsonify, session
ROLE_LEVELS: dict[str, int] = {
'viewer': 10,
'analyst': 20,
'operator': 30,
'supervisor': 40,
'admin': 50,
}
def current_username() -> str:
"""Get current username from session."""
return str(session.get('username') or 'anonymous')
def current_role() -> str:
"""Get current role from session with safe default."""
role = str(session.get('role') or 'viewer').strip().lower()
return role if role in ROLE_LEVELS else 'viewer'
def has_role(required_role: str) -> bool:
"""Return True if current session role satisfies required role."""
required = ROLE_LEVELS.get(required_role, ROLE_LEVELS['admin'])
actual = ROLE_LEVELS.get(current_role(), ROLE_LEVELS['viewer'])
return actual >= required
def require_role(required_role: str) -> Callable:
"""Decorator enforcing minimum role."""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any):
if not has_role(required_role):
return jsonify({
'status': 'error',
'message': f'{required_role} role required',
'required_role': required_role,
'current_role': current_role(),
}), 403
return func(*args, **kwargs)
return wrapper
return decorator
def require_armed(func: Callable) -> Callable:
"""Decorator enforcing armed state for active actions."""
@wraps(func)
def wrapper(*args: Any, **kwargs: Any):
from utils.drone import get_drone_ops_service
service = get_drone_ops_service()
policy = service.get_policy_state()
if not policy.get('armed'):
return jsonify({
'status': 'error',
'message': 'Action plane is not armed',
'policy': policy,
}), 403
return func(*args, **kwargs)
return wrapper