Merge pull request #1535 from normanjaeckel/RESTProjectorPermissions

Added permission check for objects required for active projector elem…
This commit is contained in:
Oskar Hahn 2015-06-16 10:35:42 +02:00
commit 237b0630ca
6 changed files with 125 additions and 5 deletions

View File

@ -18,7 +18,7 @@ def add_default_projector(apps, schema_editor):
weight=-500)
Projector = apps.get_model('core', 'Projector')
projector_config = [
{'name': 'core/clock'},
{'name': 'core/clock', 'stable': True},
{'name': 'core/customslide', 'id': custom_slide.id}]
Projector.objects.create(config=projector_config)

View File

@ -52,7 +52,7 @@ class Projector(RESTModelMixin, models.Model):
for element in ProjectorElement.get_all():
elements[element.name] = element
for config_entry in self.config:
name = config_entry.get('name')
name = config_entry['name']
element = elements.get(name)
data = {'name': name}
if element is None:
@ -66,6 +66,22 @@ class Projector(RESTModelMixin, models.Model):
data['error'] = str(e)
yield data
@classmethod
def get_all_requirements(cls):
"""
Generator which returns all ProjectorRequirement instances of all
active projector elements.
"""
elements = {}
for element in ProjectorElement.get_all():
elements[element.name] = element
for projector in cls.objects.all():
for config_entry in projector.config:
element = elements.get(config_entry['name'])
if element is not None:
for requirement in element.get_requirements(config_entry):
yield requirement
class CustomSlide(RESTModelMixin, AbsoluteUrlMixin, models.Model):
"""

View File

@ -1,10 +1,11 @@
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from openslides.utils.projector import ProjectorElement
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
from .exceptions import ProjectorException
from .models import CustomSlide
from .views import CustomSlideViewSet
class CustomSlideSlide(ProjectorElement):
@ -22,6 +23,19 @@ class CustomSlideSlide(ProjectorElement):
'collection': 'core/customslide',
'id': pk}]
def get_requirements(self, config_entry):
self.config_entry = config_entry
try:
pk = self.get_context()[0]['id']
except ProjectorException:
# Custom slide does not exist so just do nothing.
pass
else:
yield ProjectorRequirement(
view_class=CustomSlideViewSet,
view_action='retrieve',
pk=str(pk))
class Clock(ProjectorElement):
"""

View File

@ -1,9 +1,10 @@
from django.utils.translation import ugettext as _
from openslides.core.exceptions import ProjectorException
from openslides.utils.projector import ProjectorElement
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
from .models import User
from .views import GroupViewSet, UserViewSet
class UserSlide(ProjectorElement):
@ -27,3 +28,17 @@ class UserSlide(ProjectorElement):
'collection': 'users/group',
'id': group.pk})
return result
def get_requirements(self, config_entry):
self.config_entry = config_entry
try:
context = self.get_context()
except ProjectorException:
# User does not exist so just do nothing.
pass
else:
for item in context:
yield ProjectorRequirement(
view_class=UserViewSet if item['collection'] == 'users/user' else GroupViewSet,
view_action='retrieve',
pk=str(item['id']))

View File

@ -64,3 +64,39 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
Returns the context of the projector element.
"""
return None
def get_requirements(self, config_entry):
"""
Returns an iterable of ProjectorRequirement instances to setup
which views should be accessable for projector clients if the
projector element is active. The config_entry has to be given.
"""
return ()
class ProjectorRequirement:
"""
Container for required views. Such a view is defined by its class, its
action and its kwargs which come from the URL path.
"""
def __init__(self, view_class, view_action, **kwargs):
self.view_class = view_class
self.view_action = view_action
self.kwargs = kwargs
def is_currently_required(self, view_instance):
"""
Returns True if the view_instance matches the initiated data of this
requirement.
"""
if not type(view_instance) == self.view_class:
result = False
elif not view_instance.action == self.view_action:
result = False
else:
result = True
for key in view_instance.kwargs:
if not self.kwargs[key] == view_instance.kwargs[key]:
result = False
break
return result

View File

@ -19,7 +19,8 @@ from rest_framework.serializers import ( # noqa
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa
from rest_framework.response import Response # noqa
from rest_framework.routers import DefaultRouter
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet, ViewSet # noqa
from rest_framework.viewsets import ModelViewSet as _ModelViewSet
from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet, ViewSet # noqa
from rest_framework.decorators import list_route # noqa
from .exceptions import OpenSlidesError
@ -51,6 +52,44 @@ class RESTModelMixin:
return reverse(rest_url, args=[str(root_instance.pk)])
class ModelViewSet(_ModelViewSet):
"""
Viewset for models. Before the method check_permission is called we
check projector requirements. If access for projector client users is
not currently required, check_permission is called, else not.
"""
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Ensure that the incoming request is permitted
self.perform_authentication(request)
if not self.check_projector_requirements():
self.check_permissions(request)
self.check_throttles(request)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
def check_projector_requirements(self):
"""
Helper method which returns True if the current request (on this
view instance) is required for at least one active projector element.
"""
from openslides.core.models import Projector
result = False
if self.request.user.has_perm('core.can_see_projector'):
for requirement in Projector.get_all_requirements():
if requirement.is_currently_required(view_instance=self):
result = True
break
return result
def get_collection_and_id_from_url(url):
"""
Helper function. Returns a tuple containing the collection name and the id