Merge pull request #2336 from normanjaeckel/SerializerRefactoring

Refactored user serializers for different client permissions. See #1871.
This commit is contained in:
Norman Jäckel 2016-09-05 15:31:17 +02:00 committed by GitHub
commit 54dd21dce0
5 changed files with 85 additions and 110 deletions

View File

@ -15,35 +15,34 @@ class UserAccessPermissions(BaseAccessPermissions):
""" """
Returns different serializer classes with respect user's permissions. Returns different serializer classes with respect user's permissions.
""" """
from .serializers import UserFullSerializer, UserShortSerializer from .serializers import UserCanSeeSerializer, UserCanSeeExtraSerializer, UserFullSerializer
if user is None or user.has_perm('users.can_see_extra_data'): if (user is None or (user.has_perm('users.can_see_extra_data') and user.has_perm('users.can_manage'))):
# Return the UserFullSerializer for requests of users with more
# permissions.
serializer_class = UserFullSerializer serializer_class = UserFullSerializer
elif user.has_perm('users.can_see_extra_data'):
serializer_class = UserCanSeeExtraSerializer
else: else:
serializer_class = UserShortSerializer serializer_class = UserCanSeeSerializer
return serializer_class return serializer_class
def get_restricted_data(self, full_data, user): def get_restricted_data(self, full_data, user):
""" """
Returns the restricted serialized data for the instance prepared Returns the restricted serialized data for the instance prepared
for the user. Removes several fields for non admins so that they do for the user. Removes several fields for non admins so that they do
not get the default_password or even get only the fields as the not get the fields they should not get.
UserShortSerializer would give them.
""" """
from .serializers import USERSHORTSERIALIZER_FIELDS from .serializers import USERCANSEESERIALIZER_FIELDS, USERCANSEEEXTRASERIALIZER_FIELDS
if user.has_perm('users.can_manage'): if user.has_perm('users.can_manage'):
data = full_data data = full_data
elif user.has_perm('users.can_see_extra_data'):
# Only remove default password from full data.
data = full_data.copy()
del data['default_password']
else: else:
# Let only fields as in the UserShortSerializer pass this method. if user.has_perm('users.can_see_extra_data'):
fields = USERCANSEEEXTRASERIALIZER_FIELDS
else:
fields = USERCANSEESERIALIZER_FIELDS
# Let only some fields pass this method.
data = {} data = {}
for key in full_data.keys(): for key in full_data.keys():
if key in USERSHORTSERIALIZER_FIELDS: if key in fields:
data[key] = full_data[key] data[key] = full_data[key]
return data return data

View File

