Refactored user serializers for different client permissions. See #1871.

This commit is contained in:
Norman Jäckel 2016-08-31 16:53:02 +02:00
parent 29f9203377
commit b4d4026316
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.
"""
from .serializers import UserFullSerializer, UserShortSerializer
from .serializers import UserCanSeeSerializer, UserCanSeeExtraSerializer, UserFullSerializer
if user is None or user.has_perm('users.can_see_extra_data'):
# Return the UserFullSerializer for requests of users with more
# permissions.
if (user is None or (user.has_perm('users.can_see_extra_data') and user.has_perm('users.can_manage'))):
serializer_class = UserFullSerializer
elif user.has_perm('users.can_see_extra_data'):
serializer_class = UserCanSeeExtraSerializer
else:
serializer_class = UserShortSerializer
serializer_class = UserCanSeeSerializer
return serializer_class
def get_restricted_data(self, full_data, user):
"""
Returns the restricted serialized data for the instance prepared
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
UserShortSerializer would give them.
not get the fields they should not get.
"""
from .serializers import USERSHORTSERIALIZER_FIELDS
from .serializers import USERCANSEESERIALIZER_FIELDS, USERCANSEEEXTRASERIALIZER_FIELDS
if user.has_perm('users.can_manage'):
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:
# 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 = {}
for key in full_data.keys():
if key in USERSHORTSERIALIZER_FIELDS:
if key in fields:
data[key] = full_data[key]
return data

View File

@ -11,7 +11,8 @@ from ..utils.rest_api import (
)
from .models import Group, User
USERSHORTSERIALIZER_FIELDS = (
USERCANSEESERIALIZER_FIELDS = (
'id',
'username',
'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:
model = User
fields = USERSHORTSERIALIZER_FIELDS
fields = USERCANSEESERIALIZER_FIELDS
read_only_fields = (
'number',
'groups',
'is_comittee',
)
class UserFullSerializer(ModelSerializer):
"""
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 = (
USERCANSEEEXTRASERIALIZER_FIELDS = (
'id',
'is_present',
'username',
@ -63,16 +55,60 @@ class UserFullSerializer(ModelSerializer):
'about_me',
'comment',
'groups',
'default_password',
'is_active',
'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):
"""
Checks that first_name or last_name is given.
Generates the username if it is empty.
Checks that first_name or last_name is given. Generates the
username if it is empty.
"""
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.')})
@ -92,8 +128,7 @@ class UserFullSerializer(ModelSerializer):
def create(self, validated_data):
"""
Creates the user. Sets the default password. Adds the new user to the
registered group.
Creates the user. Sets the default password.
"""
# Prepare setup password.
if not validated_data.get('default_password'):

View File

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

View File

@ -57,47 +57,11 @@ class UserViewSet(ModelViewSet):
# Return the UserFullSerializer for edit requests.
serializer_class = UserFullSerializer
else:
# Return different serializers according to user permsissions via
# access permissions class.
serializer_class = super().get_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):
"""
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:
# A user can not deactivate himself.
raise ValidationError({'detail': _('You can not deactivate yourself.')})
response = super().update(request, *args, **kwargs)
else:
# Get user.
user = self.get_object()
# Check permissions only to update yourself.
if request.user != user:
if str(request.user.pk) != self.kwargs['pk']:
self.permission_denied(request)
# Check permission to send only some data.
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)
response = super().update(request, *args, **kwargs)
return response
def destroy(self, request, *args, **kwargs):