mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
allow deleting hosts and groups from inv src sublists
This commit is contained in:
parent
f2d46baf09
commit
d2e0b26287
@ -37,9 +37,10 @@ from awx.api.metadata import SublistAttachDetatchMetadata
|
|||||||
|
|
||||||
__all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView',
|
__all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView',
|
||||||
'ListCreateAPIView', 'SubListAPIView', 'SubListCreateAPIView',
|
'ListCreateAPIView', 'SubListAPIView', 'SubListCreateAPIView',
|
||||||
|
'SubListDestroyAPIView',
|
||||||
'SubListCreateAttachDetachAPIView', 'RetrieveAPIView',
|
'SubListCreateAttachDetachAPIView', 'RetrieveAPIView',
|
||||||
'RetrieveUpdateAPIView', 'RetrieveDestroyAPIView',
|
'RetrieveUpdateAPIView', 'RetrieveDestroyAPIView',
|
||||||
'RetrieveUpdateDestroyAPIView', 'DestroyAPIView',
|
'RetrieveUpdateDestroyAPIView',
|
||||||
'SubDetailAPIView',
|
'SubDetailAPIView',
|
||||||
'ResourceAccessList',
|
'ResourceAccessList',
|
||||||
'ParentMixin',
|
'ParentMixin',
|
||||||
@ -440,6 +441,41 @@ class SubListAPIView(ParentMixin, ListAPIView):
|
|||||||
return qs & sublist_qs
|
return qs & sublist_qs
|
||||||
|
|
||||||
|
|
||||||
|
class DestroyAPIView(generics.DestroyAPIView):
|
||||||
|
|
||||||
|
def has_delete_permission(self, obj):
|
||||||
|
return self.request.user.can_access(self.model, 'delete', obj)
|
||||||
|
|
||||||
|
def perform_destroy(self, instance, check_permission=True):
|
||||||
|
if check_permission and not self.has_delete_permission(instance):
|
||||||
|
raise PermissionDenied()
|
||||||
|
super(DestroyAPIView, self).perform_destroy(instance)
|
||||||
|
|
||||||
|
|
||||||
|
class SubListDestroyAPIView(DestroyAPIView, SubListAPIView):
|
||||||
|
"""
|
||||||
|
Concrete view for deleting everything related by `relationship`.
|
||||||
|
"""
|
||||||
|
check_sub_obj_permission = True
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
instance_list = self.get_queryset()
|
||||||
|
if (not self.check_sub_obj_permission and
|
||||||
|
not request.user.can_access(self.parent_model, 'delete', self.get_parent_object())):
|
||||||
|
raise PermissionDenied()
|
||||||
|
self.perform_list_destroy(instance_list)
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
def perform_list_destroy(self, instance_list):
|
||||||
|
if self.check_sub_obj_permission:
|
||||||
|
# Check permissions for all before deleting, avoiding half-deleted lists
|
||||||
|
for instance in instance_list:
|
||||||
|
if self.has_delete_permission(instance):
|
||||||
|
raise PermissionDenied()
|
||||||
|
for instance in instance_list:
|
||||||
|
self.perform_destroy(instance, check_permission=False)
|
||||||
|
|
||||||
|
|
||||||
class SubListCreateAPIView(SubListAPIView, ListCreateAPIView):
|
class SubListCreateAPIView(SubListAPIView, ListCreateAPIView):
|
||||||
# Base class for a sublist view that allows for creating subobjects
|
# Base class for a sublist view that allows for creating subobjects
|
||||||
# associated with the parent object.
|
# associated with the parent object.
|
||||||
@ -678,22 +714,11 @@ class RetrieveUpdateAPIView(RetrieveAPIView, generics.RetrieveUpdateAPIView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RetrieveDestroyAPIView(RetrieveAPIView, generics.RetrieveDestroyAPIView):
|
class RetrieveDestroyAPIView(RetrieveAPIView, DestroyAPIView):
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
# somewhat lame that delete has to call it's own permissions check
|
|
||||||
obj = self.get_object()
|
|
||||||
if not request.user.can_access(self.model, 'delete', obj):
|
|
||||||
raise PermissionDenied()
|
|
||||||
obj.delete()
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
||||||
|
|
||||||
|
|
||||||
class RetrieveUpdateDestroyAPIView(RetrieveUpdateAPIView, RetrieveDestroyAPIView):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DestroyAPIView(GenericAPIView, generics.DestroyAPIView):
|
class RetrieveUpdateDestroyAPIView(RetrieveUpdateAPIView, DestroyAPIView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
6
awx/api/templates/api/sub_list_destroy_api_view.md
Normal file
6
awx/api/templates/api/sub_list_destroy_api_view.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% include "api/sub_list_create_api_view.md" %}
|
||||||
|
|
||||||
|
# Delete all {{ model_verbose_name_plural }} of this {{ parent_model_verbose_name|title }}:
|
||||||
|
|
||||||
|
Make a DELETE request to this resource to delete all {{ model_verbose_name_plural }} show in the list.
|
||||||
|
The {{ parent_model_verbose_name|title }} will not be deleted by this request.
|
@ -2616,23 +2616,25 @@ class InventorySourceNotificationTemplatesSuccessList(InventorySourceNotificatio
|
|||||||
relationship = 'notification_templates_success'
|
relationship = 'notification_templates_success'
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceHostsList(SubListAPIView):
|
class InventorySourceHostsList(SubListDestroyAPIView):
|
||||||
|
|
||||||
model = Host
|
model = Host
|
||||||
serializer_class = HostSerializer
|
serializer_class = HostSerializer
|
||||||
parent_model = InventorySource
|
parent_model = InventorySource
|
||||||
relationship = 'hosts'
|
relationship = 'hosts'
|
||||||
new_in_148 = True
|
new_in_148 = True
|
||||||
|
check_sub_obj_permission = False
|
||||||
capabilities_prefetch = ['inventory.admin']
|
capabilities_prefetch = ['inventory.admin']
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceGroupsList(SubListAPIView):
|
class InventorySourceGroupsList(SubListDestroyAPIView):
|
||||||
|
|
||||||
model = Group
|
model = Group
|
||||||
serializer_class = GroupSerializer
|
serializer_class = GroupSerializer
|
||||||
parent_model = InventorySource
|
parent_model = InventorySource
|
||||||
relationship = 'groups'
|
relationship = 'groups'
|
||||||
new_in_148 = True
|
new_in_148 = True
|
||||||
|
check_sub_obj_permission = False
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceUpdatesList(SubListAPIView):
|
class InventorySourceUpdatesList(SubListAPIView):
|
||||||
|
@ -60,3 +60,34 @@ def test_proxy_ip_whitelist(get, patch, admin):
|
|||||||
REMOTE_HOST='my.proxy.example.org',
|
REMOTE_HOST='my.proxy.example.org',
|
||||||
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
||||||
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestDeleteViews:
|
||||||
|
def test_sublist_delete_permission_check(self, inventory_source, host, rando, delete):
|
||||||
|
inventory_source.hosts.add(host)
|
||||||
|
inventory_source.inventory.read_role.members.add(rando)
|
||||||
|
delete(
|
||||||
|
reverse(
|
||||||
|
'api:inventory_source_hosts_list',
|
||||||
|
kwargs={'version': 'v2', 'pk': inventory_source.pk}
|
||||||
|
), user=rando, expect=403
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_sublist_delete_functionality(self, inventory_source, host, rando, delete):
|
||||||
|
inventory_source.hosts.add(host)
|
||||||
|
inventory_source.inventory.admin_role.members.add(rando)
|
||||||
|
delete(
|
||||||
|
reverse(
|
||||||
|
'api:inventory_source_hosts_list',
|
||||||
|
kwargs={'version': 'v2', 'pk': inventory_source.pk}
|
||||||
|
), user=rando, expect=204
|
||||||
|
)
|
||||||
|
assert inventory_source.hosts.count() == 0
|
||||||
|
|
||||||
|
def test_destroy_permission_check(self, job_factory, system_auditor, delete):
|
||||||
|
job = job_factory()
|
||||||
|
resp = delete(
|
||||||
|
job.get_absolute_url(), user=system_auditor
|
||||||
|
)
|
||||||
|
assert resp.status_code == 403
|
||||||
|
@ -377,7 +377,7 @@ def admin(user):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def system_auditor(user):
|
def system_auditor(user):
|
||||||
u = user(False)
|
u = user('sys-auditor', False)
|
||||||
Role.singleton('system_auditor').members.add(u)
|
Role.singleton('system_auditor').members.add(u)
|
||||||
return u
|
return u
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user