Permalink
Browse files

first

  • Loading branch information...
0 parents commit 6029a39d9040d96cbb47525af359df7ab300954c @florentin committed Mar 20, 2012
@@ -0,0 +1,2 @@
+*.pyc
+
@@ -0,0 +1,7 @@
+# Django Better Perms
+
+A permission app for Django.
+
+## Installation
+
+* add "better.perms" to INSTALLED_APPS
No changes.
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+from .guards import Guard
+from .exceptions import ObjectPermissionException
+#import django.contrib.auth.backends.ModelBackend
+
+class ObjectPermissionBackend(object):
+ supports_object_permissions = True
+ supports_anonymous_user = True
+ supports_inactive_user = True
+
+ def authenticate(self, username, password):
+ return None
+
+ def get_guard(self, obj):
+ guard = getattr(obj, 'get_guard', lambda: None)()
+ if not isinstance(guard, Guard):
+ raise ObjectPermissionException('obj.get_guard() must return a Guard instance.')
+ return guard
+
+ def has_perm(self, user, perm, obj=None):
+ # Only check row (object) level permissions
+ if obj is None:
+ return False
+
+ # Inactive users never have permissions
+ if not user.is_active:
+ return False
+
+ # Anonymous users
+ if user.is_anonymous():
+ return False
+ # TODO: permissions for anonymous users
+ guard = self.get_guard(obj)
+ return guard.full_check(user=user, perm=perm, obj=obj) if guard else False
+
+ def has_module_perms(self, user, app_label):
+ raise ObjectPermissionException('Not implemented.')
+
+ def get_all_permissions(self, user, obj):
+ if not hasattr(user, '_perm_cache_obj'):
+ guard = self.get_guard(obj)
+ user._perm_cache_obj = guard.get_all_permissions(user, obj) if guard else set()
+ return user._perm_cache_obj
+
+ """
+ def get_group_permissions(self, user, obj):
+ if not hasattr(user, '_group_perm_cache_obj'):
+ guard = self.get_guard(obj)
+ user._group_perm_cache_obj = guard.get_group_permissions(user, obj) if guard else set()
+ return user._group_perm_cache_obj
+ """
@@ -0,0 +1,6 @@
+class GuardException(Exception):
+ pass
+
+
+class ObjectPermissionException(Exception):
+ pass
@@ -0,0 +1,146 @@
+from collections import defaultdict
+from django.db import models
+from django.template.defaultfilters import slugify
+from .exceptions import GuardException
+
+
+class Guard(object):
+ perms = None
+ def __init__(self, perms=None, default_for_perms=False):
+ """
+ perms is required if you use get_all_permissions() or get_group_permissions()
+ perms can be a:
+ * dictionary, i.e. {'app_lable.permission_name': True}
+ * list, i.e. ['app_lable.permission_name'] or ['just_the_permission_name']
+ default_for_perms is the boolean returned by the full_check()
+ in case there was no decision on a specific permission.
+ """
+ self.perms = perms
+ self.default_for_perms = default_for_perms
+
+ def get_all_perms(self):
+ """
+ Returns all the permissions handled by this Guard
+ """
+ if self.perms is None:
+ raise GuardException("You must pass the 'perms' argument to the Guard subclass")
+
+ if not self.perms:
+ return set()
+
+ if isinstance(self.perms, dict):
+ return set(self.perms.keys())
+
+ if isinstance(self.perms, list):
+ return set(self.perms)
+
+ def get_default_for_perm(self, perm):
+ if self.perms and isinstance(self.perms, dict):
+ return self.perms[perm]
+ else:
+ return self.default_for_perms
+
+ def unpack_perm(self, perm):
+ if "." in perm:
+ app_label, perm_name = str(perm).split(".", 2)
+ else:
+ app_label = ''
+ perm_name = str(perm)
+
+ # TODO: test app_label format as well
+ if perm_name != slugify(perm_name).replace('-', '_'):
+ raise GuardException("'perm' must only contain alphanumeric characters")
+
+ return app_label, str(perm_name)
+
+ def get_check_methods(self, perm):
+ app_label, perm_name = self.unpack_perm(perm)
+ return ('check_%s'%perm_name, 'check')
+
+ def full_check(self, user, perm, obj, from_user=True, from_groups=True):
+ """
+ dispatcher
+ """
+ # TODO: support app_label, obj's Model belongs to an app, look into the "django_content_type" table
+
+ if not (from_user or from_groups):
+ return None
+
+ is_authorized = None
+ app_label, perm_name = self.unpack_perm(perm)
+
+ for perm_method in self.get_check_methods(perm):
+ if hasattr(self, perm_method):
+ is_authorized = getattr(self, perm_method)(user, perm, obj, from_user, from_groups)
+ if is_authorized is not None:
+ break
+
+ if is_authorized is not None:
+ return is_authorized
+ else:
+ return self.get_default_for_perm(perm)
+
+ def get_all_permissions(self, user, obj, from_user=True, from_groups=True):
+ """
+ it returns all the permissions available for the total
+ permission list (self.perm) only if self.perm exists
+ """
+ allowed_permissions = []
+ for perm in self.get_all_perms():
+ if user.is_superuser or self.full_check(user, perm, obj, from_user, from_groups):
+ allowed_permissions.append(perm)
+ return set(allowed_permissions)
+
+ """
+ def get_group_permissions(self, user, obj):
+ return self.get_all_permissions(user, obj, False, True)
+ """
+
+class DbGuard(Guard):
+ obj_field = 'obj'
+ user_field = 'user'
+ group_field = 'group'
+
+ def __init__(self, model, perms=None, default_perm=False):
+ super(DbGuard, self).__init__(perms, default_perm)
+ # TODO: accept model as string
+ self.model = model
+ self._cached_perms = None
+
+ def get_all_perms(self):
+ if self.perms is None:
+ perms = {}
+ for field in self.model._meta.fields:
+ if isinstance(field, models.BooleanField):
+ perms.update({field.name: field.default})
+ return perms
+ else:
+ return super(DbGuard, self).get_all_perms()
+
+ def get_check_methods(self, perm):
+ app_label, perm_name = self.unpack_perm(perm)
+ return ('check', 'check_%s'%perm_name)
+
+ def get_cached_perms(self, user, obj):
+ if not self._cached_perms:
+ qs = self.model.objects.order_by(self.user_field)\
+ .filter(**{self.obj_field: obj})\
+ .filter(models.Q(**{self.user_field: user}) | models.Q(**{"%s__user"%self.group_field: user}))
+ self._cached_perms = list(qs)
+ return self._cached_perms
+
+ def clear_cached_perms(self):
+ self._cached_perms = None
+
+ def check(self, user, perm, obj, from_user, from_groups):
+ is_authorized = None
+ app_label, perm_name = self.unpack_perm(perm)
+ field_names = self.model._meta.get_all_field_names()
+ if perm_name in field_names:
+ for cached_perm in self.get_cached_perms(user, obj):
+ if from_groups and getattr(cached_perm, self.group_field):
+ is_authorized = getattr(cached_perm, perm_name)
+ if from_user and getattr(cached_perm, self.user_field):
+ is_authorized = getattr(cached_perm, perm_name)
+
+ return is_authorized
@@ -0,0 +1,29 @@
+from django.db import models
+from django.contrib.auth.models import User, Group
+
+class Permission(models.Model):
+ """
+ The subclass must add the obj reference field:
+ obj = models.ForeignKey(ReferredModel)
+ and the permission columns:
+ view_obj = models.BooleanField('Can view')
+
+ The referred model must have this method:
+ def get_guard(self):
+ return Guard(model=ArticlePermission)
+ """
+ user = models.ForeignKey(User, blank=True, null=True, unique=True)
+ group = models.ForeignKey(Group, blank=True, null=True, unique=True)
+
+ class Meta:
+ abstract = True
+
+
+class CrudPermission(Permission):
+ create_obj = models.NullBooleanField('Can create')
+ read_obj = models.NullBooleanField('Can read')
+ update_obj = models.NullBooleanField('Can update')
+ delete_obj = models.NullBooleanField('Can delete')
+
+ class Meta:
+ abstract = True
@@ -0,0 +1 @@
+from .test_backend import BackendTest
@@ -0,0 +1,10 @@
+from better.perms.guards import DbGuard, Guard
+
+class ArticleDbGuard(DbGuard):
+ def check_read_obj(self, *args, **kwargs):
+ return True
+
+
+class ArticleGuard(Guard):
+ def check_read_obj(self, *args, **kwargs):
+ return True
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+from django.db import models
+from better.perms.models import CrudPermission
+from better.perms.guards import DbGuard
+from .guards import ArticleDbGuard, ArticleGuard
+
+
+class Article(models.Model):
+ title = models.CharField(max_length=128)
+ description = models.TextField()
+
+ class Meta:
+ db_table = 'better_perms__article'
+
+ def __unicode__(self):
+ return self.title
+
+ def get_guard(self):
+ if not hasattr(self, '_guard'):
+ self._guard = ArticleDbGuard(model=ArticlePermission)
+ return self._guard
+
+
+class ArticlePermission(CrudPermission):
+ obj = models.ForeignKey('Article')
+
+ class Meta:
+ db_table = 'better_perms__articlepermission'
Oops, something went wrong.

0 comments on commit 6029a39

Please sign in to comment.