Massive refactoring for autoupdate optimization.
This commit is contained in:
parent
3db2f2fc16
commit
4daa61888f
@ -1,9 +1,20 @@
|
||||
class AccessPermissions:
|
||||
def get_serializer_class(self, user):
|
||||
return None
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class ItemAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Item and ItemViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
TODO
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('assignments.can_see')
|
||||
return user.has_perm('agenda.can_see')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import ItemSerializer
|
||||
|
||||
return ItemSerializer
|
||||
|
@ -33,4 +33,4 @@ class AgendaAppConfig(AppConfig):
|
||||
dispatch_uid='listen_to_related_object_post_delete')
|
||||
|
||||
# Register viewsets.
|
||||
router.register('agenda/item', ItemViewSet)
|
||||
router.register(self.get_model('Item').get_collection_string(), ItemViewSet)
|
||||
|
@ -15,6 +15,8 @@ from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.utils import to_roman
|
||||
|
||||
from .access_permissions import ItemAccessPermissions
|
||||
|
||||
|
||||
class ItemManager(models.Manager):
|
||||
"""
|
||||
@ -184,6 +186,7 @@ class Item(RESTModelMixin, models.Model):
|
||||
"""
|
||||
An Agenda Item
|
||||
"""
|
||||
access_permissions = ItemAccessPermissions()
|
||||
objects = ItemManager()
|
||||
|
||||
AGENDA_ITEM = 1
|
||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy
|
||||
from reportlab.platypus import Paragraph
|
||||
|
||||
from openslides.core.config import config
|
||||
from openslides.agenda.access_permissions import AccessPermissions
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.rest_api import (
|
||||
@ -22,8 +21,8 @@ from openslides.utils.rest_api import (
|
||||
)
|
||||
from openslides.utils.views import PDFView
|
||||
|
||||
from .access_permissions import ItemAccessPermissions
|
||||
from .models import Item, Speaker
|
||||
from .serializers import ItemSerializer
|
||||
|
||||
|
||||
# Viewsets for the REST API
|
||||
@ -35,16 +34,15 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update, destroy, manage_speaker, speak and tree.
|
||||
"""
|
||||
access_permissions = ItemAccessPermissions()
|
||||
queryset = Item.objects.all()
|
||||
serializer_class = ItemSerializer
|
||||
access_permissions = AccessPermissions()
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action == 'retrieve':
|
||||
result = self.access_permissions.can_retrieve(self.request.user)
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list', 'manage_speaker', 'tree'):
|
||||
result = self.request.user.has_perm('agenda.can_see')
|
||||
# For manage_speaker and tree requests the rest of the check is
|
||||
@ -65,6 +63,7 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
|
||||
Checks if the requesting user has permission to see also an
|
||||
organizational item if it is one.
|
||||
"""
|
||||
#TODO
|
||||
if obj.is_hidden() and not request.user.has_perm('agenda.can_see_hidden_items'):
|
||||
self.permission_denied(request)
|
||||
|
||||
|
@ -1,18 +1,24 @@
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class AssignmentAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Assignment and AssignmentViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('assignments.can_see')
|
||||
|
||||
class AccessPermissions:
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns different serializer classes according to users permissions.
|
||||
"""
|
||||
from openslides.assignments.serializers import AssignmentFullSerializer, AssignmentShortSerializer
|
||||
from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
|
||||
|
||||
if user.has_perm('assignments.can_manage'):
|
||||
serializer_class = AssignmentFullSerializer
|
||||
else:
|
||||
serializer_class = AssignmentShortSerializer
|
||||
return serializer_class
|
||||
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
return user.has_perm('agenda.can_see')
|
||||
|
@ -22,5 +22,5 @@ class AssignmentsAppConfig(AppConfig):
|
||||
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
|
||||
|
||||
# Register viewsets.
|
||||
router.register(self.get_model('Assignment').get_collection_name(), AssignmentViewSet)
|
||||
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)
|
||||
router.register('assignments/poll', AssignmentPollViewSet)
|
||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.assignments.access_permissions import AccessPermissions
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Tag
|
||||
from openslides.poll.models import (
|
||||
@ -21,6 +20,8 @@ from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.search import user_name_helper
|
||||
|
||||
from .access_permissions import AssignmentAccessPermissions
|
||||
|
||||
|
||||
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
||||
"""
|
||||
@ -50,7 +51,10 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
||||
|
||||
|
||||
class Assignment(RESTModelMixin, models.Model):
|
||||
access_permissions = AccessPermissions()
|
||||
"""
|
||||
Model for assignments.
|
||||
"""
|
||||
access_permissions = AssignmentAccessPermissions()
|
||||
|
||||
PHASE_SEARCH = 0
|
||||
PHASE_VOTING = 1
|
||||
|
@ -17,7 +17,6 @@ from reportlab.platypus import (
|
||||
TableStyle,
|
||||
)
|
||||
|
||||
from openslides.assignments.access_permissions import AccessPermissions
|
||||
from openslides.core.config import config
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.rest_api import (
|
||||
@ -31,12 +30,9 @@ from openslides.utils.rest_api import (
|
||||
)
|
||||
from openslides.utils.views import PDFView
|
||||
|
||||
from .access_permissions import AssignmentAccessPermissions
|
||||
from .models import Assignment, AssignmentPoll
|
||||
from .serializers import (
|
||||
AssignmentAllPollSerializer,
|
||||
AssignmentFullSerializer,
|
||||
AssignmentShortSerializer,
|
||||
)
|
||||
from .serializers import AssignmentAllPollSerializer
|
||||
|
||||
|
||||
# Viewsets for the REST API
|
||||
@ -49,15 +45,15 @@ class AssignmentViewSet(ModelViewSet):
|
||||
partial_update, update, destroy, candidature_self, candidature_other,
|
||||
mark_elected and create_poll.
|
||||
"""
|
||||
access_permissions = AssignmentAccessPermissions()
|
||||
queryset = Assignment.objects.all()
|
||||
access_permissions = AccessPermissions()
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action == 'retrieve':
|
||||
result = self.access_permissions.can_retrieve(self.request.user)
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
result = self.request.user.has_perm('assignments.can_see')
|
||||
elif self.action in ('create', 'partial_update', 'update', 'destroy',
|
||||
|
@ -1,9 +1,109 @@
|
||||
class AccessPermissions:
|
||||
def get_serializer_class(self, user):
|
||||
return None
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class ProjectorAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Projector and ProjectorViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
TODO
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('core.can_see_projector')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import ProjectorSerializer
|
||||
|
||||
return ProjectorSerializer
|
||||
|
||||
|
||||
class CustomSlideAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for CustomSlide and CustomSlideViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('core.can_manage_projector')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import CustomSlideSerializer
|
||||
|
||||
return CustomSlideSerializer
|
||||
|
||||
|
||||
class TagAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Tag and TagViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
from .config import config
|
||||
|
||||
# Every authenticated user can retrieve tags. Anonymous users can do
|
||||
# so if they are enabled.
|
||||
return user.is_authenticated() or config['general_system_enable_anonymous']
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import TagSerializer
|
||||
|
||||
return TagSerializer
|
||||
|
||||
|
||||
class ChatMessageAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for ChatMessage and ChatMessageViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
# Anonymous users can see the chat if the anonymous group has the
|
||||
# permission core.can_use_chat. But they can not use it. See views.py.
|
||||
return user.has_perm('core.can_use_chat')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import ChatMessageSerializer
|
||||
|
||||
return ChatMessageSerializer
|
||||
|
||||
|
||||
class ConfigAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for the config (ConfigStore and
|
||||
ConfigViewSet).
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
from .config import config
|
||||
|
||||
# Every authenticated user can see the metadata and list or retrieve
|
||||
# the config. Anonymous users can do so if they are enabled.
|
||||
return user.is_authenticated() or config['general_system_enable_anonymous']
|
||||
|
||||
def get_serialized_data(self, instance, user):
|
||||
"""
|
||||
Returns the serlialized config data or None if the user is not
|
||||
allowed to see it.
|
||||
"""
|
||||
from .config import config
|
||||
|
||||
if self.can_retrieve(user) is not None:
|
||||
return {'key': instance.key, 'value': config[instance.key]}
|
||||
|
@ -12,11 +12,11 @@ class CoreAppConfig(AppConfig):
|
||||
# Load projector elements.
|
||||
# Do this by just importing all from these files.
|
||||
from . import projector # noqa
|
||||
|
||||
# Import all required stuff.
|
||||
from django.db.models import signals
|
||||
from openslides.core.signals import config_signal, post_permission_creation
|
||||
from openslides.utils.autoupdate import inform_changed_data_receiver
|
||||
from openslides.utils.autoupdate import inform_deleted_data_receiver
|
||||
from openslides.utils.autoupdate import inform_changed_data_receiver, inform_deleted_data_receiver
|
||||
from openslides.utils.rest_api import router
|
||||
from openslides.utils.search import index_add_instance, index_del_instance
|
||||
from .signals import delete_django_app_permissions, setup_general_config
|
||||
@ -37,11 +37,11 @@ class CoreAppConfig(AppConfig):
|
||||
dispatch_uid='delete_django_app_permissions')
|
||||
|
||||
# Register viewsets.
|
||||
router.register('core/projector', ProjectorViewSet)
|
||||
router.register('core/chatmessage', ChatMessageViewSet)
|
||||
router.register('core/customslide', CustomSlideViewSet)
|
||||
router.register('core/tag', TagViewSet)
|
||||
router.register('core/config', ConfigViewSet, 'config')
|
||||
router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
|
||||
router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
|
||||
router.register(self.get_model('CustomSlide').get_collection_string(), CustomSlideViewSet)
|
||||
router.register(self.get_model('Tag').get_collection_string(), TagViewSet)
|
||||
router.register(self.get_model('ConfigStore').get_collection_string(), ConfigViewSet, 'config')
|
||||
|
||||
# Update data when any model of any installed app is saved or deleted.
|
||||
# TODO: Test if the m2m_changed signal is also needed.
|
||||
|
@ -1,6 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from jsonfield import JSONField
|
||||
|
||||
@ -8,6 +7,13 @@ from openslides.mediafiles.models import Mediafile
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.projector import ProjectorElement
|
||||
|
||||
from .access_permissions import (
|
||||
ChatMessageAccessPermissions,
|
||||
ConfigAccessPermissions,
|
||||
CustomSlideAccessPermissions,
|
||||
ProjectorAccessPermissions,
|
||||
TagAccessPermissions,
|
||||
)
|
||||
from .exceptions import ProjectorException
|
||||
|
||||
|
||||
@ -51,6 +57,8 @@ class Projector(RESTModelMixin, models.Model):
|
||||
The projector can be controlled using the REST API with POST requests
|
||||
on e. g. the URL /rest/core/projector/1/activate_elements/.
|
||||
"""
|
||||
access_permissions = ProjectorAccessPermissions()
|
||||
|
||||
config = JSONField()
|
||||
|
||||
scale = models.IntegerField(default=0)
|
||||
@ -121,6 +129,8 @@ class CustomSlide(RESTModelMixin, models.Model):
|
||||
"""
|
||||
Model for slides with custom content.
|
||||
"""
|
||||
access_permissions = CustomSlideAccessPermissions()
|
||||
|
||||
title = models.CharField(
|
||||
max_length=256)
|
||||
text = models.TextField(
|
||||
@ -175,6 +185,8 @@ class Tag(RESTModelMixin, models.Model):
|
||||
Model for tags. This tags can be used for other models like agenda items,
|
||||
motions or assignments.
|
||||
"""
|
||||
access_permissions = TagAccessPermissions()
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
unique=True)
|
||||
@ -189,10 +201,11 @@ class Tag(RESTModelMixin, models.Model):
|
||||
return self.name
|
||||
|
||||
|
||||
class ConfigStore(models.Model):
|
||||
class ConfigStore(RESTModelMixin, models.Model):
|
||||
"""
|
||||
A model class to store all config variables in the database.
|
||||
"""
|
||||
access_permissions = ConfigAccessPermissions()
|
||||
|
||||
key = models.CharField(max_length=255, unique=True, db_index=True)
|
||||
"""A string, the key of the config variable."""
|
||||
@ -205,11 +218,15 @@ class ConfigStore(models.Model):
|
||||
permissions = (
|
||||
('can_manage_config', 'Can manage configuration'),)
|
||||
|
||||
def get_root_rest_url(self):
|
||||
@classmethod
|
||||
def get_collection_string(cls):
|
||||
return 'core/config'
|
||||
|
||||
def get_rest_pk(self):
|
||||
"""
|
||||
Returns the detail url of config value.
|
||||
Returns the primary key used in the REST API.
|
||||
"""
|
||||
return reverse('config-detail', args=[str(self.key)])
|
||||
return self.key
|
||||
|
||||
|
||||
class ChatMessage(RESTModelMixin, models.Model):
|
||||
@ -218,6 +235,8 @@ class ChatMessage(RESTModelMixin, models.Model):
|
||||
|
||||
At the moment we only have one global chat room for managers.
|
||||
"""
|
||||
access_permissions = ChatMessageAccessPermissions()
|
||||
|
||||
message = models.TextField()
|
||||
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
|
@ -13,7 +13,6 @@ from django.http import Http404, HttpResponse
|
||||
from django.utils.timezone import now
|
||||
|
||||
from openslides import __version__ as version
|
||||
from openslides.core.access_permissions import AccessPermissions
|
||||
from openslides.utils import views as utils_views
|
||||
from openslides.utils.plugins import (
|
||||
get_plugin_description,
|
||||
@ -31,15 +30,16 @@ from openslides.utils.rest_api import (
|
||||
)
|
||||
from openslides.utils.search import search
|
||||
|
||||
from .access_permissions import (
|
||||
ChatMessageAccessPermissions,
|
||||
ConfigAccessPermissions,
|
||||
CustomSlideAccessPermissions,
|
||||
ProjectorAccessPermissions,
|
||||
TagAccessPermissions,
|
||||
)
|
||||
from .config import config
|
||||
from .exceptions import ConfigError, ConfigNotFound
|
||||
from .models import ChatMessage, CustomSlide, Projector, Tag
|
||||
from .serializers import (
|
||||
ChatMessageSerializer,
|
||||
CustomSlideSerializer,
|
||||
ProjectorSerializer,
|
||||
TagSerializer,
|
||||
)
|
||||
|
||||
|
||||
# Special Django views
|
||||
@ -153,16 +153,15 @@ class ProjectorViewSet(ReadOnlyModelViewSet):
|
||||
activate_elements, prune_elements, update_elements,
|
||||
deactivate_elements, clear_elements and control_view.
|
||||
"""
|
||||
access_permissions = ProjectorAccessPermissions()
|
||||
queryset = Projector.objects.all()
|
||||
serializer_class = ProjectorSerializer
|
||||
access_permissions = AccessPermissions()
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action == 'retrieve':
|
||||
result = self.access_permissions.can_retrieve(self.request.user)
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
result = self.request.user.has_perm('core.can_see_projector')
|
||||
elif self.action in ('activate_elements', 'prune_elements', 'update_elements',
|
||||
@ -370,14 +369,18 @@ class CustomSlideViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update and destroy.
|
||||
"""
|
||||
access_permissions = CustomSlideAccessPermissions()
|
||||
queryset = CustomSlide.objects.all()
|
||||
serializer_class = CustomSlideSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
return self.request.user.has_perm('core.can_manage_projector')
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
else:
|
||||
result = self.request.user.has_perm('core.can_manage_projector')
|
||||
return result
|
||||
|
||||
|
||||
class TagViewSet(ModelViewSet):
|
||||
@ -387,16 +390,18 @@ class TagViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update and destroy.
|
||||
"""
|
||||
access_permissions = TagAccessPermissions()
|
||||
queryset = Tag.objects.all()
|
||||
serializer_class = TagSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve'):
|
||||
# Every authenticated user can see the metadata and list or
|
||||
# retrieve tags. Anonymous users can do so if they are enabled.
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
# Every authenticated user can see the metadata and list tags.
|
||||
# Anonymous users can do so if they are enabled.
|
||||
result = self.request.user.is_authenticated() or config['general_system_enable_anonymous']
|
||||
elif self.action in ('create', 'update', 'destroy'):
|
||||
result = self.request.user.has_perm('core.can_manage_tags')
|
||||
@ -444,13 +449,16 @@ class ConfigViewSet(ViewSet):
|
||||
|
||||
There are the following views: metadata, list, retrieve and update.
|
||||
"""
|
||||
access_permissions = ConfigAccessPermissions()
|
||||
metadata_class = ConfigMetadata
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
# Every authenticated user can see the metadata and list or
|
||||
# retrieve the config. Anonymous users can do so if they are
|
||||
# enabled.
|
||||
@ -476,6 +484,8 @@ class ConfigViewSet(ViewSet):
|
||||
value = config[key]
|
||||
except ConfigNotFound:
|
||||
raise Http404
|
||||
# Attention: The format of this response has to be the same as in
|
||||
# the get_serialized_data method of ConfigAccessPermissions.
|
||||
return Response({'key': key, 'value': value})
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
@ -508,18 +518,23 @@ class ChatMessageViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve and create.
|
||||
The views partial_update, update and destroy are disabled.
|
||||
"""
|
||||
access_permissions = ChatMessageAccessPermissions()
|
||||
queryset = ChatMessage.objects.all()
|
||||
serializer_class = ChatMessageSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
# We do not want anonymous users to use the chat even the anonymous
|
||||
# group has the permission core.can_use_chat.
|
||||
return (self.action in ('metadata', 'list', 'retrieve', 'create') and
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
else:
|
||||
# We do not want anonymous users to use the chat even the anonymous
|
||||
# group has the permission core.can_use_chat.
|
||||
result = (
|
||||
self.action in ('metadata', 'list', 'create') and
|
||||
self.request.user.is_authenticated() and
|
||||
self.request.user.has_perm('core.can_use_chat'))
|
||||
return result
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""
|
||||
|
20
openslides/mediafiles/access_permissions.py
Normal file
20
openslides/mediafiles/access_permissions.py
Normal file
@ -0,0 +1,20 @@
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class MediafileAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Mediafile and MediafileViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('mediafiles.can_see')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import MediafileSerializer
|
||||
|
||||
return MediafileSerializer
|
@ -18,4 +18,4 @@ class MediafilesAppConfig(AppConfig):
|
||||
from .views import MediafileViewSet
|
||||
|
||||
# Register viewsets.
|
||||
router.register('mediafiles/mediafile', MediafileViewSet)
|
||||
router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet)
|
||||
|
@ -5,12 +5,15 @@ from django.utils.translation import ugettext as _
|
||||
from openslides.utils.search import user_name_helper
|
||||
|
||||
from ..utils.models import RESTModelMixin
|
||||
from .access_permissions import MediafileAccessPermissions
|
||||
|
||||
|
||||
class Mediafile(RESTModelMixin, models.Model):
|
||||
"""
|
||||
Class for uploaded files which can be delivered under a certain url.
|
||||
"""
|
||||
access_permissions = MediafileAccessPermissions()
|
||||
|
||||
mediafile = models.FileField(upload_to='file')
|
||||
"""
|
||||
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
|
||||
|
@ -1,6 +1,6 @@
|
||||
from ..utils.rest_api import ModelViewSet, ValidationError
|
||||
from .access_permissions import MediafileAccessPermissions
|
||||
from .models import Mediafile
|
||||
from .serializers import MediafileSerializer
|
||||
|
||||
|
||||
# Viewsets for the REST API
|
||||
@ -12,14 +12,16 @@ class MediafileViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update and destroy.
|
||||
"""
|
||||
access_permissions = MediafileAccessPermissions()
|
||||
queryset = Mediafile.objects.all()
|
||||
serializer_class = MediafileSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
result = self.request.user.has_perm('mediafiles.can_see')
|
||||
elif self.action == 'create':
|
||||
result = (self.request.user.has_perm('mediafiles.can_see') and
|
||||
|
58
openslides/motions/access_permissions.py
Normal file
58
openslides/motions/access_permissions.py
Normal file
@ -0,0 +1,58 @@
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class MotionAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Motion and MotionViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('motions.can_see')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import MotionSerializer
|
||||
|
||||
return MotionSerializer
|
||||
|
||||
|
||||
class CategoryAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Category and CategoryViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('motions.can_see')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import CategorySerializer
|
||||
|
||||
return CategorySerializer
|
||||
|
||||
|
||||
class WorkflowAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for Workflow and WorkflowViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('motions.can_see')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns serializer class.
|
||||
"""
|
||||
from .serializers import WorkflowSerializer
|
||||
|
||||
return WorkflowSerializer
|
@ -25,7 +25,7 @@ class MotionsAppConfig(AppConfig):
|
||||
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')
|
||||
|
||||
# Register viewsets.
|
||||
router.register('motions/category', CategoryViewSet)
|
||||
router.register('motions/motion', MotionViewSet)
|
||||
router.register(self.get_model('Category').get_collection_string(), CategoryViewSet)
|
||||
router.register(self.get_model('Motion').get_collection_string(), MotionViewSet)
|
||||
router.register(self.get_model('Workflow').get_collection_string(), WorkflowViewSet)
|
||||
router.register('motions/motionpoll', MotionPollViewSet)
|
||||
router.register('motions/workflow', WorkflowViewSet)
|
||||
|
@ -20,6 +20,11 @@ from openslides.poll.models import (
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.search import user_name_helper
|
||||
|
||||
from .access_permissions import (
|
||||
CategoryAccessPermissions,
|
||||
MotionAccessPermissions,
|
||||
WorkflowAccessPermissions,
|
||||
)
|
||||
from .exceptions import WorkflowError
|
||||
|
||||
|
||||
@ -29,6 +34,7 @@ class Motion(RESTModelMixin, models.Model):
|
||||
|
||||
This class is the main entry point to all other classes related to a motion.
|
||||
"""
|
||||
access_permissions = MotionAccessPermissions()
|
||||
|
||||
active_version = models.ForeignKey(
|
||||
'MotionVersion',
|
||||
@ -624,6 +630,11 @@ class MotionVersion(RESTModelMixin, models.Model):
|
||||
|
||||
|
||||
class Category(RESTModelMixin, models.Model):
|
||||
"""
|
||||
Model for categories of motions.
|
||||
"""
|
||||
access_permissions = CategoryAccessPermissions()
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
"""Name of the category."""
|
||||
|
||||
@ -879,7 +890,10 @@ class State(RESTModelMixin, models.Model):
|
||||
|
||||
|
||||
class Workflow(RESTModelMixin, models.Model):
|
||||
"""Defines a workflow for a motion."""
|
||||
"""
|
||||
Defines a workflow for a motion.
|
||||
"""
|
||||
access_permissions = WorkflowAccessPermissions()
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
"""A string representing the workflow."""
|
||||
|
@ -18,15 +18,15 @@ from openslides.utils.rest_api import (
|
||||
)
|
||||
from openslides.utils.views import PDFView, SingleObjectMixin
|
||||
|
||||
from .access_permissions import (
|
||||
CategoryAccessPermissions,
|
||||
MotionAccessPermissions,
|
||||
WorkflowAccessPermissions,
|
||||
)
|
||||
from .exceptions import WorkflowError
|
||||
from .models import Category, Motion, MotionPoll, MotionVersion, Workflow
|
||||
from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf
|
||||
from .serializers import (
|
||||
CategorySerializer,
|
||||
MotionPollSerializer,
|
||||
MotionSerializer,
|
||||
WorkflowSerializer,
|
||||
)
|
||||
from .serializers import MotionPollSerializer
|
||||
|
||||
|
||||
# Viewsets for the REST API
|
||||
@ -39,14 +39,16 @@ class MotionViewSet(ModelViewSet):
|
||||
partial_update, update, destroy, manage_version, support, set_state and
|
||||
create_poll.
|
||||
"""
|
||||
access_permissions = MotionAccessPermissions()
|
||||
queryset = Motion.objects.all()
|
||||
serializer_class = MotionSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve', 'partial_update', 'update'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list', 'partial_update', 'update'):
|
||||
result = self.request.user.has_perm('motions.can_see')
|
||||
# For partial_update and update requests the rest of the check is
|
||||
# done in the update method. See below.
|
||||
@ -281,14 +283,16 @@ class CategoryViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update and destroy.
|
||||
"""
|
||||
access_permissions = CategoryAccessPermissions()
|
||||
queryset = Category.objects.all()
|
||||
serializer_class = CategorySerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
result = self.request.user.has_perm('motions.can_see')
|
||||
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
||||
result = (self.request.user.has_perm('motions.can_see') and
|
||||
@ -305,14 +309,16 @@ class WorkflowViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update and destroy.
|
||||
"""
|
||||
access_permissions = WorkflowAccessPermissions()
|
||||
queryset = Workflow.objects.all()
|
||||
serializer_class = WorkflowSerializer
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list'):
|
||||
result = self.request.user.has_perm('motions.can_see')
|
||||
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
||||
result = (self.request.user.has_perm('motions.can_see') and
|
||||
|
26
openslides/users/access_permissions.py
Normal file
26
openslides/users/access_permissions.py
Normal file
@ -0,0 +1,26 @@
|
||||
from ..utils.access_permissions import BaseAccessPermissions
|
||||
|
||||
|
||||
class UserAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
Access permissions container for User and UserViewSet.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return user.has_perm('users.can_see_name')
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns different serializer classes with respect user's permissions.
|
||||
"""
|
||||
from .serializers import UserFullSerializer, UserShortSerializer
|
||||
|
||||
if user.has_perm('users.can_see_extra_data'):
|
||||
# Return the UserFullSerializer for requests of users with more
|
||||
# permissions.
|
||||
serializer_class = UserFullSerializer
|
||||
else:
|
||||
serializer_class = UserShortSerializer
|
||||
return serializer_class
|
@ -28,5 +28,5 @@ class UsersAppConfig(AppConfig):
|
||||
dispatch_uid='create_builtin_groups_and_admin')
|
||||
|
||||
# Register viewsets.
|
||||
router.register('users/user', UserViewSet)
|
||||
router.register(self.get_model('User').get_collection_string(), UserViewSet)
|
||||
router.register('users/group', GroupViewSet)
|
||||
|
@ -13,6 +13,7 @@ from openslides.utils.search import user_name_helper
|
||||
|
||||
from ..core.config import config
|
||||
from ..utils.models import RESTModelMixin
|
||||
from .access_permissions import UserAccessPermissions
|
||||
from .exceptions import UsersError
|
||||
|
||||
|
||||
@ -94,6 +95,8 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
|
||||
in other OpenSlides apps like motion submitter or (assignment) election
|
||||
candidates.
|
||||
"""
|
||||
access_permissions = UserAccessPermissions()
|
||||
|
||||
USERNAME_FIELD = 'username'
|
||||
|
||||
username = models.CharField(
|
||||
|
@ -14,13 +14,10 @@ from ..utils.rest_api import (
|
||||
status,
|
||||
)
|
||||
from ..utils.views import APIView, PDFView
|
||||
from .access_permissions import UserAccessPermissions
|
||||
from .models import Group, User
|
||||
from .pdf import users_passwords_to_pdf, users_to_pdf
|
||||
from .serializers import (
|
||||
GroupSerializer,
|
||||
UserFullSerializer,
|
||||
UserShortSerializer,
|
||||
)
|
||||
from .serializers import GroupSerializer, UserFullSerializer
|
||||
|
||||
|
||||
# Viewsets for the REST API
|
||||
@ -32,13 +29,16 @@ class UserViewSet(ModelViewSet):
|
||||
There are the following views: metadata, list, retrieve, create,
|
||||
partial_update, update, destroy and reset_password.
|
||||
"""
|
||||
access_permissions = UserAccessPermissions()
|
||||
queryset = User.objects.all()
|
||||
|
||||
def check_view_permissions(self):
|
||||
"""
|
||||
Returns True if the user has required permissions.
|
||||
"""
|
||||
if self.action in ('metadata', 'list', 'retrieve', 'update', 'partial_update'):
|
||||
if self.action == 'retrieve':
|
||||
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||
elif self.action in ('metadata', 'list', 'update', 'partial_update'):
|
||||
result = self.request.user.has_perm('users.can_see_name')
|
||||
elif self.action in ('create', 'destroy', 'reset_password'):
|
||||
result = (self.request.user.has_perm('users.can_see_name') and
|
||||
@ -50,16 +50,13 @@ class UserViewSet(ModelViewSet):
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
Returns different serializer classes with respect to action and user's
|
||||
permissions.
|
||||
Returns different serializer classes with respect to action.
|
||||
"""
|
||||
if (self.action in ('create', 'partial_update', 'update') or
|
||||
self.request.user.has_perm('users.can_see_extra_data')):
|
||||
# Return the UserFullSerializer for edit requests or for
|
||||
# list/retrieve requests of users with more permissions.
|
||||
if self.action in ('create', 'partial_update', 'update'):
|
||||
# Return the UserFullSerializer for edit requests.
|
||||
serializer_class = UserFullSerializer
|
||||
else:
|
||||
serializer_class = UserShortSerializer
|
||||
serializer_class = super().get_serializer_class()
|
||||
return serializer_class
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
@ -78,6 +75,7 @@ class UserViewSet(ModelViewSet):
|
||||
|
||||
Hides the default_password for non admins.
|
||||
"""
|
||||
#TODO: Hide default_password also in case of autoupdate.
|
||||
response = super().retrieve(request, *args, **kwargs)
|
||||
self.extract_default_password(response)
|
||||
return response
|
||||
|
30
openslides/utils/access_permissions.py
Normal file
30
openslides/utils/access_permissions.py
Normal file
@ -0,0 +1,30 @@
|
||||
class BaseAccessPermissions:
|
||||
"""
|
||||
Base access permissions container.
|
||||
"""
|
||||
def can_retrieve(self, user):
|
||||
"""
|
||||
Returns True if the user has read access model instances.
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_serializer_class(self, user):
|
||||
"""
|
||||
Returns different serializer classes according to users permissions.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"You have to add the classmethod 'get_serializer_class' to your "
|
||||
"access permissions class.".format(self))
|
||||
|
||||
def get_serialized_data(self, instance, user):
|
||||
"""
|
||||
Returns the serialized data for the instance prepared for the user.
|
||||
|
||||
Returns None if the user has no read access.
|
||||
"""
|
||||
if self.can_retrieve(user):
|
||||
serializer_class = self.get_serializer_class(user)
|
||||
data = serializer_class(instance).data
|
||||
else:
|
||||
data = None
|
||||
return data
|
@ -1,15 +1,12 @@
|
||||
import json
|
||||
import os
|
||||
import posixpath
|
||||
from importlib import import_module
|
||||
from urllib.parse import unquote
|
||||
|
||||
from django.conf import settings
|
||||
from openslides.users.auth import get_user
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
from django.utils.importlib import import_module
|
||||
from sockjs.tornado import SockJSConnection, SockJSRouter
|
||||
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.httputil import HTTPHeaders
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.options import parse_command_line
|
||||
from tornado.web import (
|
||||
@ -19,7 +16,8 @@ from tornado.web import (
|
||||
StaticFileHandler,
|
||||
)
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from .rest_api import get_collection_and_id_from_url
|
||||
|
||||
from openslides.users.auth import AnonymousUser, get_user
|
||||
|
||||
RUNNING_HOST = None
|
||||
RUNNING_PORT = None
|
||||
@ -59,9 +57,6 @@ class DjangoStaticFileHandler(StaticFileHandler):
|
||||
return absolute_path
|
||||
|
||||
|
||||
class FakeRequest:
|
||||
pass
|
||||
|
||||
class OpenSlidesSockJSConnection(SockJSConnection):
|
||||
"""
|
||||
SockJS connection for OpenSlides.
|
||||
@ -80,36 +75,29 @@ class OpenSlidesSockJSConnection(SockJSConnection):
|
||||
"""
|
||||
Sends an OpenSlides object to all connected clients (waiters).
|
||||
"""
|
||||
# Send out internal HTTP request to get data from the REST api.
|
||||
for waiter in cls.waiters:
|
||||
# Read waiter's former cookies and parse session cookie to new header object.
|
||||
headers = HTTPHeaders()
|
||||
# Read waiter's former cookies and parse session cookie to get user instance.
|
||||
try:
|
||||
session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME]
|
||||
except KeyError:
|
||||
# There is no session cookie so use anonymous user here.
|
||||
user = AnonymousUser()
|
||||
else:
|
||||
# Get session from session store and use it to retrieve the user.
|
||||
engine = import_module(settings.SESSION_ENGINE)
|
||||
session = engine.SessionStore(session_cookie)
|
||||
|
||||
request = FakeRequest()
|
||||
request.session = session
|
||||
|
||||
user = get_user(request)
|
||||
serializer_class = instance.access_permissions.get_serializer_class(user)
|
||||
serialized_instance_data = serializer_class(instance).data
|
||||
|
||||
session = engine.SessionStore(session_cookie.value)
|
||||
fake_request = type('FakeRequest', (), {})()
|
||||
fake_request.session = session
|
||||
user = get_user(fake_request)
|
||||
# Fetch serialized data and send them out to the waiter (client).
|
||||
serialized_instance_data = instance.get_access_permissions().get_serialized_data(instance, user)
|
||||
if serialized_instance_data is not None:
|
||||
data = {
|
||||
'url': "foobar",
|
||||
'status_code': 404 if is_delete else 200,
|
||||
'collection': instance.get_collection_name(),
|
||||
'id': instance.id,
|
||||
'status_code': 404 if is_delete else 200, # TODO: Refactor this. Use strings like 'change' or 'delete'.
|
||||
'collection': instance.get_collection_string(),
|
||||
'id': instance.get_rest_pk(),
|
||||
'data': serialized_instance_data}
|
||||
waiter.send(data)
|
||||
except KeyError:
|
||||
# There is no session cookie
|
||||
pass
|
||||
else:
|
||||
headers.add('Cookie', '%s=%s' % (settings.SESSION_COOKIE_NAME, session_cookie.value))
|
||||
|
||||
|
||||
|
||||
|
||||
def run_tornado(addr, port, *args, **kwargs):
|
||||
@ -152,22 +140,28 @@ def inform_changed_data(is_delete, *args):
|
||||
"""
|
||||
Informs all users about changed data.
|
||||
|
||||
The arguments are Django/OpenSlides models.
|
||||
The first argument is whether the object or the objects are deleted.
|
||||
The other arguments are the changed or deleted Django/OpenSlides model
|
||||
instances.
|
||||
"""
|
||||
root_instances = set()
|
||||
for instance in args:
|
||||
try:
|
||||
root_instances.add(instance.get_root_rest_element())
|
||||
except AttributeError:
|
||||
# Instance has no method get_root_rest_url. Just skip it.
|
||||
pass
|
||||
|
||||
if settings.USE_TORNADO_AS_WSGI_SERVER:
|
||||
for root_instance in root_instances:
|
||||
OpenSlidesSockJSConnection.send_object(root_instance, is_delete)
|
||||
for instance in args:
|
||||
try:
|
||||
root_instance = instance.get_root_rest_element()
|
||||
except AttributeError:
|
||||
# Instance has no method get_root_rest_element. Just skip it.
|
||||
pass
|
||||
else:
|
||||
if is_delete and instance == root_instance:
|
||||
# A root instance is deleted.
|
||||
OpenSlidesSockJSConnection.send_object(root_instance, is_delete)
|
||||
else:
|
||||
# A non root instance is deleted or any instance is just changed.
|
||||
root_instance.refresh_from_db()
|
||||
OpenSlidesSockJSConnection.send_object(root_instance, False)
|
||||
else:
|
||||
pass
|
||||
# TODO: Implement big varainte with Apache or Nginx as wsgi webserver.
|
||||
# TODO: Implement big variant with Apache or Nginx as WSGI webserver.
|
||||
|
||||
|
||||
def inform_changed_data_receiver(sender, instance, **kwargs):
|
||||
|
@ -1,4 +1,3 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
|
||||
|
||||
@ -19,15 +18,11 @@ class MinMaxIntegerField(models.IntegerField):
|
||||
|
||||
class RESTModelMixin:
|
||||
"""
|
||||
Mixin for django models which are used in our rest api.
|
||||
Mixin for Django models which are used in our REST API.
|
||||
"""
|
||||
|
||||
access_permissions = None
|
||||
|
||||
@classmethod
|
||||
def get_collection_name(cls):
|
||||
return "{0}/{1}".format(cls._meta.app_label.lower(), cls._meta.object_name.lower())
|
||||
|
||||
def get_root_rest_element(self):
|
||||
"""
|
||||
Returns the root rest instance.
|
||||
@ -36,20 +31,25 @@ class RESTModelMixin:
|
||||
"""
|
||||
return self
|
||||
|
||||
def get_root_rest_url(self):
|
||||
def get_access_permissions(self):
|
||||
"""
|
||||
Returns the detail url of the root model of this object.
|
||||
Returns a container to handle access permissions for this model and
|
||||
its corresponding viewset.
|
||||
"""
|
||||
# Gets the default url-name in the same way as django rest framework
|
||||
# does in relations.HyperlinkedModelSerializer
|
||||
root_instance = self.get_root_rest_element()
|
||||
rest_url = '%s-detail' % type(root_instance)._meta.object_name.lower()
|
||||
return reverse(rest_url, args=[str(root_instance.pk)])
|
||||
return self.access_permissions
|
||||
|
||||
def get_collection_string(self):
|
||||
@classmethod
|
||||
def get_collection_string(cls):
|
||||
"""
|
||||
Returns the string representing the name of the collection.
|
||||
Returns the string representing the name of the collection. Returns
|
||||
None if this is not a so called root rest instance.
|
||||
"""
|
||||
# TODO: find a way not to use the url. See #1791
|
||||
from .rest_api import get_collection_and_id_from_url
|
||||
return get_collection_and_id_from_url(self.get_root_rest_url())[0]
|
||||
# TODO Check if this is a root rest element class and return None if not.
|
||||
return '/'.join((cls._meta.app_label.lower(), cls._meta.object_name.lower()))
|
||||
|
||||
def get_rest_pk(self):
|
||||
"""
|
||||
Returns the primary key used in the REST API. By default this is
|
||||
the database pk.
|
||||
"""
|
||||
return self.pk
|
||||
|
@ -101,20 +101,17 @@ class PermissionMixin:
|
||||
|
||||
The methods check_view_permissions or check_projector_requirements are
|
||||
evaluated. If both return False self.permission_denied() is called.
|
||||
Django REST framework's permission system is disabled.
|
||||
"""
|
||||
Django REST Framework's permission system is disabled.
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
serializer_class = self.access_permissions.get_serializer_class(self.request.user) if self.access_permissions is not None else None
|
||||
return super().get_serializer_class() if serializer_class is None else serializer_class
|
||||
Also connects container to handle access permissions for model and
|
||||
viewset.
|
||||
"""
|
||||
access_permissions = None
|
||||
|
||||
def get_permissions(self):
|
||||
"""
|
||||
Overriden method to check view and projector permissions. Returns an
|
||||
empty interable so Django REST framework won't do any other
|
||||
Overridden method to check view and projector permissions. Returns an
|
||||
empty iterable so Django REST framework won't do any other
|
||||
permission checks by evaluating Django REST framework style permission
|
||||
classes and the request passes.
|
||||
"""
|
||||
@ -126,6 +123,8 @@ class PermissionMixin:
|
||||
"""
|
||||
Override this and return True if the requesting user should be able to
|
||||
get access to your view.
|
||||
|
||||
Use access permissions container for retrieve requests.
|
||||
"""
|
||||
return False
|
||||
|
||||
@ -144,6 +143,24 @@ class PermissionMixin:
|
||||
break
|
||||
return result
|
||||
|
||||
def get_access_permissions(self):
|
||||
"""
|
||||
Returns a container to handle access permissions for this viewset and
|
||||
its corresponding model.
|
||||
"""
|
||||
return self.access_permissions
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
Overridden method to return the serializer class given by the
|
||||
access permissions container.
|
||||
"""
|
||||
if self.get_access_permissions() is not None:
|
||||
serializer_class = self.get_access_permissions().get_serializer_class(self.request.user)
|
||||
else:
|
||||
serializer_class = super().get_serializer_class()
|
||||
return serializer_class
|
||||
|
||||
|
||||
class ModelSerializer(_ModelSerializer):
|
||||
"""
|
||||
@ -172,7 +189,7 @@ class GenericViewSet(PermissionMixin, _GenericViewSet):
|
||||
|
||||
|
||||
class ModelViewSet(PermissionMixin, _ModelViewSet):
|
||||
access_permissions = None
|
||||
pass
|
||||
|
||||
|
||||
class ReadOnlyModelViewSet(PermissionMixin, _ReadOnlyModelViewSet):
|
||||
@ -183,12 +200,13 @@ class ViewSet(PermissionMixin, _ViewSet):
|
||||
pass
|
||||
|
||||
|
||||
#TODO: Remove this method
|
||||
def get_collection_and_id_from_url(url):
|
||||
"""
|
||||
Helper function. Returns a tuple containing the collection name and the id
|
||||
extracted out of the given REST api URL.
|
||||
|
||||
For example get_collection_and_id_from_url('http://localhost/api/users/user/3/')
|
||||
For example get_collection_and_id_from_url('http://localhost/rest/users/user/3/')
|
||||
returns ('users/user', '3').
|
||||
|
||||
Raises OpenSlidesError if the URL is invalid.
|
||||
@ -196,5 +214,5 @@ def get_collection_and_id_from_url(url):
|
||||
path = urlparse(url).path
|
||||
match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path)
|
||||
if not match:
|
||||
raise OpenSlidesError('Invalid REST api URL: %s' % url)
|
||||
raise OpenSlidesError('Invalid REST API URL: %s' % url)
|
||||
return match.group('collection'), match.group('id')
|
||||
|
@ -10,7 +10,3 @@ def to_roman(number):
|
||||
return roman.toRoman(number)
|
||||
except (roman.NotIntegerError, roman.OutOfRangeError):
|
||||
return None
|
||||
|
||||
|
||||
def collection_name(model_class):
|
||||
return "{1}/{2}".format(model_class.Meta.app_label.lower(), model_class.Meta.object_name)
|
||||
|
Loading…
Reference in New Issue
Block a user