Refactor code to associate a model with a set of permissions. Update related apps.

This commit is contained in:
Roberto Rosario
2015-06-30 22:00:14 -04:00
parent 6ceb71d0b3
commit ee1b05fb57
15 changed files with 99 additions and 226 deletions

View File

@@ -0,0 +1 @@
from .classes import ModelPermission # NOQA

View File

@@ -1,28 +0,0 @@
from __future__ import unicode_literals
from django.contrib.contenttypes.models import ContentType
_class_permissions = {}
def class_permissions(cls, permission_list):
"""
Associate a permissions list to a class
"""
stored_permissions = _class_permissions.setdefault(cls, [])
stored_permissions.extend(permission_list)
def get_class_permissions_for(obj):
"""
Return a list of permissions associated with a content type
"""
content_type = ContentType.objects.get_for_model(obj)
return _class_permissions.get(content_type.model_class(), [])
def get_classes():
"""
Return a list of encapsulated classes that have been registered
"""
return _class_permissions.keys()

View File

@@ -1,138 +1,22 @@
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
import logging
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.base import ModelBase
from common.models import AnonymousUserSingleton
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 = 'source_object'
class ModelPermission(object):
_registry = {}
@classmethod
def object_key(cls, app_label=None, model=None, pk=None):
if pk:
return '%s.%s.%s.%s' % (cls.__name__, app_label, model, pk)
else:
return '%s.%s.%s' % (cls.__name__, app_label, model)
def register(cls, model, permissions):
cls._registry.setdefault(model, [])
for permission in permissions:
cls._registry[model].append(permission.stored_permission.pk)
@classmethod
def add_to_class(cls, name, value):
if hasattr(value, 'contribute_to_class'):
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
def get_for_instance(cls, instance):
from permissions.models import StoredPermission
@classmethod
def set_source_object_name(cls, new_name):
cls.source_object_name = new_name
# @classmethod
# def encapsulate_list(cls, source_object=None, app_label=None, model=None, pk=None):
@classmethod
def encapsulate(cls, source_object):
source_object = AnonymousUserSingleton.objects.passthru_check(source_object)
content_type = ContentType.objects.get_for_model(source_object)
if hasattr(source_object, 'pk'):
# Object
object_key = cls.object_key(content_type.app_label, content_type.model, source_object.pk)
else:
# Class
object_key = cls.object_key(content_type.app_label, content_type.model)
try:
return _cache[object_key]
except KeyError:
encapsulated_object = cls(source_object)
_cache[object_key] = encapsulated_object
return encapsulated_object
@classmethod
def get(cls, gid):
elements = gid.split('.')
if len(elements) == 3:
app_label, model, pk = elements[0], elements[1], elements[2]
elif len(elements) == 2:
app_label, model = elements[0], elements[1]
pk = None
object_key = cls.object_key(*elements)
try:
return _cache[object_key]
except KeyError:
try:
content_type = ContentType.objects.get(app_label=app_label, model=model)
except ContentType.DoesNotExist:
raise ObjectDoesNotExist('%s matching query does not exist.' % ContentType._meta.object_name)
else:
source_object_model_class = content_type.model_class()
if pk:
try:
source_object = content_type.get_object_for_this_type(pk=pk)
except source_object_model_class.DoesNotExist:
raise ObjectDoesNotExist('%s matching query does not exist.' % source_object_model_class._meta.object_name)
else:
source_object = source_object_model_class
return cls.encapsulate(source_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.model)
if isinstance(source_object, ModelBase):
# Class
self.gid = '%s.%s' % (self.content_type.app_label, self.content_type.model)
else:
# Object
self.gid = '%s.%s.%s' % (self.content_type.app_label, self.content_type.model, source_object.pk)
setattr(self, self.__class__.source_object_name, source_object)
def __unicode__(self):
if isinstance(self.source_object, ModelBase):
return unicode(self.source_object._meta.verbose_name_plural)
elif self.ct_fullname == 'auth.user':
return '%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 '%s %s' % (self.source_object._meta.verbose_name, self.source_object)
def __repr__(self):
return self.__unicode__()
@property
def source_object(self):
return getattr(self, self.__class__.source_object_name, None)
class AccessHolder(EncapsulatedObject):
source_object_name = 'holder_object'
class AccessObject(EncapsulatedObject):
source_object_name = 'obj'
class AccessObjectClass(EncapsulatedObject):
source_object_name = 'cls'
pks = cls._registry.get(type(instance), ())
return StoredPermission.objects.filter(pk__in=pks)

