Covers upload (sessions, artifacts, local-md), scripts (deploy/run/delete), settings (get/dataset), memory (CRUD, voting, admin governance), access-requests (create, approve, deny), permissions (grant/revoke/list), metadata (get/save/push), and admin configure+registry endpoints. Each file tests happy path, auth required (401), role enforcement (403), and input validation (422) independently using the seeded_app fixture.
168 lines
5.7 KiB
Python
168 lines
5.7 KiB
Python
"""Tests for admin permissions API — grant, revoke, list."""
|
|
|
|
import pytest
|
|
|
|
|
|
def _auth(token):
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
class TestGrantPermission:
|
|
def test_grant_permission_as_admin(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
resp = c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "sales", "access": "read"},
|
|
headers=_auth(token),
|
|
)
|
|
assert resp.status_code == 201
|
|
data = resp.json()
|
|
assert data["user_id"] == "analyst1"
|
|
assert data["dataset"] == "sales"
|
|
assert data["access"] == "read"
|
|
|
|
def test_grant_analyst_gets_403(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["analyst_token"]
|
|
resp = c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "sales", "access": "read"},
|
|
headers=_auth(token),
|
|
)
|
|
assert resp.status_code == 403
|
|
|
|
def test_grant_requires_auth(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
resp = c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "sales", "access": "read"},
|
|
)
|
|
assert resp.status_code == 401
|
|
|
|
def test_grant_missing_fields_returns_422(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
resp = c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1"}, # missing 'dataset'
|
|
headers=_auth(token),
|
|
)
|
|
assert resp.status_code == 422
|
|
|
|
|
|
class TestRevokePermission:
|
|
def _delete_with_json(self, c, url, body, headers=None):
|
|
"""TestClient.delete() does not support a body — use request() method."""
|
|
import json
|
|
h = {"Content-Type": "application/json"}
|
|
if headers:
|
|
h.update(headers)
|
|
return c.request("DELETE", url, content=json.dumps(body), headers=h)
|
|
|
|
def test_revoke_permission_as_admin(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
|
|
# Grant first
|
|
c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "to_revoke", "access": "read"},
|
|
headers=_auth(token),
|
|
)
|
|
|
|
# Then revoke
|
|
resp = self._delete_with_json(
|
|
c, "/api/admin/permissions",
|
|
{"user_id": "analyst1", "dataset": "to_revoke"},
|
|
headers=_auth(token),
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["revoked"] is True
|
|
|
|
def test_revoke_analyst_gets_403(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["analyst_token"]
|
|
resp = self._delete_with_json(
|
|
c, "/api/admin/permissions",
|
|
{"user_id": "analyst1", "dataset": "sales"},
|
|
headers=_auth(token),
|
|
)
|
|
assert resp.status_code == 403
|
|
|
|
def test_revoke_requires_auth(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
resp = self._delete_with_json(
|
|
c, "/api/admin/permissions",
|
|
{"user_id": "analyst1", "dataset": "sales"},
|
|
)
|
|
assert resp.status_code == 401
|
|
|
|
|
|
class TestListUserPermissions:
|
|
def test_list_user_permissions_as_admin(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
|
|
# Grant a permission
|
|
c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "finance", "access": "read"},
|
|
headers=_auth(token),
|
|
)
|
|
|
|
resp = c.get("/api/admin/permissions/analyst1", headers=_auth(token))
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["user_id"] == "analyst1"
|
|
assert "permissions" in data
|
|
datasets = [p["dataset"] for p in data["permissions"]]
|
|
assert "finance" in datasets
|
|
|
|
def test_list_user_permissions_analyst_gets_403(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["analyst_token"]
|
|
resp = c.get("/api/admin/permissions/analyst1", headers=_auth(token))
|
|
assert resp.status_code == 403
|
|
|
|
def test_list_user_permissions_requires_auth(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
resp = c.get("/api/admin/permissions/analyst1")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
class TestListAllPermissions:
|
|
def test_list_all_permissions_empty(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
resp = c.get("/api/admin/permissions", headers=_auth(token))
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert "permissions" in data
|
|
assert isinstance(data["permissions"], list)
|
|
|
|
def test_list_all_permissions_after_grant(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["admin_token"]
|
|
|
|
c.post(
|
|
"/api/admin/permissions",
|
|
json={"user_id": "analyst1", "dataset": "all_perms_test", "access": "read"},
|
|
headers=_auth(token),
|
|
)
|
|
|
|
resp = c.get("/api/admin/permissions", headers=_auth(token))
|
|
assert resp.status_code == 200
|
|
datasets = [p["dataset"] for p in resp.json()["permissions"]]
|
|
assert "all_perms_test" in datasets
|
|
|
|
def test_list_all_permissions_analyst_gets_403(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
token = seeded_app["analyst_token"]
|
|
resp = c.get("/api/admin/permissions", headers=_auth(token))
|
|
assert resp.status_code == 403
|
|
|
|
def test_list_all_permissions_requires_auth(self, seeded_app):
|
|
c = seeded_app["client"]
|
|
resp = c.get("/api/admin/permissions")
|
|
assert resp.status_code == 401
|