Skip to content

Commit 284c224

Browse files
authored
fix(organizations): prevent creation of organizations for deleted or inactive users DEV-1241 (#6456)
### 📣 Summary Stop automatically creating an organization when a user has just been deleted. ### 📖 Description This change ensures that no organization is created or restored for users who are deleted or inactive. Previously an organization could be recreated right after a user was removed, or one could be created for a user that was no longer active. The fix adds validation to skip organization creation in these cases, preventing unnecessary records.
1 parent 621139d commit 284c224

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

hub/tests/test_user_details.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# coding: utf-8
22
from django.test import TestCase
3+
from django.utils import timezone
34

45
from kobo.apps.kobo_auth.shortcuts import User
6+
from kobo.apps.organizations.models import Organization
57

68

79
class UserDetailTestCase(TestCase):
@@ -47,3 +49,59 @@ def test_user_details_can_be_updated(self):
4749
self.user.extra_details.data.update(some_details)
4850
self.user.extra_details.save()
4951
self.assertEqual(self.user.extra_details.data, some_details)
52+
53+
54+
class UserOrganizationCreationTestCase(TestCase):
55+
fixtures = ['test_data']
56+
57+
def setUp(self):
58+
self.user = User.objects.get(username='someuser')
59+
60+
# Delete the existing organization
61+
Organization.objects.filter(
62+
organization_users__user=self.user
63+
).delete()
64+
65+
def test_no_org_created_when_user_removed(self):
66+
"""
67+
When a user is marked as removed (extra_details.date_removed is set),
68+
accessing user.organization should NOT create an Organization
69+
"""
70+
before_count = Organization.objects.filter(
71+
organization_users__user=self.user
72+
).count()
73+
self.assertEqual(before_count, 0)
74+
75+
# Simulate the user being removed (trash emptied)
76+
self.user.extra_details.date_removed = timezone.now()
77+
self.user.extra_details.save(update_fields=['date_removed'])
78+
79+
# Access property, should return None and NOT create an Organization
80+
self.assertIsNone(self.user.organization)
81+
82+
after_count = Organization.objects.filter(
83+
organization_users__user=self.user
84+
).count()
85+
self.assertEqual(after_count, 0)
86+
87+
def test_no_org_created_when_user_inactive(self):
88+
"""
89+
If a user is inactive (is_active == False), accessing user.organization
90+
should NOT create an Organization.
91+
"""
92+
before_count = Organization.objects.filter(
93+
organization_users__user=self.user
94+
).count()
95+
self.assertEqual(before_count, 0)
96+
97+
# Make user inactive and save
98+
self.user.is_active = False
99+
self.user.save(update_fields=['is_active'])
100+
101+
# Access property,should return None and NOT create an Organization
102+
self.assertIsNone(self.user.organization)
103+
104+
after_count = Organization.objects.filter(
105+
organization_users__user=self.user
106+
).count()
107+
self.assertEqual(after_count, 0)

kobo/apps/kobo_auth/models.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ def is_org_owner(self):
4949

5050
@property
5151
@cache_for_request
52-
def organization(self):
52+
def organization(self) -> Organization | None:
5353
if is_user_anonymous(self):
54-
return
54+
return None
5555

5656
# Database allows multiple organizations per user, but we restrict it to one.
5757
if (
@@ -61,6 +61,14 @@ def organization(self):
6161
):
6262
return organization
6363

64+
try:
65+
date_removed = self.extra_details.date_removed
66+
except self.__class__.extra_details.RelatedObjectDoesNotExist:
67+
date_removed = None
68+
69+
if not self.is_active or date_removed:
70+
return None
71+
6472
return create_organization(
6573
self, f"{self.username}'s organization"
6674
)

0 commit comments

Comments
 (0)