diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 772784d875..bda63004c4 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1469,6 +1469,16 @@ class RoleSerializer(BaseSerializer): class ResourceAccessListElementSerializer(UserSerializer): def to_representation(self, user): + ''' + With this method we derive "direct" and "indirect" access lists. Contained + in the direct access list are all the roles the user is a member of, and + all of the roles that are directly granted to any teams that the user is a + member of. + + The indirect access list is a list of all of the roles that the user is + a member of that are ancestors of any roles that grant permissions to + the resource. + ''' ret = super(ResourceAccessListElementSerializer, self).to_representation(user) object_id = self.context['view'].object_id obj = self.context['view'].resource_model.objects.get(pk=object_id) @@ -1487,8 +1497,8 @@ class ResourceAccessListElementSerializer(UserSerializer): pass return { 'role': role_dict, 'permissions': get_role_permissions_on_resource(obj, role)} - def format_team_role_perm(team_role, all_permissive_role_ids): - role = team_role.children.filter(id__in=all_permissive_role_ids)[0] + def format_team_role_perm(team_role, permissive_role_ids): + role = team_role.children.filter(id__in=permissive_role_ids)[0] role_dict = { 'id': role.id, @@ -1507,24 +1517,37 @@ class ResourceAccessListElementSerializer(UserSerializer): team_content_type = ContentType.objects.get_for_model(Team) content_type = ContentType.objects.get_for_model(obj) - direct_permissive_role_ids = RolePermission.objects.filter(content_type=content_type, object_id=obj.id).values_list('role__id') - direct_access_roles = user.roles.filter(id__in=direct_permissive_role_ids).all() - ret['summary_fields']['direct_access'] = [format_role_perm(r) for r in direct_access_roles] + direct_permissive_role_ids = RolePermission.objects.filter(content_type=content_type, object_id=obj.id).values_list('role__id') all_permissive_role_ids = RolePermission.objects.filter(content_type=content_type, object_id=obj.id).values_list('role__ancestors__id') - team_roles = Role.objects \ - .filter(content_type=team_content_type, - members=user, - children__in=all_permissive_role_ids) + direct_access_roles = user.roles \ + .filter(id__in=direct_permissive_role_ids).all() + + direct_team_roles = Role.objects \ + .filter(content_type=team_content_type, + members=user, + children__in=direct_permissive_role_ids) + + indirect_team_roles = Role.objects \ + .filter(content_type=team_content_type, + members=user, + children__in=all_permissive_role_ids) \ + .exclude(id__in=direct_team_roles) indirect_access_roles = user.roles \ - .filter(id__in=all_permissive_role_ids) \ + .filter(id__in=all_permissive_role_ids) \ .exclude(id__in=direct_permissive_role_ids) \ - .exclude(id__in=team_roles) + .exclude(id__in=direct_team_roles) \ + .exclude(id__in=indirect_team_roles) + + ret['summary_fields']['direct_access'] \ + = [format_role_perm(r) for r in direct_access_roles] \ + + [format_team_role_perm(r, direct_permissive_role_ids) for r in direct_team_roles] + ret['summary_fields']['indirect_access'] \ = [format_role_perm(r) for r in indirect_access_roles] \ - + [format_team_role_perm(r, all_permissive_role_ids) for r in team_roles] + + [format_team_role_perm(r, all_permissive_role_ids) for r in indirect_team_roles] return ret diff --git a/awx/main/tests/functional/api/test_resource_access_lists.py b/awx/main/tests/functional/api/test_resource_access_lists.py index 48e261b977..75e55fd8ca 100644 --- a/awx/main/tests/functional/api/test_resource_access_lists.py +++ b/awx/main/tests/functional/api/test_resource_access_lists.py @@ -3,38 +3,54 @@ import pytest from django.core.urlresolvers import reverse @pytest.mark.django_db -def test_indirect_access_list(get, organization, project, team, alice, bob, admin): +def test_indirect_access_list(get, organization, project, team_factory, user, admin): + project_admin = user('project_admin') + org_admin_team_member = user('org_admin_team_member') + project_admin_team_member = user('project_admin_team_member') - project.admin_role.members.add(alice) - team.member_role.members.add(bob) - team.member_role.children.add(organization.admin_role) + org_admin_team = team_factory('org-admin-team') + project_admin_team = team_factory('project-admin-team') + + project.admin_role.members.add(project_admin) + org_admin_team.member_role.members.add(org_admin_team_member) + org_admin_team.member_role.children.add(organization.admin_role) + project_admin_team.member_role.members.add(project_admin_team_member) + project_admin_team.member_role.children.add(project.admin_role) result = get(reverse('api:project_access_list', args=(project.id,)), admin) assert result.status_code == 200 - # Result should be alice should have direct access, bob should have - # indirect access through being a team member -> org admin -> project admin, - # and admin should have access through system admin -> org admin -> project admin - assert result.data['count'] == 3 + # Result should be: + # project_admin should have direct access, + # project_team_admin should have "direct" access through being a team member -> project admin, + # org_admin_team_member should have indirect access through being a team member -> org admin -> project admin, + # admin should have access through system admin -> org admin -> project admin + assert result.data['count'] == 4 - alice_res = [r for r in result.data['results'] if r['id'] == alice.id][0] - bob_res = [r for r in result.data['results'] if r['id'] == bob.id][0] + project_admin_res = [r for r in result.data['results'] if r['id'] == project_admin.id][0] + org_admin_team_member_res = [r for r in result.data['results'] if r['id'] == org_admin_team_member.id][0] + project_admin_team_member_res = [r for r in result.data['results'] if r['id'] == project_admin_team_member.id][0] admin_res = [r for r in result.data['results'] if r['id'] == admin.id][0] - assert len(alice_res['summary_fields']['direct_access']) == 1 - assert len(alice_res['summary_fields']['indirect_access']) == 0 - assert len(bob_res['summary_fields']['direct_access']) == 0 - assert len(bob_res['summary_fields']['indirect_access']) == 1 + assert len(project_admin_res['summary_fields']['direct_access']) == 1 + assert len(project_admin_res['summary_fields']['indirect_access']) == 0 + assert len(org_admin_team_member_res['summary_fields']['direct_access']) == 0 + assert len(org_admin_team_member_res['summary_fields']['indirect_access']) == 1 assert len(admin_res['summary_fields']['direct_access']) == 0 assert len(admin_res['summary_fields']['indirect_access']) == 1 - alice_entry = alice_res['summary_fields']['direct_access'][0]['role'] - assert alice_entry['id'] == project.admin_role.id + project_admin_entry = project_admin_res['summary_fields']['direct_access'][0]['role'] + assert project_admin_entry['id'] == project.admin_role.id - bob_entry = bob_res['summary_fields']['indirect_access'][0]['role'] - assert bob_entry['id'] == organization.admin_role.id - assert bob_entry['team_id'] == team.id - assert bob_entry['team_name'] == team.name + project_admin_team_member_entry = project_admin_team_member_res['summary_fields']['direct_access'][0]['role'] + assert project_admin_team_member_entry['id'] == project.admin_role.id + assert project_admin_team_member_entry['team_id'] == project_admin_team.id + assert project_admin_team_member_entry['team_name'] == project_admin_team.name + + org_admin_team_member_entry = org_admin_team_member_res['summary_fields']['indirect_access'][0]['role'] + assert org_admin_team_member_entry['id'] == organization.admin_role.id + assert org_admin_team_member_entry['team_id'] == org_admin_team.id + assert org_admin_team_member_entry['team_name'] == org_admin_team.name admin_entry = admin_res['summary_fields']['indirect_access'][0]['role'] assert admin_entry['name'] == 'System Administrator'