View File

@@ -13,8 +13,6 @@ from django.utils.translation import ugettext
from common.models import AnonymousUserSingleton
from permissions import Permission
from .classes import AccessHolder, get_source_object
logger = logging.getLogger(__name__)

View File

@@ -17,6 +17,7 @@ from common.views import (
from permissions import Permission, PermissionNamespace
from permissions.models import StoredPermission
from .classes import ModelPermission
from .models import AccessControlList
from .permissions import permission_acl_edit, permission_acl_view
@@ -137,7 +138,7 @@ class ACLPermissionsView(AssignRemoveView):
def left_list(self):
results = []
for namespace, permissions in itertools.groupby(StoredPermission.objects.exclude(id__in=self.get_object().permissions.values_list('pk', flat=True)), lambda entry: entry.namespace):
for namespace, permissions in itertools.groupby(ModelPermission.get_for_instance(instance=self.get_object().content_object).exclude(id__in=self.get_object().permissions.values_list('pk', flat=True)), lambda entry: entry.namespace):
permission_options = [(unicode(permission.pk), permission) for permission in permissions]
results.append((PermissionNamespace.get(namespace), permission_options))

View File

@@ -5,7 +5,7 @@ from datetime import timedelta
from django.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_main, menu_sidebar
from documents.models import Document, DocumentVersion
from mayan.celery import app
@@ -47,11 +47,13 @@ class CheckoutsApp(MayanAppConfig):
},
})
class_permissions(Document, [
ModelPermission.register(
model=Document, permissions=(
permission_document_checkout,
permission_document_checkin,
permission_document_checkin_override,
])
)
)
menu_facet.bind_links(links=[link_checkout_info], sources=[Document])
menu_main.bind_links(links=[link_checkout_list])

View File

