from contextlib import contextmanager from unittest import mock import pytest from django.contrib.auth import get_user_model from django.core.cache import cache from django.db import transaction from django.dispatch import receiver from django.test import override_settings from rest_framework.test import APIClient from infrasynth.audit.models import ModelChangeLog, SecurityEvent from infrasynth.audit.signals import security_event_occurred from infrasynth.features.models import FeatureFlag, FeatureFlagOverride from infrasynth.features.registry import FeatureRegistry from infrasynth.features.services import FeatureService from infrasynth.security.models import Grant, Revoke, Role from infrasynth.security.services import AuthorizationService from infrasynth.webhooks.models import ( OutboundDelivery, OutboundEndpoint, OutboundSubscription, ) from infrasynth.webhooks.registry import EventRegistry from infrasynth.webhooks.signals import outbound_delivery_succeeded UserModel = get_user_model() pytestmark = pytest.mark.django_db @contextmanager def _emit_and_run_commit_hooks(): with transaction.atomic(): yield connection = transaction.get_connection() pending = list(connection.run_on_commit) connection.run_on_commit = [] for entry in pending: func = entry[1] func() @override_settings(CELERY_TASK_ALWAYS_EAGER=True) def test_e2e_login_permission_feature_webhook_audit(user, clean_feature_registry): authz = AuthorizationService() fs = FeatureService() # a. Force-authenticate the user and verify the session check endpoint client = APIClient() client.force_authenticate(user=user) resp = client.get("/api/auth/check/") assert resp.status_code == 200 assert resp.json()["id"] == user.pk # b. Create Role with specific permissions and assign to user role = Role.objects.create( name="Webhook Manager", slug="webhook-manager", permissions=["webhooks.manage_endpoints"], ) role.users.add(user) # c. Create Grant for an additional permission Grant.objects.create(user=user, codename="audit.view_logs") # d. Verify permission resolution via AuthorizationService assert authz.has_permission(user, "webhooks.manage_endpoints") is True assert authz.has_permission(user, "audit.view_logs") is True assert authz.has_permission(user, "unknown.perm") is False # e. Feature flag registration and DB gating FeatureRegistry.register("e2e.beta", default=True, name="E2E Beta") FeatureFlag.objects.create(slug="e2e.beta", is_active=True) cache.clear() assert fs.is_enabled("e2e.beta", user=user) is True assert fs.is_enabled("unknown.feature", user=user) is False # f. Register event, create outbound endpoint + subscription EventRegistry.register( "e2e.test.event", description="E2E test event", example_payload={"test_key": "test_value"}, ) endpoint = OutboundEndpoint.objects.create( name="E2E Target", url="https://example.com/e2e-hook", secret="e2e-secret", ) sub = OutboundSubscription.objects.create( endpoint=endpoint, event_name="e2e.test.event", ) # Connect receiver bridge: outbound_delivery_succeeded -> SecurityEvent @receiver(outbound_delivery_succeeded) def bridge_create_audit_event(sender, **kwargs): security_event_occurred.send( sender=OutboundDelivery, event_type="webhook_delivered", actor=user, metadata={ "delivery_id": kwargs.get("delivery_id"), "event_name": kwargs.get("event_name"), "status_code": kwargs.get("status_code"), }, ) payload = {"test_key": "test_value"} with mock.patch("infrasynth.webhooks.dispatch.requests.post") as mock_post: mock_post.return_value.status_code = 200 mock_post.return_value.text = "ok" with _emit_and_run_commit_hooks(): EventRegistry.emit("e2e.test.event", payload) # g. Verify OutboundDelivery was created via Celery eager delivery assert OutboundDelivery.objects.filter(subscription=sub).exists() delivery = OutboundDelivery.objects.get(subscription=sub) assert delivery.status == OutboundDelivery.Status.SUCCESS assert delivery.payload is not None assert delivery.response_status == 200 # h. Verify SecurityEvent was created by the signal bridge assert SecurityEvent.objects.filter(event_type="webhook_delivered").exists() se = SecurityEvent.objects.get(event_type="webhook_delivered") assert se.actor == user assert se.metadata["delivery_id"] == delivery.id assert se.metadata["event_name"] == "e2e.test.event" assert se.metadata["status_code"] == 200 # i. Verify ModelChangeLog entries exist for the created models labels = set(ModelChangeLog.objects.values_list("model_label", flat=True)) expected_labels = [ "infrasynth_security.Role", "infrasynth_security.Grant", "infrasynth_webhooks.OutboundEndpoint", "infrasynth_webhooks.OutboundSubscription", "infrasynth_webhooks.OutboundDelivery", ] for label in expected_labels: assert label in labels, f"Missing ModelChangeLog for {label}" outbound_delivery_succeeded.disconnect(bridge_create_audit_event) def test_e2e_permission_chain_resolution(user, admin_user): authz = AuthorizationService() # a. Create role + grant + revoke for the same user role = Role.objects.create( name="Tester", slug="tester", permissions=["perm.role_a", "perm.role_b"], ) role.users.add(user) Grant.objects.create(user=user, codename="perm.grant_c") Revoke.objects.create(user=user, codename="perm.role_a") # b. Verify Revoke > Grant > Role > Default precedence assert authz.has_permission(user, "perm.role_a") is False assert authz.has_permission(user, "perm.role_b") is True assert authz.has_permission(user, "perm.grant_c") is True assert authz.has_permission(user, "perm.default") is False # c. Verify superuser gets ["*"] wildcard assert authz.has_permission(admin_user, "anything.at.all") is True assert authz.get_effective_permissions(admin_user) == {"*"} # d. Verify get_effective_permissions returns union minus revokes perms = authz.get_effective_permissions(user) assert "perm.role_a" not in perms assert "perm.role_b" in perms assert "perm.grant_c" in perms assert len(perms) == 2 def test_e2e_feature_flag_with_user_override(user, clean_feature_registry): fs = FeatureService() # a. Register feature in registry (default True) FeatureRegistry.register("e2e.feature", default=True) assert fs.is_enabled("e2e.feature") is True # b. Create DB FeatureFlag (is_active=False) — overrides registry default flag = FeatureFlag.objects.create(slug="e2e.feature", is_active=False) cache.clear() assert fs.is_enabled("e2e.feature") is False # c. Create FeatureFlagOverride for the specific user (is_enabled=True) FeatureFlagOverride.objects.create(flag=flag, user=user, is_enabled=True) assert fs.is_enabled("e2e.feature", user=user) is True # d. Verify the override is user-specific other_user = UserModel.objects.create_user( username="other", email="other@example.com", password="pass123", ) assert fs.is_enabled("e2e.feature", user=other_user) is False