Reimplement the ViewExemptModelBackend to explicitly cache all exempted view permissions on the User instance

This commit is contained in:
Jeremy Stretch 2020-05-12 16:07:07 -04:00
parent c90f680284
commit a275a30dca

View File

@ -3,7 +3,6 @@ import logging
from django.conf import settings from django.conf import settings
from django.contrib.auth.backends import ModelBackend, RemoteUserBackend as RemoteUserBackend_ from django.contrib.auth.backends import ModelBackend, RemoteUserBackend as RemoteUserBackend_
from django.contrib.auth.models import Group, Permission from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from users.models import ObjectPermission from users.models import ObjectPermission
@ -14,28 +13,26 @@ class ViewExemptModelBackend(ModelBackend):
Custom implementation of Django's stock ModelBackend which allows for the exemption of arbitrary models from view Custom implementation of Django's stock ModelBackend which allows for the exemption of arbitrary models from view
permission enforcement. permission enforcement.
""" """
def has_perm(self, user_obj, perm, obj=None): def _get_user_permissions(self, user_obj):
# If this is a view permission, check whether the model has been exempted from enforcement if not settings.EXEMPT_VIEW_PERMISSIONS:
try: # No view permissions have been exempted from enforcement, so fall back to the built-in logic.
app, codename = perm.split('.') return super()._get_user_permissions(user_obj)
action, model = codename.split('_')
if action == 'view':
if (
# All models are exempt from view permission enforcement
'*' in settings.EXEMPT_VIEW_PERMISSIONS
) or (
# This specific model is exempt from view permission enforcement
'.'.join((app, model)) in settings.EXEMPT_VIEW_PERMISSIONS
):
return True
except ValueError:
pass
# Fall back to ModelBackend's default behavior, with one exception: Set obj to None. Model-level permissions if '*' in settings.EXEMPT_VIEW_PERMISSIONS:
# override object-level permissions, so if a user has the model-level permission we can ignore any specified # All view permissions have been exempted from enforcement, so include all view permissions when fetching
# object. (By default, ModelBackend will return False if an object is specified.) # User permissions.
return super().has_perm(user_obj, perm, None) return Permission.objects.filter(
Q(user=user_obj) | Q(codename__startswith='view_')
)
# Return all Permissions that are either assigned to the user or that are view permissions listed in
# EXEMPT_VIEW_PERMISSIONS.
qs_filter = Q(user=user_obj)
for model in settings.EXEMPT_VIEW_PERMISSIONS:
app, name = model.split('.')
qs_filter |= Q(content_type__app_label=app, codename=f'view_{name}')
return Permission.objects.filter(qs_filter)
class ObjectPermissionBackend(ModelBackend): class ObjectPermissionBackend(ModelBackend):