@ -11,7 +11,8 @@ from ..utils.rest_api import (
) )
from .models import Group, User from .models import Group, User
USERSHORTSERIALIZER_FIELDS = (
USERCANSEESERIALIZER_FIELDS = (
'id', 'id',
'username', 'username',
'title', 'title',
@ -25,33 +26,24 @@ USERSHORTSERIALIZER_FIELDS = (
) )
class UserShortSerializer(ModelSerializer): class UserCanSeeSerializer(ModelSerializer):
""" """
Serializer for users.models.User objects. Serializer for users.models.User objects to be used by users who have
only the permission to see users and to change some date of theirselfs.
Serializes only name fields and about me field. Attention: Viewset has to ensure that a user can update only himself.
""" """
class Meta: class Meta:
model = User model = User
fields = USERSHORTSERIALIZER_FIELDS fields = USERCANSEESERIALIZER_FIELDS
read_only_fields = (
'number',
'groups',
'is_comittee',
)
class UserFullSerializer(ModelSerializer): USERCANSEEEXTRASERIALIZER_FIELDS = (
"""
Serializer for users.models.User objects.
Serializes all relevant fields.
"""
groups = IdPrimaryKeyRelatedField(
many=True,
queryset=Group.objects.exclude(pk=1),
help_text=ugettext_lazy('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her groups.'))
class Meta:
model = User
fields = (
'id', 'id',
'is_present', 'is_present',
'username', 'username',
@ -63,16 +55,60 @@ class UserFullSerializer(ModelSerializer):
'about_me', 'about_me',
'comment', 'comment',
'groups', 'groups',
'default_password',
'is_active', 'is_active',
'is_committee', 'is_committee',
) )
class UserCanSeeExtraSerializer(ModelSerializer):
"""
Serializer for users.models.User objects to be used by users who have
the permission to see users with extra data and to change some date of
theirselfs.
Attention: Viewset has to ensure that a user can update only himself.
"""
groups = IdPrimaryKeyRelatedField(
many=True,
queryset=Group.objects.exclude(pk=1),
help_text=ugettext_lazy('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her groups.'))
class Meta:
model = User
fields = USERCANSEEEXTRASERIALIZER_FIELDS
read_only_fields = (
'is_present',
'number',
'comment',
'groups',
'is_comittee',
'is_active',
)
class UserFullSerializer(ModelSerializer):
"""
Serializer for users.models.User objects.
Serializes all relevant fields for manager.
"""
groups = IdPrimaryKeyRelatedField(
many=True,
queryset=Group.objects.exclude(pk=1),
help_text=ugettext_lazy('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her groups.'))
class Meta:
model = User
fields = USERCANSEEEXTRASERIALIZER_FIELDS + ('default_password',)
def validate(self, data): def validate(self, data):
""" """
Checks that first_name or last_name is given. Checks that first_name or last_name is given. Generates the
username if it is empty.
Generates the username if it is empty.
""" """
if not (data.get('username') or data.get('first_name') or data.get('last_name')): if not (data.get('username') or data.get('first_name') or data.get('last_name')):
raise ValidationError({'detail': _('Username, first name and last name can not all be empty.')}) raise ValidationError({'detail': _('Username, first name and last name can not all be empty.')})
@ -92,8 +128,7 @@ class UserFullSerializer(ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
""" """
Creates the user. Sets the default password. Adds the new user to the Creates the user. Sets the default password.
registered group.
""" """
# Prepare setup password. # Prepare setup password.
if not validated_data.get('default_password'): if not validated_data.get('default_password'):

View File

@ -421,7 +421,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
'Group', 'Group',
function($scope, $state, ngDialog, UserForm, User, Group) { function($scope, $state, ngDialog, UserForm, User, Group) {
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
$scope.alert = {}; $scope.alert = {};
$scope.groupFilter = undefined; $scope.groupFilter = undefined;
@ -532,7 +532,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
'Group', 'Group',
function($scope, ngDialog, UserForm, User, user, Group) { function($scope, ngDialog, UserForm, User, user, Group) {
User.bindOne(user.id, $scope, 'user'); User.bindOne(user.id, $scope, 'user');
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups'); Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
// open edit dialog // open edit dialog
$scope.openDialog = function (user) { $scope.openDialog = function (user) {
@ -629,7 +629,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
$scope.user = user; // autoupdate is not activated $scope.user = user; // autoupdate is not activated
$scope.tinymceOption = Editor.getOptions(); $scope.tinymceOption = Editor.getOptions();
$scope.save = function (user) { $scope.save = function (user) {
User.save(user, { method: 'PATCH' }).then( User.save(user).then(
function(success) { function(success) {
$state.go('users.user.list'); $state.go('users.user.list');
}, },

View File

@ -57,47 +57,11 @@ class UserViewSet(ModelViewSet):
# Return the UserFullSerializer for edit requests. # Return the UserFullSerializer for edit requests.
serializer_class = UserFullSerializer serializer_class = UserFullSerializer
else: else:
# Return different serializers according to user permsissions via
# access permissions class.
serializer_class = super().get_serializer_class() serializer_class = super().get_serializer_class()
return serializer_class return serializer_class
def list(self, request, *args, **kwargs):
"""
Customized view endpoint to list all user.
Hides the default_password for non admins.
"""
response = super().list(request, *args, **kwargs)
self.extract_default_password(response)
return response
def retrieve(self, request, *args, **kwargs):
"""
Customized view endpoint to retrieve a user.
Hides the default_password for non admins.
"""
response = super().retrieve(request, *args, **kwargs)
self.extract_default_password(response)
return response
def extract_default_password(self, response):
"""
Checks if a user is not a manager. If yes, the default password is
extracted from the response.
"""
if not self.request.user.has_perm('users.can_manage'):
if isinstance(response.data, dict):
try:
del response.data['default_password']
except KeyError:
pass
elif isinstance(response.data, list):
for user in response.data:
try:
del user['default_password']
except KeyError:
pass
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
""" """
Customized view endpoint to update an user. Customized view endpoint to update an user.
@ -113,34 +77,11 @@ class UserViewSet(ModelViewSet):
if request.data.get('is_active') is False and self.get_object() == request.user: if request.data.get('is_active') is False and self.get_object() == request.user:
# A user can not deactivate himself. # A user can not deactivate himself.
raise ValidationError({'detail': _('You can not deactivate yourself.')}) raise ValidationError({'detail': _('You can not deactivate yourself.')})
response = super().update(request, *args, **kwargs)
else: else:
# Get user.
user = self.get_object()
# Check permissions only to update yourself. # Check permissions only to update yourself.
if request.user != user: if str(request.user.pk) != self.kwargs['pk']:
self.permission_denied(request) self.permission_denied(request)
# Check permission to send only some data. response = super().update(request, *args, **kwargs)
whitelist = (
'username',
'title',
'first_name',
'last_name',
'structure_level',
'about_me',)
keys = list(request.data.keys())
for key in keys:
if key not in whitelist:
# Non-staff users are allowed to send only some data. Ignore other data.
del request.data[key]
# Validate data and update user.
serializer = self.get_serializer(
user,
data=request.data,
partial=kwargs.get('partial', False))
serializer.is_valid(raise_exception=True)
serializer.save()
response = Response(serializer.data)
return response return response
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):