Initial models and managers to support ACLs for a generic object
This commit is contained in:
21
apps/acls/__init__.py
Normal file
21
apps/acls/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
#from documents.models import Document
|
||||
from navigation.api import register_links, register_multi_item_links
|
||||
from project_setup.api import register_setup
|
||||
|
||||
from permissions.api import register_permission, set_namespace_title
|
||||
|
||||
|
||||
ACLS_EDIT_ACL = {'namespace': 'acls', 'name': 'acl_edit', 'label': _(u'Edit ACLs')}
|
||||
ACLS_VIEW_ACL = {'namespace': 'acls', 'name': 'acl_view', 'label': _(u'View ACLs')}
|
||||
|
||||
set_namespace_title('acls', _(u'Access control lists'))
|
||||
register_permission(ACLS_EDIT_ACL)
|
||||
register_permission(ACLS_VIEW_ACL)
|
||||
|
||||
acl_list = {'text': _(u'ACLs'), 'view': 'acl_list', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]}
|
||||
|
||||
|
||||
|
||||
#register_links(Document, [acl_list], menu_name='form_header')
|
||||
23
apps/acls/admin.py
Normal file
23
apps/acls/admin.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from acls.models import AccessEntry
|
||||
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
#class PermissionHolderInline(admin.StackedInline):
|
||||
# model = PermissionHolder
|
||||
# extra = 1
|
||||
# classes = ('collapse-open',)
|
||||
# allow_add = True#
|
||||
#
|
||||
class AccessEntryAdmin(admin.ModelAdmin):
|
||||
related_lookup_fields = {
|
||||
'generic': [['holder_type', 'holder_id'], ['content_type', 'object_id']],
|
||||
}
|
||||
#inlines = [PermissionHolderInline]
|
||||
list_display = ('pk', 'holder_object', 'permission', 'content_object')
|
||||
list_display_links = ('pk',)
|
||||
model = AccessEntry
|
||||
|
||||
admin.site.register(AccessEntry, AccessEntryAdmin)
|
||||
62
apps/acls/api.py
Normal file
62
apps/acls/api.py
Normal file
@@ -0,0 +1,62 @@
|
||||
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
|
||||
|
||||
namespace_titles = {
|
||||
'permissions': _(u'Permissions')
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
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.'))
|
||||
|
||||
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)
|
||||
84
apps/acls/models.py
Normal file
84
apps/acls/models.py
Normal file
@@ -0,0 +1,84 @@
|
||||
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.shortcuts import get_object_or_404
|
||||
#from django.utils.translation import ugettext
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
from permissions.models import Permission
|
||||
|
||||
|
||||
class AccessEntryManager(models.Manager):
|
||||
def grant(self, permission, requester, obj):
|
||||
"""
|
||||
Grant a permission (what), (to) a requester, (on) a specific object
|
||||
"""
|
||||
access_entry, created = AccessEntry.objects.get_or_create(
|
||||
permission=permission,
|
||||
holder_type=ContentType.objects.get_for_model(requester),
|
||||
holder_id=requester.pk,
|
||||
content_type=ContentType.objects.get_for_model(obj),
|
||||
object_id=obj.pk
|
||||
)
|
||||
return created
|
||||
|
||||
def revoke(self, permission, holder, obj):
|
||||
try:
|
||||
access_entry = AccessEntry.objects.get(
|
||||
permission=permission,
|
||||
holder_type=ContentType.objects.get_for_model(holder),
|
||||
holder_id=holder.pk,
|
||||
content_type=ContentType.objects.get_for_model(obj),
|
||||
object_id=obj.pk
|
||||
)
|
||||
access_entry.delete()
|
||||
return True
|
||||
except AccessEntry.DoesNotExist:
|
||||
return False
|
||||
|
||||
def check_accesses(self, permission_list, requester, obj):
|
||||
for permission_item in permission_list:
|
||||
permission = get_object_or_404(Permission,
|
||||
namespace=permission_item['namespace'], name=permission_item['name'])
|
||||
try:
|
||||
access_entry = AccessEntry.objects.get(
|
||||
permission=permission,
|
||||
holder_type=ContentType.objects.get_for_model(requester),
|
||||
holder_id=requester.pk,
|
||||
content_type=ContentType.objects.get_for_model(obj),
|
||||
object_id=obj.pk
|
||||
)
|
||||
return True
|
||||
except AccessEntry.DoesNotExist:
|
||||
raise PermissionDenied(ugettext(u'Insufficient permissions.'))
|
||||
|
||||
def get_acl_url(self, obj):
|
||||
content_type = ContentType.objects.get_for_model(obj.__class__)
|
||||
return reverse('acl_list', args=[content_type.app_label, content_type.model, obj.pk])
|
||||
|
||||
|
||||
class AccessEntry(models.Model):
|
||||
permission = models.ForeignKey(Permission, verbose_name=_(u'permission'))
|
||||
holder_type = models.ForeignKey(ContentType,
|
||||
related_name='access_holder',
|
||||
limit_choices_to={'model__in': ('user', 'group', 'role')})
|
||||
holder_id = models.PositiveIntegerField()
|
||||
holder_object = generic.GenericForeignKey(ct_field='holder_type', fk_field='holder_id')
|
||||
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
related_name='object_content_type')
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
|
||||
|
||||
objects = AccessEntryManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'access entry')
|
||||
verbose_name_plural = _(u'access entries')
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s: %s' % (self.content_type, self.content_object)
|
||||
16
apps/acls/tests.py
Normal file
16
apps/acls/tests.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
15
apps/acls/urls.py
Normal file
15
apps/acls/urls.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('acls.views',
|
||||
url(r'^list_for/(?P<app_label>[-\w]+)/(?P<model_name>[-\w]+)/(?P<object_id>\d+)/$', 'acl_list', (), 'acl_list'),
|
||||
|
||||
# url(r'^role/list/$', 'role_list', (), 'role_list'),
|
||||
# url(r'^role/create/$', 'role_create', (), 'role_create'),
|
||||
# url(r'^role/(?P<role_id>\d+)/permissions/$', 'role_permissions', (), 'role_permissions'),
|
||||
# url(r'^role/(?P<role_id>\d+)/edit/$', 'role_edit', (), 'role_edit'),
|
||||
# url(r'^role/(?P<role_id>\d+)/delete/$', 'role_delete', (), 'role_delete'),
|
||||
# url(r'^role/(?P<role_id>\d+)/members/$', 'role_members', (), 'role_members'),
|
||||
#
|
||||
# url(r'^permissions/multiple/grant/$', 'permission_grant', (), 'permission_multiple_grant'),
|
||||
# url(r'^permissions/multiple/revoke/$', 'permission_revoke', (), 'permission_multiple_revoke'),
|
||||
)
|
||||
39
apps/acls/views.py
Normal file
39
apps/acls/views.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.contrib import messages
|
||||
from django.views.generic.list_detail import object_list
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views.generic.create_update import create_object, delete_object, update_object
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.auth.models import User, Group
|
||||
|
||||
from permissions.api import check_permissions, namespace_titles
|
||||
|
||||
from acls import ACLS_EDIT_ACL, ACLS_VIEW_ACL
|
||||
|
||||
|
||||
def acl_list_for(request, obj, extra_context=None):
|
||||
check_permissions(request.user, [ACLS_VIEW_ACL])
|
||||
|
||||
context = {
|
||||
'object_list': [],
|
||||
'title': _(u'access control lists for: %s' % obj),
|
||||
#'multi_select_as_buttons': True,
|
||||
#'hide_links': True,
|
||||
}
|
||||
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
|
||||
return render_to_response('generic_list.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def acl_list(request, app_label, model_name, object_id):
|
||||
ct = get_object_or_404(ContentType, app_label=app_label, model=model_name)
|
||||
obj = get_object_or_404(ContentType.user_type.get_object_for_this_type, pk=object_id)
|
||||
return acl_list_for(request, obj)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user