130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
import pytest
|
|
from rest_framework import status
|
|
|
|
from infrasynth.audit.models import APIInteractionLog, ModelChangeLog, SecurityEvent
|
|
|
|
|
|
@pytest.fixture
|
|
def change_log(db, user):
|
|
return ModelChangeLog.objects.create(
|
|
model_label="infrasynth_security.Role",
|
|
object_id="1",
|
|
action="create",
|
|
changes={"name": [None, "Test"]},
|
|
actor=user,
|
|
request_id="req-1",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def api_log(db, user):
|
|
return APIInteractionLog.objects.create(
|
|
method="GET",
|
|
path="/api/features/",
|
|
status_code=200,
|
|
actor=user,
|
|
duration_ms=12,
|
|
request_id="req-2",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def security_event(db, user):
|
|
return SecurityEvent.objects.create(
|
|
event_type="login_failed",
|
|
actor=user,
|
|
ip_address="127.0.0.1",
|
|
metadata={"reason": "bad_password"},
|
|
request_id="req-3",
|
|
)
|
|
|
|
|
|
class TestModelChangeLogEndpoints:
|
|
def test_list_changes(self, authenticated_client, change_log):
|
|
resp = authenticated_client.get("/api/audit/changes/", {"request_id": "req-1"})
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["count"] == 1
|
|
assert resp.json()["results"][0]["action"] == "create"
|
|
|
|
def test_retrieve_change(self, authenticated_client, change_log):
|
|
resp = authenticated_client.get(f"/api/audit/changes/{change_log.pk}/")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["changes"] == {"name": [None, "Test"]}
|
|
|
|
def test_requires_auth(self, api_client, change_log):
|
|
resp = api_client.get("/api/audit/changes/")
|
|
assert resp.status_code == status.HTTP_401_UNAUTHORIZED
|
|
|
|
def test_list_does_not_allow_create(self, authenticated_client):
|
|
resp = authenticated_client.post("/api/audit/changes/", {"model_label": "x"}, format="json")
|
|
assert resp.status_code in (
|
|
status.HTTP_405_METHOD_NOT_ALLOWED,
|
|
status.HTTP_403_FORBIDDEN,
|
|
)
|
|
|
|
|
|
class TestAPIInteractionLogEndpoints:
|
|
def test_list_api_logs(self, authenticated_client, api_log):
|
|
resp = authenticated_client.get("/api/audit/api-logs/")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["count"] == 1
|
|
assert resp.json()["results"][0]["method"] == "GET"
|
|
|
|
def test_retrieve_api_log(self, authenticated_client, api_log):
|
|
resp = authenticated_client.get(f"/api/audit/api-logs/{api_log.pk}/")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["path"] == "/api/features/"
|
|
|
|
def test_requires_auth(self, api_client, api_log):
|
|
resp = api_client.get("/api/audit/api-logs/")
|
|
assert resp.status_code == status.HTTP_401_UNAUTHORIZED
|
|
|
|
|
|
class TestSecurityEventEndpoints:
|
|
def test_list_security_events(self, authenticated_client, security_event):
|
|
resp = authenticated_client.get("/api/audit/security-events/")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["count"] == 1
|
|
assert resp.json()["results"][0]["event_type"] == "login_failed"
|
|
|
|
def test_retrieve_security_event(self, authenticated_client, security_event):
|
|
resp = authenticated_client.get(f"/api/audit/security-events/{security_event.pk}/")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
assert resp.json()["metadata"] == {"reason": "bad_password"}
|
|
|
|
def test_requires_auth(self, api_client, security_event):
|
|
resp = api_client.get("/api/audit/security-events/")
|
|
assert resp.status_code == status.HTTP_401_UNAUTHORIZED
|
|
|
|
|
|
class TestFiltering:
|
|
def test_filter_by_model_label(self, authenticated_client, db):
|
|
ModelChangeLog.objects.create(
|
|
model_label="infrasynth_security.Role",
|
|
object_id="10",
|
|
action="create",
|
|
changes={},
|
|
request_id="x1",
|
|
)
|
|
ModelChangeLog.objects.create(
|
|
model_label="infrasynth_security.Grant",
|
|
object_id="99",
|
|
action="create",
|
|
changes={},
|
|
request_id="x2",
|
|
)
|
|
resp = authenticated_client.get("/api/audit/changes/", {"model_label": "infrasynth_security.Role"})
|
|
assert resp.json()["count"] == 1
|
|
|
|
def test_filter_by_action(self, authenticated_client, change_log):
|
|
ModelChangeLog.objects.create(
|
|
model_label="infrasynth_security.Role",
|
|
object_id="77",
|
|
action="delete",
|
|
changes={},
|
|
request_id="req-4",
|
|
)
|
|
resp = authenticated_client.get("/api/audit/changes/", {"action": "create"})
|
|
assert all(r["action"] == "create" for r in resp.json()["results"])
|
|
resp = authenticated_client.get("/api/audit/changes/", {"action": "delete"})
|
|
assert all(r["action"] == "delete" for r in resp.json()["results"])
|