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 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__) logger = logging.getLogger(__name__)
_cache = {}
class ModelPermission(object):
def get_source_object(obj): _registry = {}
if isinstance(obj, EncapsulatedObject):
return obj.source_object
else:
return obj
class EncapsulatedObject(object):
source_object_name = 'source_object'
@classmethod @classmethod
def object_key(cls, app_label=None, model=None, pk=None): def register(cls, model, permissions):
if pk: cls._registry.setdefault(model, [])
return '%s.%s.%s.%s' % (cls.__name__, app_label, model, pk) for permission in permissions:
else: cls._registry[model].append(permission.stored_permission.pk)
return '%s.%s.%s' % (cls.__name__, app_label, model)
@classmethod @classmethod
def add_to_class(cls, name, value): def get_for_instance(cls, instance):
if hasattr(value, 'contribute_to_class'): from permissions.models import StoredPermission
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
@classmethod pks = cls._registry.get(type(instance), ())
def set_source_object_name(cls, new_name): return StoredPermission.objects.filter(pk__in=pks)
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'

View File

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

View File

@@ -17,6 +17,7 @@ from common.views import (
from permissions import Permission, PermissionNamespace from permissions import Permission, PermissionNamespace
from permissions.models import StoredPermission from permissions.models import StoredPermission
from .classes import ModelPermission
from .models import AccessControlList from .models import AccessControlList
from .permissions import permission_acl_edit, permission_acl_view from .permissions import permission_acl_edit, permission_acl_view
@@ -137,7 +138,7 @@ class ACLPermissionsView(AssignRemoveView):
def left_list(self): def left_list(self):
results = [] 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] permission_options = [(unicode(permission.pk), permission) for permission in permissions]
results.append((PermissionNamespace.get(namespace), permission_options)) 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.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _ 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 common import MayanAppConfig, menu_facet, menu_main, menu_sidebar
from documents.models import Document, DocumentVersion from documents.models import Document, DocumentVersion
from mayan.celery import app 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_checkout,
permission_document_checkin, permission_document_checkin,
permission_document_checkin_override, permission_document_checkin_override,
]) )
)
menu_facet.bind_links(links=[link_checkout_info], sources=[Document]) menu_facet.bind_links(links=[link_checkout_info], sources=[Document])
menu_main.bind_links(links=[link_checkout_list]) 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.contrib.contenttypes import generic
from django.utils.translation import ugettext_lazy as _ 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 import MayanAppConfig, menu_facet, menu_object, menu_sidebar
from common.classes import ModelAttribute from common.classes import ModelAttribute
from common.utils import encapsulate 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=_('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') SourceColumn(source=Comment, label=_('Comment'), attribute='comment')
class_permissions(Document, [ ModelPermission.register(
permission_comment_create, model=Document, permissions=(
permission_comment_delete, permission_comment_create, permission_comment_delete,
permission_comment_view] 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']) 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 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 common import MayanAppConfig, menu_facet, menu_sidebar
from django_gpg.exceptions import GPGDecryptionError from django_gpg.exceptions import GPGDecryptionError
from django_gpg.runtime import gpg 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_post_save_hook(1, document_version_post_save_hook)
DocumentVersion.register_pre_open_hook(1, document_pre_open_hook) DocumentVersion.register_pre_open_hook(1, document_pre_open_hook)
class_permissions(Document, [ ModelPermission.register(
permission_document_verify, model=Document, permissions=(
permission_signature_delete, permission_document_verify, permission_signature_delete,
permission_signature_download, permission_signature_download, permission_signature_upload,
permission_signature_upload, )
]) )
menu_facet.bind_links(links=[link_document_verify], sources=[Document]) 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']) 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 actstream import registry
from acls.api import class_permissions from acls import ModelPermission
from acls.links import link_acl_list from acls.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view from acls.permissions import permission_acl_edit, permission_acl_view
from common import ( from common import (
@@ -78,10 +78,8 @@ class DocumentsApp(MayanAppConfig):
ModelAttribute(Document, label=_('Label'), name='label', type_name='field') ModelAttribute(Document, label=_('Label'), name='label', type_name='field')
class_permissions(Document, [ ModelPermission.register(
]) model=Document, permissions=(
class_permissions(Document, [
permission_acl_edit, permission_acl_view, permission_acl_edit, permission_acl_view,
permission_document_delete, permission_document_download, permission_document_delete, permission_document_download,
permission_document_edit, permission_document_new_version, permission_document_edit, permission_document_new_version,
@@ -90,7 +88,8 @@ class DocumentsApp(MayanAppConfig):
permission_events_view, permission_transformation_create, permission_events_view, permission_transformation_create,
permission_transformation_delete, permission_transformation_edit, permission_transformation_delete, permission_transformation_edit,
permission_transformation_view, permission_transformation_view,
]) )
)
menu_front_page.bind_links(links=[link_document_list_recent, link_document_list]) menu_front_page.bind_links(links=[link_document_list_recent, link_document_list])
menu_setup.bind_links(links=[link_document_type_setup]) 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 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.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view from acls.permissions import permission_acl_edit, permission_acl_view
from common import ( from common import (
@@ -38,14 +38,18 @@ class FoldersApp(MayanAppConfig):
APIEndPoint('folders') APIEndPoint('folders')
class_permissions(Document, [ ModelPermission.register(
model=Document, permissions=(
permission_folder_add_document, permission_folder_remove_document 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_acl_edit, permission_acl_view, permission_folder_delete,
permission_folder_edit, permission_folder_view permission_folder_edit, permission_folder_view
]) )
)
menu_facet.bind_links(links=[link_document_folder_list], sources=[Document]) menu_facet.bind_links(links=[link_document_folder_list], sources=[Document])
menu_main.bind_links(links=[link_folder_list]) 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 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.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view from acls.permissions import permission_acl_edit, permission_acl_view
from common import ( from common import (
@@ -34,11 +34,13 @@ class LinkingApp(MayanAppConfig):
def ready(self): def ready(self):
super(LinkingApp, self).ready() super(LinkingApp, self).ready()
class_permissions(SmartLink, [ ModelPermission.register(
model=SmartLink, permissions=(
permission_acl_edit, permission_acl_view, permission_acl_edit, permission_acl_view,
permission_smart_link_delete, permission_smart_link_edit, permission_smart_link_delete, permission_smart_link_edit,
permission_smart_link_view permission_smart_link_view
]) )
)
menu_facet.bind_links(links=[link_smart_link_instances_for_document], sources=[Document]) 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]) 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 django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions from acls import ModelPermission
from common import MayanAppConfig, menu_object from common import MayanAppConfig, menu_object
from documents.models import Document from documents.models import Document
@@ -19,8 +19,10 @@ class MailerApp(MayanAppConfig):
def ready(self): def ready(self):
super(MailerApp, self).ready() super(MailerApp, self).ready()
class_permissions(Document, [ ModelPermission.register(
model=Document, permissions=(
permission_mailing_link, permission_mailing_send_document permission_mailing_link, permission_mailing_send_document
]) )
)
menu_object.bind_links(links=[link_send_document_link, link_send_document], sources=[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.db.models.signals import post_delete, post_save
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions from acls import ModelPermission
from common import ( from common import (
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary, MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_setup, menu_sidebar, menu_tools 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))) 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_add, permission_metadata_document_edit,
permission_metadata_document_remove, permission_metadata_document_view, 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__metadata_type__name', label=_('Metadata type'))
document_search.add_model_field(field='metadata__value', label=_('Metadata value')) 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 django.utils.translation import ugettext_lazy as _
from acls.api import class_permissions from acls import ModelPermission
from common import ( from common import (
MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary, MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary,
menu_tools menu_tools
@@ -59,10 +59,10 @@ class OCRApp(MayanAppConfig):
SourceColumn(source=DocumentVersionOCRError, label=_('Added'), attribute='datetime_submitted') SourceColumn(source=DocumentVersionOCRError, label=_('Added'), attribute='datetime_submitted')
SourceColumn(source=DocumentVersionOCRError, label=_('Result'), attribute='result') SourceColumn(source=DocumentVersionOCRError, label=_('Result'), attribute='result')
class_permissions( ModelPermission.register(
Document, [ model=Document, permissions=(
permission_ocr_document, permission_ocr_content_view permission_ocr_document, permission_ocr_content_view
] )
) )
document_search.add_model_field(field='versions__pages__ocr_content__content', label=_('Content')) 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 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.links import link_acl_list
from acls.permissions import permission_acl_edit, permission_acl_view from acls.permissions import permission_acl_edit, permission_acl_view
from common import ( from common import (
@@ -38,19 +38,24 @@ class TagsApp(MayanAppConfig):
APIEndPoint('tags') 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=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=_('Preview'), attribute=encapsulate(lambda tag: widget_single_tag(tag)))
SourceColumn(source=Tag, label=_('Tagged items'), attribute=encapsulate(lambda tag: tag.documents.count())) 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')) document_search.add_model_field(field='tags__label', label=_('Tags'))
menu_facet.bind_links(links=[link_tag_document_list], sources=[Document]) menu_facet.bind_links(links=[link_tag_document_list], sources=[Document])