Merge branch 'feature/apply_default_acls' into development
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -9,4 +9,5 @@ CONTENT_TYPE_ICON_MAP = {
|
||||
'taggit.tag': 'tag_blue',
|
||||
'linking.smartlink': 'page_link',
|
||||
'common.anonymoususersingleton': 'user',
|
||||
'acls.creatorsingleton': 'user',
|
||||
}
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
33
apps/acls/utils.py
Normal 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()
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user