Merge pull request #1482 from normanjaeckel/Groups
Updated REST API for group create, update and delete.
This commit is contained in:
commit
de52634561
@ -4,7 +4,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
|||||||
|
|
||||||
from openslides.utils.rest_api import ModelSerializer, PrimaryKeyRelatedField, RelatedField, ValidationError
|
from openslides.utils.rest_api import ModelSerializer, PrimaryKeyRelatedField, RelatedField, ValidationError
|
||||||
|
|
||||||
from .models import Group, User
|
from .models import Group, Permission, User
|
||||||
|
|
||||||
|
|
||||||
class UserShortSerializer(ModelSerializer):
|
class UserShortSerializer(ModelSerializer):
|
||||||
@ -122,18 +122,40 @@ class PermissionRelatedField(RelatedField):
|
|||||||
"""
|
"""
|
||||||
A custom field to use for the permission relationship.
|
A custom field to use for the permission relationship.
|
||||||
"""
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'incorrect_value': ugettext_lazy('Incorrect value "{value}". Expected app_label.codename string.'),
|
||||||
|
'does_not_exist': ugettext_lazy('Invalid permission "{value}". Object does not exist.')}
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
"""
|
"""
|
||||||
Returns the permission name (app_label.codename).
|
Returns the permission code string (app_label.codename).
|
||||||
"""
|
"""
|
||||||
return '.'.join((value.content_type.app_label, value.codename,))
|
return '.'.join((value.content_type.app_label, value.codename,))
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
"""
|
||||||
|
Returns the permission object represented by data. The argument data is
|
||||||
|
what is sent by the client. This method expects permission code strings
|
||||||
|
(app_label.codename) like to_representation() returns.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
app_label, codename = data.split('.')
|
||||||
|
except ValueError:
|
||||||
|
self.fail('incorrect_value', value=data)
|
||||||
|
try:
|
||||||
|
permission = Permission.objects.get(content_type__app_label=app_label, codename=codename)
|
||||||
|
except Permission.DoesNotExist:
|
||||||
|
self.fail('does_not_exist', value=data)
|
||||||
|
return permission
|
||||||
|
|
||||||
|
|
||||||
class GroupSerializer(ModelSerializer):
|
class GroupSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for django.contrib.auth.models.Group objects.
|
Serializer for django.contrib.auth.models.Group objects.
|
||||||
"""
|
"""
|
||||||
permissions = PermissionRelatedField(many=True, read_only=True)
|
permissions = PermissionRelatedField(
|
||||||
|
many=True,
|
||||||
|
queryset=Permission.objects.all())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Group
|
model = Group
|
||||||
|
@ -4,8 +4,9 @@ from django.contrib.auth import logout as auth_logout
|
|||||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
|
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import activate, ugettext_lazy
|
from django.utils.translation import activate, ugettext_lazy
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
from openslides.utils.rest_api import ModelViewSet
|
from openslides.utils.rest_api import ModelViewSet, Response
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
CSVImportView,
|
CSVImportView,
|
||||||
FormView,
|
FormView,
|
||||||
@ -121,6 +122,19 @@ class GroupViewSet(ModelViewSet):
|
|||||||
request.user.has_perm('users.can_see_extra_data')))):
|
request.user.has_perm('users.can_see_extra_data')))):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Protects builtin groups 'Anonymous' (pk=1) and 'Registered' (pk=2)
|
||||||
|
from being deleted.
|
||||||
|
"""
|
||||||
|
instance = self.get_object()
|
||||||
|
if instance.pk in (1, 2,):
|
||||||
|
self.permission_denied(request)
|
||||||
|
else:
|
||||||
|
self.perform_destroy(instance)
|
||||||
|
response = Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class UserSettingsView(LoginMixin, UpdateView):
|
class UserSettingsView(LoginMixin, UpdateView):
|
||||||
required_permission = None
|
required_permission = None
|
||||||
|
@ -2,11 +2,11 @@ from django.core.urlresolvers import reverse
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
from openslides.users.models import User
|
from openslides.users.models import Group, User
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
class UserCreation(TestCase):
|
class UserCreate(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests creation of users via REST API.
|
Tests creation of users via REST API.
|
||||||
"""
|
"""
|
||||||
@ -22,26 +22,31 @@ class UserCreation(TestCase):
|
|||||||
|
|
||||||
def test_creation_with_group(self):
|
def test_creation_with_group(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
|
# These are the builtin groups 'Delegates' and 'Staff'. The pks are valid.
|
||||||
|
group_pks = (3, 4,)
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse('user-list'),
|
reverse('user-list'),
|
||||||
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
||||||
'groups': ['3', '4']})
|
'groups': group_pks})
|
||||||
|
|
||||||
user = User.objects.get(username='Test name aedah1iequoof0Ashed4')
|
user = User.objects.get(username='Test name aedah1iequoof0Ashed4')
|
||||||
self.assertTrue(user.groups.filter(pk=3).exists())
|
self.assertTrue(user.groups.filter(pk=group_pks[0]).exists())
|
||||||
self.assertTrue(user.groups.filter(pk=4).exists())
|
self.assertTrue(user.groups.filter(pk=group_pks[1]).exists())
|
||||||
|
|
||||||
def test_creation_with_anonymous_or_registered_group(self):
|
def test_creation_with_anonymous_or_registered_group(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
|
# These are the builtin groups 'Anonymous' and 'Registered'.
|
||||||
|
# The pks are valid. But these groups can not be added to users.
|
||||||
|
group_pks = (1, 2,)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse('user-list'),
|
reverse('user-list'),
|
||||||
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
||||||
'groups': ['1', '2']})
|
'groups': group_pks})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(response.data, {'groups': ["Invalid pk '1' - object does not exist."]})
|
self.assertEqual(response.data, {'groups': ["Invalid pk '%d' - object does not exist." % group_pks[0]]})
|
||||||
|
|
||||||
|
|
||||||
class UserUpdate(TestCase):
|
class UserUpdate(TestCase):
|
||||||
@ -51,22 +56,26 @@ class UserUpdate(TestCase):
|
|||||||
def test_simple_update_via_patch(self):
|
def test_simple_update_via_patch(self):
|
||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
|
# This is the builtin user 'Administrator' with username 'admin'. The pk is valid.
|
||||||
|
user_pk = 1
|
||||||
|
|
||||||
response = admin_client.patch(
|
response = admin_client.patch(
|
||||||
reverse('user-detail', args=['1']),
|
reverse('user-detail', args=[user_pk]),
|
||||||
{'last_name': 'New name tu3ooh5Iez5Aec2laefo'})
|
{'last_name': 'New name tu3ooh5Iez5Aec2laefo'})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
user = User.objects.get(pk=1)
|
user = User.objects.get(pk=user_pk)
|
||||||
self.assertEqual(user.last_name, 'New name tu3ooh5Iez5Aec2laefo')
|
self.assertEqual(user.last_name, 'New name tu3ooh5Iez5Aec2laefo')
|
||||||
self.assertEqual(user.username, 'admin')
|
self.assertEqual(user.username, 'admin')
|
||||||
|
|
||||||
def test_simple_update_via_put(self):
|
def test_simple_update_via_put(self):
|
||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
|
# This is the builtin user 'Administrator'. The pk is valid.
|
||||||
|
user_pk = 1
|
||||||
|
|
||||||
response = admin_client.put(
|
response = admin_client.put(
|
||||||
reverse('user-detail', args=['1']),
|
reverse('user-detail', args=[user_pk]),
|
||||||
{'last_name': 'New name Ohy4eeyei5Sahzah0Os2'})
|
{'last_name': 'New name Ohy4eeyei5Sahzah0Os2'})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -81,9 +90,120 @@ class UserDelete(TestCase):
|
|||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
User.objects.create(username='Test name bo3zieT3iefahng0ahqu')
|
User.objects.create(username='Test name bo3zieT3iefahng0ahqu')
|
||||||
self.assertTrue(User.objects.filter(username='Test name bo3zieT3iefahng0ahqu').exists())
|
|
||||||
|
|
||||||
response = admin_client.delete(reverse('user-detail', args=['2']))
|
response = admin_client.delete(reverse('user-detail', args=['2']))
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
self.assertFalse(User.objects.filter(username='Test name bo3zieT3iefahng0ahqu').exists())
|
self.assertFalse(User.objects.filter(username='Test name bo3zieT3iefahng0ahqu').exists())
|
||||||
|
|
||||||
|
|
||||||
|
class GroupCreate(TestCase):
|
||||||
|
"""
|
||||||
|
Tests creation of groups via REST API.
|
||||||
|
"""
|
||||||
|
def test_creation(self):
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
# This contains two valid permissions of the users app.
|
||||||
|
permissions = ('users.can_see_name', 'users.can_see_extra_data')
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('group-list'),
|
||||||
|
{'name': 'Test name la8eephu9vaecheiKeif',
|
||||||
|
'permissions': permissions})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
group = Group.objects.get(name='Test name la8eephu9vaecheiKeif')
|
||||||
|
for permission in permissions:
|
||||||
|
app_label, codename = permission.split('.')
|
||||||
|
self.assertTrue(group.permissions.get(content_type__app_label=app_label, codename=codename))
|
||||||
|
|
||||||
|
def test_failed_creation_invalid_value(self):
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
permissions = ('invalid_permission',)
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('group-list'),
|
||||||
|
{'name': 'Test name ool5aeb6Rai2aiLaith1',
|
||||||
|
'permissions': permissions})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(
|
||||||
|
response.data,
|
||||||
|
{'permissions': ['Incorrect value "invalid_permission". Expected app_label.codename string.']})
|
||||||
|
|
||||||
|
def test_failed_creation_invalid_permission(self):
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
permissions = ('invalid_app.invalid_permission',)
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('group-list'),
|
||||||
|
{'name': 'Test name wei2go2aiV3eophi9Ohg',
|
||||||
|
'permissions': permissions})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(
|
||||||
|
response.data,
|
||||||
|
{'permissions': ['Invalid permission "invalid_app.invalid_permission". Object does not exist.']})
|
||||||
|
|
||||||
|
|
||||||
|
class GroupUpdate(TestCase):
|
||||||
|
"""
|
||||||
|
Tests update of groups via REST API.
|
||||||
|
"""
|
||||||
|
def test_simple_update_via_patch(self):
|
||||||
|
admin_client = APIClient()
|
||||||
|
admin_client.login(username='admin', password='admin')
|
||||||
|
# This is the builtin group 'Delegates'. The pk is valid.
|
||||||
|
group_pk = 3
|
||||||
|
# This contains one valid permission of the users app.
|
||||||
|
permissions = ('users.can_see_name',)
|
||||||
|
|
||||||
|
response = admin_client.patch(
|
||||||
|
reverse('group-detail', args=[group_pk]),
|
||||||
|
{'permissions': permissions})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
group = Group.objects.get(pk=group_pk)
|
||||||
|
for permission in permissions:
|
||||||
|
app_label, codename = permission.split('.')
|
||||||
|
self.assertTrue(group.permissions.get(content_type__app_label=app_label, codename=codename))
|
||||||
|
|
||||||
|
def test_simple_update_via_put(self):
|
||||||
|
admin_client = APIClient()
|
||||||
|
admin_client.login(username='admin', password='admin')
|
||||||
|
# This is the builtin group 'Delegates'. The pk is valid.
|
||||||
|
group_pk = 3
|
||||||
|
# This contains one valid permission of the users app.
|
||||||
|
permissions = ('users.can_see_name',)
|
||||||
|
|
||||||
|
response = admin_client.put(
|
||||||
|
reverse('group-detail', args=[group_pk]),
|
||||||
|
{'permissions': permissions})
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(response.data, {'name': ['This field is required.']})
|
||||||
|
|
||||||
|
|
||||||
|
class GroupDelete(TestCase):
|
||||||
|
"""
|
||||||
|
Tests delete of groups via REST API.
|
||||||
|
"""
|
||||||
|
def test_delete(self):
|
||||||
|
admin_client = APIClient()
|
||||||
|
admin_client.login(username='admin', password='admin')
|
||||||
|
group = Group.objects.create(name='Test name Koh4lohlaewoog9Ahsh5')
|
||||||
|
|
||||||
|
response = admin_client.delete(reverse('group-detail', args=[group.pk]))
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
self.assertFalse(Group.objects.filter(name='Test name Koh4lohlaewoog9Ahsh5').exists())
|
||||||
|
|
||||||
|
def test_delete_builtin_groups(self):
|
||||||
|
admin_client = APIClient()
|
||||||
|
admin_client.login(username='admin', password='admin')
|
||||||
|
# The pks of builtin groups 'Anonymous' and 'Registered'
|
||||||
|
group_pks = (1, 2,)
|
||||||
|
|
||||||
|
for group_pk in group_pks:
|
||||||
|
response = admin_client.delete(reverse('group-detail', args=[group_pk]))
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
Loading…
Reference in New Issue
Block a user