Protect element fields from the projector model. Fixes creation of

projectors.
This commit is contained in:
FinnStutzenstein 2019-04-09 16:13:40 +02:00
parent fc5f6f4e54
commit 5a1f638f8d
7 changed files with 189 additions and 26 deletions

View File

@ -148,11 +148,7 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
public create(): void {
if (this.createForm.valid && this.projectorToCreate) {
this.projectorToCreate.patchValues(this.createForm.value as Projector);
// TODO: the server shouldn't want to have element data..
this.projectorToCreate.patchValues({
elements: [{ name: 'core/clock', stable: true }],
elements_preview: [],
elements_history: [],
reference_projector_id: this.projectors[0].reference_projector_id
});
this.repo.create(this.projectorToCreate).then(() => (this.projectorToCreate = null), this.raiseError);

View File

@ -91,7 +91,7 @@ class Projector(RESTModelMixin, models.Model):
show_title = models.BooleanField(default=True)
show_logo = models.BooleanField(default=True)
name = models.CharField(max_length=255, unique=True, blank=True)
name = models.CharField(max_length=255, unique=True)
reference_projector = models.ForeignKey(
"self",

View File

@ -1,7 +1,13 @@
from typing import Any
from ..utils.projector import projector_slides
from ..utils.rest_api import Field, IntegerField, ModelSerializer, ValidationError
from ..utils.rest_api import (
Field,
IdPrimaryKeyRelatedField,
IntegerField,
ModelSerializer,
ValidationError,
)
from ..utils.validate import validate_html
from .models import (
ChatMessage,
@ -77,13 +83,17 @@ class ProjectorSerializer(ModelSerializer):
Serializer for core.models.Projector objects.
"""
elements = JSONSerializerField(validators=[elements_validator])
elements_preview = JSONSerializerField(validators=[elements_validator])
elements_history = JSONSerializerField(validators=[elements_array_validator])
elements = JSONSerializerField(read_only=True)
elements_preview = JSONSerializerField(read_only=True)
elements_history = JSONSerializerField(read_only=True)
width = IntegerField(min_value=800, max_value=3840, required=False)
height = IntegerField(min_value=340, max_value=2880, required=False)
projectiondefaults = IdPrimaryKeyRelatedField(
many=True, required=False, queryset=ProjectionDefault.objects.all()
)
class Meta:
model = Projector
fields = (

View File

@ -57,6 +57,7 @@ from .models import (
ProjectorMessage,
Tag,
)
from .serializers import elements_array_validator, elements_validator
# Special Django views
@ -139,6 +140,11 @@ class ProjectorViewSet(ModelViewSet):
result = False
return result
def perform_create(self, serializer):
projector = serializer.save()
projector.elements = [{"name": "core/clock", "stable": True}]
projector.save()
def destroy(self, *args, **kwargs):
"""
REST API operation for DELETE requests.
@ -184,22 +190,24 @@ class ProjectorViewSet(ModelViewSet):
"delete_last_history_element", False
)
changed_data = {}
if elements is not None:
changed_data["elements"] = elements
elements_validator(elements)
projector.elements = elements
if preview is not None:
changed_data["elements_preview"] = preview
elements_validator(preview)
projector.elements_preview = preview
elements_history = None
if history_element is not None and delete_last_history_element is False:
history = projector.elements_history + [history_element]
changed_data["elements_history"] = history
elements_history = projector.elements_history + [history_element]
if history_element is None and delete_last_history_element is True:
history = projector.elements_history[:-1]
changed_data["elements_history"] = history
serializer = self.get_serializer(projector, data=changed_data, partial=True)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
elements_history = projector.elements_history[:-1]
if elements_history is not None:
elements_array_validator(elements_history)
projector.elements_history = elements_history
projector.save()
return Response()
@detail_route(methods=["post"])

6
tests/common_groups.py Normal file
View File

@ -0,0 +1,6 @@
# In this file are the default group ids which should be used in all tests.
GROUP_DEFAULT_PK = 1
GROUP_ADMIN_PK = 2
GROUP_DELEGATE_PK = 3
GROUP_STAFF_PK = 4

View File

@ -1,11 +1,14 @@
import pytest
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from openslides.core.config import config
from openslides.core.models import ChatMessage, Projector, Tag
from openslides.users.models import User
from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.test import TestCase
from tests.common_groups import GROUP_ADMIN_PK, GROUP_DELEGATE_PK
from ..helpers import count_queries
@ -59,6 +62,146 @@ def test_config_db_queries():
assert count_queries(Tag.get_elements) == 1
class ProjectorViewSet(TestCase):
"""
Tests (currently just parts) of the ProjectorViewSet.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
def test_create(self):
response = self.client.post(
reverse("projector-list"), {"name": "test_name_efIOLJHF32f&EF)NG3fw"}
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# pk=1 should be the default projector and pk=2 the new one
self.assertEqual(Projector.objects.all().count(), 2)
self.assertTrue(Projector.objects.filter(pk=2).exists())
projector = Projector.objects.get(pk=2)
self.assertEqual(projector.name, "test_name_efIOLJHF32f&EF)NG3fw")
self.assertEqual(projector.elements, [{"name": "core/clock", "stable": True}])
def test_create_no_data(self):
response = self.client.post(reverse("projector-list"))
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Projector.objects.all().count(), 1)
def test_no_permission(self):
admin = User.objects.get(username="admin")
admin.groups.add(GROUP_DELEGATE_PK)
admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(admin)
response = self.client.post(reverse("projector-list"))
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(Projector.objects.all().count(), 1)
class Projection(TestCase):
"""
Tests the projection view.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
self.projector = Projector.objects.get(pk=1) # the default projector
def test_add_element(self):
elements = [{"name": "core/clock"}]
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"elements": elements},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, elements)
self.assertEqual(self.projector.elements_preview, [])
self.assertEqual(self.projector.elements_history, [])
def test_add_element_without_name(self):
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"elements": [{}]},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, [])
self.assertEqual(self.projector.elements_preview, [])
self.assertEqual(self.projector.elements_history, [])
def test_no_permissions(self):
admin = User.objects.get(username="admin")
admin.groups.add(GROUP_DELEGATE_PK)
admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(admin)
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]), {}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_remove_element(self):
self.projector.elements = [{"name": "core/clock"}]
self.projector.save()
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"elements": []},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, [])
self.assertEqual(self.projector.elements_preview, [])
self.assertEqual(self.projector.elements_history, [])
def test_add_element_to_history(self):
element = [{"name": "core/clock"}]
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"append_to_history": element},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, [])
self.assertEqual(self.projector.elements_preview, [])
self.assertEqual(self.projector.elements_history, [element])
def test_remove_last_history_element(self):
element1 = [{"name": "core/clock"}]
element2 = [{"name": "motions/motion"}]
self.projector.elements_history = [element1, element2]
self.projector.save()
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"delete_last_history_element": True},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, [])
self.assertEqual(self.projector.elements_preview, [])
self.assertEqual(self.projector.elements_history, [element1])
def test_set_preview(self):
elements = [{"name": "core/clock"}]
response = self.client.post(
reverse("projector-project", args=[self.projector.pk]),
{"preview": elements},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.projector = Projector.objects.get(pk=1)
self.assertEqual(self.projector.elements, [])
self.assertEqual(self.projector.elements_preview, elements)
self.assertEqual(self.projector.elements_history, [])
class ChatMessageViewSet(TestCase):
"""
Tests requests to deal with chat messages.

View File

@ -23,16 +23,16 @@ from openslides.motions.models import (
from openslides.utils.auth import get_group_model
from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.test import TestCase
from tests.common_groups import (
GROUP_ADMIN_PK,
GROUP_DEFAULT_PK,
GROUP_DELEGATE_PK,
GROUP_STAFF_PK,
)
from ..helpers import count_queries
GROUP_DEFAULT_PK = 1
GROUP_ADMIN_PK = 2
GROUP_DELEGATE_PK = 3
GROUP_STAFF_PK = 4
@pytest.mark.django_db(transaction=False)
def test_motion_db_queries():
"""