From 6ae1e156c87e7a95d888b0c79628916d6d2d8c6a Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 2 Nov 2018 14:13:05 -0400 Subject: [PATCH] do not block superusers with MANAGE_ORGANIZATION_AUTH setting --- awx/main/access.py | 22 +++------ awx/main/tests/functional/test_rbac_user.py | 54 ++++++++++++++++++++- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 5c1ea23a3a..74f1c70f83 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -524,7 +524,7 @@ class UserAccess(BaseAccess): # A user can be changed if they are themselves, or by org admins or # superusers. Change permission implies changing only certain fields # that a user should be able to edit for themselves. - if not settings.MANAGE_ORGANIZATION_AUTH: + if not settings.MANAGE_ORGANIZATION_AUTH and not self.user.is_superuser: return False return bool(self.user == obj or self.can_admin(obj, data)) @@ -577,7 +577,7 @@ class UserAccess(BaseAccess): return False def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): - if not settings.MANAGE_ORGANIZATION_AUTH: + if not settings.MANAGE_ORGANIZATION_AUTH and not self.user.is_superuser: return False # Reverse obj and sub_obj, defer to RoleAccess if this is a role assignment. @@ -587,7 +587,7 @@ class UserAccess(BaseAccess): return super(UserAccess, self).can_attach(obj, sub_obj, relationship, *args, **kwargs) def can_unattach(self, obj, sub_obj, relationship, *args, **kwargs): - if not settings.MANAGE_ORGANIZATION_AUTH: + if not settings.MANAGE_ORGANIZATION_AUTH and not self.user.is_superuser: return False if relationship == 'roles': @@ -1157,13 +1157,10 @@ class TeamAccess(BaseAccess): def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): """Reverse obj and sub_obj, defer to RoleAccess if this is an assignment of a resource role to the team.""" - if not settings.MANAGE_ORGANIZATION_AUTH: - return False + # MANAGE_ORGANIZATION_AUTH setting checked in RoleAccess if isinstance(sub_obj, Role): if sub_obj.content_object is None: raise PermissionDenied(_("The {} role cannot be assigned to a team").format(sub_obj.name)) - elif isinstance(sub_obj.content_object, User): - raise PermissionDenied(_("The admin_role for a User cannot be assigned to a team")) if isinstance(sub_obj.content_object, ResourceMixin): role_access = RoleAccess(self.user) @@ -1175,9 +1172,7 @@ class TeamAccess(BaseAccess): *args, **kwargs) def can_unattach(self, obj, sub_obj, relationship, *args, **kwargs): - if not settings.MANAGE_ORGANIZATION_AUTH: - return False - + # MANAGE_ORGANIZATION_AUTH setting checked in RoleAccess if isinstance(sub_obj, Role): if isinstance(sub_obj.content_object, ResourceMixin): role_access = RoleAccess(self.user) @@ -2552,14 +2547,13 @@ class RoleAccess(BaseAccess): # Unsupported for now return False - def can_attach(self, obj, sub_obj, relationship, data, - skip_sub_obj_read_check=False): - return self.can_unattach(obj, sub_obj, relationship, data, skip_sub_obj_read_check) + def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): + return self.can_unattach(obj, sub_obj, relationship, *args, **kwargs) @check_superuser def can_unattach(self, obj, sub_obj, relationship, data=None, skip_sub_obj_read_check=False): if isinstance(obj.content_object, Team): - if not settings.MANAGE_ORGANIZATION_AUTH: + if not settings.MANAGE_ORGANIZATION_AUTH and not self.user.is_superuser: return False if not skip_sub_obj_read_check and relationship in ['members', 'member_role.parents', 'parents']: diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/test_rbac_user.py index fc2c8cec2c..15035740d3 100644 --- a/awx/main/tests/functional/test_rbac_user.py +++ b/awx/main/tests/functional/test_rbac_user.py @@ -1,8 +1,9 @@ import pytest +import mock from django.test import TransactionTestCase -from awx.main.access import UserAccess +from awx.main.access import UserAccess, RoleAccess, TeamAccess from awx.main.models import User, Organization, Inventory @@ -59,6 +60,57 @@ def test_user_queryset(user): assert qs.count() == 1 +@pytest.mark.django_db +@pytest.mark.parametrize('ext_auth,superuser,expect', [ + (True, True, True), + (False, True, True), # your setting can't touch me, I'm superuser + (True, False, True), # org admin, managing my peeps + (False, False, False), # setting blocks org admin +], ids=['superuser', 'superuser-off', 'org', 'org-off']) +def test_manage_org_auth_setting(ext_auth, superuser, expect, organization, rando, user, team): + u = user('foo-user', is_superuser=superuser) + if not superuser: + organization.admin_role.members.add(u) + + with mock.patch('awx.main.access.settings') as settings_mock: + settings_mock.MANAGE_ORGANIZATION_AUTH = ext_auth + assert [ + # use via /api/v2/users/N/roles/ + UserAccess(u).can_attach(rando, organization.admin_role, 'roles'), + UserAccess(u).can_attach(rando, team.admin_role, 'roles'), + # use via /api/v2/roles/N/users/ + RoleAccess(u).can_attach(organization.admin_role, rando, 'members'), + RoleAccess(u).can_attach(team.admin_role, rando, 'members') + ] == [expect for i in range(4)] + assert [ + # use via /api/v2/users/N/roles/ + UserAccess(u).can_unattach(rando, organization.admin_role, 'roles'), + UserAccess(u).can_unattach(rando, team.admin_role, 'roles'), + # use via /api/v2/roles/N/users/ + RoleAccess(u).can_unattach(organization.admin_role, rando, 'members'), + RoleAccess(u).can_unattach(team.admin_role, rando, 'members') + ] == [expect for i in range(4)] + + +@pytest.mark.django_db +@pytest.mark.parametrize('ext_auth', [True, False]) +def test_team_org_resource_role(ext_auth, organization, rando, org_admin, team): + with mock.patch('awx.main.access.settings') as settings_mock: + settings_mock.MANAGE_ORGANIZATION_AUTH = ext_auth + assert [ + # use via /api/v2/teams/N/roles/ + TeamAccess(org_admin).can_attach(team, organization.workflow_admin_role, 'roles'), + # use via /api/v2/roles/teams/ + RoleAccess(org_admin).can_attach(organization.workflow_admin_role, team, 'member_role.parents') + ] == [True for i in range(2)] + assert [ + # use via /api/v2/teams/N/roles/ + TeamAccess(org_admin).can_unattach(team, organization.workflow_admin_role, 'roles'), + # use via /api/v2/roles/teams/ + RoleAccess(org_admin).can_unattach(organization.workflow_admin_role, team, 'member_role.parents') + ] == [True for i in range(2)] + + @pytest.mark.django_db def test_user_accessible_objects(user, organization): '''