Massive refactoring for autoupdate optimization.

This commit is contained in:
Norman Jäckel 2016-02-11 22:58:32 +01:00
parent 3db2f2fc16
commit 4daa61888f
29 changed files with 501 additions and 180 deletions

View File

@ -1,9 +1,20 @@
class AccessPermissions: from ..utils.access_permissions import BaseAccessPermissions
def get_serializer_class(self, user):
return None
class ItemAccessPermissions(BaseAccessPermissions):
"""
Access permissions container for Item and ItemViewSet.
"""
def can_retrieve(self, user): 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

View File

@ -33,4 +33,4 @@ class AgendaAppConfig(AppConfig):
dispatch_uid='listen_to_related_object_post_delete') dispatch_uid='listen_to_related_object_post_delete')
# Register viewsets. # Register viewsets.
router.register('agenda/item', ItemViewSet) router.register(self.get_model('Item').get_collection_string(), ItemViewSet)

View File

@ -15,6 +15,8 @@ from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import RESTModelMixin from openslides.utils.models import RESTModelMixin
from openslides.utils.utils import to_roman from openslides.utils.utils import to_roman
from .access_permissions import ItemAccessPermissions
class ItemManager(models.Manager): class ItemManager(models.Manager):
""" """
@ -184,6 +186,7 @@ class Item(RESTModelMixin, models.Model):
""" """
An Agenda Item An Agenda Item
""" """
access_permissions = ItemAccessPermissions()
objects = ItemManager() objects = ItemManager()
AGENDA_ITEM = 1 AGENDA_ITEM = 1

View File

@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy
from reportlab.platypus import Paragraph from reportlab.platypus import Paragraph
from openslides.core.config import config from openslides.core.config import config
from openslides.agenda.access_permissions import AccessPermissions
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from openslides.utils.rest_api import ( from openslides.utils.rest_api import (
@ -22,8 +21,8 @@ from openslides.utils.rest_api import (
) )
from openslides.utils.views import PDFView from openslides.utils.views import PDFView
from .access_permissions import ItemAccessPermissions
from .models import Item, Speaker from .models import Item, Speaker
from .serializers import ItemSerializer
# Viewsets for the REST API # Viewsets for the REST API
@ -35,16 +34,15 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
There are the following views: metadata, list, retrieve, create, There are the following views: metadata, list, retrieve, create,
partial_update, update, destroy, manage_speaker, speak and tree. partial_update, update, destroy, manage_speaker, speak and tree.
""" """
access_permissions = ItemAccessPermissions()
queryset = Item.objects.all() queryset = Item.objects.all()
serializer_class = ItemSerializer
access_permissions = AccessPermissions()
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. Returns True if the user has required permissions.
""" """
if self.action == 'retrieve': 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'): elif self.action in ('metadata', 'list', 'manage_speaker', 'tree'):
result = self.request.user.has_perm('agenda.can_see') result = self.request.user.has_perm('agenda.can_see')
# For manage_speaker and tree requests the rest of the check is # 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 Checks if the requesting user has permission to see also an
organizational item if it is one. organizational item if it is one.
""" """
#TODO
if obj.is_hidden() and not request.user.has_perm('agenda.can_see_hidden_items'): if obj.is_hidden() and not request.user.has_perm('agenda.can_see_hidden_items'):
self.permission_denied(request) self.permission_denied(request)

View File

@ -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): def get_serializer_class(self, user):
""" """
Returns different serializer classes according to users permissions. 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'): if user.has_perm('assignments.can_manage'):
serializer_class = AssignmentFullSerializer serializer_class = AssignmentFullSerializer
else: else:
serializer_class = AssignmentShortSerializer serializer_class = AssignmentShortSerializer
return serializer_class return serializer_class
def can_retrieve(self, user):
"""
TODO
"""
return user.has_perm('agenda.can_see')

View File

@ -22,5 +22,5 @@ class AssignmentsAppConfig(AppConfig):
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config') config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
# Register viewsets. # 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) router.register('assignments/poll', AssignmentPollViewSet)

View File

