diff --git a/onadata/apps/api/models/organization_profile.py b/onadata/apps/api/models/organization_profile.py index cd6625bebf..24a81dbf1a 100644 --- a/onadata/apps/api/models/organization_profile.py +++ b/onadata/apps/api/models/organization_profile.py @@ -183,6 +183,11 @@ def __str__(self): def save(self, *args, **kwargs): # pylint: disable=arguments-differ super().save(*args, **kwargs) + @property + def email(self): + "organization email" + return self.user.email + def remove_user_from_organization(self, user): """Removes a user from all teams/groups in the organization. diff --git a/onadata/apps/api/tests/viewsets/test_abstract_viewset.py b/onadata/apps/api/tests/viewsets/test_abstract_viewset.py index 81ac952be4..dc109421f2 100644 --- a/onadata/apps/api/tests/viewsets/test_abstract_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_abstract_viewset.py @@ -201,6 +201,7 @@ def _org_create(self, org_data=None): response = view(request) self.assertEqual(response.status_code, 200) data = { + "email": "mail@mail-server.org", "org": "denoinc", "name": "Dennis", "city": "Denoville", @@ -224,6 +225,7 @@ def _org_create(self, org_data=None): data["url"] = f"http://testserver/api/v1/orgs/{data['org']}" data["user"] = f"http://testserver/api/v1/users/{data['org']}" data["creator"] = "http://testserver/api/v1/users/bob" + data.pop("email") self.assertDictContainsSubset(data, response.data) # pylint: disable=attribute-defined-outside-init self.company_data = response.data diff --git a/onadata/apps/api/tests/viewsets/test_organization_profile_viewset.py b/onadata/apps/api/tests/viewsets/test_organization_profile_viewset.py index 3425b6fe23..2925786a03 100644 --- a/onadata/apps/api/tests/viewsets/test_organization_profile_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_organization_profile_viewset.py @@ -6,7 +6,7 @@ from builtins import str as text from unittest.mock import patch -from django.contrib.auth.models import User, timezone +from django.contrib.auth.models import User, timezone, AnonymousUser from django.core.cache import cache from django.test.utils import override_settings @@ -89,6 +89,7 @@ def test_orgs_list(self): self.assertNotEqual(response.get("Cache-Control"), None) self.assertEqual(response.status_code, 200) del self.company_data["metadata"] + del self.company_data["email"] self.assertEqual(response.data, [self.company_data]) # inactive organization @@ -113,6 +114,7 @@ def test_orgs_list_for_authenticated_user(self): self.assertNotEqual(response.get("Cache-Control"), None) self.assertEqual(response.status_code, 200) del self.company_data["metadata"] + del self.company_data["email"] self.assertEqual(response.data, [self.company_data]) def test_orgs_list_shared_with_user(self): @@ -194,6 +196,8 @@ def test_orgs_get_not_creator(self): response = view(request, user="denoinc") self.assertNotEqual(response.get("Cache-Control"), None) self.assertEqual(response.status_code, 200) + del self.company_data['email'] + del self.company_data['metadata'] self.assertEqual(response.data, self.company_data) self.assertIn("users", list(response.data)) for user in response.data["users"]: @@ -207,6 +211,8 @@ def test_orgs_get_anon(self): response = view(request, user="denoinc") self.assertNotEqual(response.get("Cache-Control"), None) self.assertEqual(response.status_code, 200) + del self.company_data["email"] + del self.company_data['metadata'] self.assertEqual(response.data, self.company_data) self.assertIn("users", list(response.data)) for user in response.data["users"]: @@ -216,12 +222,14 @@ def test_orgs_get_anon(self): def test_orgs_create(self): self._org_create() self.assertTrue(self.organization.user.is_active) + self.assertEqual(self.organization.user.email, "mail@mail-server.org") def test_orgs_create_without_name(self): data = { "org": "denoinc", "city": "Denoville", "country": "US", + "email": "user@mail.org", "home_page": "deno.com", "twitter": "denoinc", "description": "", @@ -235,6 +243,29 @@ def test_orgs_create_without_name(self): response = self.view(request) self.assertEqual(response.data, {"name": ["This field is required."]}) + def test_org_create_and_fetch_by_admin_user(self): + org_email = "mail@mail-sever.org" + data = { + "name": "denoinc", + "org": "denoinc", + "city": "Denoville", + "country": "US", + "home_page": "deno.com", + "twitter": "denoinc", + "email": org_email, + "description": "", + "address": "", + "phonenumber": "", + "require_auth": False, + } + request = self.factory.post( + "/", data=json.dumps(data), content_type="application/json" + ) + request.user = self.user + response = self.view(request) + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data['email'], org_email) + def test_org_create_with_anonymous_user(self): data = { "name": "denoinc", @@ -389,6 +420,7 @@ def test_member_sees_orgs_added_to(self): } ) del expected_data["metadata"] + del expected_data['email'] request = self.factory.get("/", **self.extra) response = view(request) @@ -420,6 +452,8 @@ def test_role_for_org_non_owner(self): request = self.factory.get("/", **self.extra) response = view(request, user="denoinc") self.assertEqual(response.status_code, 200) + self.assertTrue('email' in response.data) + self.assertEqual(response.data['email'], 'mail@mail-server.org') self.assertIn("users", list(response.data)) for user in response.data["users"]: @@ -432,6 +466,21 @@ def test_role_for_org_non_owner(self): ) self.assertEqual(role, expected_role) + # getting profile as a member + request = self.factory.get("/", **self.extra) + request.user = User.objects.get(username="aboy") + response = view(request, user="denoinc") + self.assertEqual(response.status_code, 200) + + # get profile as a anonymous user + request = self.factory.get("/", **self.extra) + request.user = AnonymousUser() + request.headers = None + request.META['HTTP_AUTHORIZATION'] = "" + response = view(request, user="denoinc") + self.assertEqual(response.status_code, 200) + self.assertFalse('email' in response.data) + def test_add_members_to_org_with_anonymous_user(self): self._org_create() view = OrganizationProfileViewSet.as_view({"post": "members"}) @@ -561,6 +610,7 @@ def test_orgs_create_with_mixed_case(self): "home_page": "deno.com", "twitter": "denoinc", "description": "", + "email": "user@mail.com", "address": "", "phonenumber": "", "require_auth": False, diff --git a/onadata/apps/api/viewsets/organization_profile_viewset.py b/onadata/apps/api/viewsets/organization_profile_viewset.py index d42a2501b5..b9ab39040d 100644 --- a/onadata/apps/api/viewsets/organization_profile_viewset.py +++ b/onadata/apps/api/viewsets/organization_profile_viewset.py @@ -66,11 +66,12 @@ class OrganizationProfileViewSet( def retrieve(self, request, *args, **kwargs): """Get organization from cache or db""" username = kwargs.get("user") - cached_org = cache.get(f"{ORG_PROFILE_CACHE}{username}") + cache_key = f"{ORG_PROFILE_CACHE}{username}{request.user.username}" + cached_org = cache.get(cache_key) if cached_org: return Response(cached_org) response = super().retrieve(request, *args, **kwargs) - cache.set(f"{ORG_PROFILE_CACHE}{username}", response.data) + cache.set(cache_key, response.data) return response def create(self, request, *args, **kwargs): @@ -78,20 +79,21 @@ def create(self, request, *args, **kwargs): response = super().create(request, *args, **kwargs) organization = response.data username = organization.get("org") - cache.set(f"{ORG_PROFILE_CACHE}{username}", organization) + cache.set(f"{ORG_PROFILE_CACHE}{username}{request.user.username}", organization) return response def destroy(self, request, *args, **kwargs): """Clear cache and destroy organization""" username = kwargs.get("user") - safe_delete(f"{ORG_PROFILE_CACHE}{username}") + safe_delete(f"{ORG_PROFILE_CACHE}{username}{request.user.username}") return super().destroy(request, *args, **kwargs) def update(self, request, *args, **kwargs): """Update org in cache and db""" username = kwargs.get("user") response = super().update(request, *args, **kwargs) - cache.set(f"{ORG_PROFILE_CACHE}{username}", response.data) + cache_key = f"{ORG_PROFILE_CACHE}{username}{request.user.username}" + cache.set(cache_key, response.data) return response @action(methods=["DELETE", "GET", "POST", "PUT"], detail=True) @@ -120,7 +122,7 @@ def members(self, request, *args, **kwargs): data = OrganizationSerializer( organization, context={"request": request} ).data - cache.set(f"{ORG_PROFILE_CACHE}{username}", data) + cache.set(f"{ORG_PROFILE_CACHE}{username}{request.user.username}", data) else: return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/onadata/libs/serializers/organization_serializer.py b/onadata/libs/serializers/organization_serializer.py index 0887c830a5..ca1f0f006f 100644 --- a/onadata/libs/serializers/organization_serializer.py +++ b/onadata/libs/serializers/organization_serializer.py @@ -37,6 +37,7 @@ class OrganizationSerializer(serializers.HyperlinkedModelSerializer): user = serializers.HyperlinkedRelatedField( view_name="user-detail", lookup_field="username", read_only=True ) + email = serializers.EmailField(allow_blank=True) creator = serializers.HyperlinkedRelatedField( view_name="user-detail", lookup_field="username", read_only=True ) @@ -47,7 +48,7 @@ class OrganizationSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = OrganizationProfile exclude = ("created_by", "is_organization", "organization") - owner_only_fields = ("metadata",) + owner_only_fields = ("metadata", "email") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)