diff --git a/corehq/apps/api/resources/v0_5.py b/corehq/apps/api/resources/v0_5.py index 04596a18f61a..361b7eb35b47 100644 --- a/corehq/apps/api/resources/v0_5.py +++ b/corehq/apps/api/resources/v0_5.py @@ -127,6 +127,7 @@ log_user_change, verify_modify_user_conditions, ) +from corehq.apps.users.role_utils import get_commcare_analytics_roles_by_user_domains from corehq.const import USER_CHANGE_VIA_API from corehq.util import get_document_or_404 from corehq.util.couch import DocumentNotFound @@ -903,6 +904,40 @@ class Meta(object): include_resource_uri = False +AnalyticsRoles = namedtuple('AnalyticsRoles', 'domain_name role_names') +AnalyticsRoles.__new__.__defaults__ = ('', '') + + +class CommCareAnalyticsRolesResource(CorsResourceMixin, Resource): + domain_name = fields.CharField(attribute='domain_name', readonly=True) + role_names = fields.CharField(attribute='role_names', readonly=True) + + def obj_get_list(self, bundle, **kwargs): + request = bundle.request + roles_by_domain = get_commcare_analytics_roles_by_user_domains(request.couch_user) + + domain = request.GET.get('domain') + if domain: + if domain not in roles_by_domain: + return [] + return [ + AnalyticsRoles(domain, roles_by_domain[domain]) + ] + + results = [] + for domain, roles in roles_by_domain.items(): + results.append( + AnalyticsRoles(domain, roles) + ) + return results + + class Meta(object): + resource_name = 'commcare_analytics_roles' + authentication = LoginAuthentication() + list_allowed_methods = ['get'] + include_resource_uri = False + + Form = namedtuple('Form', 'form_xmlns form_name') Form.__new__.__defaults__ = ('', '') diff --git a/corehq/apps/api/urls.py b/corehq/apps/api/urls.py index cf84fd07e674..3f504e461c68 100644 --- a/corehq/apps/api/urls.py +++ b/corehq/apps/api/urls.py @@ -172,6 +172,7 @@ def api_url_patterns(): NON_GLOBAL_USER_API_LIST = ( v0_5.IdentityResource, + v0_5.CommCareAnalyticsRolesResource, ) diff --git a/corehq/apps/users/permissions.py b/corehq/apps/users/permissions.py index 05c6c609483a..36e1769fe76c 100644 --- a/corehq/apps/users/permissions.py +++ b/corehq/apps/users/permissions.py @@ -18,6 +18,16 @@ ODATA_FEED_PERMISSION, } +COMMCARE_ANALYTICS_GAMMA = "gamma" +COMMCARE_ANALYTICS_SQL_LAB = "sql_lab" +COMMCARE_ANALYTICS_DATASET_EDITOR = "dataset_editor" + +COMMCARE_ANALYTICS_USER_PERMISSIONS = [ + COMMCARE_ANALYTICS_GAMMA, + COMMCARE_ANALYTICS_SQL_LAB, + COMMCARE_ANALYTICS_DATASET_EDITOR, +] + ReportPermission = namedtuple('ReportPermission', ['slug', 'title', 'is_visible']) diff --git a/corehq/apps/users/role_utils.py b/corehq/apps/users/role_utils.py index c8e5b3f0b705..f8a24f5ec7ba 100644 --- a/corehq/apps/users/role_utils.py +++ b/corehq/apps/users/role_utils.py @@ -1,4 +1,6 @@ from corehq.apps.users.models import UserRole, HqPermissions +from corehq.apps.users.permissions import COMMCARE_ANALYTICS_USER_PERMISSIONS +from corehq.toggles import SUPERSET_ANALYTICS class UserRolePresets: @@ -126,3 +128,19 @@ def enable_attendance_coordinator_role_for_domain(domain): if role.is_archived: role.is_archived = False role.save() + + +def get_commcare_analytics_roles_by_user_domains(couch_user): + enabled_domains = SUPERSET_ANALYTICS.get_enabled_domains() + + domain_roles = {} + for domain_membership in couch_user.domain_memberships: + if domain_membership.domain not in enabled_domains: + continue + if domain_membership.is_admin: + analytics_roles = COMMCARE_ANALYTICS_USER_PERMISSIONS + else: + analytics_roles = domain_membership.role.permissions.commcare_analytics_roles_list + domain_roles[domain_membership.domain] = analytics_roles + + return domain_roles diff --git a/corehq/apps/users/tests/test_role_utils.py b/corehq/apps/users/tests/test_role_utils.py index bf5764f363d6..8b9ddb786bf2 100644 --- a/corehq/apps/users/tests/test_role_utils.py +++ b/corehq/apps/users/tests/test_role_utils.py @@ -1,6 +1,7 @@ from django.test import TestCase +from unittest.mock import patch -from corehq.apps.users.models import HqPermissions, UserRole +from corehq.apps.users.models import HqPermissions, UserRole, WebUser, PermissionInfo from corehq.apps.users.role_utils import ( UserRolePresets, archive_custom_roles_for_domain, @@ -9,8 +10,16 @@ reset_initial_roles_for_domain, unarchive_roles_for_domain, enable_attendance_coordinator_role_for_domain, - archive_attendance_coordinator_role_for_domain + archive_attendance_coordinator_role_for_domain, + get_commcare_analytics_roles_by_user_domains, ) +from corehq.apps.domain.shortcuts import create_domain +from corehq.apps.users.permissions import ( + COMMCARE_ANALYTICS_USER_PERMISSIONS, + COMMCARE_ANALYTICS_GAMMA, + COMMCARE_ANALYTICS_SQL_LAB, +) +from corehq.toggles import SUPERSET_ANALYTICS class RoleUtilsTests(TestCase): @@ -118,3 +127,74 @@ def _delete_presets(self): for role in UserRole.objects.get_by_domain(self.domain): if role.id != self.role1.id: role.delete() + + +class TestCommcareAnalyticsRolesByUser(TestCase): + + USERNAME = "username" + PASSWORD = "***" + + @classmethod + def setUpClass(cls): + cls.domain = create_domain("domain1") + cls.user = WebUser.create("domain1", cls.USERNAME, cls.PASSWORD, None, None) + + cls.hq_no_cca_role = cls.create_role("No CCA role", analytics_roles=None) + + cls.limited_cca_roles = [ + COMMCARE_ANALYTICS_GAMMA, + COMMCARE_ANALYTICS_SQL_LAB, + ] + cls.hq_limited_cca_role = cls.create_role("Limited CCA role", analytics_roles=cls.limited_cca_roles) + + @classmethod + def tearDownClass(cls): + for role in UserRole.objects.get_by_domain(cls.domain): + role.delete() + + cls.user.delete(deleted_by_domain=cls.domain.name, deleted_by=None) + cls.domain.delete() + + def test_user_domain_does_not_have_flag_enabled(self): + analytics_roles = get_commcare_analytics_roles_by_user_domains(self.user) + self.assertTrue("domain1" not in analytics_roles) + + @patch.object(SUPERSET_ANALYTICS, "get_enabled_domains") + def test_admin_user(self, get_enabled_domains_mock): + get_enabled_domains_mock.return_value = ["domain1"] + + self.user.domain_memberships[0].is_admin = True + self.assertTrue(self.user.get_domain_membership("domain1").is_admin) + + analytics_roles = get_commcare_analytics_roles_by_user_domains(self.user) + self.assertTrue(analytics_roles["domain1"], COMMCARE_ANALYTICS_USER_PERMISSIONS) + + @patch.object(SUPERSET_ANALYTICS, "get_enabled_domains") + def test_non_admin_user(self, get_enabled_domains_mock): + get_enabled_domains_mock.return_value = ["domain1"] + + self.user.set_role(self.domain.name, self.hq_no_cca_role.get_qualified_id()) + self.assertFalse(self.user.get_domain_membership("domain1").is_admin) + + analytics_roles = get_commcare_analytics_roles_by_user_domains(self.user) + self.assertEqual(analytics_roles["domain1"], []) + + @patch.object(SUPERSET_ANALYTICS, "get_enabled_domains") + def test_user_has_limited_roles(self, get_enabled_domains_mock): + get_enabled_domains_mock.return_value = ["domain1"] + + self.user.set_role(self.domain.name, self.hq_limited_cca_role.get_qualified_id()) + self.assertFalse(self.user.get_domain_membership("domain1").is_admin) + + analytics_roles = get_commcare_analytics_roles_by_user_domains(self.user) + self.assertEqual(analytics_roles["domain1"], self.limited_cca_roles) + + @classmethod + def create_role(cls, role_name, analytics_roles=None): + if analytics_roles is None: + analytics_roles = [] + permissions_infos = [PermissionInfo('commcare_analytics_roles', analytics_roles)] + permissions = HqPermissions.from_permission_list(permissions_infos) + + role = UserRole.create(domain=cls.domain, name=role_name, permissions=permissions) + return role diff --git a/corehq/apps/users/views/__init__.py b/corehq/apps/users/views/__init__.py index 7487dd8ca15e..c5bcbc46bfad 100644 --- a/corehq/apps/users/views/__init__.py +++ b/corehq/apps/users/views/__init__.py @@ -131,6 +131,11 @@ WorksheetNotFound, get_workbook, ) +from corehq.apps.users.permissions import ( + COMMCARE_ANALYTICS_GAMMA, + COMMCARE_ANALYTICS_SQL_LAB, + COMMCARE_ANALYTICS_DATASET_EDITOR, +) from dimagi.utils.logging import notify_exception @@ -783,15 +788,15 @@ def page_context(self): def _commcare_analytics_roles_options(): return [ { - 'slug': 'gamma', + 'slug': COMMCARE_ANALYTICS_GAMMA, 'name': 'Gamma' }, { - 'slug': 'sql_lab', + 'slug': COMMCARE_ANALYTICS_SQL_LAB, 'name': 'SQL Lab' }, { - 'slug': 'dataset_editor', + 'slug': COMMCARE_ANALYTICS_DATASET_EDITOR, 'name': 'Dataset Editor' } ]