@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.agenda.models import Item, Speaker from openslides.agenda.models import Item, Speaker
from openslides.assignments.access_permissions import AccessPermissions
from openslides.core.config import config from openslides.core.config import config
from openslides.core.models import Tag from openslides.core.models import Tag
from openslides.poll.models import ( from openslides.poll.models import (
@ -21,6 +20,8 @@ from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import RESTModelMixin from openslides.utils.models import RESTModelMixin
from openslides.utils.search import user_name_helper from openslides.utils.search import user_name_helper
from .access_permissions import AssignmentAccessPermissions
class AssignmentRelatedUser(RESTModelMixin, models.Model): class AssignmentRelatedUser(RESTModelMixin, models.Model):
""" """
@ -50,7 +51,10 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
class Assignment(RESTModelMixin, models.Model): class Assignment(RESTModelMixin, models.Model):
access_permissions = AccessPermissions() """
Model for assignments.
"""
access_permissions = AssignmentAccessPermissions()
PHASE_SEARCH = 0 PHASE_SEARCH = 0
PHASE_VOTING = 1 PHASE_VOTING = 1

View File

@ -17,7 +17,6 @@ from reportlab.platypus import (
TableStyle, TableStyle,
) )
from openslides.assignments.access_permissions import AccessPermissions
from openslides.core.config import config from openslides.core.config import config
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from openslides.utils.rest_api import ( from openslides.utils.rest_api import (
@ -31,12 +30,9 @@ from openslides.utils.rest_api import (
) )
from openslides.utils.views import PDFView from openslides.utils.views import PDFView
from .access_permissions import AssignmentAccessPermissions
from .models import Assignment, AssignmentPoll from .models import Assignment, AssignmentPoll
from .serializers import ( from .serializers import AssignmentAllPollSerializer
AssignmentAllPollSerializer,
AssignmentFullSerializer,
AssignmentShortSerializer,
)
# Viewsets for the REST API # Viewsets for the REST API
@ -49,15 +45,15 @@ class AssignmentViewSet(ModelViewSet):
partial_update, update, destroy, candidature_self, candidature_other, partial_update, update, destroy, candidature_self, candidature_other,
mark_elected and create_poll. mark_elected and create_poll.
""" """
access_permissions = AssignmentAccessPermissions()
queryset = Assignment.objects.all() queryset = Assignment.objects.all()
access_permissions = AccessPermissions()
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. Returns True if the user has required permissions.
""" """
if self.action == 'retrieve': 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'): elif self.action in ('metadata', 'list'):
result = self.request.user.has_perm('assignments.can_see') result = self.request.user.has_perm('assignments.can_see')
elif self.action in ('create', 'partial_update', 'update', 'destroy', elif self.action in ('create', 'partial_update', 'update', 'destroy',

View File

@ -1,9 +1,109 @@
class AccessPermissions: from ..utils.access_permissions import BaseAccessPermissions
def get_serializer_class(self, user):
return None
class ProjectorAccessPermissions(BaseAccessPermissions):
"""
Access permissions container for Projector and ProjectorViewSet.
"""
def can_retrieve(self, user): def can_retrieve(self, user):
""" """
TODO Returns True if the user has read access model instances.
""" """
return user.has_perm('core.can_see_projector') 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]}

View File

@ -12,11 +12,11 @@ class CoreAppConfig(AppConfig):
# Load projector elements. # Load projector elements.
# Do this by just importing all from these files. # Do this by just importing all from these files.
from . import projector # noqa from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from django.db.models import signals from django.db.models import signals
from openslides.core.signals import config_signal, post_permission_creation 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_changed_data_receiver, inform_deleted_data_receiver
from openslides.utils.autoupdate import inform_deleted_data_receiver
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from openslides.utils.search import index_add_instance, index_del_instance from openslides.utils.search import index_add_instance, index_del_instance
from .signals import delete_django_app_permissions, setup_general_config from .signals import delete_django_app_permissions, setup_general_config
@ -37,11 +37,11 @@ class CoreAppConfig(AppConfig):
dispatch_uid='delete_django_app_permissions') dispatch_uid='delete_django_app_permissions')
# Register viewsets. # Register viewsets.
router.register('core/projector', ProjectorViewSet) router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
router.register('core/chatmessage', ChatMessageViewSet) router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
router.register('core/customslide', CustomSlideViewSet) router.register(self.get_model('CustomSlide').get_collection_string(), CustomSlideViewSet)
router.register('core/tag', TagViewSet) router.register(self.get_model('Tag').get_collection_string(), TagViewSet)
router.register('core/config', ConfigViewSet, 'config') router.register(self.get_model('ConfigStore').get_collection_string(), ConfigViewSet, 'config')
# Update data when any model of any installed app is saved or deleted. # Update data when any model of any installed app is saved or deleted.
# TODO: Test if the m2m_changed signal is also needed. # TODO: Test if the m2m_changed signal is also needed.

