162 lines
5.3 KiB
Python
162 lines
5.3 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.alert import AlertHistory
|
|
from app.models.domain import Domain
|
|
from app.models.mail_source import MailSource
|
|
from app.models.mail_source_import import MailSourceImport
|
|
from app.models.report import DMARCReport
|
|
from app.models.workspace import Workspace
|
|
from app.models.workspace_access import WorkspaceAuditLog
|
|
|
|
|
|
def _workspace(db_session: Session, slug: str, name: str) -> Workspace:
|
|
workspace = Workspace(slug=slug, name=name, active=True)
|
|
db_session.add(workspace)
|
|
db_session.flush()
|
|
return workspace
|
|
|
|
|
|
def test_operator_view_summarizes_each_workspace(
|
|
authed_client: TestClient,
|
|
db_session: Session,
|
|
):
|
|
"""MSP operators can see health, import, alert, drift, and retention summaries."""
|
|
now = datetime.utcnow()
|
|
alpha = _workspace(db_session, "alpha", "Alpha Client")
|
|
beta = _workspace(db_session, "beta", "Beta Client")
|
|
alpha_domain = Domain(
|
|
workspace_id=alpha.id,
|
|
name="alpha.example",
|
|
active=True,
|
|
verified=True,
|
|
)
|
|
beta_domain = Domain(
|
|
workspace_id=beta.id,
|
|
name="beta.example",
|
|
active=True,
|
|
verified=False,
|
|
)
|
|
alpha_source = MailSource(
|
|
workspace_id=alpha.id,
|
|
name="Alpha inbox",
|
|
method="IMAP",
|
|
enabled=True,
|
|
)
|
|
beta_source = MailSource(
|
|
workspace_id=beta.id,
|
|
name="Beta inbox",
|
|
method="IMAP",
|
|
enabled=False,
|
|
)
|
|
db_session.add_all([alpha_domain, beta_domain, alpha_source, beta_source])
|
|
db_session.flush()
|
|
db_session.add_all(
|
|
[
|
|
DMARCReport(
|
|
domain_id=alpha_domain.id,
|
|
report_id="alpha-report",
|
|
org_name="Google",
|
|
begin_date=1,
|
|
end_date=2,
|
|
),
|
|
MailSourceImport(
|
|
mail_source_id=alpha_source.id,
|
|
trigger="manual",
|
|
status="success",
|
|
finished_at=now,
|
|
),
|
|
MailSourceImport(
|
|
mail_source_id=beta_source.id,
|
|
trigger="manual",
|
|
status="failed",
|
|
finished_at=now,
|
|
),
|
|
AlertHistory(
|
|
fingerprint="beta-alert",
|
|
rule="missing_reports",
|
|
severity="warning",
|
|
domain="beta.example",
|
|
title="Missing reports",
|
|
detail="No reports received",
|
|
is_active=True,
|
|
),
|
|
WorkspaceAuditLog(
|
|
workspace_id=beta.id,
|
|
actor_type="api_key",
|
|
action="mail_source.updated",
|
|
entity_type="mail_source",
|
|
entity_name="Beta inbox",
|
|
created_at=now - timedelta(days=1),
|
|
),
|
|
]
|
|
)
|
|
db_session.commit()
|
|
|
|
response = authed_client.get("/api/v1/operator/workspaces")
|
|
|
|
assert response.status_code == 200
|
|
by_slug = {item["workspace"]["slug"]: item for item in response.json()["workspaces"]}
|
|
assert by_slug["alpha"]["health"]["status"] == "healthy"
|
|
assert by_slug["alpha"]["reports"]["aggregate_total"] == 1
|
|
assert by_slug["alpha"]["mail_sources"]["last_import_status"] == "success"
|
|
assert by_slug["beta"]["health"]["status"] == "critical"
|
|
assert by_slug["beta"]["health"]["active_alerts"] == 1
|
|
assert by_slug["beta"]["health"]["failed_imports_7d"] == 1
|
|
assert by_slug["beta"]["health"]["drift_events_7d"] == 1
|
|
assert by_slug["beta"]["recent_drift"][0]["action"] == "mail_source.updated"
|
|
|
|
|
|
def test_operator_view_does_not_include_inactive_workspaces(
|
|
authed_client: TestClient,
|
|
db_session: Session,
|
|
):
|
|
"""Inactive workspaces are not listed in the cross-workspace operator view."""
|
|
_workspace(db_session, "active", "Active Client")
|
|
inactive = Workspace(slug="inactive", name="Inactive Client", active=False)
|
|
db_session.add(inactive)
|
|
db_session.commit()
|
|
|
|
response = authed_client.get("/api/v1/operator/workspaces")
|
|
|
|
assert response.status_code == 200
|
|
slugs = {item["workspace"]["slug"] for item in response.json()["workspaces"]}
|
|
assert slugs == {"active"}
|
|
|
|
|
|
def test_workspace_retention_update_is_audited(
|
|
authed_client: TestClient,
|
|
db_session: Session,
|
|
):
|
|
"""Operators can update workspace retention controls with an audit record."""
|
|
workspace = _workspace(db_session, "client", "Client")
|
|
db_session.commit()
|
|
|
|
response = authed_client.put(
|
|
f"/api/v1/operator/workspaces/{workspace.id}/retention",
|
|
json={
|
|
"aggregate_reports_days": 730,
|
|
"forensic_reports_days": 120,
|
|
"tls_reports_days": 365,
|
|
},
|
|
headers={"x-forwarded-for": "203.0.113.9"},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
db_session.refresh(workspace)
|
|
assert workspace.report_retention_days == 730
|
|
assert workspace.forensic_retention_days == 120
|
|
assert workspace.tls_report_retention_days == 365
|
|
assert response.json()["retention"]["aggregate_reports_days"] == 730
|
|
|
|
audit = (
|
|
db_session.query(WorkspaceAuditLog)
|
|
.filter(WorkspaceAuditLog.action == "workspace.retention_updated")
|
|
.one()
|
|
)
|
|
assert audit.workspace_id == workspace.id
|
|
assert audit.ip_address == "203.0.113.9"
|
|
assert "730" in (audit.details or "")
|