Skip to content

Instantly share code, notes, and snippets.

@tarkatronic
Created March 13, 2012 18:08
Show Gist options
  • Save tarkatronic/2030365 to your computer and use it in GitHub Desktop.
Save tarkatronic/2030365 to your computer and use it in GitHub Desktop.
Django row-level permissions
"""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)
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)
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 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