View File

@ -1,6 +1,5 @@
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from jsonfield import JSONField from jsonfield import JSONField
@ -8,6 +7,13 @@ from openslides.mediafiles.models import Mediafile
from openslides.utils.models import RESTModelMixin from openslides.utils.models import RESTModelMixin
from openslides.utils.projector import ProjectorElement from openslides.utils.projector import ProjectorElement
from .access_permissions import (
ChatMessageAccessPermissions,
ConfigAccessPermissions,
CustomSlideAccessPermissions,
ProjectorAccessPermissions,
TagAccessPermissions,
)
from .exceptions import ProjectorException 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 The projector can be controlled using the REST API with POST requests
on e. g. the URL /rest/core/projector/1/activate_elements/. on e. g. the URL /rest/core/projector/1/activate_elements/.
""" """
access_permissions = ProjectorAccessPermissions()
config = JSONField() config = JSONField()
scale = models.IntegerField(default=0) scale = models.IntegerField(default=0)
@ -121,6 +129,8 @@ class CustomSlide(RESTModelMixin, models.Model):
""" """
Model for slides with custom content. Model for slides with custom content.
""" """
access_permissions = CustomSlideAccessPermissions()
title = models.CharField( title = models.CharField(
max_length=256) max_length=256)
text = models.TextField( 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, Model for tags. This tags can be used for other models like agenda items,
motions or assignments. motions or assignments.
""" """
access_permissions = TagAccessPermissions()
name = models.CharField( name = models.CharField(
max_length=255, max_length=255,
unique=True) unique=True)
@ -189,10 +201,11 @@ class Tag(RESTModelMixin, models.Model):
return self.name return self.name
class ConfigStore(models.Model): class ConfigStore(RESTModelMixin, models.Model):
""" """
A model class to store all config variables in the database. 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) key = models.CharField(max_length=255, unique=True, db_index=True)
"""A string, the key of the config variable.""" """A string, the key of the config variable."""
@ -205,11 +218,15 @@ class ConfigStore(models.Model):
permissions = ( permissions = (
('can_manage_config', 'Can manage configuration'),) ('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): 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. At the moment we only have one global chat room for managers.
""" """
access_permissions = ChatMessageAccessPermissions()
message = models.TextField() message = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True) timestamp = models.DateTimeField(auto_now_add=True)

View File

@ -13,7 +13,6 @@ from django.http import Http404, HttpResponse
from django.utils.timezone import now from django.utils.timezone import now
from openslides import __version__ as version from openslides import __version__ as version
from openslides.core.access_permissions import AccessPermissions
from openslides.utils import views as utils_views from openslides.utils import views as utils_views
from openslides.utils.plugins import ( from openslides.utils.plugins import (
get_plugin_description, get_plugin_description,
@ -31,15 +30,16 @@ from openslides.utils.rest_api import (
) )
from openslides.utils.search import search from openslides.utils.search import search
from .access_permissions import (
ChatMessageAccessPermissions,
ConfigAccessPermissions,
CustomSlideAccessPermissions,
ProjectorAccessPermissions,
TagAccessPermissions,
)
from .config import config from .config import config
from .exceptions import ConfigError, ConfigNotFound from .exceptions import ConfigError, ConfigNotFound
from .models import ChatMessage, CustomSlide, Projector, Tag from .models import ChatMessage, CustomSlide, Projector, Tag
from .serializers import (
ChatMessageSerializer,
CustomSlideSerializer,
ProjectorSerializer,
TagSerializer,
)
# Special Django views # Special Django views
@ -153,16 +153,15 @@ class ProjectorViewSet(ReadOnlyModelViewSet):
activate_elements, prune_elements, update_elements, activate_elements, prune_elements, update_elements,
deactivate_elements, clear_elements and control_view. deactivate_elements, clear_elements and control_view.
""" """
access_permissions = ProjectorAccessPermissions()
queryset = Projector.objects.all() queryset = Projector.objects.all()
serializer_class = ProjectorSerializer
access_permissions = AccessPermissions()
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. Returns True if the user has required permissions.
""" """
if self.action == 'retrieve': 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'): elif self.action in ('metadata', 'list'):
result = self.request.user.has_perm('core.can_see_projector') result = self.request.user.has_perm('core.can_see_projector')
elif self.action in ('activate_elements', 'prune_elements', 'update_elements', 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, There are the following views: metadata, list, retrieve, create,
partial_update, update and destroy. partial_update, update and destroy.
""" """
access_permissions = CustomSlideAccessPermissions()
queryset = CustomSlide.objects.all() queryset = CustomSlide.objects.all()
serializer_class = CustomSlideSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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): class TagViewSet(ModelViewSet):
@ -387,16 +390,18 @@ class TagViewSet(ModelViewSet):
There are the following views: metadata, list, retrieve, create, There are the following views: metadata, list, retrieve, create,
partial_update, update and destroy. partial_update, update and destroy.
""" """
access_permissions = TagAccessPermissions()
queryset = Tag.objects.all() queryset = Tag.objects.all()
serializer_class = TagSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. Returns True if the user has required permissions.
""" """
if self.action in ('metadata', 'list', 'retrieve'): if self.action == 'retrieve':
# Every authenticated user can see the metadata and list or result = self.get_access_permissions().can_retrieve(self.request.user)
# retrieve tags. Anonymous users can do so if they are enabled. 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'] result = self.request.user.is_authenticated() or config['general_system_enable_anonymous']
elif self.action in ('create', 'update', 'destroy'): elif self.action in ('create', 'update', 'destroy'):
result = self.request.user.has_perm('core.can_manage_tags') 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. There are the following views: metadata, list, retrieve and update.
""" """
access_permissions = ConfigAccessPermissions()
metadata_class = ConfigMetadata metadata_class = ConfigMetadata
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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 # Every authenticated user can see the metadata and list or
# retrieve the config. Anonymous users can do so if they are # retrieve the config. Anonymous users can do so if they are
# enabled. # enabled.
@ -476,6 +484,8 @@ class ConfigViewSet(ViewSet):
value = config[key] value = config[key]
except ConfigNotFound: except ConfigNotFound:
raise Http404 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}) return Response({'key': key, 'value': value})
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
@ -508,18 +518,23 @@ class ChatMessageViewSet(ModelViewSet):
There are the following views: metadata, list, retrieve and create. There are the following views: metadata, list, retrieve and create.
The views partial_update, update and destroy are disabled. The views partial_update, update and destroy are disabled.
""" """
access_permissions = ChatMessageAccessPermissions()
queryset = ChatMessage.objects.all() queryset = ChatMessage.objects.all()
serializer_class = ChatMessageSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. Returns True if the user has required permissions.
""" """
# We do not want anonymous users to use the chat even the anonymous if self.action == 'retrieve':
# group has the permission core.can_use_chat. result = self.get_access_permissions().can_retrieve(self.request.user)
return (self.action in ('metadata', 'list', 'retrieve', 'create') and 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.is_authenticated() and
self.request.user.has_perm('core.can_use_chat')) self.request.user.has_perm('core.can_use_chat'))
return result
def perform_create(self, serializer): def perform_create(self, serializer):
""" """

