Django: Decoupling Permission-Check from Calling the View
I would like to decouple the permission checking in django.
Current draw-back: If you use decorators like login_required, then you can't know in advance whether a user has the permission to do so or not.
I would like to split this into two steps:
- check permissions
- call the view.
Use Case 1: Admin tool
I want an tool for admins where they can check the access-permissions of users. This requires:
- The check must not use the current
request.usersince this is the wrong user object. - The check must not actually call the view, since this might alter data.
Use Case 2: Show Link as disabled.
I want to show links as disabled (grayed out and without "href") if a user does not have the permission to see linked page.
Dream
Returning a boolean for "ok" and "permission denied" is nice. But the big benefit would be if the admin could get a reason.
Example:
- Admin opens "Check Perm Tool"
- He selects a view/URL
- The admin hits "submit"
Result:
------------------------------ | User | Allowed | Reason ------------------------------ | fooadmin | Yes | is_superuser | foouser | No | missing permission "view-bar-at-midnight" | foobar | Yes | User has permission "view-bar-at-midnight" Question
How to get this dream come true?
1 Answers
Answers 1
I'm assuming you already have a list of all the permissions needed to view each view/URL? i.e. You know that URL /bar requires permissions superuser and view-bar-at-night ahead of time?
If so, try adding a ViewPermissions model to models.py first:
from django.db import models class ViewPermissions(models.Model): view_url = models.CharField(help_text="The URL of this view") Then in admin.py:
from models import ViewPermissions from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.auth.models import Permission from django.contrib import admin from django import forms class ViewPermissionFormField(forms.ModelForm): permissions = forms.ModelMultipleChoiceField( widget=FilteredSelectMultiple("Permissions", is_stacked=False), queryset=models.Permission.objects.all() ) def save(self, commit=True): extra_field = self.cleaned_data.get('extra_field', None) return super(ViewPermissionFormField, self).save(commit=commit) class Meta: fields = '__all__' model = ViewPermissions class ViewPermissionsAdmin(admin.ModelAdmin): form = ViewPermissionsFormField fieldsets = ( (None, { 'fields': 'view_url', 'permissions' }) ) Now you can add permissions to each view URL in admin to keep track of things. FYI you can create your own custom permissions and add them to Django's existing ones.
You can create an admin form that pulls up the users, lists their permissions, then leverages ViewPermissions to see what URLs that user can visit based on what permissions they have (Use case 1).
You can now check the user's permission in your view function (or class if that's how you're defining your views) like so:
from django.contrib.auth.models import Permission from models import ViewPermissions def my_view(request) view_url = request.get_current_url() permissions_required = ViewPermissions.objects.filter(view_url = view_url).permissions context['user_permissions'] = [] for p in permissions_required: if request.user.has_permission(p): context['user_permissions'].append(p) Now you can pass those permissions into your template and grey out links accordingly (Use case 2).
So something like that might work...
0 comments:
Post a Comment