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

View File

@@ -16,6 +16,13 @@ logger = logging.getLogger(__name__)
_cache = {}
def get_source_object(obj):
if isinstance(obj, EncapsulatedObject):
return obj.source_object
else:
return obj
class EncapsulatedObject(object):
source_object_name = u'source_object'
@@ -95,7 +102,7 @@ class EncapsulatedObject(object):
def __init__(self, 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):
# Class
@@ -109,9 +116,12 @@ class EncapsulatedObject(object):
def __unicode__(self):
if isinstance(self.source_object, ModelBase):
return capfirst(unicode(self.source_object._meta.verbose_name_plural))
elif self.ct_fullname == 'auth.user':
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:
return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object)

View File

@@ -9,13 +9,14 @@ from common.utils import get_object_name
from common.models import AnonymousUserSingleton
from .classes import AccessHolder
from .models import CreatorSingleton
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])
class HolderSelectionForm(forms.Form):
class BaseHolderSelectionForm(forms.Form):
holder_gid = forms.ChoiceField(
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)
roles = set(Role.objects.all()) - set(current_holders)
groups = set(Group.objects.all()) - set(current_holders)
special = set(self.special_holders) - set(current_holders)
non_holder_list = []
if users:
@@ -41,7 +43,16 @@ class HolderSelectionForm(forms.Form):
if 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
class HolderSelectionForm(BaseHolderSelectionForm):
special_holders = [AnonymousUserSingleton.objects.get()]
class ClassHolderSelectionForm(BaseHolderSelectionForm):
special_holders = [AnonymousUserSingleton.objects.get(), CreatorSingleton.objects.get()]

View File

@@ -9,4 +9,5 @@ CONTENT_TYPE_ICON_MAP = {
'taggit.tag': 'tag_blue',
'linking.smartlink': 'page_link',
'common.anonymoususersingleton': 'user',
'acls.creatorsingleton': 'user',
}

View File

