Merge branch 'feature/apply_default_acls' into development

This commit is contained in:
Roberto Rosario
2012-01-21 04:36:36 -04:00
11 changed files with 123 additions and 43 deletions
+12 -2
View File
@@ -16,6 +16,13 @@ logger = logging.getLogger(__name__)
_cache = {} _cache = {}
def get_source_object(obj):
if isinstance(obj, EncapsulatedObject):
return obj.source_object
else:
return obj
class EncapsulatedObject(object): class EncapsulatedObject(object):
source_object_name = u'source_object' source_object_name = u'source_object'
@@ -95,7 +102,7 @@ class EncapsulatedObject(object):
def __init__(self, source_object): def __init__(self, source_object):
self.content_type = ContentType.objects.get_for_model(source_object) self.content_type = ContentType.objects.get_for_model(source_object)
self.ct_fullname = '%s.%s' % (self.content_type.app_label, self.content_type.name) self.ct_fullname = '%s.%s' % (self.content_type.app_label, self.content_type.model)
if isinstance(source_object, ModelBase): if isinstance(source_object, ModelBase):
# Class # Class
@@ -109,9 +116,12 @@ class EncapsulatedObject(object):
def __unicode__(self): def __unicode__(self):
if isinstance(self.source_object, ModelBase): if isinstance(self.source_object, ModelBase):
return capfirst(unicode(self.source_object._meta.verbose_name_plural)) return capfirst(unicode(self.source_object._meta.verbose_name_plural))
elif self.ct_fullname == 'auth.user': elif self.ct_fullname == 'auth.user':
return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object.get_full_name()) return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object.get_full_name())
elif self.ct_fullname == 'common.anonymoususersingleton':
return unicode(self.source_object)
elif self.ct_fullname == 'acls.creatorsingleton':
return unicode(self.source_object)
else: else:
return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object) return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object)
+14 -3
View File
@@ -9,13 +9,14 @@ from common.utils import get_object_name
from common.models import AnonymousUserSingleton from common.models import AnonymousUserSingleton
from .classes import AccessHolder from .classes import AccessHolder
from .models import CreatorSingleton
def _as_choice_list(holders): def _as_choice_list(holders):
return sorted([(AccessHolder.encapsulate(holder).gid, get_object_name(holder, display_object_type=False)) for holder in holders], key=lambda x: x[1]) return sorted([(AccessHolder.encapsulate(holder).gid, get_object_name(holder, display_object_type=False)) for holder in holders], key=lambda x: x[1])
class HolderSelectionForm(forms.Form): class BaseHolderSelectionForm(forms.Form):
holder_gid = forms.ChoiceField( holder_gid = forms.ChoiceField(
label=_(u'New holder') label=_(u'New holder')
) )
@@ -30,6 +31,7 @@ class HolderSelectionForm(forms.Form):
users = set(User.objects.filter(is_active=True)) - set(staff_users) - set(super_users) - set(current_holders) users = set(User.objects.filter(is_active=True)) - set(staff_users) - set(super_users) - set(current_holders)
roles = set(Role.objects.all()) - set(current_holders) roles = set(Role.objects.all()) - set(current_holders)
groups = set(Group.objects.all()) - set(current_holders) groups = set(Group.objects.all()) - set(current_holders)
special = set(self.special_holders) - set(current_holders)
non_holder_list = [] non_holder_list = []
if users: if users:
@@ -41,7 +43,16 @@ class HolderSelectionForm(forms.Form):
if roles: if roles:
non_holder_list.append((_(u'Roles'), _as_choice_list(list(roles)))) non_holder_list.append((_(u'Roles'), _as_choice_list(list(roles))))
non_holder_list.append((_(u'Special'), _as_choice_list([AnonymousUserSingleton.objects.get()]))) if special:
non_holder_list.append((_(u'Special'), _as_choice_list(list(special))))
super(HolderSelectionForm, self).__init__(*args, **kwargs) super(BaseHolderSelectionForm, self).__init__(*args, **kwargs)
self.fields['holder_gid'].choices = non_holder_list self.fields['holder_gid'].choices = non_holder_list
class HolderSelectionForm(BaseHolderSelectionForm):
special_holders = [AnonymousUserSingleton.objects.get()]
class ClassHolderSelectionForm(BaseHolderSelectionForm):
special_holders = [AnonymousUserSingleton.objects.get(), CreatorSingleton.objects.get()]
+1
View File
@@ -9,4 +9,5 @@ CONTENT_TYPE_ICON_MAP = {
'taggit.tag': 'tag_blue', 'taggit.tag': 'tag_blue',
'linking.smartlink': 'page_link', 'linking.smartlink': 'page_link',
'common.anonymoususersingleton': 'user', 'common.anonymoususersingleton': 'user',
'acls.creatorsingleton': 'user',
} }
+22 -25
View File
@@ -13,7 +13,8 @@ from django.core.urlresolvers import reverse
from common.models import AnonymousUserSingleton from common.models import AnonymousUserSingleton
from permissions.models import Permission from permissions.models import Permission
from .classes import EncapsulatedObject, AccessHolder, ClassAccessHolder from .classes import (EncapsulatedObject, AccessHolder, ClassAccessHolder,
get_source_object)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -23,18 +24,12 @@ class AccessEntryManager(models.Manager):
Implement a 3 tier permission system, involving a permissions, an actor Implement a 3 tier permission system, involving a permissions, an actor
and an object and an object
""" """
def source_object(self, obj):
if isinstance(obj, EncapsulatedObject):
return obj.source_object
else:
return obj
def grant(self, permission, actor, obj): def grant(self, permission, actor, obj):
""" """
Grant a permission (what), (to) an actor, (on) a specific object Grant a permission (what), (to) an actor, (on) a specific object
""" """
obj = self.source_object(obj) obj = get_source_object(obj)
actor = self.source_object(actor) actor = get_source_object(actor)
access_entry, created = self.model.objects.get_or_create( access_entry, created = self.model.objects.get_or_create(
permission=permission, permission=permission,
@@ -49,8 +44,8 @@ class AccessEntryManager(models.Manager):
""" """
Revoke a permission (what), (from) an actor, (on) a specific object Revoke a permission (what), (from) an actor, (on) a specific object
""" """
obj = self.source_object(obj) obj = get_source_object(obj)
actor = self.source_object(actor) actor = get_source_object(actor)
try: try:
access_entry = self.model.objects.get( access_entry = self.model.objects.get(
@@ -60,19 +55,20 @@ class AccessEntryManager(models.Manager):
content_type=ContentType.objects.get_for_model(obj), content_type=ContentType.objects.get_for_model(obj),
object_id=obj.pk object_id=obj.pk
) )
access_entry.delete()
return True
except self.model.DoesNotExist: except self.model.DoesNotExist:
return False return False
else:
access_entry.delete()
return True
def has_access(self, permission, actor, obj): def has_access(self, permission, actor, obj, db_only=False):
""" """
Returns whether an actor has a specific permission for an object Returns whether an actor has a specific permission for an object
""" """
obj = self.source_object(obj) obj = get_source_object(obj)
actor = self.source_object(actor) actor = get_source_object(actor)
if isinstance(actor, User): if isinstance(actor, User) and db_only == False:
if actor.is_superuser or actor.is_staff: if actor.is_superuser or actor.is_staff:
return True return True
@@ -93,8 +89,8 @@ class AccessEntryManager(models.Manager):
def check_access(self, permission, actor, obj): def check_access(self, permission, actor, obj):
# TODO: Merge with has_access # TODO: Merge with has_access
obj = self.source_object(obj) obj = get_source_object(obj)
actor = self.source_object(actor) actor = get_source_object(actor)
if self.has_access(permission, actor, obj): if self.has_access(permission, actor, obj):
return True return True
@@ -105,8 +101,8 @@ class AccessEntryManager(models.Manager):
""" """
Returns whether an actor has at least one of a list of permissions for an object Returns whether an actor has at least one of a list of permissions for an object
""" """
obj = self.source_object(obj) obj = get_source_object(obj)
actor = self.source_object(actor) actor = get_source_object(actor)
for permission in permission_list: for permission in permission_list:
if self.has_access(permission, actor, obj): if self.has_access(permission, actor, obj):
return True return True
@@ -148,7 +144,7 @@ class AccessEntryManager(models.Manager):
return holder_list return holder_list
def get_holder_permissions_for(self, obj, actor): def get_holder_permissions_for(self, obj, actor, db_only=False):
""" """
Returns a list of actors that hold at least one permission for Returns a list of actors that hold at least one permission for
a specific object a specific object
@@ -156,7 +152,7 @@ class AccessEntryManager(models.Manager):
logger.debug('obj: %s' % obj) logger.debug('obj: %s' % obj)
logger.debug('actor: %s' % actor) logger.debug('actor: %s' % actor)
if isinstance(actor, User): if isinstance(actor, User) and db_only == False:
if actor.is_superuser or actor.is_staff: if actor.is_superuser or actor.is_staff:
return Permission.objects.all() return Permission.objects.all()
@@ -211,8 +207,9 @@ class DefaultAccessEntryManager(models.Manager):
content type is created. content type is created.
""" """
def get_holders_for(self, cls): def get_holders_for(self, cls):
if isinstance(cls, EncapsulatedObject): cls = get_source_object(cls)
cls = cls.source_object #if isinstance(cls, EncapsulatedObject):
# cls = cls.source_object
content_type = ContentType.objects.get_for_model(cls) content_type = ContentType.objects.get_for_model(cls)
holder_list = [] holder_list = []
+20
View File
@@ -11,6 +11,7 @@ from django.core.exceptions import PermissionDenied
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from permissions.models import StoredPermission from permissions.models import StoredPermission
from common.models import Singleton, SingletonManager
from .managers import AccessEntryManager, DefaultAccessEntryManager from .managers import AccessEntryManager, DefaultAccessEntryManager
from .classes import AccessObjectClass from .classes import AccessObjectClass
@@ -91,3 +92,22 @@ class DefaultAccessEntry(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s: %s' % (self.content_type, self.content_object) return u'%s: %s' % (self.content_type, self.content_object)
class CreatorSingletonManager(SingletonManager):
def passthru_check(self, holder, creator=None):
if isinstance(holder, self.model):
# TODO: raise explicit error if is instance and creator=None
return creator
else:
return holder
class CreatorSingleton(Singleton):
objects = CreatorSingletonManager()
def __unicode__(self):
return ugettext('Creator')
class Meta:
verbose_name = _(u'creator')
verbose_name_plural = _(u'creator')
+33
View File
@@ -0,0 +1,33 @@
from __future__ import absolute_import
import logging
from django.contrib.contenttypes.models import ContentType
from common.models import AnonymousUserSingleton
from .models import AccessEntry, DefaultAccessEntry, CreatorSingleton
from .classes import EncapsulatedObject, AccessHolder, ClassAccessHolder
logger = logging.getLogger(__name__)
def apply_default_acls(obj, actor=None):
logger.debug('actor, init: %s' % actor)
if isinstance(obj, EncapsulatedObject):
obj = obj.source_object
if actor:
actor = AnonymousUserSingleton.objects.passthru_check(actor)
content_type = ContentType.objects.get_for_model(obj)
for default_acl in DefaultAccessEntry.objects.filter(content_type=content_type):
holder = CreatorSingleton.objects.passthru_check(default_acl.holder_object, actor)
access_entry = AccessEntry(
permission=default_acl.permission,
holder_object=holder,
content_object=obj,
)
access_entry.save()
+12 -13
View File
@@ -24,7 +24,7 @@ from .models import AccessEntry, DefaultAccessEntry
from .classes import (AccessHolder, AccessObject, AccessObjectClass, from .classes import (AccessHolder, AccessObject, AccessObjectClass,
ClassAccessHolder) ClassAccessHolder)
from .widgets import object_w_content_type_icon from .widgets import object_w_content_type_icon
from .forms import HolderSelectionForm from .forms import HolderSelectionForm, ClassHolderSelectionForm
from .api import get_class_permissions_for from .api import get_class_permissions_for
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ def acl_list_for(request, obj, extra_context=None):
'title': _(u'access control lists for: %s' % obj), 'title': _(u'access control lists for: %s' % obj),
'extra_columns': [ 'extra_columns': [
{'name': _(u'holder'), 'attribute': encapsulate(lambda x: object_w_content_type_icon(x.source_object))}, {'name': _(u'holder'), 'attribute': encapsulate(lambda x: object_w_content_type_icon(x.source_object))},
{'name': _(u'permissions'), 'attribute': encapsulate(lambda x: _permission_titles(AccessEntry.objects.get_holder_permissions_for(obj, x.source_object)))}, {'name': _(u'permissions'), 'attribute': encapsulate(lambda x: _permission_titles(AccessEntry.objects.get_holder_permissions_for(obj, x.source_object, db_only=True)))},
], ],
'hide_object': True, 'hide_object': True,
'access_object': AccessObject.encapsulate(obj), 'access_object': AccessObject.encapsulate(obj),
@@ -78,7 +78,8 @@ def acl_detail(request, access_object_gid, holder_object_gid):
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404 raise Http404
return acl_detail_for(request, holder.source_object, access_object.source_object) #return acl_detail_for(request, holder.source_object, access_object.source_object)
return acl_detail_for(request, holder, access_object)
def acl_detail_for(request, actor, obj): def acl_detail_for(request, actor, obj):
@@ -87,8 +88,7 @@ def acl_detail_for(request, actor, obj):
except PermissionDenied: except PermissionDenied:
AccessEntry.objects.check_accesses([ACLS_VIEW_ACL], actor, obj) AccessEntry.objects.check_accesses([ACLS_VIEW_ACL], actor, obj)
permission_list = get_class_permissions_for(obj) permission_list = get_class_permissions_for(obj.source_object)
#TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware) #TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware)
subtemplates_list = [ subtemplates_list = [
{ {
@@ -105,7 +105,7 @@ def acl_detail_for(request, actor, obj):
{'name': _(u'label'), 'attribute': 'label'}, {'name': _(u'label'), 'attribute': 'label'},
{ {
'name':_(u'has permission'), 'name':_(u'has permission'),
'attribute': encapsulate(lambda permission: two_state_template(AccessEntry.objects.has_access(permission, actor, obj))) 'attribute': encapsulate(lambda permission: two_state_template(AccessEntry.objects.has_access(permission, actor, obj, db_only=True)))
}, },
], ],
'hide_object': True, 'hide_object': True,
@@ -114,15 +114,15 @@ def acl_detail_for(request, actor, obj):
] ]
context = { context = {
'object': obj, 'object': obj.source_object,
'subtemplates_list': subtemplates_list, 'subtemplates_list': subtemplates_list,
'multi_select_as_buttons': True, 'multi_select_as_buttons': True,
'multi_select_item_properties': { 'multi_select_item_properties': {
'permission_pk': lambda x: x.pk, 'permission_pk': lambda x: x.pk,
'holder_gid': lambda x: AccessHolder(actor).gid, 'holder_gid': lambda x: actor.gid,
'object_gid': lambda x: AccessObject(obj).gid, 'object_gid': lambda x: obj.gid,
}, },
'access_object': AccessObject.encapsulate(obj), 'access_object': obj,
'navigation_object_list': [ 'navigation_object_list': [
{'object': 'object'}, {'object': 'object'},
{'object': 'access_object'} {'object': 'access_object'}
@@ -425,7 +425,6 @@ def acl_class_acl_detail(request, access_object_class_gid, holder_object_gid):
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404 raise Http404
#permission_list = list(access_object_class.get_class_permissions())
permission_list = get_class_permissions_for(access_object_class.content_type.model_class()) permission_list = get_class_permissions_for(access_object_class.content_type.model_class())
#TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware) #TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware)
subtemplates_list = [ subtemplates_list = [
@@ -468,7 +467,7 @@ def acl_class_new_holder_for(request, access_object_class_gid):
access_object_class = AccessObjectClass.get(gid=access_object_class_gid) access_object_class = AccessObjectClass.get(gid=access_object_class_gid)
if request.method == 'POST': if request.method == 'POST':
form = HolderSelectionForm(request.POST) form = ClassHolderSelectionForm(request.POST)
if form.is_valid(): if form.is_valid():
try: try:
access_holder = ClassAccessHolder.get(form.cleaned_data['holder_gid']) access_holder = ClassAccessHolder.get(form.cleaned_data['holder_gid'])
@@ -477,7 +476,7 @@ def acl_class_new_holder_for(request, access_object_class_gid):
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404 raise Http404
else: else:
form = HolderSelectionForm(current_holders=DefaultAccessEntry.objects.get_holders_for(access_object_class)) form = ClassHolderSelectionForm(current_holders=DefaultAccessEntry.objects.get_holders_for(access_object_class))
context = { context = {
'form': form, 'form': form,
+2
View File
@@ -17,6 +17,7 @@ from permissions import Permission
from common.utils import encapsulate from common.utils import encapsulate
from acls.models import AccessEntry from acls.models import AccessEntry
from acls.views import acl_list_for from acls.views import acl_list_for
from acls.utils import apply_default_acls
from .models import Folder from .models import Folder
from .forms import FolderForm, FolderListForm from .forms import FolderForm, FolderListForm
@@ -64,6 +65,7 @@ def folder_create(request):
if form.is_valid(): if form.is_valid():
folder, created = Folder.objects.get_or_create(user=request.user, title=form.cleaned_data['title']) folder, created = Folder.objects.get_or_create(user=request.user, title=form.cleaned_data['title'])
if created: if created:
apply_default_acls(folder, request.user)
messages.success(request, _(u'Folder created successfully')) messages.success(request, _(u'Folder created successfully'))
return HttpResponseRedirect(reverse('folder_list')) return HttpResponseRedirect(reverse('folder_list'))
else: else:
+2
View File
@@ -17,6 +17,7 @@ from documents.permissions import PERMISSION_DOCUMENT_VIEW
from permissions.models import Permission from permissions.models import Permission
from acls.views import acl_list_for from acls.views import acl_list_for
from acls.models import AccessEntry, PermissionDenied from acls.models import AccessEntry, PermissionDenied
from acls.utils import apply_default_acls
from .models import SmartLink, SmartLinkCondition from .models import SmartLink, SmartLinkCondition
from .conf.settings import SHOW_EMPTY_SMART_LINKS from .conf.settings import SHOW_EMPTY_SMART_LINKS
@@ -147,6 +148,7 @@ def smart_link_create(request):
form = SmartLinkForm(request.POST) form = SmartLinkForm(request.POST)
if form.is_valid(): if form.is_valid():
document_group = form.save() document_group = form.save()
apply_default_acls(document_group, request.user)
messages.success(request, _(u'Smart link: %s created successfully.') % document_group) messages.success(request, _(u'Smart link: %s created successfully.') % document_group)
return HttpResponseRedirect(reverse('document_group_list')) return HttpResponseRedirect(reverse('document_group_list'))
else: else:
+3
View File
@@ -17,6 +17,7 @@ from history.api import create_history
from metadata.models import MetadataType from metadata.models import MetadataType
from metadata.api import save_metadata_list from metadata.api import save_metadata_list
from scheduler.api import register_interval_job, remove_job from scheduler.api import register_interval_job, remove_job
from acls.utils import apply_default_acls
from .managers import SourceTransformationManager from .managers import SourceTransformationManager
from .literals import (SOURCE_CHOICES, SOURCE_CHOICES_PLURAL, from .literals import (SOURCE_CHOICES, SOURCE_CHOICES_PLURAL,
@@ -76,6 +77,8 @@ class BaseModel(models.Model):
document.document_type = document_type document.document_type = document_type
document.save() document.save()
apply_default_acls(document, user)
if metadata_dict_list: if metadata_dict_list:
save_metadata_list(metadata_dict_list, document, create=True) save_metadata_list(metadata_dict_list, document, create=True)
warnings = update_indexes(document) warnings = update_indexes(document)
+2
View File
@@ -17,6 +17,7 @@ from documents.permissions import PERMISSION_DOCUMENT_VIEW
from common.utils import encapsulate from common.utils import encapsulate
from acls.models import AccessEntry, PermissionDenied from acls.models import AccessEntry, PermissionDenied
from acls.views import acl_list_for, acl_new_holder_for from acls.views import acl_list_for, acl_new_holder_for
from acls.utils import apply_default_acls
from .forms import TagListForm, TagForm from .forms import TagListForm, TagForm
from .models import TagProperties from .models import TagProperties
@@ -44,6 +45,7 @@ def tag_create(request):
tag = Tag(name=tag_name) tag = Tag(name=tag_name)
tag.save() tag.save()
TagProperties(tag=tag, color=form.cleaned_data['color']).save() TagProperties(tag=tag, color=form.cleaned_data['color']).save()
apply_default_acls(tag, request.user)
messages.success(request, _(u'Tag created succesfully.')) messages.success(request, _(u'Tag created succesfully.'))
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)