# Permissions helper # This class inspects the user and determines if they have a specific model permission from django.contrib.auth import get_permission_codename from django.contrib.auth.models import Permission class PermissionsHelper(object): """ Provides permission-related helper functions to help determine what a user can do with a 'typical' model (where permissions are granted model-wide), and to a specific instance of that model. """ def __init__(self, model, inspect_view_enabled=False): self.model = model self.opts = model._meta self.inspect_view_enabled = inspect_view_enabled def get_all_model_permissions(self): """ Return a queryset of all Permission objects pertaining to the `model` specified at initialisation. """ return Permission.objects.filter( content_type__app_label=self.opts.app_label, content_type__model=self.opts.model_name, ) def get_perm_codename(self, action): return get_permission_codename(action, self.opts) def user_has_specific_permission(self, user, perm_codename): """ Call the provided django user's built-in `has_perm` method. """ return user.has_perm("%s.%s" % (self.opts.app_label, perm_codename)) def user_has_any_permissions(self, user): """ Return a boolean to indicate whether `user` has any model-wide permissions """ for perm in self.get_all_model_permissions().values('codename'): if self.user_has_specific_permission(user, perm['codename']): return True return False def user_can_list(self, user): """ Return a boolean to indicate whether `user` is permitted on at least one action. """ return self.user_has_any_permissions(user) def user_can_view(self, user): """ Return a boolean to indicate whether `user` is permitted on at least one action and if we are allowing a view perm. """ return self.inspect_view_enabled and self.user_has_any_permissions(user) def user_can_create(self, user): """ Return a boolean to indicate whether `user` is permitted to create new instances of `self.model` """ perm_codename = self.get_perm_codename('add') return self.user_has_specific_permission(user, perm_codename) def user_can_edit(self, user): """ Return a boolean to indicate whether `user` is permitted to 'change' a specific `self.model` instance. """ perm_codename = self.get_perm_codename('change') return self.user_has_specific_permission(user, perm_codename) def user_can_delete(self, user): """ Return a boolean to indicate whether `user` is permitted to 'delete' a specific `self.model` instance. """ perm_codename = self.get_perm_codename('delete') return self.user_has_specific_permission(user, perm_codename) ########################################################## # Generic view mixin # Use on a generic view to test the permission from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from users.permissions import PermissionsHelper class PermissionRequiredMixin(LoginRequiredMixin): """ Check a user has the required permission before proceeding """ # one of PermissionsHelper.user_can_* methods dispatch_requires_permission = None @property def permission_helper(self): return PermissionsHelper(model=self.model) def dispatch(self, request, *args, **kwargs): # ensure self.dispatch_requires_permission exists if not self.dispatch_requires_permission: raise NotImplementedError('Please define self.dispatch_requires_permission') # ensure value of self.dispatch_requires_permission is a method on the class PermissionsHelper if not hasattr(self.permission_helper, self.dispatch_requires_permission): raise AttributeError( 'The class {} has no attribute {}'.format(self.permission_helper, self.dispatch_requires_permission) ) # check user has the permission check = getattr(self.permission_helper, self.dispatch_requires_permission)(request.user) if not check: raise PermissionDenied return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): """ pass users permissions into the context to use in templates """ context = super(PermissionRequiredMixin, self).get_context_data(**kwargs) context.update({ 'user_can_list': self.permission_helper.user_can_list(self.request.user), 'user_can_view': self.permission_helper.user_can_view(self.request.user), 'user_can_create': self.permission_helper.user_can_create(self.request.user), 'user_can_edit': self.permission_helper.user_can_edit(self.request.user), 'user_can_delete': self.permission_helper.user_can_delete(self.request.user), }) return context ########################################################## # Generic view example from django.contrib.messages.views import SuccessMessageMixin from django.urls import reverse_lazy from django.views.generic import CreateView from django.utils.translation import ugettext as _ from someapp.forms import CreateForm from someapp.models import SomeModel from .mixins import PermissionRequiredMixin class SomeModelCreateView(PermissionRequiredMixin, SuccessMessageMixin, CreateView): model = SomeModel form_class = CreateForm dispatch_requires_permission = 'user_can_create' template_name = 'someapp/somemodel/create.html' success_message = _("Created successfully") success_url = reverse_lazy('someapp:somemodel-list') ########################################################## # Template usage # In the template you can also do stuff like: