Динамические разрешения Django (drf) от BasePermission

Я хочу иметь простой способ проверить, является ли кто-то владельцем или администратором post, proposal и т. д., которые он пытается редактировать\удалять.

Итак, каждый раз, когда я использую разрешение IsAuthenticated, а в методе ModelViewSet я получаю экземпляр и проверяю, является ли instance.author или иногда instance.owner пользователем, который его запросил (request.user == instance.owner для некоторых объектов это request.user == instance.author).

Вопрос

Главный вопрос: как я могу создать класс разрешений, который может проверять этот вид владения с динамическим именем атрибута пользователя в экземпляре?

Одно из моих решений (думаю, не самое лучшее)

Я создал функцию, которая принимает имя экземпляра атрибута пользователя и возвращает класс разрешений:

def is_owner_or_admin_permission_factory(owner_prop_name):
    class IsOwnerOrAdmin(BasePermission):
        def has_permission(self, request, view, *args, **kwargs):
            instance = view.get_object()
            try:
                owner = getattr(instance, owner_prop_name)
            except AttributeError:
                return False
            return (
                request.user and request.user.id and (owner == request.user or request.user.is_staff)
            )

    return IsOwnerOrAdmin

person Dionid    schedule 29.07.2017    source источник


Ответы (1)


Я также был разочарован одной и той же проблемой в течение нескольких дней, и мне удалось найти подходящий обходной путь (по крайней мере, для меня, конечно) при работе с несколькими моделями с разными именами поиска для пользовательских атрибутов.

Обходной путь был примерно таким: в ModelViewSet определялся отдельный атрибут user_lookup_kwarg в представлении, который можно было использовать для проверки соответствующих разрешений.

Eg,

class YourViewSet(viewsets.ModelViewSet):
    queryset = YourModel.objects.all()
    serializer_class = YourSerializer
    user_lookup_kwarg = 'user' #or 'account/created_by' whatever.

Теперь ваш permission_class будет примерно таким,

class CustomPermission(BasePermission):

    def has_object_permission(self, request, view, obj):
        try:
            return request.user.is_superuser or getattr(obj, view.user_lookup_kwarg) == request.user
        except:
            pass
        return request.user.is_superuser

Вам просто нужно переопределить метод has_object_permission(), чтобы проверить разрешения на уровне экземпляра.

person zaidfazil    schedule 29.07.2017
comment
Один момент: когда вы проверяете request.user.is_superuser и он возвращает True, он будет возвращен из метода, если это False и второй аргумент вызывает исключение, вы можете просто вернуть False, потому что request.user.is_superuser уже должно быть False. - person Dionid; 29.07.2017
comment
Также, я думаю, лучше проверить, что пользователь авторизован, потому что (в большинстве случаев) AnonymousUser не может быть владельцем объекта, поэтому он должен быть авторизован. - person Dionid; 29.07.2017
comment
Вы сказали, что IsAuthenticated является вашим разрешением по умолчанию, тогда вам просто нужно добавить его в свой класс разрешений. Не пытайтесь запихнуть все в один класс, разделите его и используйте повторно. - person zaidfazil; 29.07.2017
comment
По вашему предыдущему вопросу, если request.user.is_superuser возвращает False, то если вы определяете user_lookup_kwarg, исключение не будет возбуждено, но будет возвращен результат выражения. - person zaidfazil; 29.07.2017