import json from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app.models.workspace_access import WorkspaceAuditLog from app.services.workspace_access import ( PERMISSION_WORKSPACE_ADMIN, ROLE_DOMAIN_ADMIN, ROLE_WORKSPACE_OWNER, require_workspace_permission, role_for_auth_context, ) from app.services.workspace_audit import ( actor_from_auth, audit_log_to_dict, list_workspace_audit_logs, record_workspace_audit_log, sanitize_audit_details, ) from app.services.workspaces import get_or_create_default_workspace def test_workspace_roles_endpoint_lists_permissions(authed_client: TestClient): """Operators can discover the workspace RBAC vocabulary.""" response = authed_client.get("/api/v1/audit/roles") assert response.status_code == 200 roles = {item["role"]: set(item["permissions"]) for item in response.json()["roles"]} assert ROLE_WORKSPACE_OWNER in roles assert ROLE_DOMAIN_ADMIN in roles assert "workspace:admin" in roles[ROLE_WORKSPACE_OWNER] assert "mail_sources:write" in roles[ROLE_DOMAIN_ADMIN] def test_audit_details_sanitize_secret_like_fields(): """Audit helper redacts nested secret-shaped fields.""" details = sanitize_audit_details( { "name": "mailbox", "password": "super-secret", "nested": {"refresh_token": "token-secret"}, } ) assert details["name"] == "mailbox" assert details["password"] == "[redacted]" assert details["nested"]["refresh_token"] == "[redacted]" assert "super-secret" not in json.dumps(details) assert "token-secret" not in json.dumps(details) def test_workspace_permission_denial_and_actor_variants(db_session: Session): """RBAC and audit helpers cover fallback actors and denial paths.""" workspace = get_or_create_default_workspace(db_session) assert role_for_auth_context({"auth_type": "unexpected"}) == "auditor" try: require_workspace_permission({"auth_type": "unexpected"}, PERMISSION_WORKSPACE_ADMIN) except Exception as exc: # pylint: disable=broad-exception-caught assert getattr(exc, "status_code", None) == 403 else: raise AssertionError("permission denial was not raised") assert ( actor_from_auth({"auth_type": "jwt", "payload": {"sub": "user-123"}})["actor_id"] == "user-123" ) assert actor_from_auth({"auth_type": "api_token", "token_id": 42})["actor_id"] == "42" assert sanitize_audit_details({"object": object()})["object"].startswith("