# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework.permissions import DjangoObjectPermissions from .backends import PermissionBackend SAFE_METHODS = ('HEAD', 'OPTIONS', ) class StrongDjangoObjectPermissions(DjangoObjectPermissions): """ Default DjangoObjectPermissions grant view permission to all. This is a simple patch of this class that controls view access. """ # The queryset is filtered, and permissions are more powerful than a simple check than just "can view this model" perms_map = { 'GET': ['%(app_label)s.view_%(model_name)s'], 'OPTIONS': [], 'HEAD': [], 'POST': ['%(app_label)s.add_%(model_name)s'], 'PUT': [], # ['%(app_label)s.change_%(model_name)s'], 'PATCH': [], # ['%(app_label)s.change_%(model_name)s'], 'DELETE': ['%(app_label)s.delete_%(model_name)s'], } def get_required_object_permissions(self, method, model_cls): kwargs = { 'app_label': model_cls._meta.app_label, 'model_name': model_cls._meta.model_name } if method not in self.perms_map: from rest_framework import exceptions raise exceptions.MethodNotAllowed(method) return [perm % kwargs for perm in self.perms_map[method]] def has_object_permission(self, request, view, obj): # authentication checks have already executed via has_permission queryset = self._queryset(view) model_cls = queryset.model user = request.user perms = self.get_required_object_permissions(request.method, model_cls) # if not user.has_perms(perms, obj): if not all(PermissionBackend.check_perm(user, perm, obj) for perm in perms): # If the user does not have permissions we need to determine if # they have read permissions to see 403, or not, and simply see # a 404 response. from django.http import Http404 if request.method in SAFE_METHODS: # Read permissions already checked and failed, no need # to make another lookup. raise Http404 read_perms = self.get_required_object_permissions('GET', model_cls) if not user.has_perms(read_perms, obj): raise Http404 # Has read permissions. return False return True