Created
March 13, 2012 18:08
-
-
Save tarkatronic/2030365 to your computer and use it in GitHub Desktop.
Django row-level permissions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""This is just an example of how to provide permissions on a per-object basis in the admin""" | |
from django.contrib import admin | |
from django.contrib.auth.models import Permission | |
from django.contrib.contenttypes import generic | |
from django.contrib.contenttypes.models import ContentType | |
from account.models import ObjectGroupPermission, ObjectUserPermission | |
from myapp.models import MyModel | |
class MyModelPermissionInline(generic.GenericTabularInline): | |
model = ObjectUserPermission | |
def formfield_for_foreignkey(self, db_field, request, **kwargs): | |
if db_field.name == 'permission': | |
kwargs['queryset'] = Permission.objects.filter(content_type=ContentType.objects.get_for_model(MyModel)) | |
return super(MyModelPermissionInline, self).formfield_for_foreignkey(db_field, request, **kwargs) | |
class MyModelAdmin(admin.ModelAdmin): | |
inlines = [MyModelPermissionInline,] | |
admin.site.register(MyModel, MyModelAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib.auth.backends import ModelBackend | |
from django.contrib.auth.models import Permission | |
from django.contrib.contenttypes.models import ContentType | |
class RowLevelAuthBackend(ModelBackend): | |
supports_object_permissions = True | |
def get_group_permissions(self, user_obj, obj=None): | |
if obj is not None: | |
if not hasattr(user_obj, '_group_obj_perm_cache'): | |
user_obj._group_obj_perm_cache = {} | |
if not obj in user_obj._group_obj_perm_cache.keys(): | |
if user_obj.is_superuser: | |
perms = Permission.objects.all() | |
else: | |
perms = Permission.objects.filter(objectgrouppermission__group__user=user_obj, | |
objectgrouppermission__content_type=ContentType.objects.get_for_model(obj), | |
objectgrouppermission__object_id=obj.pk) | |
perms = perms.values_list('content_type__app_label', 'codename').order_by() | |
user_obj._group_obj_perm_cache[obj] = set(["%s.%s" % (ct, name) for ct, name in perms]) | |
return user_obj._group_obj_perm_cache[obj] | |
return super(RowLevelAuthBackend, self).get_group_permissions(user_obj) | |
def get_all_permissions(self, user_obj, obj=None): | |
if user_obj.is_anonymous(): | |
return set() | |
if obj is not None: | |
if not hasattr(user_obj, '_obj_perm_cache'): | |
user_obj._obj_perm_cache = {} | |
if not obj in user_obj._obj_perm_cache.keys(): | |
perms = Permission.objects.filter(objectuserpermission__user=user_obj, | |
objectuserpermission__content_type=ContentType.objects.get_for_model(obj), | |
objectuserpermission__object_id=obj.pk) | |
user_obj._obj_perm_cache[obj] = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in perms]) | |
user_obj._obj_perm_cache[obj].update(self.get_group_permissions(user_obj, obj)) | |
return user_obj._obj_perm_cache[obj] | |
return super(RowLevelAuthBackend, self).get_all_permissions(user_obj) | |
def has_perm(self, user_obj, perm, obj=None): | |
if not user_obj.is_active: | |
return False | |
if obj is not None: | |
perms = self.get_all_permissions(user_obj, obj) | |
return perm in perms | |
else: | |
return perm in self.get_all_permissions(user_obj) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib.auth.models import Group, Permission, User | |
from django.contrib.contenttypes.models import ContentType | |
from django.contrib.contenttypes import generic | |
from django.db import models | |
class ObjectUserPermission(models.Model): | |
permission = models.ForeignKey(Permission) | |
user = models.ForeignKey(User) | |
content_type = models.ForeignKey(ContentType) | |
object_id = models.PositiveIntegerField() | |
content_object = generic.GenericForeignKey('content_type', 'object_id') | |
class ObjectGroupPermission(models.Model): | |
permission = models.ForeignKey(Permission) | |
group = models.ForeignKey(Group) | |
content_type = models.ForeignKey(ContentType) | |
object_id = models.PositiveIntegerField() | |
content_object = generic.GenericForeignKey('content_type', 'object_id') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""This template tag is the missing piece in row-level permissions. | |
It is used like: {% ifpermitted "content_type.permission" object %}...{% elif %}...{% endifpermitted %}""" | |
from django.contrib.auth.models import User | |
from django.template.base import Library, Node, NodeList, TemplateSyntaxError, Variable, VariableDoesNotExist | |
register = Library() | |
class IfPermittedNode(Node): | |
def __init__(self, perm, obj, nodelist_true, nodelist_false=None, user_obj=None): | |
self.perm, self.obj = perm, Variable(obj) | |
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false | |
if user_obj is not None: | |
self.user_obj = Variable(user_obj) | |
else: | |
self.user_obj = None | |
def render(self, context): | |
# If obj doesn't exist, resolve will raise VariableDoesNotExist; we want this. | |
obj = self.obj.resolve(context) | |
if self.user_obj: | |
user_obj = self.obj.resolve(context) | |
else: | |
req = context.get('request', None) | |
user_obj = getattr(req, 'user', None) | |
if not isinstance(user_obj, User): | |
raise VariableDoesNotExist("ifpermitted requires either the 'request' context processor or an explicit user object") | |
if user_obj.has_perm(self.perm, obj): | |
return self.nodelist_true.render(context) | |
elif self.nodelist_false: | |
return self.nodelist_false.render(context) | |
return '' | |
@register.tag | |
def ifpermitted(parser, token): | |
bits = list(token.split_contents()) | |
user_obj = None | |
if len(bits) == 3: | |
(tag_name, perm, obj) = bits | |
elif len(bits) == 4: | |
(tag_name, perm, obj, user_obj) = bits | |
else: | |
raise TemplateSyntaxError("%s takes either two or three arguments" % tag_name) | |
if not (perm[0] == perm[-1] and perm[0] in ('"', "'")): | |
raise TemplateSyntaxError("%s permission argument should be in quotes" % tag_name) | |
nodelist_true = parser.parse(('else', 'endifpermitted')) | |
token = parser.next_token() | |
if token.contents == 'else': | |
nodelist_false = parser.parse(('endifpermitted',)) | |
parser.delete_first_token() | |
else: | |
nodelist_false = NodeList() | |
return IfPermittedNode(perm[1:-1], obj, nodelist_true, nodelist_false, user_obj) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment