Merge pull request #1535 from normanjaeckel/RESTProjectorPermissions
Added permission check for objects required for active projector elem…
This commit is contained in:
commit
237b0630ca
@ -18,7 +18,7 @@ def add_default_projector(apps, schema_editor):
|
|||||||
weight=-500)
|
weight=-500)
|
||||||
Projector = apps.get_model('core', 'Projector')
|
Projector = apps.get_model('core', 'Projector')
|
||||||
projector_config = [
|
projector_config = [
|
||||||
{'name': 'core/clock'},
|
{'name': 'core/clock', 'stable': True},
|
||||||
{'name': 'core/customslide', 'id': custom_slide.id}]
|
{'name': 'core/customslide', 'id': custom_slide.id}]
|
||||||
Projector.objects.create(config=projector_config)
|
Projector.objects.create(config=projector_config)
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
for element in ProjectorElement.get_all():
|
for element in ProjectorElement.get_all():
|
||||||
elements[element.name] = element
|
elements[element.name] = element
|
||||||
for config_entry in self.config:
|
for config_entry in self.config:
|
||||||
name = config_entry.get('name')
|
name = config_entry['name']
|
||||||
element = elements.get(name)
|
element = elements.get(name)
|
||||||
data = {'name': name}
|
data = {'name': name}
|
||||||
if element is None:
|
if element is None:
|
||||||
@ -66,6 +66,22 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
data['error'] = str(e)
|
data['error'] = str(e)
|
||||||
yield data
|
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):
|
class CustomSlide(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext as _
|
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 .exceptions import ProjectorException
|
||||||
from .models import CustomSlide
|
from .models import CustomSlide
|
||||||
|
from .views import CustomSlideViewSet
|
||||||
|
|
||||||
|
|
||||||
class CustomSlideSlide(ProjectorElement):
|
class CustomSlideSlide(ProjectorElement):
|
||||||
@ -22,6 +23,19 @@ class CustomSlideSlide(ProjectorElement):
|
|||||||
'collection': 'core/customslide',
|
'collection': 'core/customslide',
|
||||||
'id': pk}]
|
'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):
|
class Clock(ProjectorElement):
|
||||||
"""
|
"""
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.core.exceptions import ProjectorException
|
from openslides.core.exceptions import ProjectorException
|
||||||
from openslides.utils.projector import ProjectorElement
|
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
from .views import GroupViewSet, UserViewSet
|
||||||
|
|
||||||
|
|
||||||
class UserSlide(ProjectorElement):
|
class UserSlide(ProjectorElement):
|
||||||
@ -27,3 +28,17 @@ class UserSlide(ProjectorElement):
|
|||||||
'collection': 'users/group',
|
'collection': 'users/group',
|
||||||
'id': group.pk})
|
'id': group.pk})
|
||||||
return result
|
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']))
|
||||||
|
@ -64,3 +64,39 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
|
|||||||
Returns the context of the projector element.
|
Returns the context of the projector element.
|
||||||
"""
|
"""
|
||||||
return None
|
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
|
||||||
|
@ -19,7 +19,8 @@ from rest_framework.serializers import ( # noqa
|
|||||||
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa
|
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa
|
||||||
from rest_framework.response import Response # noqa
|
from rest_framework.response import Response # noqa
|
||||||
from rest_framework.routers import DefaultRouter
|
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 rest_framework.decorators import list_route # noqa
|
||||||
|
|
||||||
from .exceptions import OpenSlidesError
|
from .exceptions import OpenSlidesError
|
||||||
@ -51,6 +52,44 @@ class RESTModelMixin:
|
|||||||
return reverse(rest_url, args=[str(root_instance.pk)])
|
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):
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user