Reputation: 27806
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:
I want an tool for admins where they can check the access-permissions of users. This requires:
request.user
since this is the wrong user object.I want to show links as disabled (grayed out and without "href") if a user does not have the permission to see linked page.
Returning a boolean for "ok" and "permission denied" is nice. But the big benefit would be if the admin could get a reason.
Example:
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"
How to get this dream come true?
... Just for the records, I posted the idea on the django-develop list: https://groups.google.com/forum/#!topic/django-developers/rpTh4G3BgIQ
Upvotes: 1
Views: 611
Reputation: 506
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...
Edit:
The link to Django docs no longer available. One might visit this instead.
Upvotes: 3