@@ -4,7 +4,7 @@ from django.contrib.comments.models import Comment
from django.contrib.contenttypes import generic
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_object, menu_sidebar
from common.classes import ModelAttribute
from common.utils import encapsulate
@@ -44,10 +44,11 @@ class DocumentCommentsApp(MayanAppConfig):
SourceColumn(source=Comment, label=_('User'), attribute=encapsulate(lambda x: x.user.get_full_name() if x.user.get_full_name() else x.user))
SourceColumn(source=Comment, label=_('Comment'), attribute='comment')
class_permissions(Document, [
permission_comment_create,
permission_comment_delete,
permission_comment_view]
ModelPermission.register(
model=Document, permissions=(
permission_comment_create, permission_comment_delete,
permission_comment_view
)
)
menu_sidebar.bind_links(links=[link_comment_add], sources=['comments:comments_for_document', 'comments:comment_add', 'comments:comment_delete', 'comments:comment_multiple_delete'])

View File

@@ -9,7 +9,7 @@ except ImportError:
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_sidebar
from django_gpg.exceptions import GPGDecryptionError
from django_gpg.runtime import gpg
@@ -67,12 +67,12 @@ class DocumentSignaturesApp(MayanAppConfig):
DocumentVersion.register_post_save_hook(1, document_version_post_save_hook)
DocumentVersion.register_pre_open_hook(1, document_pre_open_hook)
class_permissions(Document, [
permission_document_verify,
permission_signature_delete,
permission_signature_download,
permission_signature_upload,
])
ModelPermission.register(
model=Document, permissions=(
permission_document_verify, permission_signature_delete,
permission_signature_download, permission_signature_upload,
)
)
menu_facet.bind_links(links=[link_document_verify], sources=[Document])
menu_sidebar.bind_links(links=[link_document_signature_upload, link_document_signature_download, link_document_signature_delete], sources=['signatures:document_verify', 'signatures:document_signature_upload', 'signatures:document_signature_download', 'signatures:document_signature_delete'])

View File

@@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
from actstream import registry
from acls.api import class_permissions
from acls import ModelPermission
from acls.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view
from common import (
@@ -78,10 +78,8 @@ class DocumentsApp(MayanAppConfig):
ModelAttribute(Document, label=_('Label'), name='label', type_name='field')
class_permissions(Document, [
])
class_permissions(Document, [
ModelPermission.register(
model=Document, permissions=(
permission_acl_edit, permission_acl_view,
permission_document_delete, permission_document_download,
permission_document_edit, permission_document_new_version,
@@ -90,7 +88,8 @@ class DocumentsApp(MayanAppConfig):
permission_events_view, permission_transformation_create,
permission_transformation_delete, permission_transformation_edit,
permission_transformation_view,
])
)
)
menu_front_page.bind_links(links=[link_document_list_recent, link_document_list])
menu_setup.bind_links(links=[link_document_type_setup])

View File

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from acls.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view
from common import (
@@ -38,14 +38,18 @@ class FoldersApp(MayanAppConfig):
APIEndPoint('folders')
class_permissions(Document, [
ModelPermission.register(
model=Document, permissions=(
permission_folder_add_document, permission_folder_remove_document
])
)
)
class_permissions(Folder, [
ModelPermission.register(
model=Folder, permissions=(
permission_acl_edit, permission_acl_view, permission_folder_delete,
permission_folder_edit, permission_folder_view
])
)
)
menu_facet.bind_links(links=[link_document_folder_list], sources=[Document])
menu_main.bind_links(links=[link_folder_list])

View File

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from acls.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view
from common import (
@@ -34,11 +34,13 @@ class LinkingApp(MayanAppConfig):
def ready(self):
super(LinkingApp, self).ready()
class_permissions(SmartLink, [
ModelPermission.register(
model=SmartLink, permissions=(
permission_acl_edit, permission_acl_view,
permission_smart_link_delete, permission_smart_link_edit,
permission_smart_link_view
])
)
)
menu_facet.bind_links(links=[link_smart_link_instances_for_document], sources=[Document])
menu_object.bind_links(links=[link_smart_link_condition_edit, link_smart_link_condition_delete], sources=[SmartLinkCondition])

View File

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import MayanAppConfig, menu_object
from documents.models import Document
@@ -19,8 +19,10 @@ class MailerApp(MayanAppConfig):
def ready(self):
super(MailerApp, self).ready()
class_permissions(Document, [
ModelPermission.register(
model=Document, permissions=(
permission_mailing_link, permission_mailing_send_document
])
)
)
menu_object.bind_links(links=[link_send_document_link, link_send_document], sources=[Document])

View File

@@ -5,7 +5,7 @@ import logging
from django.db.models.signals import post_delete, post_save
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import (
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_setup, menu_sidebar, menu_tools
@@ -60,10 +60,12 @@ class MetadataApp(MayanAppConfig):
SourceColumn(source=Document, label=_('Metadata'), attribute=encapsulate(lambda document: get_metadata_string(document)))
class_permissions(Document, [
ModelPermission.register(
model=Document, permissions=(
permission_metadata_document_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view,
])
)
)
document_search.add_model_field(field='metadata__metadata_type__name', label=_('Metadata type'))
document_search.add_model_field(field='metadata__value', label=_('Metadata value'))

View File

@@ -6,7 +6,7 @@ import sh
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from common import (
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_tools
@@ -59,10 +59,10 @@ class OCRApp(MayanAppConfig):
SourceColumn(source=DocumentVersionOCRError, label=_('Added'), attribute='datetime_submitted')
SourceColumn(source=DocumentVersionOCRError, label=_('Result'), attribute='result')
class_permissions(
Document, [
ModelPermission.register(
model=Document, permissions=(
permission_ocr_document, permission_ocr_content_view
]
)
)
document_search.add_model_field(field='versions__pages__ocr_content__content', label=_('Content'))

View File

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions
from acls import ModelPermission
from acls.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view
from common import (
@@ -38,19 +38,24 @@ class TagsApp(MayanAppConfig):
APIEndPoint('tags')
ModelPermission.register(
model=Document, permissions=(
permission_tag_attach, permission_tag_remove,
)
)
ModelPermission.register(
model=Tag, permissions=(
permission_acl_edit, permission_acl_view, permission_tag_delete,
permission_tag_edit, permission_tag_view,
)
)
SourceColumn(source=Document, label=_('Tags'), attribute=encapsulate(lambda document: widget_inline_tags(document)))
SourceColumn(source=Tag, label=_('Preview'), attribute=encapsulate(lambda tag: widget_single_tag(tag)))
SourceColumn(source=Tag, label=_('Tagged items'), attribute=encapsulate(lambda tag: tag.documents.count()))
class_permissions(Document, [
permission_tag_attach, permission_tag_remove,
])
class_permissions(Tag, [
permission_acl_edit, permission_acl_view, permission_tag_delete,
permission_tag_edit, permission_tag_view,
])
document_search.add_model_field(field='tags__label', label=_('Tags'))
menu_facet.bind_links(links=[link_tag_document_list], sources=[Document])