mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 15:09:32 -02:30
Move validation of credential creation to the serializer. Use SubListCreateAPIView for creating a credential under another object, and remove fields from serializer and API docs that aren't valid when creating a credential under another object.
This commit is contained in:
@@ -9,6 +9,7 @@ import time
|
|||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
from django.http import QueryDict
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.encoding import smart_text
|
from django.utils.encoding import smart_text
|
||||||
@@ -328,10 +329,11 @@ class SubListCreateAPIView(SubListAPIView, ListCreateAPIView):
|
|||||||
|
|
||||||
# Make a copy of the data provided (since it's readonly) in order to
|
# Make a copy of the data provided (since it's readonly) in order to
|
||||||
# inject additional data.
|
# inject additional data.
|
||||||
if hasattr(request.data, 'dict'):
|
if hasattr(request.data, 'copy'):
|
||||||
data = request.data.dict()
|
data = request.data.copy()
|
||||||
else:
|
else:
|
||||||
data = request.data
|
data = QueryDict('')
|
||||||
|
data.update(request.data)
|
||||||
|
|
||||||
# add the parent key to the post data using the pk from the URL
|
# add the parent key to the post data using the pk from the URL
|
||||||
parent_key = getattr(self, 'parent_key', None)
|
parent_key = getattr(self, 'parent_key', None)
|
||||||
|
|||||||
@@ -1701,11 +1701,51 @@ class CredentialSerializerCreate(CredentialSerializer):
|
|||||||
model = Credential
|
model = Credential
|
||||||
fields = ('*', 'user', 'team', 'organization')
|
fields = ('*', 'user', 'team', 'organization')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
owner_fields = set()
|
||||||
|
for field in ('user', 'team', 'organization'):
|
||||||
|
if field in attrs:
|
||||||
|
if attrs[field]:
|
||||||
|
owner_fields.add(field)
|
||||||
|
else:
|
||||||
|
attrs.pop(field)
|
||||||
|
if not owner_fields:
|
||||||
|
raise serializers.ValidationError({"detail": "Missing 'user', 'team', or 'organization'."})
|
||||||
|
elif len(owner_fields) > 1:
|
||||||
|
raise serializers.ValidationError({"detail": "Expecting exactly one of 'user', 'team', or 'organization'."})
|
||||||
|
|
||||||
|
return super(CredentialSerializerCreate, self).validate(attrs)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Remove the user, team, and organization processed in view
|
user = validated_data.pop('user', None)
|
||||||
for field in ['user', 'team', 'organization']:
|
team = validated_data.pop('team', None)
|
||||||
validated_data.pop(field, None)
|
credential = super(CredentialSerializer, self).create(validated_data)
|
||||||
return super(CredentialSerializer, self).create(validated_data)
|
if user:
|
||||||
|
credential.owner_role.members.add(user)
|
||||||
|
if team:
|
||||||
|
credential.owner_role.parents.add(team.member_role)
|
||||||
|
return credential
|
||||||
|
|
||||||
|
|
||||||
|
class UserCredentialSerializerCreate(CredentialSerializerCreate):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Credential
|
||||||
|
fields = ('*', '-team', '-organization')
|
||||||
|
|
||||||
|
|
||||||
|
class TeamCredentialSerializerCreate(CredentialSerializerCreate):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Credential
|
||||||
|
fields = ('*', '-user', '-organization')
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationCredentialSerializerCreate(CredentialSerializerCreate):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Credential
|
||||||
|
fields = ('*', '-user', '-team')
|
||||||
|
|
||||||
|
|
||||||
class JobOptionsSerializer(BaseSerializer):
|
class JobOptionsSerializer(BaseSerializer):
|
||||||
@@ -2731,4 +2771,3 @@ class FactSerializer(BaseFactSerializer):
|
|||||||
res = super(FactSerializer, self).get_related(obj)
|
res = super(FactSerializer, self).get_related(obj)
|
||||||
res['host'] = obj.host.get_absolute_url()
|
res['host'] = obj.host.get_absolute_url()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% for fn, fm in serializer_fields.items %}{% spaceless %}
|
{% for fn, fm in serializer_fields.items %}{% spaceless %}
|
||||||
{% if write_only and fm.read_only or not write_only and fm.write_only %}
|
{% if write_only and fm.read_only or not write_only and fm.write_only or write_only and fn == parent_key %}
|
||||||
{% else %}
|
{% else %}
|
||||||
* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if write_only and fm.required %}, required{% endif %}{% if write_only and fm.read_only %}, read-only{% endif %}{% if write_only and not fm.choices and not fm.required %}, default=`{% if fm.type == "string" or fm.type == "email" %}"{% firstof fm.default "" %}"{% else %}{% if fm.type == "field" and not fm.default %}None{% else %}{{ fm.default }}{% endif %}{% endif %}`{% endif %}){% if fm.choices %}{% for c in fm.choices %}
|
* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if write_only and fm.required %}, required{% endif %}{% if write_only and fm.read_only %}, read-only{% endif %}{% if write_only and not fm.choices and not fm.required %}, default=`{% if fm.type == "string" or fm.type == "email" %}"{% firstof fm.default "" %}"{% else %}{% if fm.type == "field" and not fm.default %}None{% else %}{{ fm.default }}{% endif %}{% endif %}`{% endif %}){% if fm.choices %}{% for c in fm.choices %}
|
||||||
- `{% if c.0 == "" %}""{% else %}{{ c.0 }}{% endif %}`{% if c.1 != c.0 %}: {{ c.1 }}{% endif %}{% if write_only and c.0 == fm.default %} (default){% endif %}{% endfor %}{% endif %}{% endif %}
|
- `{% if c.0 == "" %}""{% else %}{{ c.0 }}{% endif %}`{% if c.1 != c.0 %}: {{ c.1 }}{% endif %}{% if write_only and c.0 == fm.default %} (default){% endif %}{% endfor %}{% endif %}{% endif %}
|
||||||
|
|||||||
@@ -1313,55 +1313,15 @@ class UserAccessList(ResourceAccessList):
|
|||||||
resource_model = User
|
resource_model = User
|
||||||
new_in_300 = True
|
new_in_300 = True
|
||||||
|
|
||||||
|
|
||||||
class CredentialList(ListCreateAPIView):
|
class CredentialList(ListCreateAPIView):
|
||||||
|
|
||||||
model = Credential
|
model = Credential
|
||||||
serializer_class = CredentialSerializerCreate
|
serializer_class = CredentialSerializerCreate
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
|
|
||||||
# Check the validity of POST data, including special fields
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
|
|
||||||
for field in [x for x in ['user', 'team', 'organization'] if x in request.data and request.data[x] in ('', None)]:
|
|
||||||
request.data.pop(field)
|
|
||||||
kwargs.pop(field, None)
|
|
||||||
|
|
||||||
if not any([x in request.data for x in ['user', 'team', 'organization']]):
|
|
||||||
return Response({"detail": "Missing 'user', 'team', or 'organization'."}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
if sum([1 if x in request.data else 0 for x in ['user', 'team', 'organization']]) != 1:
|
|
||||||
return Response({"detail": "Expecting exactly one of 'user', 'team', or 'organization'."}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
if 'user' in request.data:
|
|
||||||
user = User.objects.get(pk=request.data['user'])
|
|
||||||
can_add_params = {'user': user.id}
|
|
||||||
if 'team' in request.data:
|
|
||||||
team = Team.objects.get(pk=request.data['team'])
|
|
||||||
can_add_params = {'team': team.id}
|
|
||||||
if 'organization' in request.data:
|
|
||||||
organization = Organization.objects.get(pk=request.data['organization'])
|
|
||||||
can_add_params = {'organization': organization.id}
|
|
||||||
|
|
||||||
if not self.request.user.can_access(Credential, 'add', can_add_params):
|
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
ret = super(CredentialList, self).post(request, *args, **kwargs)
|
|
||||||
credential = Credential.objects.get(id=ret.data['id'])
|
|
||||||
|
|
||||||
if 'user' in request.data:
|
|
||||||
credential.owner_role.members.add(user)
|
|
||||||
if 'team' in request.data:
|
|
||||||
credential.owner_role.parents.add(team.member_role)
|
|
||||||
if 'organization' in request.data:
|
|
||||||
credential.organization = organization
|
|
||||||
credential.save()
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class CredentialOwnerUsersList(SubListAPIView):
|
class CredentialOwnerUsersList(SubListAPIView):
|
||||||
|
|
||||||
model = User
|
model = User
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
parent_model = Credential
|
parent_model = Credential
|
||||||
@@ -1370,6 +1330,7 @@ class CredentialOwnerUsersList(SubListAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class CredentialOwnerTeamsList(SubListAPIView):
|
class CredentialOwnerTeamsList(SubListAPIView):
|
||||||
|
|
||||||
model = Team
|
model = Team
|
||||||
serializer_class = TeamSerializer
|
serializer_class = TeamSerializer
|
||||||
parent_model = Credential
|
parent_model = Credential
|
||||||
@@ -1386,53 +1347,48 @@ class CredentialOwnerTeamsList(SubListAPIView):
|
|||||||
return self.model.objects.filter(pk__in=teams)
|
return self.model.objects.filter(pk__in=teams)
|
||||||
|
|
||||||
|
|
||||||
class UserCredentialsList(CredentialList):
|
class UserCredentialsList(SubListCreateAPIView):
|
||||||
|
|
||||||
model = Credential
|
model = Credential
|
||||||
serializer_class = CredentialSerializer
|
serializer_class = UserCredentialSerializerCreate
|
||||||
|
parent_model = User
|
||||||
|
parent_key = 'user'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = get_object_or_404(User,pk=self.kwargs['pk'])
|
user = self.get_parent_object()
|
||||||
if not self.request.user.can_access(User, 'read', user):
|
self.check_parent_access(user)
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
visible_creds = Credential.accessible_objects(self.request.user, 'read_role')
|
visible_creds = Credential.accessible_objects(self.request.user, 'read_role')
|
||||||
user_creds = Credential.accessible_objects(user, 'read_role')
|
user_creds = Credential.accessible_objects(user, 'read_role')
|
||||||
return user_creds & visible_creds
|
return user_creds & visible_creds
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
request.data['user'] = self.kwargs['pk']
|
|
||||||
# The following post takes care of ensuring the current user can add a cred to this user
|
|
||||||
return super(UserCredentialsList, self).post(request, args, kwargs)
|
|
||||||
|
|
||||||
class TeamCredentialsList(CredentialList):
|
class TeamCredentialsList(SubListCreateAPIView):
|
||||||
|
|
||||||
model = Credential
|
model = Credential
|
||||||
serializer_class = CredentialSerializer
|
serializer_class = TeamCredentialSerializerCreate
|
||||||
|
parent_model = Team
|
||||||
|
parent_key = 'team'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
team = get_object_or_404(Team, pk=self.kwargs['pk'])
|
team = self.get_parent_object()
|
||||||
if not self.request.user.can_access(Team, 'read', team):
|
self.check_parent_access(team)
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
visible_creds = Credential.accessible_objects(self.request.user, 'read_role')
|
visible_creds = Credential.accessible_objects(self.request.user, 'read_role')
|
||||||
team_creds = Credential.objects.filter(owner_role__parents=team.member_role)
|
team_creds = Credential.objects.filter(owner_role__parents=team.member_role)
|
||||||
return team_creds & visible_creds
|
return team_creds & visible_creds
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
request.data['team'] = self.kwargs['pk']
|
|
||||||
# The following post takes care of ensuring the current user can add a cred to this user
|
|
||||||
return super(TeamCredentialsList, self).post(request, args, kwargs)
|
|
||||||
|
|
||||||
class OrganizationCredentialList(CredentialList):
|
class OrganizationCredentialList(SubListCreateAPIView):
|
||||||
|
|
||||||
model = Credential
|
model = Credential
|
||||||
serializer_class = CredentialSerializer
|
serializer_class = OrganizationCredentialSerializerCreate
|
||||||
|
parent_model = Organization
|
||||||
|
parent_key = 'organization'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
organization = Organization.objects.get(pk=self.kwargs['pk'])
|
organization = self.get_parent_object()
|
||||||
if not self.request.user.can_access(Organization, 'read', organization):
|
self.check_parent_access(organization)
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
user_visible = Credential.accessible_objects(self.request.user, 'read_role').all()
|
user_visible = Credential.accessible_objects(self.request.user, 'read_role').all()
|
||||||
org_set = Credential.accessible_objects(organization.admin_role, 'read_role').all()
|
org_set = Credential.accessible_objects(organization.admin_role, 'read_role').all()
|
||||||
@@ -1442,13 +1398,6 @@ class OrganizationCredentialList(CredentialList):
|
|||||||
|
|
||||||
return org_set & user_visible
|
return org_set & user_visible
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
organization = Organization.objects.get(pk=self.kwargs['pk'])
|
|
||||||
request.data['organization'] = organization.id
|
|
||||||
# The following post takes care of ensuring the current user can add a cred to this user
|
|
||||||
return super(OrganizationCredentialList, self).post(request, args, kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CredentialDetail(RetrieveUpdateDestroyAPIView):
|
class CredentialDetail(RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user