diff --git a/awx/api/generics.py b/awx/api/generics.py index 3371e0bc09..878dce4fa4 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -510,6 +510,9 @@ class SubListAPIView(ParentMixin, ListAPIView): # And optionally (user must have given access permission on parent object # to view sublist): # parent_access = 'read' + # filter_read_permission sets whether or not to override the default intersection behavior + # implemented here + filter_read_permission = True def get_description_context(self): d = super(SubListAPIView, self).get_description_context() @@ -524,8 +527,14 @@ class SubListAPIView(ParentMixin, ListAPIView): def get_queryset(self): parent = self.get_parent_object() self.check_parent_access(parent) - qs = self.request.user.get_queryset(self.model).distinct() sublist_qs = self.get_sublist_queryset(parent) + if not self.filter_read_permission: + access_class = access_registry[self.model] + if access_class.prefetch_related: + return sublist_qs.prefetch_related(*access_class.prefetch_related) + if access_class.select_related: + return sublist_qs.select_related(*access_class.select_related) + qs = self.request.user.get_queryset(self.model).distinct() return qs & sublist_qs def get_sublist_queryset(self, parent): diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 8e0775c34e..676a90eca9 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -2581,16 +2581,7 @@ class JobTemplateCredentialsList(SubListCreateAttachDetachAPIView): serializer_class = serializers.CredentialSerializer parent_model = models.JobTemplate relationship = 'credentials' - - def get_queryset(self): - # Return the full list of credentials - parent = self.get_parent_object() - self.check_parent_access(parent) - sublist_qs = getattrd(parent, self.relationship) - sublist_qs = sublist_qs.prefetch_related( - 'created_by', 'modified_by', 'admin_role', 'use_role', 'read_role', 'admin_role__parents', 'admin_role__members' - ) - return sublist_qs + filter_read_permission = False def is_valid_relation(self, parent, sub, created=False): if sub.unique_hash() in [cred.unique_hash() for cred in parent.credentials.all()]: @@ -2780,6 +2771,7 @@ class JobTemplateInstanceGroupsList(SubListAttachDetachAPIView): serializer_class = serializers.InstanceGroupSerializer parent_model = models.JobTemplate relationship = 'instance_groups' + filter_read_permission = False class JobTemplateAccessList(ResourceAccessList): diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index ece00c157b..1dd03388d7 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -207,6 +207,7 @@ class OrganizationInstanceGroupsList(SubListAttachDetachAPIView): serializer_class = InstanceGroupSerializer parent_model = Organization relationship = 'instance_groups' + filter_read_permission = False class OrganizationGalaxyCredentialsList(SubListAttachDetachAPIView): @@ -214,6 +215,7 @@ class OrganizationGalaxyCredentialsList(SubListAttachDetachAPIView): serializer_class = CredentialSerializer parent_model = Organization relationship = 'galaxy_credentials' + filter_read_permission = False def is_valid_relation(self, parent, sub, created=False): if sub.kind != 'galaxy_api_token': diff --git a/awx/main/tests/functional/api/test_organizations.py b/awx/main/tests/functional/api/test_organizations.py index f86963ecc6..af2f918ba0 100644 --- a/awx/main/tests/functional/api/test_organizations.py +++ b/awx/main/tests/functional/api/test_organizations.py @@ -329,3 +329,21 @@ def test_galaxy_credential_association(alice, admin, organization, post, get): 'Public Galaxy 4', 'Public Galaxy 5', ] + + +@pytest.mark.django_db +def test_org_admin_credential_count(org_admin, admin, organization, post, get): + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + + for i in range(3): + cred = Credential.objects.create(credential_type=galaxy, name=f'test_{i}', inputs={'url': 'https://galaxy.ansible.com/'}) + url = reverse('api:organization_galaxy_credentials_list', kwargs={'pk': organization.pk}) + post(url, {'associate': True, 'id': cred.pk}, user=admin, expect=204) + # org admin should see all associated galaxy credentials + resp = get(url, user=org_admin) + assert resp.data['count'] == 3 + # removing one to validate new count + post(url, {'disassociate': True, 'id': Credential.objects.get(name='test_1').pk}, user=admin, expect=204) + resp_new = get(url, user=org_admin) + assert resp_new.data['count'] == 2