-
-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into jc/commcare_client_version_compliance_report
- Loading branch information
Showing
76 changed files
with
13,670 additions
and
9,198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
from django.test import TestCase | ||
from unittest.mock import patch | ||
|
||
from corehq.apps.api.validation import WebUserResourceValidator | ||
from corehq.apps.domain.models import Domain | ||
from corehq.apps.users.models import WebUser | ||
from corehq.util.test_utils import flag_enabled, flag_disabled | ||
|
||
|
||
class TestWebUserResourceValidator(TestCase): | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
cls.domain = Domain(name="test-domain", is_active=True) | ||
cls.domain.save() | ||
cls.addClassCleanup(cls.domain.delete) | ||
cls.requesting_user = WebUser.create(cls.domain.name, "[email protected]", "123", None, None) | ||
cls.validator = WebUserResourceValidator(cls.domain.name, cls.requesting_user) | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
cls.requesting_user.delete(None, None) | ||
super().tearDownClass() | ||
|
||
def test_validate_parameters(self): | ||
params = {"email": "[email protected]", "role": "Admin"} | ||
self.assertIsNone(self.validator.validate_parameters(params)) | ||
|
||
invalid_params = {"invalid_param": "value"} | ||
self.assertEqual(self.validator.validate_parameters(invalid_params), "Invalid parameter(s): invalid_param") | ||
|
||
@flag_enabled('TABLEAU_USER_SYNCING') | ||
@patch('corehq.apps.users.models.WebUser.has_permission', return_value=True) | ||
def test_validate_parameters_with_tableau_edit_permission(self, mock_has_permission): | ||
params = {"email": "[email protected]", "role": "Admin", "tableau_role": "Viewer"} | ||
self.assertIsNone(self.validator.validate_parameters(params)) | ||
|
||
@flag_disabled('TABLEAU_USER_SYNCING') | ||
@patch('corehq.apps.users.models.WebUser.has_permission', return_value=False) | ||
def test_validate_parameters_without_tableau_edit_permission(self, mock_has_permission): | ||
params = {"email": "[email protected]", "role": "Admin", "tableau_role": "Viewer"} | ||
self.assertEqual(self.validator.validate_parameters(params), | ||
"You do not have permission to edit Tableau Configuration.") | ||
|
||
@patch('corehq.apps.registration.validation.domain_has_privilege', return_value=True) | ||
def test_validate_parameters_with_profile_permission(self, mock_domain_has_privilege): | ||
params = {"email": "[email protected]", "role": "Admin", "profile": "some_profile"} | ||
self.assertIsNone(self.validator.validate_parameters(params)) | ||
|
||
@patch('corehq.apps.registration.validation.domain_has_privilege', return_value=False) | ||
def test_validate_parameters_without_profile_permission(self, mock_domain_has_privilege): | ||
params = {"email": "[email protected]", "role": "Admin", "profile": "some_profile"} | ||
self.assertEqual(self.validator.validate_parameters(params), | ||
"This domain does not have user profile privileges.") | ||
|
||
@patch('corehq.apps.registration.validation.domain_has_privilege', return_value=True) | ||
def test_validate_parameters_with_location_privilege(self, mock_domain_has_privilege): | ||
params = {"email": "[email protected]", "role": "Admin", "primary_location": "some_location"} | ||
self.assertIsNone(self.validator.validate_parameters(params)) | ||
params = {"email": "[email protected]", "role": "Admin", "assigned_locations": "some_location"} | ||
self.assertIsNone(self.validator.validate_parameters(params)) | ||
|
||
@patch('corehq.apps.registration.validation.domain_has_privilege', return_value=False) | ||
def test_validate_parameters_without_location_privilege(self, mock_domain_has_privilege): | ||
params = {"email": "[email protected]", "role": "Admin", "primary_location": "some_location"} | ||
self.assertEqual(self.validator.validate_parameters(params), | ||
"This domain does not have locations privileges.") | ||
|
||
params = {"email": "[email protected]", "role": "Admin", "assigned_locations": "some_location"} | ||
self.assertEqual(self.validator.validate_parameters(params), | ||
"This domain does not have locations privileges.") | ||
|
||
def test_validate_email(self): | ||
self.assertIsNone(self.validator.validate_email("[email protected]", True)) | ||
|
||
self.assertEqual(self.validator.validate_email("[email protected]", True), | ||
"A user with this email address is already in " | ||
"this project or has a pending invitation.") | ||
|
||
deactivated_user = WebUser.create(self.domain.name, "[email protected]", "123", None, None) | ||
deactivated_user.is_active = False | ||
deactivated_user.save() | ||
self.assertEqual(self.validator.validate_email("[email protected]", True), | ||
"A user with this email address is deactivated. ") | ||
|
||
def test_validate_locations(self): | ||
with patch('corehq.apps.user_importer.validation.LocationValidator.validate_spec') as mock_validate_spec: | ||
mock_validate_spec.return_value = None | ||
self.assertIsNone(self.validator.validate_locations(self.requesting_user.username, | ||
["loc1", "loc2"], "loc1")) | ||
|
||
actual_spec = mock_validate_spec.call_args[0][0] | ||
self.assertEqual(actual_spec['username'], self.requesting_user.username) | ||
self.assertCountEqual(actual_spec['location_code'], ["loc1", "loc2"]) | ||
|
||
self.assertEqual( | ||
self.validator.validate_locations(self.requesting_user.username, ["loc1", "loc2"], "loc3"), | ||
"Primary location must be one of the user's locations" | ||
) | ||
|
||
self.assertEqual( | ||
self.validator.validate_locations(self.requesting_user.username, ["loc1", "loc2"], ""), | ||
"Primary location can't be empty if the user has any locations set" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from memoized import memoized | ||
|
||
from corehq.apps.custom_data_fields.models import CustomDataFieldsDefinition | ||
from corehq.apps.reports.util import get_allowed_tableau_groups_for_domain | ||
from corehq.apps.user_importer.importer import SiteCodeToLocationCache | ||
from corehq.apps.user_importer.validation import ( | ||
RoleValidator, | ||
ProfileValidator, | ||
LocationValidator, | ||
TableauGroupsValidator, | ||
TableauRoleValidator, | ||
CustomDataValidator, | ||
EmailValidator, | ||
) | ||
from corehq.apps.users.validation import validate_primary_location_assignment | ||
from corehq.apps.registration.validation import AdminInvitesUserFormValidator | ||
|
||
|
||
class WebUserResourceValidator(): | ||
def __init__(self, domain, requesting_user): | ||
self.domain = domain | ||
self.requesting_user = requesting_user | ||
|
||
@property | ||
def roles_by_name(self): | ||
from corehq.apps.users.views.utils import get_editable_role_choices | ||
return {role[1]: role[0] for role in get_editable_role_choices(self.domain, self.requesting_user, | ||
allow_admin_role=True)} | ||
|
||
@property | ||
@memoized | ||
def profiles_by_name(self): | ||
from corehq.apps.users.views.mobile.custom_data_fields import UserFieldsView | ||
return CustomDataFieldsDefinition.get_profiles_by_name(self.domain, UserFieldsView.field_type) | ||
|
||
@property | ||
def location_cache(self): | ||
return SiteCodeToLocationCache(self.domain) | ||
|
||
def validate_parameters(self, parameters): | ||
allowed_params = ['email', 'role', 'primary_location', 'assigned_locations', | ||
'profile', 'custom_user_data', 'tableau_role', 'tableau_groups'] | ||
invalid_params = [param for param in parameters if param not in allowed_params] | ||
if invalid_params: | ||
return f"Invalid parameter(s): {', '.join(invalid_params)}" | ||
return AdminInvitesUserFormValidator.validate_parameters(self.domain, self.requesting_user, parameters) | ||
|
||
def validate_role(self, role): | ||
spec = {'role': role} | ||
return RoleValidator(self.domain, self.roles_by_name()).validate_spec(spec) | ||
|
||
def validate_profile(self, new_profile_name): | ||
profile_validator = ProfileValidator(self.domain, self.requesting_user, True, self.profiles_by_name()) | ||
spec = {'user_profile': new_profile_name} | ||
return profile_validator.validate_spec(spec) | ||
|
||
def validate_custom_data(self, custom_data, profile_name): | ||
custom_data_validator = CustomDataValidator(self.domain, self.profiles_by_name()) | ||
spec = {'data': custom_data, 'user_profile': profile_name} | ||
return custom_data_validator.validate_spec(spec) | ||
|
||
def validate_email(self, email, is_post): | ||
if is_post: | ||
error = AdminInvitesUserFormValidator.validate_email(self.domain, email) | ||
if error: | ||
return error | ||
email_validator = EmailValidator(self.domain, 'email') | ||
spec = {'email': email} | ||
return email_validator.validate_spec(spec) | ||
|
||
def validate_locations(self, editable_user, assigned_location_codes, primary_location_code): | ||
error = validate_primary_location_assignment(primary_location_code, assigned_location_codes) | ||
if error: | ||
return error | ||
|
||
location_validator = LocationValidator(self.domain, self.requesting_user, self.location_cache, True) | ||
location_codes = list(set(assigned_location_codes + [primary_location_code])) | ||
spec = {'location_code': location_codes, | ||
'username': editable_user} | ||
return location_validator.validate_spec(spec) | ||
|
||
def validate_tableau_group(self, tableau_groups): | ||
allowed_groups_for_domain = get_allowed_tableau_groups_for_domain(self.domain) or [] | ||
return TableauGroupsValidator.validate_tableau_groups(allowed_groups_for_domain, tableau_groups) | ||
|
||
def validate_tableau_role(self, tableau_role): | ||
return TableauRoleValidator.validate_tableau_role(tableau_role) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 0 additions & 31 deletions
31
corehq/apps/domain/templates/domain/admin/bootstrap3/commtrack_action_table.html
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.