View 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

View File

@ -18,4 +18,4 @@ class MediafilesAppConfig(AppConfig):
from .views import MediafileViewSet from .views import MediafileViewSet
# Register viewsets. # Register viewsets.
router.register('mediafiles/mediafile', MediafileViewSet) router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet)

View File

@ -5,12 +5,15 @@ from django.utils.translation import ugettext as _
from openslides.utils.search import user_name_helper from openslides.utils.search import user_name_helper
from ..utils.models import RESTModelMixin from ..utils.models import RESTModelMixin
from .access_permissions import MediafileAccessPermissions
class Mediafile(RESTModelMixin, models.Model): class Mediafile(RESTModelMixin, models.Model):
""" """
Class for uploaded files which can be delivered under a certain url. Class for uploaded files which can be delivered under a certain url.
""" """
access_permissions = MediafileAccessPermissions()
mediafile = models.FileField(upload_to='file') mediafile = models.FileField(upload_to='file')
""" """
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield

View File

@ -1,6 +1,6 @@
from ..utils.rest_api import ModelViewSet, ValidationError from ..utils.rest_api import ModelViewSet, ValidationError
from .access_permissions import MediafileAccessPermissions
from .models import Mediafile from .models import Mediafile
from .serializers import MediafileSerializer
# Viewsets for the REST API # Viewsets for the REST API
@ -12,14 +12,16 @@ class MediafileViewSet(ModelViewSet):
There are the following views: metadata, list, retrieve, create, There are the following views: metadata, list, retrieve, create,
partial_update, update and destroy. partial_update, update and destroy.
""" """
access_permissions = MediafileAccessPermissions()
queryset = Mediafile.objects.all() queryset = Mediafile.objects.all()
serializer_class = MediafileSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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') result = self.request.user.has_perm('mediafiles.can_see')
elif self.action == 'create': elif self.action == 'create':
result = (self.request.user.has_perm('mediafiles.can_see') and result = (self.request.user.has_perm('mediafiles.can_see') and

View 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

View File

@ -25,7 +25,7 @@ class MotionsAppConfig(AppConfig):
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows') post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')
# Register viewsets. # Register viewsets.
router.register('motions/category', CategoryViewSet) router.register(self.get_model('Category').get_collection_string(), CategoryViewSet)
router.register('motions/motion', MotionViewSet) 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/motionpoll', MotionPollViewSet)
router.register('motions/workflow', WorkflowViewSet)

View File

@ -20,6 +20,11 @@ from openslides.poll.models import (
from openslides.utils.models import RESTModelMixin from openslides.utils.models import RESTModelMixin
from openslides.utils.search import user_name_helper from openslides.utils.search import user_name_helper
from .access_permissions import (
CategoryAccessPermissions,
MotionAccessPermissions,
WorkflowAccessPermissions,
)
from .exceptions import WorkflowError 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. This class is the main entry point to all other classes related to a motion.
""" """
access_permissions = MotionAccessPermissions()
active_version = models.ForeignKey( active_version = models.ForeignKey(
'MotionVersion', 'MotionVersion',
@ -624,6 +630,11 @@ class MotionVersion(RESTModelMixin, models.Model):
class Category(RESTModelMixin, models.Model): class Category(RESTModelMixin, models.Model):
"""
Model for categories of motions.
"""
access_permissions = CategoryAccessPermissions()
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
"""Name of the category.""" """Name of the category."""
@ -879,7 +890,10 @@ class State(RESTModelMixin, models.Model):
class Workflow(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) name = models.CharField(max_length=255)
"""A string representing the workflow.""" """A string representing the workflow."""

View File

@ -18,15 +18,15 @@ from openslides.utils.rest_api import (
) )
from openslides.utils.views import PDFView, SingleObjectMixin from openslides.utils.views import PDFView, SingleObjectMixin
from .access_permissions import (
CategoryAccessPermissions,
MotionAccessPermissions,
WorkflowAccessPermissions,
)
from .exceptions import WorkflowError from .exceptions import WorkflowError
from .models import Category, Motion, MotionPoll, MotionVersion, Workflow from .models import Category, Motion, MotionPoll, MotionVersion, Workflow
from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf
from .serializers import ( from .serializers import MotionPollSerializer
CategorySerializer,
MotionPollSerializer,
MotionSerializer,
WorkflowSerializer,
)
# Viewsets for the REST API # Viewsets for the REST API
@ -39,14 +39,16 @@ class MotionViewSet(ModelViewSet):
partial_update, update, destroy, manage_version, support, set_state and partial_update, update, destroy, manage_version, support, set_state and
create_poll. create_poll.
""" """
access_permissions = MotionAccessPermissions()
queryset = Motion.objects.all() queryset = Motion.objects.all()
serializer_class = MotionSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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') result = self.request.user.has_perm('motions.can_see')
# For partial_update and update requests the rest of the check is # For partial_update and update requests the rest of the check is
# done in the update method. See below. # done in the update method. See below.
@ -281,14 +283,16 @@ class CategoryViewSet(ModelViewSet):
There are the following views: metadata, list, retrieve, create, There are the following views: metadata, list, retrieve, create,
partial_update, update and destroy. partial_update, update and destroy.
""" """
access_permissions = CategoryAccessPermissions()
queryset = Category.objects.all() queryset = Category.objects.all()
serializer_class = CategorySerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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') result = self.request.user.has_perm('motions.can_see')
elif self.action in ('create', 'partial_update', 'update', 'destroy'): elif self.action in ('create', 'partial_update', 'update', 'destroy'):
result = (self.request.user.has_perm('motions.can_see') and 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, There are the following views: metadata, list, retrieve, create,
partial_update, update and destroy. partial_update, update and destroy.
""" """
access_permissions = WorkflowAccessPermissions()
queryset = Workflow.objects.all() queryset = Workflow.objects.all()
serializer_class = WorkflowSerializer
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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') result = self.request.user.has_perm('motions.can_see')
elif self.action in ('create', 'partial_update', 'update', 'destroy'): elif self.action in ('create', 'partial_update', 'update', 'destroy'):
result = (self.request.user.has_perm('motions.can_see') and result = (self.request.user.has_perm('motions.can_see') and

View 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

View File

@ -28,5 +28,5 @@ class UsersAppConfig(AppConfig):
dispatch_uid='create_builtin_groups_and_admin') dispatch_uid='create_builtin_groups_and_admin')
# Register viewsets. # Register viewsets.
router.register('users/user', UserViewSet) router.register(self.get_model('User').get_collection_string(), UserViewSet)
router.register('users/group', GroupViewSet) router.register('users/group', GroupViewSet)

View File

@ -13,6 +13,7 @@ from openslides.utils.search import user_name_helper
from ..core.config import config from ..core.config import config
from ..utils.models import RESTModelMixin from ..utils.models import RESTModelMixin
from .access_permissions import UserAccessPermissions
from .exceptions import UsersError from .exceptions import UsersError
@ -94,6 +95,8 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
in other OpenSlides apps like motion submitter or (assignment) election in other OpenSlides apps like motion submitter or (assignment) election
candidates. candidates.
""" """
access_permissions = UserAccessPermissions()
USERNAME_FIELD = 'username' USERNAME_FIELD = 'username'
username = models.CharField( username = models.CharField(

View File

@ -14,13 +14,10 @@ from ..utils.rest_api import (
status, status,
) )
from ..utils.views import APIView, PDFView from ..utils.views import APIView, PDFView
from .access_permissions import UserAccessPermissions
from .models import Group, User from .models import Group, User
from .pdf import users_passwords_to_pdf, users_to_pdf from .pdf import users_passwords_to_pdf, users_to_pdf
from .serializers import ( from .serializers import GroupSerializer, UserFullSerializer
GroupSerializer,
UserFullSerializer,
UserShortSerializer,
)
# Viewsets for the REST API # Viewsets for the REST API
@ -32,13 +29,16 @@ class UserViewSet(ModelViewSet):
There are the following views: metadata, list, retrieve, create, There are the following views: metadata, list, retrieve, create,
partial_update, update, destroy and reset_password. partial_update, update, destroy and reset_password.
""" """
access_permissions = UserAccessPermissions()
queryset = User.objects.all() queryset = User.objects.all()
def check_view_permissions(self): def check_view_permissions(self):
""" """
Returns True if the user has required permissions. 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') result = self.request.user.has_perm('users.can_see_name')
elif self.action in ('create', 'destroy', 'reset_password'): elif self.action in ('create', 'destroy', 'reset_password'):
result = (self.request.user.has_perm('users.can_see_name') and result = (self.request.user.has_perm('users.can_see_name') and
@ -50,16 +50,13 @@ class UserViewSet(ModelViewSet):
def get_serializer_class(self): def get_serializer_class(self):
""" """
Returns different serializer classes with respect to action and user's Returns different serializer classes with respect to action.
permissions.
""" """
if (self.action in ('create', 'partial_update', 'update') or if self.action in ('create', 'partial_update', 'update'):
self.request.user.has_perm('users.can_see_extra_data')): # Return the UserFullSerializer for edit requests.
# Return the UserFullSerializer for edit requests or for
# list/retrieve requests of users with more permissions.
serializer_class = UserFullSerializer serializer_class = UserFullSerializer
else: else:
serializer_class = UserShortSerializer serializer_class = super().get_serializer_class()
return serializer_class return serializer_class
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@ -78,6 +75,7 @@ class UserViewSet(ModelViewSet):
Hides the default_password for non admins. Hides the default_password for non admins.
""" """
#TODO: Hide default_password also in case of autoupdate.
response = super().retrieve(request, *args, **kwargs) response = super().retrieve(request, *args, **kwargs)
self.extract_default_password(response) self.extract_default_password(response)
return response return response

View 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

View File

@ -1,15 +1,12 @@
import json
import os import os
import posixpath import posixpath
from importlib import import_module
from urllib.parse import unquote from urllib.parse import unquote
from django.conf import settings from django.conf import settings
from openslides.users.auth import get_user
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
from django.utils.importlib import import_module
from sockjs.tornado import SockJSConnection, SockJSRouter from sockjs.tornado import SockJSConnection, SockJSRouter
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.httputil import HTTPHeaders
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.options import parse_command_line from tornado.options import parse_command_line
from tornado.web import ( from tornado.web import (
@ -19,7 +16,8 @@ from tornado.web import (
StaticFileHandler, StaticFileHandler,
) )
from tornado.wsgi import WSGIContainer 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_HOST = None
RUNNING_PORT = None RUNNING_PORT = None
@ -59,9 +57,6 @@ class DjangoStaticFileHandler(StaticFileHandler):
return absolute_path return absolute_path
class FakeRequest:
pass
class OpenSlidesSockJSConnection(SockJSConnection): class OpenSlidesSockJSConnection(SockJSConnection):
""" """
SockJS connection for OpenSlides. SockJS connection for OpenSlides.
@ -80,36 +75,29 @@ class OpenSlidesSockJSConnection(SockJSConnection):
""" """
Sends an OpenSlides object to all connected clients (waiters). 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: for waiter in cls.waiters:
# Read waiter's former cookies and parse session cookie to new header object. # Read waiter's former cookies and parse session cookie to get user instance.
headers = HTTPHeaders()
try: try:
session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME] 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) engine = import_module(settings.SESSION_ENGINE)
session = engine.SessionStore(session_cookie) session = engine.SessionStore(session_cookie.value)
fake_request = type('FakeRequest', (), {})()
request = FakeRequest() fake_request.session = session
request.session = session user = get_user(fake_request)
# Fetch serialized data and send them out to the waiter (client).
user = get_user(request) serialized_instance_data = instance.get_access_permissions().get_serialized_data(instance, user)
serializer_class = instance.access_permissions.get_serializer_class(user) if serialized_instance_data is not None:
serialized_instance_data = serializer_class(instance).data
data = { data = {
'url': "foobar", 'status_code': 404 if is_delete else 200, # TODO: Refactor this. Use strings like 'change' or 'delete'.
'status_code': 404 if is_delete else 200, 'collection': instance.get_collection_string(),
'collection': instance.get_collection_name(), 'id': instance.get_rest_pk(),
'id': instance.id,
'data': serialized_instance_data} 'data': serialized_instance_data}
waiter.send(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): def run_tornado(addr, port, *args, **kwargs):
@ -152,22 +140,28 @@ def inform_changed_data(is_delete, *args):
""" """
Informs all users about changed data. 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: if settings.USE_TORNADO_AS_WSGI_SERVER:
for root_instance in root_instances: for instance in args:
OpenSlidesSockJSConnection.send_object(root_instance, is_delete) 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: else:
pass 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): def inform_changed_data_receiver(sender, instance, **kwargs):

View File

@ -1,4 +1,3 @@
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
@ -19,15 +18,11 @@ class MinMaxIntegerField(models.IntegerField):
class RESTModelMixin: 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 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): def get_root_rest_element(self):
""" """
Returns the root rest instance. Returns the root rest instance.
@ -36,20 +31,25 @@ class RESTModelMixin:
""" """
return self 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 return self.access_permissions
# 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)])
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 # TODO Check if this is a root rest element class and return None if not.
from .rest_api import get_collection_and_id_from_url return '/'.join((cls._meta.app_label.lower(), cls._meta.object_name.lower()))
return get_collection_and_id_from_url(self.get_root_rest_url())[0]
def get_rest_pk(self):
"""
Returns the primary key used in the REST API. By default this is
the database pk.
"""
return self.pk

View File

@ -101,20 +101,17 @@ class PermissionMixin:
The methods check_view_permissions or check_projector_requirements are The methods check_view_permissions or check_projector_requirements are
evaluated. If both return False self.permission_denied() is called. 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): Also connects container to handle access permissions for model and
""" viewset.
TODO """
""" access_permissions = None
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
def get_permissions(self): def get_permissions(self):
""" """
Overriden method to check view and projector permissions. Returns an Overridden method to check view and projector permissions. Returns an
empty interable so Django REST framework won't do any other empty iterable so Django REST framework won't do any other
permission checks by evaluating Django REST framework style permission permission checks by evaluating Django REST framework style permission
classes and the request passes. classes and the request passes.
""" """
@ -126,6 +123,8 @@ class PermissionMixin:
""" """
Override this and return True if the requesting user should be able to Override this and return True if the requesting user should be able to
get access to your view. get access to your view.
Use access permissions container for retrieve requests.
""" """
return False return False
@ -144,6 +143,24 @@ class PermissionMixin:
break break
return result 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): class ModelSerializer(_ModelSerializer):
""" """
@ -172,7 +189,7 @@ class GenericViewSet(PermissionMixin, _GenericViewSet):
class ModelViewSet(PermissionMixin, _ModelViewSet): class ModelViewSet(PermissionMixin, _ModelViewSet):
access_permissions = None pass
class ReadOnlyModelViewSet(PermissionMixin, _ReadOnlyModelViewSet): class ReadOnlyModelViewSet(PermissionMixin, _ReadOnlyModelViewSet):
@ -183,12 +200,13 @@ class ViewSet(PermissionMixin, _ViewSet):
pass pass
#TODO: Remove this method
def get_collection_and_id_from_url(url): def get_collection_and_id_from_url(url):
""" """
Helper function. Returns a tuple containing the collection name and the id Helper function. Returns a tuple containing the collection name and the id
extracted out of the given REST api URL. 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'). returns ('users/user', '3').
Raises OpenSlidesError if the URL is invalid. Raises OpenSlidesError if the URL is invalid.
@ -196,5 +214,5 @@ def get_collection_and_id_from_url(url):
path = urlparse(url).path path = urlparse(url).path
match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path) match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path)
if not match: 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') return match.group('collection'), match.group('id')

View File

@ -10,7 +10,3 @@ def to_roman(number):
return roman.toRoman(number) return roman.toRoman(number)
except (roman.NotIntegerError, roman.OutOfRangeError): except (roman.NotIntegerError, roman.OutOfRangeError):
return None return None
def collection_name(model_class):
return "{1}/{2}".format(model_class.Meta.app_label.lower(), model_class.Meta.object_name)