mirror of
https://github.com/ansible/awx.git
synced 2026-03-07 19:51:08 -03:30
Merge pull request #6245 from AlanCoding/alan_rockin_ramparts
Introduce view to allow associations but no creations
This commit is contained in:
@@ -41,7 +41,8 @@ __all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView',
|
|||||||
'SubDetailAPIView',
|
'SubDetailAPIView',
|
||||||
'ResourceAccessList',
|
'ResourceAccessList',
|
||||||
'ParentMixin',
|
'ParentMixin',
|
||||||
'DeleteLastUnattachLabelMixin',]
|
'DeleteLastUnattachLabelMixin',
|
||||||
|
'SubListAttachDetachAPIView',]
|
||||||
|
|
||||||
logger = logging.getLogger('awx.api.generics')
|
logger = logging.getLogger('awx.api.generics')
|
||||||
analytics_logger = logging.getLogger('awx.analytics.performance')
|
analytics_logger = logging.getLogger('awx.analytics.performance')
|
||||||
@@ -553,6 +554,21 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView):
|
|||||||
return self.attach(request, *args, **kwargs)
|
return self.attach(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SubListAttachDetachAPIView(SubListCreateAttachDetachAPIView):
|
||||||
|
'''
|
||||||
|
Derived version of SubListCreateAttachDetachAPIView that prohibits creation
|
||||||
|
'''
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
sub_id = request.data.get('id', None)
|
||||||
|
if not sub_id:
|
||||||
|
return Response(
|
||||||
|
dict(msg=_("{} 'id' field is missing.".format(
|
||||||
|
self.model._meta.verbose_name.title()))),
|
||||||
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return super(SubListAttachDetachAPIView, self).post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DeleteLastUnattachLabelMixin(object):
|
class DeleteLastUnattachLabelMixin(object):
|
||||||
'''
|
'''
|
||||||
Models for which you want the last instance to be deleted from the database
|
Models for which you want the last instance to be deleted from the database
|
||||||
|
|||||||
@@ -988,7 +988,7 @@ class OrganizationNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIV
|
|||||||
new_in_300 = True
|
new_in_300 = True
|
||||||
|
|
||||||
|
|
||||||
class OrganizationInstanceGroupsList(SubListCreateAttachDetachAPIView):
|
class OrganizationInstanceGroupsList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = InstanceGroup
|
model = InstanceGroup
|
||||||
serializer_class = InstanceGroupSerializer
|
serializer_class = InstanceGroupSerializer
|
||||||
@@ -996,13 +996,6 @@ class OrganizationInstanceGroupsList(SubListCreateAttachDetachAPIView):
|
|||||||
relationship = 'instance_groups'
|
relationship = 'instance_groups'
|
||||||
new_in_320 = True
|
new_in_320 = True
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
sub_id = request.data.get('id', None)
|
|
||||||
if not sub_id:
|
|
||||||
return Response(dict(msg=_("Instance Group 'id' field is missing.")),
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
return super(OrganizationInstanceGroupsList, self).post(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class OrganizationAccessList(ResourceAccessList):
|
class OrganizationAccessList(ResourceAccessList):
|
||||||
|
|
||||||
@@ -1049,7 +1042,7 @@ class TeamUsersList(BaseUsersList):
|
|||||||
relationship = 'member_role.members'
|
relationship = 'member_role.members'
|
||||||
|
|
||||||
|
|
||||||
class TeamRolesList(SubListCreateAttachDetachAPIView):
|
class TeamRolesList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = Role
|
model = Role
|
||||||
serializer_class = RoleSerializerWithParentAccess
|
serializer_class = RoleSerializerWithParentAccess
|
||||||
@@ -1065,11 +1058,9 @@ class TeamRolesList(SubListCreateAttachDetachAPIView):
|
|||||||
return Role.filter_visible_roles(self.request.user, team.member_role.children.all().exclude(pk=team.read_role.pk))
|
return Role.filter_visible_roles(self.request.user, team.member_role.children.all().exclude(pk=team.read_role.pk))
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
# Forbid implicit role creation here
|
|
||||||
sub_id = request.data.get('id', None)
|
sub_id = request.data.get('id', None)
|
||||||
if not sub_id:
|
if not sub_id:
|
||||||
data = dict(msg=_("Role 'id' field is missing."))
|
return super(TeamRolesList, self).post(request)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
role = get_object_or_400(Role, pk=sub_id)
|
role = get_object_or_400(Role, pk=sub_id)
|
||||||
org_content_type = ContentType.objects.get_for_model(Organization)
|
org_content_type = ContentType.objects.get_for_model(Organization)
|
||||||
@@ -1432,7 +1423,7 @@ class UserTeamsList(ListAPIView):
|
|||||||
Q(member_role__members=u) | Q(admin_role__members=u)).distinct()
|
Q(member_role__members=u) | Q(admin_role__members=u)).distinct()
|
||||||
|
|
||||||
|
|
||||||
class UserRolesList(SubListCreateAttachDetachAPIView):
|
class UserRolesList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = Role
|
model = Role
|
||||||
serializer_class = RoleSerializerWithParentAccess
|
serializer_class = RoleSerializerWithParentAccess
|
||||||
@@ -1452,11 +1443,9 @@ class UserRolesList(SubListCreateAttachDetachAPIView):
|
|||||||
.exclude(content_type=content_type, object_id=u.id)
|
.exclude(content_type=content_type, object_id=u.id)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
# Forbid implicit role creation here
|
|
||||||
sub_id = request.data.get('id', None)
|
sub_id = request.data.get('id', None)
|
||||||
if not sub_id:
|
if not sub_id:
|
||||||
data = dict(msg=_("Role 'id' field is missing."))
|
return super(UserRolesList, self).post(request)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
if sub_id == self.request.user.admin_role.pk:
|
if sub_id == self.request.user.admin_role.pk:
|
||||||
raise PermissionDenied(_('You may not perform any action with your own admin_role.'))
|
raise PermissionDenied(_('You may not perform any action with your own admin_role.'))
|
||||||
@@ -1847,20 +1836,13 @@ class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView
|
|||||||
return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all()))
|
return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all()))
|
||||||
|
|
||||||
|
|
||||||
class InventoryInstanceGroupsList(SubListCreateAttachDetachAPIView):
|
class InventoryInstanceGroupsList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = InstanceGroup
|
model = InstanceGroup
|
||||||
serializer_class = InstanceGroupSerializer
|
serializer_class = InstanceGroupSerializer
|
||||||
parent_model = Inventory
|
parent_model = Inventory
|
||||||
relationship = 'instance_groups'
|
relationship = 'instance_groups'
|
||||||
new_in_320 = True
|
new_in_320 = True
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
sub_id = request.data.get('id', None)
|
|
||||||
if not sub_id:
|
|
||||||
return Response(dict(msg=_("Instance Group 'id' field is missing.")),
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
return super(InventoryInstanceGroupsList, self).post(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryAccessList(ResourceAccessList):
|
class InventoryAccessList(ResourceAccessList):
|
||||||
@@ -3004,7 +2986,7 @@ class JobTemplateJobsList(SubListCreateAPIView):
|
|||||||
parent_key = 'job_template'
|
parent_key = 'job_template'
|
||||||
|
|
||||||
|
|
||||||
class JobTemplateInstanceGroupsList(SubListCreateAttachDetachAPIView):
|
class JobTemplateInstanceGroupsList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = InstanceGroup
|
model = InstanceGroup
|
||||||
serializer_class = InstanceGroupSerializer
|
serializer_class = InstanceGroupSerializer
|
||||||
@@ -3012,13 +2994,6 @@ class JobTemplateInstanceGroupsList(SubListCreateAttachDetachAPIView):
|
|||||||
relationship = 'instance_groups'
|
relationship = 'instance_groups'
|
||||||
new_in_320 = True
|
new_in_320 = True
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
sub_id = request.data.get('id', None)
|
|
||||||
if not sub_id:
|
|
||||||
return Response(dict(msg=_("Instance Group 'id' field is missing.")),
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
return super(JobTemplateInstanceGroupsList, self).post(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class JobTemplateAccessList(ResourceAccessList):
|
class JobTemplateAccessList(ResourceAccessList):
|
||||||
|
|
||||||
@@ -4345,7 +4320,7 @@ class RoleDetail(RetrieveAPIView):
|
|||||||
new_in_300 = True
|
new_in_300 = True
|
||||||
|
|
||||||
|
|
||||||
class RoleUsersList(SubListCreateAttachDetachAPIView):
|
class RoleUsersList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = User
|
model = User
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
@@ -4362,8 +4337,7 @@ class RoleUsersList(SubListCreateAttachDetachAPIView):
|
|||||||
# Forbid implicit user creation here
|
# Forbid implicit user creation here
|
||||||
sub_id = request.data.get('id', None)
|
sub_id = request.data.get('id', None)
|
||||||
if not sub_id:
|
if not sub_id:
|
||||||
data = dict(msg=_("User 'id' field is missing."))
|
return super(RoleUsersList, self).post(request)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
user = get_object_or_400(User, pk=sub_id)
|
user = get_object_or_400(User, pk=sub_id)
|
||||||
role = self.get_parent_object()
|
role = self.get_parent_object()
|
||||||
@@ -4387,7 +4361,7 @@ class RoleUsersList(SubListCreateAttachDetachAPIView):
|
|||||||
return super(RoleUsersList, self).post(request, *args, **kwargs)
|
return super(RoleUsersList, self).post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RoleTeamsList(SubListAPIView):
|
class RoleTeamsList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
model = Team
|
model = Team
|
||||||
serializer_class = TeamSerializer
|
serializer_class = TeamSerializer
|
||||||
@@ -4402,11 +4376,9 @@ class RoleTeamsList(SubListAPIView):
|
|||||||
return Team.objects.filter(member_role__children=role)
|
return Team.objects.filter(member_role__children=role)
|
||||||
|
|
||||||
def post(self, request, pk, *args, **kwargs):
|
def post(self, request, pk, *args, **kwargs):
|
||||||
# Forbid implicit team creation here
|
|
||||||
sub_id = request.data.get('id', None)
|
sub_id = request.data.get('id', None)
|
||||||
if not sub_id:
|
if not sub_id:
|
||||||
data = dict(msg=_("Team 'id' field is missing."))
|
return super(RoleTeamsList, self).post(request)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
team = get_object_or_400(Team, pk=sub_id)
|
team = get_object_or_400(Team, pk=sub_id)
|
||||||
role = Role.objects.get(pk=self.kwargs['pk'])
|
role = Role.objects.get(pk=self.kwargs['pk'])
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from rest_framework.exceptions import PermissionDenied
|
|||||||
# AWX
|
# AWX
|
||||||
from awx.api.generics import (
|
from awx.api.generics import (
|
||||||
ParentMixin,
|
ParentMixin,
|
||||||
SubListCreateAttachDetachAPIView,
|
SubListCreateAttachDetachAPIView, SubListAttachDetachAPIView,
|
||||||
DeleteLastUnattachLabelMixin,
|
DeleteLastUnattachLabelMixin,
|
||||||
ResourceAccessList
|
ResourceAccessList
|
||||||
)
|
)
|
||||||
@@ -140,6 +140,20 @@ class TestSubListCreateAttachDetachAPIView:
|
|||||||
view.unattach_by_id.assert_not_called()
|
view.unattach_by_id.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_attach_detatch_only(mocker):
|
||||||
|
mock_request = mocker.MagicMock()
|
||||||
|
mock_request.data = {'name': 'name for my new model'}
|
||||||
|
view = SubListAttachDetachAPIView()
|
||||||
|
view.model = mocker.MagicMock()
|
||||||
|
view.model._meta = mocker.MagicMock()
|
||||||
|
view.model._meta.verbose_name = "Foo Bar"
|
||||||
|
|
||||||
|
resp = view.post(mock_request)
|
||||||
|
|
||||||
|
assert 'Foo Bar' in resp.data['msg']
|
||||||
|
assert 'field is missing' in resp.data['msg']
|
||||||
|
|
||||||
|
|
||||||
class TestDeleteLastUnattachLabelMixin:
|
class TestDeleteLastUnattachLabelMixin:
|
||||||
@mock.patch('__builtin__.super')
|
@mock.patch('__builtin__.super')
|
||||||
def test_unattach_ok(self, super, mocker):
|
def test_unattach_ok(self, super, mocker):
|
||||||
|
|||||||
Reference in New Issue
Block a user