diff --git a/awx/api/permissions.py b/awx/api/permissions.py index c9a7f68d13..3224b555e9 100644 --- a/awx/api/permissions.py +++ b/awx/api/permissions.py @@ -231,8 +231,10 @@ class IsSuperUser(permissions.BasePermission): class InstanceGroupTowerPermission(ModelAccessPermission): def has_object_permission(self, request, view, obj): - if request.method not in permissions.SAFE_METHODS: - if obj.name == "tower": - return False + if request.method == 'DELETE' and obj.name == "tower": + return False + if request.method in ['PATCH', 'PUT'] and obj.name == 'tower' and \ + request and request.data and request.data.get('name', '') != 'tower': + return False return super(InstanceGroupTowerPermission, self).has_object_permission(request, view, obj) diff --git a/awx/api/views.py b/awx/api/views.py index 029b7b480f..ead6142202 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -661,6 +661,7 @@ class InstanceGroupList(ListCreateAPIView): class InstanceGroupDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAPIView): + always_allow_superuser = False view_name = _("Instance Group Detail") model = InstanceGroup serializer_class = InstanceGroupSerializer diff --git a/awx/main/access.py b/awx/main/access.py index bcba7ad3ca..5e1252e853 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -469,6 +469,15 @@ class InstanceGroupAccess(BaseAccess): def can_change(self, obj, data): return self.user.is_superuser + def can_delete(self, obj): + return self.user.is_superuser + + def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): + return self.user.is_superuser + + def can_unattach(self, obj, sub_obj, relationship, *args, **kwargs): + return self.user.is_superuser + class UserAccess(BaseAccess): ''' diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py index 6e4b8f89cb..038950927c 100644 --- a/awx/main/tests/functional/api/test_instance_group.py +++ b/awx/main/tests/functional/api/test_instance_group.py @@ -80,11 +80,12 @@ def test_delete_instance_group_jobs_running(delete, instance_group_jobs_running, @pytest.mark.django_db -def test_delete_tower_instance_group_prevented(delete, options, tower_instance_group, admin): +def test_delete_tower_instance_group_prevented(delete, options, tower_instance_group, user): url = reverse("api:instance_group_detail", kwargs={'pk': tower_instance_group.pk}) - delete(url, None, admin, expect=403) - resp = options(url, None, admin, expect=200) - actions = ['DELETE', 'PATCH', 'PUT'] + super_user = user('bob', True) + delete(url, None, super_user, expect=403) + resp = options(url, None, super_user, expect=200) + actions = ['GET', 'PUT',] + assert 'DELETE' not in resp.data['actions'] for action in actions: - assert action not in resp.data['actions'] - assert 'GET' in resp.data['actions'] + assert action in resp.data['actions']