diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 05fe7bf426..46bcfcb20d 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -329,7 +329,6 @@ class BaseSerializer(serializers.ModelSerializer): 'id': role.id, 'name': role.name, 'description': role.description, - 'url': role.get_absolute_url(), } if len(roles) > 0: summary_fields['roles'] = roles @@ -512,6 +511,8 @@ class UnifiedJobTemplateSerializer(BaseSerializer): res['last_job'] = obj.last_job.get_absolute_url() if obj.next_schedule: res['next_schedule'] = obj.next_schedule.get_absolute_url() + res['roles'] = reverse('api:job_template_roles_list', args=(obj.pk,)) + return res def get_types(self): @@ -804,7 +805,8 @@ class OrganizationSerializer(BaseSerializer): notification_templates_any = reverse('api:organization_notification_templates_any_list', args=(obj.pk,)), notification_templates_success = reverse('api:organization_notification_templates_success_list', args=(obj.pk,)), notification_templates_error = reverse('api:organization_notification_templates_error_list', args=(obj.pk,)), - access_list = reverse('api:organization_access_list', args=(obj.pk,)), + roles = reverse('api:organization_roles_list', args=(obj.pk,)), + access_list = reverse('api:organization_access_list', args=(obj.pk,)), )) return res @@ -889,6 +891,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): notification_templates_success = reverse('api:project_notification_templates_success_list', args=(obj.pk,)), notification_templates_error = reverse('api:project_notification_templates_error_list', args=(obj.pk,)), access_list = reverse('api:project_access_list', args=(obj.pk,)), + roles = reverse('api:project_roles_list', args=(obj.pk,)), )) if obj.organization: res['organization'] = reverse('api:organization_detail', @@ -992,6 +995,7 @@ class InventorySerializer(BaseSerializerWithVariables): scan_job_templates = reverse('api:inventory_scan_job_template_list', args=(obj.pk,)), ad_hoc_commands = reverse('api:inventory_ad_hoc_commands_list', args=(obj.pk,)), access_list = reverse('api:inventory_access_list', args=(obj.pk,)), + roles = reverse('api:inventory_roles_list', args=(obj.pk,)), #single_fact = reverse('api:inventory_single_fact_view', args=(obj.pk,)), )) if obj.organization: @@ -1163,6 +1167,7 @@ class GroupSerializer(BaseSerializerWithVariables): inventory_sources = reverse('api:group_inventory_sources_list', args=(obj.pk,)), ad_hoc_commands = reverse('api:group_ad_hoc_commands_list', args=(obj.pk,)), access_list = reverse('api:group_access_list', args=(obj.pk,)), + roles = reverse('api:group_roles_list', args=(obj.pk,)), #single_fact = reverse('api:group_single_fact_view', args=(obj.pk,)), )) if obj.inventory: @@ -1510,7 +1515,7 @@ class ResourceAccessListElementSerializer(UserSerializer): role_dict['related'] = reverse_gfk(role.content_object) except: pass - return { 'role': role_dict, 'active_roles': get_roles_on_resource(obj, role)} + return { 'role': role_dict, 'descendant_roles': get_roles_on_resource(obj, role)} def format_team_role_perm(team_role, permissive_role_ids): role = team_role.children.filter(id__in=permissive_role_ids)[0] @@ -1528,7 +1533,7 @@ class ResourceAccessListElementSerializer(UserSerializer): role_dict['related'] = reverse_gfk(role.content_object) except: pass - return { 'role': role_dict, 'active_roles': get_roles_on_resource(obj, team_role)} + return { 'role': role_dict, 'descendant_roles': get_roles_on_resource(obj, team_role)} team_content_type = ContentType.objects.get_for_model(Team) content_type = ContentType.objects.get_for_model(obj) @@ -1624,7 +1629,8 @@ class CredentialSerializer(BaseSerializer): res = super(CredentialSerializer, self).get_related(obj) res.update(dict( activity_stream = reverse('api:credential_activity_stream_list', args=(obj.pk,)), - access_list = reverse('api:credential_access_list', args=(obj.pk,)), + access_list = reverse('api:credential_access_list', args=(obj.pk,)), + roles = reverse('api:credential_roles_list', args=(obj.pk,)), )) parents = obj.owner_role.parents.exclude(object_id__isnull=True) diff --git a/awx/api/urls.py b/awx/api/urls.py index 9d54a0a0fe..c05cf30366 100644 --- a/awx/api/urls.py +++ b/awx/api/urls.py @@ -25,6 +25,7 @@ organization_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/notification_templates_any/$', 'organization_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', 'organization_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', 'organization_notification_templates_success_list'), + url(r'^(?P[0-9]+)/roles/$', 'organization_roles_list'), url(r'^(?P[0-9]+)/access_list/$', 'organization_access_list'), ) @@ -39,6 +40,7 @@ user_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/roles/$', 'user_roles_list'), url(r'^(?P[0-9]+)/activity_stream/$', 'user_activity_stream_list'), url(r'^(?P[0-9]+)/access_list/$', 'user_access_list'), + ) project_urls = patterns('awx.api.views', @@ -53,6 +55,7 @@ project_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/notification_templates_any/$', 'project_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', 'project_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', 'project_notification_templates_success_list'), + url(r'^(?P[0-9]+)/roles/$', 'project_roles_list'), url(r'^(?P[0-9]+)/access_list/$', 'project_access_list'), ) @@ -89,6 +92,7 @@ inventory_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'), url(r'^(?P[0-9]+)/ad_hoc_commands/$', 'inventory_ad_hoc_commands_list'), url(r'^(?P[0-9]+)/access_list/$', 'inventory_access_list'), + url(r'^(?P[0-9]+)/roles/$', 'inventory_roles_list'), #url(r'^(?P[0-9]+)/single_fact/$', 'inventory_single_fact_view'), ) @@ -123,6 +127,7 @@ group_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/inventory_sources/$', 'group_inventory_sources_list'), url(r'^(?P[0-9]+)/ad_hoc_commands/$', 'group_ad_hoc_commands_list'), url(r'^(?P[0-9]+)/access_list/$', 'group_access_list'), + url(r'^(?P[0-9]+)/roles/$', 'group_roles_list'), #url(r'^(?P[0-9]+)/single_fact/$', 'group_single_fact_view'), ) @@ -157,6 +162,7 @@ credential_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/activity_stream/$', 'credential_activity_stream_list'), url(r'^(?P[0-9]+)/$', 'credential_detail'), url(r'^(?P[0-9]+)/access_list/$', 'credential_access_list'), + url(r'^(?P[0-9]+)/roles/$', 'credential_roles_list'), # See also credentials resources on users/teams. ) @@ -182,6 +188,7 @@ job_template_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/notification_templates_error/$', 'job_template_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', 'job_template_notification_templates_success_list'), url(r'^(?P[0-9]+)/access_list/$', 'job_template_access_list'), + url(r'^(?P[0-9]+)/roles/$', 'job_template_roles_list'), url(r'^(?P[0-9]+)/labels/$', 'job_template_label_list'), ) diff --git a/awx/api/views.py b/awx/api/views.py index 6674794d12..971ff965d2 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -799,6 +799,18 @@ class OrganizationAccessList(ResourceAccessList): resource_model = Organization new_in_300 = True +class OrganizationRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Organization + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class TeamList(ListCreateAPIView): model = Team @@ -1064,6 +1076,18 @@ class ProjectAccessList(ResourceAccessList): resource_model = Project new_in_300 = True +class ProjectRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Project + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class UserList(ListCreateAPIView): model = User @@ -1359,6 +1383,18 @@ class CredentialAccessList(ResourceAccessList): resource_model = Credential new_in_300 = True +class CredentialRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Credential + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class InventoryScriptList(ListCreateAPIView): model = CustomInventoryScript @@ -1429,6 +1465,18 @@ class InventoryAccessList(ResourceAccessList): resource_model = Inventory new_in_300 = True +class InventoryRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Inventory + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class InventoryJobTemplateList(SubListAPIView): model = JobTemplate @@ -1764,6 +1812,18 @@ class GroupAccessList(ResourceAccessList): resource_model = Group new_in_300 = True +class GroupRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Group + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class InventoryGroupsList(SubListCreateAttachDetachAPIView): @@ -2482,6 +2542,18 @@ class JobTemplateAccessList(ResourceAccessList): resource_model = JobTemplate new_in_300 = True +class JobTemplateRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = JobTemplate + new_in_300 = True + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk).all() + class SystemJobTemplateList(ListAPIView): model = SystemJobTemplate diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/test_rbac_api.py index 2297868aa6..10438504e2 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/test_rbac_api.py @@ -408,15 +408,14 @@ def test_ensure_rbac_fields_are_present(organization, get, admin): assert 'summary_fields' in org assert 'roles' in org['summary_fields'] - org_role_response = get(org['summary_fields']['roles']['admin_role']['url'], admin) + role_pk = org['summary_fields']['roles']['admin_role']['id'] + role_url = reverse('api:role_detail', args=(role_pk,)) + org_role_response = get(role_url, admin) + assert org_role_response.status_code == 200 role = org_role_response.data assert role['related']['organization'] == url - - - - @pytest.mark.django_db def test_ensure_permissions_is_present(organization, get, user): url = reverse('api:organization_detail', args=(organization.id,))