# Copyright (C) 2018-2025 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(request, 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
