Refactored REST api in agenda, core and users app.
This commit is contained in:
parent
2b744942e7
commit
0d56424c90
@ -16,6 +16,7 @@ Other:
|
||||
template signals and slides.
|
||||
- Used Bower and gulp to manage third party JavaScript and Cascading Style
|
||||
Sheets libraries.
|
||||
- Added Django REST Framework api.
|
||||
|
||||
|
||||
Version 1.7 (unreleased)
|
||||
|
@ -1,11 +1,11 @@
|
||||
from rest_framework import serializers
|
||||
from openslides.utils import rest_api
|
||||
|
||||
from .models import Item, Speaker
|
||||
|
||||
|
||||
class SpeakerSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class SpeakerSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Serializer for a agenda.models.Speaker objects.
|
||||
Serializer for agenda.models.Speaker objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = Speaker
|
||||
@ -17,18 +17,17 @@ class SpeakerSerializer(serializers.HyperlinkedModelSerializer):
|
||||
'weight')
|
||||
|
||||
|
||||
class ItemSerializer(serializers.ModelSerializer):
|
||||
class ItemSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Serializer for a agenda.models.Item objects.
|
||||
"""
|
||||
get_title = serializers.CharField(read_only=True)
|
||||
get_title_supplement = serializers.CharField(read_only=True)
|
||||
item_no = serializers.CharField(read_only=True)
|
||||
get_title = rest_api.serializers.CharField(read_only=True)
|
||||
get_title_supplement = rest_api.serializers.CharField(read_only=True)
|
||||
item_no = rest_api.serializers.CharField(read_only=True)
|
||||
speaker_set = SpeakerSerializer(many=True, read_only=True)
|
||||
tags = rest_api.serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='tag-detail')
|
||||
# content_object = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
exclude = ('content_type', 'object_id')
|
||||
|
||||
# TODO: Problem: User can always see the time shedule. Filter fields with respect of permission.
|
||||
|
@ -783,13 +783,12 @@ class ItemViewSet(rest_api.viewsets.ModelViewSet):
|
||||
"""
|
||||
Calls self.permission_denied() if the requesting user has not the
|
||||
permission to see and in case of create, update or destroy requests
|
||||
the permission to manage.
|
||||
the permission to manage and to see organizational items.
|
||||
"""
|
||||
if not request.user.has_perm('agenda.can_see_agenda'):
|
||||
self.permission_denied(request)
|
||||
elif (self.action in ('create', 'update', 'destroy')
|
||||
and not request.user.has_perm('agenda.can_manage_agenda')):
|
||||
# This is the same as self.action not in ('list', 'retrieve')
|
||||
if (not request.user.has_perm('agenda.can_see_agenda') or
|
||||
(self.action in ('create', 'update', 'destroy') and not
|
||||
(request.user.has_perm('agenda.can_manage_agenda') and
|
||||
request.user.has_perm('agenda.can_see_orga_items')))):
|
||||
self.permission_denied(request)
|
||||
|
||||
def check_object_permissions(self, request, obj):
|
||||
@ -805,7 +804,6 @@ class ItemViewSet(rest_api.viewsets.ModelViewSet):
|
||||
Filters organizational items if the user has no permission to see it.
|
||||
"""
|
||||
queryset = Item.objects.all()
|
||||
if (not self.request.user.has_perm('agenda.can_see_orga_items') and
|
||||
not self.request.user.has_perm('agenda.can_manage_agenda')):
|
||||
if not self.request.user.has_perm('agenda.can_see_orga_items'):
|
||||
queryset = queryset.exclude(type__exact=Item.ORGANIZATIONAL_ITEM)
|
||||
return queryset
|
||||
|
@ -17,7 +17,7 @@ class CoreAppConfig(AppConfig):
|
||||
from openslides.utils.autoupdate import inform_changed_data_receiver
|
||||
from openslides.utils.rest_api import router
|
||||
from .signals import setup_general_config
|
||||
from .views import CustomSlideViewSet
|
||||
from .views import CustomSlideViewSet, TagViewSet
|
||||
|
||||
# Connect signals.
|
||||
config_signal.connect(setup_general_config, dispatch_uid='setup_general_config')
|
||||
@ -28,6 +28,7 @@ class CoreAppConfig(AppConfig):
|
||||
|
||||
# Register viewset.
|
||||
router.register('core/customslide', CustomSlideViewSet)
|
||||
router.register('core/tag', TagViewSet)
|
||||
|
||||
# Update data when any model of any installed app is saved or deleted
|
||||
signals.post_save.connect(inform_changed_data_receiver, dispatch_uid='inform_changed_data_receiver')
|
||||
|
@ -4,15 +4,16 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
# TODO: activate the following line after using the apploader
|
||||
# from django.contrib.auth import get_user_model
|
||||
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.rest_api import RESTModelMixin
|
||||
|
||||
# Imports the default user so that other apps can import it from here.
|
||||
# TODO: activate this with the new apploader
|
||||
# User = get_user_model()
|
||||
|
||||
|
||||
class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
class CustomSlide(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
"""
|
||||
Model for Slides, only for the projector.
|
||||
"""
|
||||
@ -46,7 +47,7 @@ class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
return url
|
||||
|
||||
|
||||
class Tag(AbsoluteUrlMixin, models.Model):
|
||||
class Tag(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
||||
"""
|
||||
Model to save tags.
|
||||
"""
|
||||
|
@ -1,11 +1,19 @@
|
||||
from rest_framework import serializers
|
||||
from openslides.utils import rest_api
|
||||
|
||||
from .models import CustomSlide
|
||||
from .models import CustomSlide, Tag
|
||||
|
||||
|
||||
class CustomSlideSerializer(serializers.ModelSerializer):
|
||||
class CustomSlideSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Serializer for a core.models.CustomSlide objects.
|
||||
Serializer for core.models.CustomSlide objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = CustomSlide
|
||||
|
||||
|
||||
class TagSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Serializer for core.models.Tag objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = Tag
|
||||
|
@ -21,7 +21,7 @@ from openslides.utils.widgets import Widget
|
||||
from .forms import SelectWidgetsForm
|
||||
from .models import CustomSlide, Tag
|
||||
from .exceptions import TagException
|
||||
from .serializers import CustomSlideSerializer
|
||||
from .serializers import CustomSlideSerializer, TagSerializer
|
||||
|
||||
|
||||
class DashboardView(utils_views.AjaxMixin, utils_views.TemplateView):
|
||||
@ -217,6 +217,23 @@ class CustomSlideDeleteView(CustomSlideViewMixin, utils_views.DeleteView):
|
||||
pass
|
||||
|
||||
|
||||
class CustomSlideViewSet(rest_api.viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint to view, edit and delete custom slides.
|
||||
"""
|
||||
model = CustomSlide
|
||||
queryset = CustomSlide.objects.all()
|
||||
serializer_class = CustomSlideSerializer
|
||||
|
||||
def check_permissions(self, request):
|
||||
"""
|
||||
Calls self.permission_denied() if the requesting user has not the
|
||||
permission to manage.
|
||||
"""
|
||||
if not request.user.has_perm('core.can_manage_projector'):
|
||||
self.permission_denied(request)
|
||||
|
||||
|
||||
class TagListView(utils_views.AjaxMixin, utils_views.ListView):
|
||||
"""
|
||||
View to list and manipulate tags.
|
||||
@ -295,18 +312,19 @@ class TagListView(utils_views.AjaxMixin, utils_views.ListView):
|
||||
**context)
|
||||
|
||||
|
||||
class CustomSlideViewSet(rest_api.viewsets.ModelViewSet):
|
||||
class TagViewSet(rest_api.viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint to view, edit and delete custom slides.
|
||||
API endpoint to view, edit and delete tags.
|
||||
"""
|
||||
model = CustomSlide
|
||||
queryset = CustomSlide.objects.all()
|
||||
serializer_class = CustomSlideSerializer
|
||||
model = Tag
|
||||
queryset = Tag.objects.all()
|
||||
serializer_class = TagSerializer
|
||||
|
||||
def check_permissions(self, request):
|
||||
"""
|
||||
Calls self.permission_denied() if the requesting user has not the
|
||||
permission to manage.
|
||||
permission to manage and it's a create, update or detroy request.
|
||||
"""
|
||||
if not request.user.has_perm('core.can_manage_projector'):
|
||||
if (self.action in ('create', 'update', 'destroy')
|
||||
and not request.user.has_perm('core.can_manage_tags')):
|
||||
self.permission_denied(request)
|
||||
|
@ -8,7 +8,7 @@ class UserMainMenuEntry(MainMenuEntry):
|
||||
Main menu entry for the participant app.
|
||||
"""
|
||||
verbose_name = ugettext_lazy('Users')
|
||||
required_permission = 'users.can_see'
|
||||
required_permission = 'users.can_see_extra_data'
|
||||
default_weight = 50
|
||||
pattern_name = 'user_list'
|
||||
icon_css_class = 'icon-user'
|
||||
|
@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.rest_api import RESTModelMixin
|
||||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
@ -21,7 +22,7 @@ class UserManager(BaseUserManager):
|
||||
return user
|
||||
|
||||
|
||||
class User(SlideMixin, AbsoluteUrlMixin, PermissionsMixin, AbstractBaseUser):
|
||||
class User(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, PermissionsMixin, AbstractBaseUser):
|
||||
USERNAME_FIELD = 'username'
|
||||
slide_callback_name = 'user'
|
||||
|
||||
@ -72,7 +73,8 @@ class User(SlideMixin, AbsoluteUrlMixin, PermissionsMixin, AbstractBaseUser):
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('can_see', ugettext_noop('Can see users')),
|
||||
('can_see_name', ugettext_noop('Can see names of users')),
|
||||
('can_see_extra_data', ugettext_noop('Can see extra data of users')),
|
||||
('can_manage', ugettext_noop('Can manage users')),
|
||||
)
|
||||
ordering = ('last_name',)
|
||||
|
@ -1,9 +1,9 @@
|
||||
from rest_framework import serializers
|
||||
from openslides.utils import rest_api
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class UserShortSerializer(rest_api.serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for a users.models.User objects.
|
||||
"""
|
||||
@ -11,5 +11,26 @@ class UserSerializer(serializers.ModelSerializer):
|
||||
model = User
|
||||
fields = (
|
||||
'username',
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name')
|
||||
'last_name',
|
||||
'structure_level')
|
||||
|
||||
|
||||
class UserFullSerializer(rest_api.serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for a users.models.User objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = User
|
||||
fields = (
|
||||
'is_present',
|
||||
'username',
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'structure_level',
|
||||
'about_me',
|
||||
'comment',
|
||||
'default_password',
|
||||
'is_active')
|
||||
|
@ -138,15 +138,27 @@ def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment')
|
||||
|
||||
ct_users = ContentType.objects.get(app_label='users', model='user')
|
||||
perm_17 = Permission.objects.get(content_type=ct_users, codename='can_see')
|
||||
perm_users_can_see_name = Permission.objects.get(content_type=ct_users, codename='can_see_name')
|
||||
perm_users_can_see_extra_data = Permission.objects.get(content_type=ct_users, codename='can_see_extra_data')
|
||||
|
||||
ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile')
|
||||
perm_18 = Permission.objects.get(content_type=ct_mediafile, codename='can_see')
|
||||
|
||||
base_permission_list = (
|
||||
perm_11,
|
||||
perm_12,
|
||||
perm_13,
|
||||
perm_14,
|
||||
perm_15,
|
||||
perm_16,
|
||||
perm_users_can_see_name,
|
||||
perm_users_can_see_extra_data,
|
||||
perm_18)
|
||||
|
||||
group_anonymous = Group.objects.create(name=ugettext_noop('Anonymous'), pk=1)
|
||||
group_anonymous.permissions.add(perm_11, perm_12, perm_13, perm_14, perm_15, perm_16, perm_17, perm_18)
|
||||
group_anonymous.permissions.add(*base_permission_list)
|
||||
group_registered = Group.objects.create(name=ugettext_noop('Registered'), pk=2)
|
||||
group_registered.permissions.add(perm_11, perm_12, perm_13, perm_14, perm_15, perm_16, perm_17, perm_18, can_speak)
|
||||
group_registered.permissions.add(can_speak, *base_permission_list)
|
||||
|
||||
# Delegates (pk 3)
|
||||
perm_31 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion')
|
||||
@ -178,8 +190,9 @@ def create_builtin_groups_and_admin(sender, **kwargs):
|
||||
group_staff.permissions.add(perm_31, perm_33, perm_34, perm_35)
|
||||
# add staff permissions
|
||||
group_staff.permissions.add(perm_41, perm_42, perm_43, perm_44, perm_45, perm_46, perm_47, perm_48, can_manage_tags)
|
||||
# add can_see_user permission
|
||||
group_staff.permissions.add(perm_17) # TODO: Remove this redundancy after cleanup of the permission system
|
||||
# add can_see_name and can_see_extra_data permissions
|
||||
# TODO: Remove this redundancy after cleanup of the permission system.
|
||||
group_staff.permissions.add(perm_users_can_see_name, perm_users_can_see_extra_data)
|
||||
|
||||
# Admin user
|
||||
create_or_reset_admin_user()
|
||||
|
@ -20,14 +20,14 @@ from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
|
||||
UsersettingsForm, UserUpdateForm)
|
||||
from .models import Group, User
|
||||
from .pdf import users_to_pdf, users_passwords_to_pdf
|
||||
from .serializers import UserSerializer
|
||||
from .serializers import UserFullSerializer, UserShortSerializer
|
||||
|
||||
|
||||
class UserListView(ListView):
|
||||
"""
|
||||
Show all users.
|
||||
"""
|
||||
required_permission = 'users.can_see'
|
||||
required_permission = 'users.can_see_extra_data'
|
||||
context_object_name = 'users'
|
||||
|
||||
def get_queryset(self):
|
||||
@ -52,7 +52,7 @@ class UserDetailView(DetailView, PermissionMixin):
|
||||
"""
|
||||
Classed based view to show a specific user in the interface.
|
||||
"""
|
||||
required_permission = 'users.can_see'
|
||||
required_permission = 'users.can_see_extra_data'
|
||||
model = User
|
||||
context_object_name = 'shown_user'
|
||||
|
||||
@ -202,7 +202,7 @@ class UsersListPDF(PDFView):
|
||||
"""
|
||||
Generate the userliste as PDF.
|
||||
"""
|
||||
required_permission = 'users.can_see'
|
||||
required_permission = 'users.can_see_extra_data'
|
||||
filename = ugettext_lazy("user-list")
|
||||
document_title = ugettext_lazy('List of Users')
|
||||
|
||||
@ -263,21 +263,32 @@ class ResetPasswordView(SingleObjectMixin, QuestionView):
|
||||
|
||||
class UserViewSet(rest_api.viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint to view, edit and delete users.
|
||||
API endpoint to create, view, edit and delete users.
|
||||
"""
|
||||
model = User
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
|
||||
def check_permissions(self, request):
|
||||
"""
|
||||
Calls self.permission_denied() if the requesting user has not the
|
||||
permission to manage.
|
||||
Calls self.permission_denied() if the requesting user has not all
|
||||
permissions to see users.
|
||||
"""
|
||||
# TODO: More work on this required.
|
||||
if not request.user.has_perm('users.can_manage'):
|
||||
if (not request.user.has_perm('users.can_see_name') or
|
||||
(self.action in ('create', 'update', 'destroy') and not
|
||||
(request.user.has_perm('users.can_manage') and
|
||||
request.user.has_perm('users.can_see_extra_data')))):
|
||||
self.permission_denied(request)
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
Returns different serializer classes with respect to users permissions.
|
||||
"""
|
||||
if self.request.user.has_perm('users.can_see_extra_data'):
|
||||
serializer_class = UserFullSerializer
|
||||
else:
|
||||
serializer_class = UserShortSerializer
|
||||
return serializer_class
|
||||
|
||||
|
||||
class GroupListView(ListView):
|
||||
"""
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from rest_framework import permissions, routers, viewsets # noqa
|
||||
from rest_framework import permissions, routers, serializers, viewsets # noqa
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
|
@ -55,7 +55,8 @@ class DefaultGroups(TestCase):
|
||||
'agenda.can_see_orga_items',
|
||||
'motion.can_see_motion',
|
||||
'assignment.can_see_assignment',
|
||||
'users.can_see',
|
||||
'users.can_see_name',
|
||||
'users.can_see_extra_data',
|
||||
'mediafile.can_see')
|
||||
for perm_string in default_perms:
|
||||
perm_string_list = []
|
||||
|
@ -189,16 +189,16 @@ class LockoutProtection(TestCase):
|
||||
field=None,
|
||||
errors='You can not remove the permission to manage users from the last group you are in.')
|
||||
|
||||
def test_remove_permission_can_see_user_from_registered(self):
|
||||
self.assertTrue(self.user.has_perm('users.can_see'))
|
||||
def test_remove_permission_user_can_see_name_from_registered(self):
|
||||
self.assertTrue(self.user.has_perm('users.can_see_name'))
|
||||
# Remove perm from registered group
|
||||
can_see_perm = Permission.objects.get(
|
||||
content_type=ContentType.objects.get(app_label='users', model='user'),
|
||||
codename='can_see')
|
||||
codename='can_see_name')
|
||||
get_registered_group().permissions.remove(can_see_perm)
|
||||
# Reload user
|
||||
self.user = User.objects.get(pk=1)
|
||||
self.assertTrue(self.user.has_perm('users.can_see'))
|
||||
self.assertTrue(self.user.has_perm('users.can_see_name'))
|
||||
|
||||
|
||||
class TestUserSettings(TestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user