feat: add MSP workspace operator views
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
"""MSP operator endpoints."""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.security import require_admin_auth
|
||||
from app.models.workspace import Workspace
|
||||
from app.services.workspace_access import (
|
||||
PERMISSION_AUDIT_READ,
|
||||
PERMISSION_WORKSPACE_ADMIN,
|
||||
require_workspace_permission,
|
||||
)
|
||||
from app.services.workspace_audit import record_workspace_audit_log
|
||||
from app.services.workspace_operator import (
|
||||
list_workspace_operator_summaries,
|
||||
retention_to_dict,
|
||||
workspace_operator_summary,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class OperatorWorkspacesResponse(BaseModel):
|
||||
"""Cross-workspace operator summaries."""
|
||||
|
||||
workspaces: List[Dict[str, Any]]
|
||||
|
||||
|
||||
class WorkspaceRetentionUpdate(BaseModel):
|
||||
"""Workspace retention controls."""
|
||||
|
||||
aggregate_reports_days: int = Field(..., ge=1, le=3650)
|
||||
forensic_reports_days: int = Field(..., ge=1, le=3650)
|
||||
tls_reports_days: int = Field(..., ge=1, le=3650)
|
||||
|
||||
|
||||
class WorkspaceRetentionResponse(BaseModel):
|
||||
"""Updated workspace retention response."""
|
||||
|
||||
workspace: Dict[str, Any]
|
||||
retention: Dict[str, int]
|
||||
|
||||
|
||||
def _workspace_or_404(db: Session, workspace_id: int) -> Workspace:
|
||||
workspace = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
if workspace is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Workspace {workspace_id} not found",
|
||||
)
|
||||
return workspace
|
||||
|
||||
|
||||
@router.get("/workspaces", response_model=OperatorWorkspacesResponse)
|
||||
async def list_operator_workspaces(
|
||||
db: Session = Depends(get_db),
|
||||
_auth: dict = Depends(require_admin_auth),
|
||||
) -> OperatorWorkspacesResponse:
|
||||
"""Return safe cross-workspace health, drift, import, and retention summaries."""
|
||||
require_workspace_permission(_auth, PERMISSION_AUDIT_READ)
|
||||
return {"workspaces": list_workspace_operator_summaries(db)}
|
||||
|
||||
|
||||
@router.get("/workspaces/{workspace_id}", response_model=Dict[str, Any])
|
||||
async def get_operator_workspace(
|
||||
workspace_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
_auth: dict = Depends(require_admin_auth),
|
||||
) -> Dict[str, Any]:
|
||||
"""Return one workspace operator summary."""
|
||||
require_workspace_permission(_auth, PERMISSION_AUDIT_READ)
|
||||
return workspace_operator_summary(db, _workspace_or_404(db, workspace_id))
|
||||
|
||||
|
||||
@router.put("/workspaces/{workspace_id}/retention", response_model=WorkspaceRetentionResponse)
|
||||
async def update_workspace_retention(
|
||||
workspace_id: int,
|
||||
payload: WorkspaceRetentionUpdate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
_auth: dict = Depends(require_admin_auth),
|
||||
) -> WorkspaceRetentionResponse:
|
||||
"""Update workspace retention controls and audit the change."""
|
||||
require_workspace_permission(_auth, PERMISSION_WORKSPACE_ADMIN)
|
||||
workspace = _workspace_or_404(db, workspace_id)
|
||||
old_retention = retention_to_dict(workspace)
|
||||
workspace.report_retention_days = payload.aggregate_reports_days
|
||||
workspace.forensic_retention_days = payload.forensic_reports_days
|
||||
workspace.tls_report_retention_days = payload.tls_reports_days
|
||||
new_retention = retention_to_dict(workspace)
|
||||
record_workspace_audit_log(
|
||||
db,
|
||||
workspace=workspace,
|
||||
action="workspace.retention_updated",
|
||||
entity_type="workspace",
|
||||
entity_id=workspace.id,
|
||||
entity_name=workspace.slug,
|
||||
details={"old": old_retention, "new": new_retention},
|
||||
auth_context=_auth,
|
||||
request=request,
|
||||
)
|
||||
db.commit()
|
||||
db.refresh(workspace)
|
||||
return {
|
||||
"workspace": {"id": workspace.id, "slug": workspace.slug, "name": workspace.name},
|
||||
"retention": retention_to_dict(workspace),
|
||||
}
|
||||
Reference in New Issue
Block a user