@@ -13,7 +13,8 @@ from django.core.urlresolvers import reverse
from common.models import AnonymousUserSingleton
from permissions.models import Permission
from .classes import EncapsulatedObject, AccessHolder, ClassAccessHolder
from .classes import (EncapsulatedObject, AccessHolder, ClassAccessHolder,
get_source_object)
logger = logging.getLogger(__name__)
@@ -23,18 +24,12 @@ class AccessEntryManager(models.Manager):
Implement a 3 tier permission system, involving a permissions, an actor
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):
"""
Grant a permission (what), (to) an actor, (on) a specific object
"""
obj = self.source_object(obj)
actor = self.source_object(actor)
obj = get_source_object(obj)
actor = get_source_object(actor)
access_entry, created = self.model.objects.get_or_create(
permission=permission,
@@ -49,8 +44,8 @@ class AccessEntryManager(models.Manager):
"""
Revoke a permission (what), (from) an actor, (on) a specific object
"""
obj = self.source_object(obj)
actor = self.source_object(actor)
obj = get_source_object(obj)
actor = get_source_object(actor)
try:
access_entry = self.model.objects.get(
@@ -60,19 +55,20 @@ class AccessEntryManager(models.Manager):
content_type=ContentType.objects.get_for_model(obj),
object_id=obj.pk
)
access_entry.delete()
return True
except self.model.DoesNotExist:
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
"""
obj = self.source_object(obj)
actor = self.source_object(actor)
obj = get_source_object(obj)
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:
return True
@@ -93,8 +89,8 @@ class AccessEntryManager(models.Manager):
def check_access(self, permission, actor, obj):
# TODO: Merge with has_access
obj = self.source_object(obj)
actor = self.source_object(actor)
obj = get_source_object(obj)
actor = get_source_object(actor)
if self.has_access(permission, actor, obj):
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
"""
obj = self.source_object(obj)
actor = self.source_object(actor)
obj = get_source_object(obj)
actor = get_source_object(actor)
for permission in permission_list:
if self.has_access(permission, actor, obj):
return True
@@ -148,7 +144,7 @@ class AccessEntryManager(models.Manager):
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
a specific object
@@ -156,7 +152,7 @@ class AccessEntryManager(models.Manager):
logger.debug('obj: %s' % obj)
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:
return Permission.objects.all()
@@ -211,8 +207,9 @@ class DefaultAccessEntryManager(models.Manager):
content type is created.
"""
def get_holders_for(self, cls):
if isinstance(cls, EncapsulatedObject):
cls = cls.source_object
cls = get_source_object(cls)
#if isinstance(cls, EncapsulatedObject):
# cls = cls.source_object
content_type = ContentType.objects.get_for_model(cls)
holder_list = []

View File

@@ -11,6 +11,7 @@ from django.core.exceptions import PermissionDenied
from django.core.exceptions import ObjectDoesNotExist
from permissions.models import StoredPermission
from common.models import Singleton, SingletonManager
from .managers import AccessEntryManager, DefaultAccessEntryManager
from .classes import AccessObjectClass
@@ -91,3 +92,22 @@ class DefaultAccessEntry(models.Model):
def __unicode__(self):
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
apps/acls/utils.py Normal file
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()

View File

@@ -24,7 +24,7 @@ from .models import AccessEntry, DefaultAccessEntry
from .classes import (AccessHolder, AccessObject, AccessObjectClass,
ClassAccessHolder)
from .widgets import object_w_content_type_icon
from .forms import HolderSelectionForm
from .forms import HolderSelectionForm, ClassHolderSelectionForm
from .api import get_class_permissions_for
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),
'extra_columns': [
{'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,
'access_object': AccessObject.encapsulate(obj),
@@ -78,7 +78,8 @@ def acl_detail(request, access_object_gid, holder_object_gid):
except ObjectDoesNotExist:
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):
@@ -87,8 +88,7 @@ def acl_detail_for(request, actor, obj):
except PermissionDenied:
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)
subtemplates_list = [
{
@@ -105,7 +105,7 @@ def acl_detail_for(request, actor, obj):
{'name': _(u'label'), 'attribute': 'label'},
{
'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,
@@ -114,15 +114,15 @@ def acl_detail_for(request, actor, obj):
]
context = {
'object': obj,
'object': obj.source_object,
'subtemplates_list': subtemplates_list,
'multi_select_as_buttons': True,
'multi_select_item_properties': {
'permission_pk': lambda x: x.pk,
'holder_gid': lambda x: AccessHolder(actor).gid,
'object_gid': lambda x: AccessObject(obj).gid,
'holder_gid': lambda x: actor.gid,
'object_gid': lambda x: obj.gid,
},
'access_object': AccessObject.encapsulate(obj),
'access_object': obj,
'navigation_object_list': [
{'object': 'object'},
{'object': 'access_object'}
@@ -425,7 +425,6 @@ def acl_class_acl_detail(request, access_object_class_gid, holder_object_gid):
except ObjectDoesNotExist:
raise Http404
#permission_list = list(access_object_class.get_class_permissions())
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)
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)
if request.method == 'POST':
form = HolderSelectionForm(request.POST)
form = ClassHolderSelectionForm(request.POST)
if form.is_valid():
try:
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:
raise Http404
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 = {
'form': form,

View File

@@ -17,6 +17,7 @@ from permissions import Permission
from common.utils import encapsulate
from acls.models import AccessEntry
from acls.views import acl_list_for
from acls.utils import apply_default_acls
from .models import Folder
from .forms import FolderForm, FolderListForm
@@ -64,6 +65,7 @@ def folder_create(request):
if form.is_valid():
folder, created = Folder.objects.get_or_create(user=request.user, title=form.cleaned_data['title'])
if created:
apply_default_acls(folder, request.user)
messages.success(request, _(u'Folder created successfully'))
return HttpResponseRedirect(reverse('folder_list'))
else:

View File

@@ -17,6 +17,7 @@ from documents.permissions import PERMISSION_DOCUMENT_VIEW
from permissions.models import Permission
from acls.views import acl_list_for
from acls.models import AccessEntry, PermissionDenied
from acls.utils import apply_default_acls
from .models import SmartLink, SmartLinkCondition
from .conf.settings import SHOW_EMPTY_SMART_LINKS
@@ -147,6 +148,7 @@ def smart_link_create(request):
form = SmartLinkForm(request.POST)
if form.is_valid():
document_group = form.save()
apply_default_acls(document_group, request.user)
messages.success(request, _(u'Smart link: %s created successfully.') % document_group)
return HttpResponseRedirect(reverse('document_group_list'))
else:

View File

@@ -17,6 +17,7 @@ from history.api import create_history
from metadata.models import MetadataType
from metadata.api import save_metadata_list
from scheduler.api import register_interval_job, remove_job
from acls.utils import apply_default_acls
from .managers import SourceTransformationManager
from .literals import (SOURCE_CHOICES, SOURCE_CHOICES_PLURAL,
@@ -76,6 +77,8 @@ class BaseModel(models.Model):
document.document_type = document_type
document.save()
apply_default_acls(document, user)
if metadata_dict_list:
save_metadata_list(metadata_dict_list, document, create=True)
warnings = update_indexes(document)

View File

@@ -17,6 +17,7 @@ from documents.permissions import PERMISSION_DOCUMENT_VIEW
from common.utils import encapsulate
from acls.models import AccessEntry, PermissionDenied
from acls.views import acl_list_for, acl_new_holder_for
from acls.utils import apply_default_acls
from .forms import TagListForm, TagForm
from .models import TagProperties
@@ -44,6 +45,7 @@ def tag_create(request):
tag = Tag(name=tag_name)
tag.save()
TagProperties(tag=tag, color=form.cleaned_data['color']).save()
apply_default_acls(tag, request.user)
messages.success(request, _(u'Tag created succesfully.'))
return HttpResponseRedirect(redirect_url)