diff --git a/apps/permissions/__init__.py b/apps/permissions/__init__.py index e143c8bbb8..8099230638 100644 --- a/apps/permissions/__init__.py +++ b/apps/permissions/__init__.py @@ -7,14 +7,16 @@ from navigation.api import register_links, register_multi_item_links from project_setup.api import register_setup from permissions.conf.settings import DEFAULT_ROLES -from permissions.models import Role +from permissions.models import Role, Permission, PermissionNamespace -PERMISSION_ROLE_VIEW = {'namespace': 'permissions', 'name': 'role_view', 'label': _(u'View roles')} -PERMISSION_ROLE_EDIT = {'namespace': 'permissions', 'name': 'role_edit', 'label': _(u'Edit roles')} -PERMISSION_ROLE_CREATE = {'namespace': 'permissions', 'name': 'role_create', 'label': _(u'Create roles')} -PERMISSION_ROLE_DELETE = {'namespace': 'permissions', 'name': 'role_delete', 'label': _(u'Delete roles')} -PERMISSION_PERMISSION_GRANT = {'namespace': 'permissions', 'name': 'permission_grant', 'label': _(u'Grant permissions')} -PERMISSION_PERMISSION_REVOKE = {'namespace': 'permissions', 'name': 'permission_revoke', 'label': _(u'Revoke permissions')} +permissions_namespace = PermissionNamespace('permissions', _(u'Permissions')) + +PERMISSION_ROLE_VIEW = Permission.objects.register(permissions_namespace, 'role_view', _(u'View roles')) +PERMISSION_ROLE_EDIT = Permission.objects.register(permissions_namespace, 'role_edit', _(u'Edit roles')) +PERMISSION_ROLE_CREATE = Permission.objects.register(permissions_namespace, 'role_create', _(u'Create roles')) +PERMISSION_ROLE_DELETE = Permission.objects.register(permissions_namespace, 'role_delete', _(u'Delete roles')) +PERMISSION_PERMISSION_GRANT = Permission.objects.register(permissions_namespace, 'permission_grant', _(u'Grant permissions')) +PERMISSION_PERMISSION_REVOKE = Permission.objects.register(permissions_namespace, 'permission_revoke', _(u'Revoke permissions')) role_list = {'text': _(u'roles'), 'view': 'role_list', 'famfam': 'medal_gold_1', 'icon': 'medal_gold_1.png', 'permissions': [PERMISSION_ROLE_VIEW]} role_create = {'text': _(u'create new role'), 'view': 'role_create', 'famfam': 'medal_gold_add', 'permissions': [PERMISSION_ROLE_CREATE]} diff --git a/apps/permissions/admin.py b/apps/permissions/admin.py index 1728199d71..b370b77498 100644 --- a/apps/permissions/admin.py +++ b/apps/permissions/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from permissions.models import Permission, PermissionHolder, Role, RoleMember +from permissions.models import StoredPermission, PermissionHolder, Role, RoleMember class PermissionHolderInline(admin.StackedInline): @@ -27,5 +27,5 @@ class RoleAdmin(admin.ModelAdmin): inlines = [RoleMemberInline] -admin.site.register(Permission, PermissionAdmin) +admin.site.register(StoredPermission, PermissionAdmin) admin.site.register(Role, RoleAdmin) diff --git a/apps/permissions/api.py b/apps/permissions/api.py index 4b469d092d..8b13789179 100644 --- a/apps/permissions/api.py +++ b/apps/permissions/api.py @@ -1,69 +1 @@ -try: - from psycopg2 import OperationalError -except ImportError: - class OperationalError(Exception): - pass -from django.core.exceptions import ImproperlyConfigured -from django.db import transaction -from django.db.utils import DatabaseError -from django.shortcuts import get_object_or_404 -from django.utils.translation import ugettext -from django.core.exceptions import PermissionDenied -from django.utils.translation import ugettext_lazy as _ - -from permissions import PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, \ - PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, \ - PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE - -from permissions.models import Permission -from permissions.runtime import namespace_titles, permission_titles - - -def set_namespace_title(namespace, title): - namespace_titles.setdefault(namespace, title) - - -@transaction.commit_manually -def register_permission(permission): - try: - permission_obj, created = Permission.objects.get_or_create( - namespace=permission['namespace'], name=permission['name']) - permission_obj.label = unicode(permission['label']) - permission_obj.save() - permission_titles['%s.%s' % (permission['namespace'], permission['name'])] = permission['label'] - except DatabaseError: - transaction.rollback() - # Special case for ./manage.py syncdb - except (OperationalError, ImproperlyConfigured): - transaction.rollback() - # Special for DjangoZoom, which executes collectstatic media - # doing syncdb and creating the database tables - else: - transaction.commit() - - -def check_permissions(requester, permission_list): - for permission_item in permission_list: - permission = get_object_or_404(Permission, - namespace=permission_item['namespace'], name=permission_item['name']) - if permission.has_permission(requester): - return True - - raise PermissionDenied(ugettext(u'Insufficient permissions.')) - - -def get_permission_label(permission): - return unicode(permission_titles.get('%s.%s' % (permission.namespace, permission.name), permission.label)) - - -def get_permission_namespace_label(permission): - return namespace_titles[permission.namespace] if permission.namespace in namespace_titles else permission.namespace - - -register_permission(PERMISSION_ROLE_VIEW) -register_permission(PERMISSION_ROLE_EDIT) -register_permission(PERMISSION_ROLE_CREATE) -register_permission(PERMISSION_ROLE_DELETE) -register_permission(PERMISSION_PERMISSION_GRANT) -register_permission(PERMISSION_PERMISSION_REVOKE) diff --git a/apps/permissions/managers.py b/apps/permissions/managers.py index 919bb720bd..a6d5cc4722 100644 --- a/apps/permissions/managers.py +++ b/apps/permissions/managers.py @@ -1,7 +1,14 @@ +import logging + from django.db import models from django.contrib.contenttypes.models import ContentType +from django.db import transaction +from django.core.exceptions import PermissionDenied +from django.shortcuts import get_object_or_404 +from django.db.utils import IntegrityError +from django.core.exceptions import ImproperlyConfigured -from permissions.runtime import permission_titles +logger = logging.getLogger(__name__) class RoleMemberManager(models.Manager): @@ -10,19 +17,7 @@ class RoleMemberManager(models.Manager): return [role_member.role for role_member in self.model.objects.filter(member_type=member_type, member_id=member_obj.pk)] -class PermissionManager(models.Manager): +class StoredPermissionManager(models.Manager): def get_for_holder(self, holder): ct = ContentType.objects.get_for_model(holder) - return self.model.objects.active_only().filter(permissionholder__holder_type=ct).filter(permissionholder__holder_id=holder.pk) - - def active_only(self): - namespaces = [] - names = [] - for key in permission_titles: - namespace, name = key.split(u'.') - if namespace: - namespaces.append(namespace) - if name: - names.append(name) - - return super(PermissionManager, self).get_query_set().filter(namespace__in=namespaces).filter(name__in=names).exclude(label=u'') + return self.model.objects.filter(permissionholder__holder_type=ct).filter(permissionholder__holder_id=holder.pk) diff --git a/apps/permissions/models.py b/apps/permissions/models.py index 8cc7c2f354..1cbbc51a2a 100644 --- a/apps/permissions/models.py +++ b/apps/permissions/models.py @@ -1,33 +1,140 @@ +import logging + from django.db import models from django.utils.translation import ugettext_lazy as _ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User +from django.core.exceptions import PermissionDenied -from permissions.managers import RoleMemberManager, PermissionManager -from permissions.runtime import namespace_titles, permission_titles +from permissions.managers import (RoleMemberManager, StoredPermissionManager) + +logger = logging.getLogger(__name__) -class Permission(models.Model): +class PermissionNamespace(object): + def __init__(self, name, label): + self.name = name + self.label = label + + def __unicode__(self): + return unicode(self.label) + +#class LazyQuerySet(list): +# def __init__(self, model, items): +# self.model = model +# self.items = items +# +# def get(self, *args, **kwargs): +# print args +# print kwargs + +class PermissionDoesNotExists(Exception): + pass + + +class PermissionManager(object): + _permissions = {} + DoesNotExist = PermissionDoesNotExists() + + @classmethod + def register(cls, namespace, name, label): + permission = Permission(namespace, name, label) + cls._permissions[permission.uuid] = permission + return permission + + @classmethod + def check_permissions(cls, requester, permission_list): + for permission in permission_list: + if permission.requester_has_this(requester): + return True + + raise PermissionDenied(ugettext(u'Insufficient permissions.')) + + @classmethod + def get_for_holder(cls, holder): + return StoredPermission.objects.get_for_holder(holder) + + @classmethod + def all(cls): + return cls._permissions.values() + #return LazyQuerySet(cls, cls._permissions) + + @classmethod + def get(cls, get_dict): + if 'pk' in get_dict: + try: + return cls._permissions[get_dict['pk']].get_stored_permission() + except KeyError: + raise Permission.DoesNotExist + + + def __init__(self, model): + self.model = model + + +class Permission(object): + DoesNotExist = PermissionDoesNotExists + + def __init__(self, namespace, name, label): + self.namespace = namespace + self.name = name + self.label = label + self.pk = self.uuid + + def __unicode__(self): + return unicode(self.label) + + @property + def uuid(self): + return u'%s.%s' % (self.namespace.name, self.name) + + def get_stored_permission(self): + stored_permission, created = StoredPermission.objects.get_or_create( + namespace=self.namespace.name, + name=self.name, + defaults={ + 'label': self.label + } + ) + stored_permission.label = self.label + stored_permission.save() + stored_permission.volatile_permission = self + return stored_permission + + def requester_has_this(self, requester): + stored_permission = self.get_stored_permission( + ) + return stored_permission.requester_has_this(requester) + + def save(self, *args, **kwargs): + return self.get_stored_permission( + ) + +Permission.objects = PermissionManager(Permission) +Permission._default_manager = Permission.objects + + +class StoredPermission(models.Model): namespace = models.CharField(max_length=64, verbose_name=_(u'namespace')) name = models.CharField(max_length=64, verbose_name=_(u'name')) label = models.CharField(max_length=96, verbose_name=_(u'label')) - objects = PermissionManager() + objects = StoredPermissionManager() class Meta: ordering = ('namespace', 'label') unique_together = ('namespace', 'name') verbose_name = _(u'permission') verbose_name_plural = _(u'permissions') - + def __unicode__(self): - return u'%s: %s' % (self.get_namespace_label(), self.get_label()) + return unicode(self.volatile_permission) def get_holders(self): return [holder.holder_object for holder in self.permissionholder_set.all()] - def has_permission(self, requester): + def requester_has_this(self, requester): if isinstance(requester, User): if requester.is_superuser or requester.is_staff: return True @@ -60,16 +167,10 @@ class Permission(models.Model): return True except PermissionHolder.DoesNotExist: return False - - def get_label(self): - return unicode(permission_titles.get('%s.%s' % (self.namespace, self.name), self.label)) - - def get_namespace_label(self): - return unicode(namespace_titles[self.namespace]) if self.namespace in namespace_titles else self.namespace class PermissionHolder(models.Model): - permission = models.ForeignKey(Permission, verbose_name=_(u'permission')) + permission = models.ForeignKey(StoredPermission, verbose_name=_(u'permission')) holder_type = models.ForeignKey(ContentType, related_name='permission_holder', limit_choices_to={'model__in': ('user', 'group', 'role')}) diff --git a/apps/permissions/runtime.py b/apps/permissions/runtime.py index a56d8cfeb4..8b13789179 100644 --- a/apps/permissions/runtime.py +++ b/apps/permissions/runtime.py @@ -1,8 +1 @@ -from django.utils.translation import ugettext_lazy as _ - -namespace_titles = { - 'permissions': _(u'Permissions') -} - -permission_titles = {} diff --git a/apps/permissions/views.py b/apps/permissions/views.py index 643fe8a50f..3e4cbd4e74 100644 --- a/apps/permissions/views.py +++ b/apps/permissions/views.py @@ -2,7 +2,7 @@ import operator import itertools from django.utils.translation import ugettext_lazy as _ -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, Http404 from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.contrib import messages @@ -22,12 +22,11 @@ from permissions.forms import RoleForm, RoleForm_view from permissions import PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, \ PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, PERMISSION_PERMISSION_GRANT, \ PERMISSION_PERMISSION_REVOKE -from permissions.api import check_permissions, namespace_titles, get_permission_label, get_permission_namespace_label from permissions.widgets import role_permission_link def role_list(request): - check_permissions(request.user, [PERMISSION_ROLE_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_VIEW]) return object_list( request, @@ -41,7 +40,7 @@ def role_list(request): def role_permissions(request, role_id): - check_permissions(request.user, [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]) role = get_object_or_404(Role, pk=role_id) form = RoleForm_view(instance=role) @@ -52,13 +51,13 @@ def role_permissions(request, role_id): 'name': u'generic_list_subtemplate.html', 'context': { 'title': _(u'permissions'), - 'object_list': Permission.objects.active_only(), + 'object_list': Permission.objects.all(), 'extra_columns': [ - {'name': _(u'namespace'), 'attribute': encapsulate(lambda x: get_permission_namespace_label(x))}, - {'name': _(u'name'), 'attribute': encapsulate(lambda x: get_permission_label(x))}, + {'name': _(u'namespace'), 'attribute': encapsulate(lambda x: x.namespace)}, + {'name': _(u'name'), 'attribute': encapsulate(lambda x: x.label)}, { 'name':_(u'has permission'), - 'attribute': encapsulate(lambda x: two_state_template(x.has_permission(role))), + 'attribute': encapsulate(lambda x: two_state_template(x.requester_has_this(role))), }, ], 'hide_link': True, @@ -83,7 +82,7 @@ def role_permissions(request, role_id): def role_edit(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_EDIT]) return update_object(request, template_name='generic_form.html', form_class=RoleForm, object_id=role_id, extra_context={ @@ -91,7 +90,7 @@ def role_edit(request, role_id): def role_create(request): - check_permissions(request.user, [PERMISSION_ROLE_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_CREATE]) return create_object(request, model=Role, template_name='generic_form.html', @@ -99,7 +98,7 @@ def role_create(request): def role_delete(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_DELETE]) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) @@ -117,7 +116,7 @@ def role_delete(request, role_id): def permission_grant(request): - check_permissions(request.user, [PERMISSION_PERMISSION_GRANT]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_GRANT]) items_property_list = loads(request.GET.get('items_property_list', [])) post_action_redirect = None @@ -126,7 +125,12 @@ def permission_grant(request): items = [] for item_properties in items_property_list: - permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + #permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + try: + permission = Permission.objects.get({'pk': item_properties['permission_id']}) + except Permission.DoesNotExist: + raise Http404 + ct = get_object_or_404(ContentType, app_label=item_properties['requester_app_label'], model=item_properties['requester_model']) requester_model = ct.model_class() requester = get_object_or_404(requester_model, pk=item_properties['requester_id']) @@ -176,7 +180,7 @@ def permission_grant(request): def permission_revoke(request): - check_permissions(request.user, [PERMISSION_PERMISSION_REVOKE]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_REVOKE]) items_property_list = loads(request.GET.get('items_property_list', [])) post_action_redirect = None @@ -185,7 +189,12 @@ def permission_revoke(request): items = [] for item_properties in items_property_list: - permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + #permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + try: + permission = Permission.objects.get({'pk': item_properties['permission_id']}) + except Permission.DoesNotExist: + raise Http404 + ct = get_object_or_404(ContentType, app_label=item_properties['requester_app_label'], model=item_properties['requester_model']) requester_model = ct.model_class() requester = get_object_or_404(requester_model, pk=item_properties['requester_id']) @@ -265,7 +274,7 @@ def remove_role_member(role, selection): def role_members(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_EDIT]) role = get_object_or_404(Role, pk=role_id) return assign_remove(