diff --git a/mayan/apps/rest_api/mixins.py b/mayan/apps/rest_api/mixins.py index ffdc10dad5..597246351a 100644 --- a/mayan/apps/rest_api/mixins.py +++ b/mayan/apps/rest_api/mixins.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals from django.core.exceptions import ImproperlyConfigured +from rest_framework.exceptions import ValidationError from rest_framework.settings import api_settings from mayan.apps.acls.models import AccessControlList @@ -16,6 +17,7 @@ class ExternalObjectListSerializerMixin(object): external_object_list_queryset external_object_list_pk_field external_object_list_pk_list_field + external_object_list_pk_type The source queryset can also be provided overriding the .get_external_object_list_queryset() method. @@ -23,14 +25,24 @@ class ExternalObjectListSerializerMixin(object): def __init__(self, *args, **kwargs): super(ExternalObjectListSerializerMixin, self).__init__(*args, **kwargs) self.external_object_list_options = getattr(self, 'Meta', None) + self.external_object_list_options_defaults = { + 'external_object_list_pk_type': int + } + + def filter_queryset(self, id_list, queryset): + """ + Allow customizing the final filtering, used for object lists that are + not a queryset like the Permission class. + """ + return queryset.filter(pk__in=id_list) def get_external_object_list(self): queryset = self.get_external_object_list_queryset() - if self.Meta.external_object_list_permission: + permission = self.get_external_object_list_option('permission') + if permission: queryset = AccessControlList.objects.restrict_queryset( - permission=self.Meta.external_object_list_permission, - queryset=queryset, + permission=permission, queryset=queryset, user=self.context['request'].user ) @@ -40,7 +52,7 @@ class ExternalObjectListSerializerMixin(object): if not pk_field and not pk_list_field: raise ImproperlyConfigured( 'ExternalObjectListSerializerMixin requires a ' - 'external_object_list_pk_field a ' + 'external_object_list_pk_field or a' 'external_object_list_pk_list_field.' ) @@ -61,12 +73,33 @@ class ExternalObjectListSerializerMixin(object): else: id_list = () - return queryset.filter(pk__in=id_list) + pk_type = self.get_external_object_list_option('pk_type') + + if pk_type: + result = [] + + for pk in id_list: + try: + result.append(pk_type(pk)) + except Exception as exception: + raise ValidationError( + { + api_settings.NON_FIELD_ERRORS_KEY: [ + 'Value "{}" is not of a valid type; {}'.format(pk, exception) + ] + }, code='invalid' + ) + + id_list = result + + return self.filter_queryset(id_list=id_list, queryset=queryset) def get_external_object_list_option(self, option_name): + full_option_name = 'external_object_list_{}'.format(option_name) + return getattr( - self.external_object_list_options, 'external_object_list_{}'.format(option_name), - None + self.external_object_list_options, full_option_name, + self.external_object_list_options_defaults.get(full_option_name) ) def get_external_object_list_queryset(self): @@ -86,6 +119,82 @@ class ExternalObjectListSerializerMixin(object): return queryset +class ExternalObjectSerializerMixin(object): + """ + Mixin to allow serializers to get a restricted object with minimal code. + This mixin adds the follow class Meta options to a serializer: + external_object_model + external_object_permission + external_object_queryset + external_object_pk_field + The source queryset can also be provided overriding the + .get_external_object_queryset() method. + """ + def __init__(self, *args, **kwargs): + super(ExternalObjectSerializerMixin, self).__init__(*args, **kwargs) + self.external_object_options = getattr(self, 'Meta', None) + + def get_external_object(self): + queryset = self.get_external_object_queryset() + + permission = self.get_external_object_permission() + if permission: + queryset = AccessControlList.objects.restrict_queryset( + permission=permission, queryset=queryset, + user=self.context['request'].user + ) + + pk_field = self.get_external_object_option('pk_field') + + if not pk_field: + raise ImproperlyConfigured( + 'ExternalObjectSerializerMixin requires a ' + 'external_object_pk_field.' + ) + + if pk_field: + pk_field_value = self.validated_data.get(pk_field) + else: + pk_field_value = None + + if pk_field_value: + try: + return queryset.get(pk=pk_field_value) + except Exception as exception: + raise ValidationError( + { + pk_field: [exception] + }, code='invalid' + ) + + def get_external_object_option(self, option_name): + full_option_name = 'external_object_{}'.format(option_name) + + return getattr( + self.external_object_options, full_option_name, + None + ) + + def get_external_object_permission(self): + return self.get_external_object_option('permission') + + def get_external_object_queryset(self): + model = self.get_external_object_option('model') + queryset = self.get_external_object_option('queryset') + + if model: + queryset = model._meta.default_manager.all() + elif queryset: + return queryset + else: + raise ImproperlyConfigured( + 'ExternalObjectListSerializerMixin requires a ' + 'external_object_model or a external_object_queryset.' + ) + + return queryset + + class SuccessHeadersMixin(object): def get_success_headers(self, data): try: