Skip to content
22 changes: 22 additions & 0 deletions openwisp_users/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,18 @@ def set_default_settings(self):
def connect_receivers(self):
OrganizationUser = load_model('openwisp_users', 'OrganizationUser')
OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner')
Organization = load_model('openwisp_users', 'Organization')
signal_tuples = [
(post_save, 'post_save'),
(post_delete, 'post_delete'),
]

pre_save.connect(
self.handle_org_is_active_change,
sender=Organization,
dispatch_uid='handle_org_is_active_change',
)

for model in [OrganizationUser, OrganizationOwner]:
for signal, name in signal_tuples:
signal.connect(
Expand Down Expand Up @@ -130,6 +137,21 @@ def connect_receivers(self):
dispatch_uid='make_first_org_user_org_owner',
)

@classmethod
def handle_org_is_active_change(cls, instance, **kwargs):
if instance._state.adding:
# If it's a new organization, we don't need to update any cache
return
Organization = instance._meta.model
try:
old_instance = Organization.objects.only('is_active').get(pk=instance.pk)
except Organization.DoesNotExist:
return
from .tasks import invalidate_org_membership_cache

if instance.is_active != old_instance.is_active:
invalidate_org_membership_cache.delay(instance.pk)

@classmethod
def pre_save_update_organizations_dict(cls, instance, **kwargs):
"""
Expand Down
16 changes: 16 additions & 0 deletions openwisp_users/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
from django.utils.timezone import now, timedelta
from django.utils.translation import gettext_lazy as _
from openwisp_utils.admin_theme.email import send_email
from swapper import load_model

from . import settings as app_settings

User = get_user_model()
OrganizationUser = load_model('openwisp_users', 'OrganizationUser')


@shared_task
Expand Down Expand Up @@ -83,3 +85,17 @@ def password_expiration_email():
sleep(random.randint(1, 2))
else:
email_counts += 1


@shared_task
def invalidate_org_membership_cache(organization_pk):
"""
Invalidates organization membership cache of all users of an
organization when organization.is_active changes
(organization is disabled or enabled again).
"""
qs = OrganizationUser.objects.filter(
organization_id=organization_pk
).select_related('user')
for org_user in qs.iterator():
org_user.user._invalidate_user_organizations_dict()
1 change: 1 addition & 0 deletions openwisp_users/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,7 @@ def test_can_change_inline_org_owner(self):
params = {
'name': org.name,
'slug': org.slug,
'is_active': 'on',
'owner-TOTAL_FORMS': '1',
'owner-INITIAL_FORMS': '1',
'owner-MIN_NUM_FORMS': '0',
Expand Down
10 changes: 5 additions & 5 deletions openwisp_users/tests/test_api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_organization_put_api(self):
'email': 'testorg@test.com',
'url': '',
}
with self.assertNumQueries(6):
with self.assertNumQueries(8):
r = self.client.put(path, data, content_type='application/json')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data['name'], 'test org change')
Expand All @@ -99,7 +99,7 @@ def test_organization_patch_api(self):
data = {
'name': 'test org change',
}
with self.assertNumQueries(5):
with self.assertNumQueries(6):
r = self.client.patch(path, data, content_type='application/json')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data['name'], 'test org change')
Expand All @@ -110,7 +110,7 @@ def test_create_organization_owner_api(self):
org1_user1 = self._create_org_user(user=user1, organization=org1)
path = reverse('users:organization_detail', args=(org1.pk,))
data = {'owner': {'organization_user': org1_user1.pk}}
with self.assertNumQueries(17):
with self.assertNumQueries(18):
r = self.client.patch(path, data, content_type='application/json')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data['owner']['organization_user'], org1_user1.pk)
Expand All @@ -122,7 +122,7 @@ def test_remove_organization_owner_api(self):
self._create_org_owner(organization_user=org1_user1, organization=org1)
path = reverse('users:organization_detail', args=(org1.pk,))
data = {'owner': {'organization_user': ''}}
with self.assertNumQueries(11):
with self.assertNumQueries(12):
r = self.client.patch(path, data, content_type='application/json')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data['owner'], None)
Expand Down Expand Up @@ -166,7 +166,7 @@ def test_change_organizationowner_for_org(self):
self.assertEqual(org1.owner.organization_user.id, org1_user1.id)
path = reverse('users:organization_detail', args=(org1.pk,))
data = {'owner': {'organization_user': org1_user2.id}}
with self.assertNumQueries(26):
with self.assertNumQueries(27):
r = self.client.patch(path, data, content_type='application/json')
org1.refresh_from_db()
self.assertEqual(org1.owner.organization_user.id, org1_user2.id)
Expand Down
10 changes: 10 additions & 0 deletions openwisp_users/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ def test_invalidate_cache_org_user_user_changed(self):
self.assertEqual(user1.is_member(org), False)
self.assertEqual(user2.is_member(org), True)

def test_invalidate_cache_org_status_changed(self):
org = self._create_org(name='testorg1')
user1 = self._create_user(username='testuser1', email='user1@test.com')
self._create_org_user(user=user1, organization=org)
self.assertEqual(user1.is_member(org), True)
org.is_active = False
org.full_clean()
org.save()
self.assertEqual(user1.is_member(org), False)

def test_organizations_managed(self):
user = self._create_user(username='organizations_pk')
self.assertEqual(user.organizations_managed, [])
Expand Down