- Author:
- stuartaccent
- Posted:
- November 3, 2017
- Language:
- Python
- Version:
- 1.10
- Score:
- 0 (after 0 ratings)
A permission helper that can be included in any generic CBV, it uses the model attribute of the class to load all the permissions and tests a user can perform that action before dispatching the view.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | # 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:
<ul>
{% if user_can_create %}
<li><a href="{% url 'someapp:somemodel-create' %}">{% trans 'Add' %}</a></li>
{% endif %}
{% if user_can_view %}
<li><a href="{% url 'someapp:somemodel-detail' pk %}">{% trans 'View' %}</a></li>
{% endif %}
{% if user_can_edit %}
<li><a href="{% url 'someapp:somemodel-update' pk %}">{% trans 'Edit' %}</a></li>
{% endif %}
</ul>
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.