Skip to content

Commit

Permalink
feat: adding capture events for created and deleting org domains unde…
Browse files Browse the repository at this point in the history
…r authentication domains and sso (#26654)
  • Loading branch information
surbhi-posthog authored Dec 4, 2024
1 parent 70f234b commit 407947f
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 3 deletions.
55 changes: 55 additions & 0 deletions posthog/api/organization_domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
from typing import Any, cast
import posthoganalytics

from rest_framework import exceptions, request, response, serializers
from posthog.api.utils import action
Expand All @@ -11,10 +12,29 @@
from posthog.models import OrganizationDomain
from posthog.models.organization import Organization
from posthog.permissions import OrganizationAdminWritePermissions
from posthog.event_usage import groups

DOMAIN_REGEX = r"^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$"


def _capture_domain_event(request, domain: OrganizationDomain, event_type: str, properties: dict | None = None) -> None:
if not properties:
properties = {}

properties.update(
{
"domain": domain.domain,
}
)

posthoganalytics.capture(
request.user.distinct_id,
f"organization domain {event_type}",
properties=properties,
groups=groups(domain.organization),
)


class OrganizationDomainSerializer(serializers.ModelSerializer):
UPDATE_ONLY_WHEN_VERIFIED = ["jit_provisioning_enabled", "sso_enforcement"]

Expand Down Expand Up @@ -96,3 +116,38 @@ def verify(self, request: request.Request, **kw) -> response.Response:

serializer = self.get_serializer(instance=instance)
return response.Response(serializer.data)

def create(self, request: request.Request, *args: Any, **kwargs: Any) -> response.Response:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()

_capture_domain_event(
request,
instance,
"created",
properties={
"jit_provisioning_enabled": instance.jit_provisioning_enabled,
"sso_enforcement": instance.sso_enforcement or None,
},
)

return response.Response(serializer.data, status=201)

def destroy(self, request: request.Request, *args: Any, **kwargs: Any) -> response.Response:
instance = self.get_object()

_capture_domain_event(
request,
instance,
"deleted",
properties={
"is_verified": instance.is_verified,
"had_saml": instance.has_saml,
"had_jit_provisioning": instance.jit_provisioning_enabled,
"had_sso_enforcement": bool(instance.sso_enforcement),
},
)

instance.delete()
return response.Response(status=204)
34 changes: 31 additions & 3 deletions posthog/api/test/test_organization_domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import datetime
from unittest.mock import patch
from unittest.mock import ANY, patch
from zoneinfo import ZoneInfo

import dns.resolver
Expand Down Expand Up @@ -84,7 +84,8 @@ def test_cannot_list_or_retrieve_domains_for_other_org(self):

# Create domains

def test_create_domain(self):
@patch("posthoganalytics.capture")
def test_create_domain(self, mock_capture):
self.organization_membership.level = OrganizationMembership.Level.ADMIN
self.organization.available_product_features = [
{"key": "automatic_provisioning", "name": "automatic_provisioning"}
Expand Down Expand Up @@ -116,6 +117,18 @@ def test_create_domain(self):
self.assertEqual(instance.last_verification_retry, None)
self.assertEqual(instance.sso_enforcement, "")

# Verify the domain creation capture event was called
mock_capture.assert_any_call(
self.user.distinct_id,
"organization domain created",
properties={
"domain": "the.posthog.com",
"jit_provisioning_enabled": False,
"sso_enforcement": None,
},
groups={"instance": ANY, "organization": str(self.organization.id)},
)

def test_cant_create_domain_without_feature(self):
self.organization_membership.level = OrganizationMembership.Level.ADMIN
self.organization_membership.save()
Expand Down Expand Up @@ -439,7 +452,8 @@ def test_cannot_update_domain_for_another_org(self):

# Delete domains

def test_admin_can_delete_domain(self):
@patch("posthoganalytics.capture")
def test_admin_can_delete_domain(self, mock_capture):
self.organization_membership.level = OrganizationMembership.Level.ADMIN
self.organization_membership.save()

Expand All @@ -449,6 +463,20 @@ def test_admin_can_delete_domain(self):

self.assertFalse(OrganizationDomain.objects.filter(id=self.domain.id).exists())

# Verify the domain deletion capture event was called
mock_capture.assert_any_call(
self.user.distinct_id,
"organization domain deleted",
properties={
"domain": "myposthog.com",
"is_verified": False,
"had_saml": False,
"had_jit_provisioning": False,
"had_sso_enforcement": False,
},
groups={"instance": ANY, "organization": str(self.organization.id)},
)

def test_only_admin_can_delete_domain(self):
response = self.client.delete(f"/api/organizations/@current/domains/{self.domain.id}")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
Expand Down

0 comments on commit 407947f

Please sign in to comment.