Merge pull request #2336 from normanjaeckel/SerializerRefactoring
Refactored user serializers for different client permissions. See #1871.
This commit is contained in:
commit
54dd21dce0
@ -1404,7 +1404,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
||||
});
|
||||
|
||||
// TODO (Issue #2293):
|
||||
// fix _.uniqWith(importedXXX, _.isEqual);
|
||||
// fix _.uniqWith(importedXXX, _.isEqual);
|
||||
// (You need lodash version >= 4.0.0)
|
||||
|
||||
// unique users
|
||||
@ -1466,7 +1466,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
||||
var index = motion.submitter.indexOf(' ');
|
||||
var first_name = motion.submitter.substr(0, index);
|
||||
var last_name = motion.submitter.substr(index+1);
|
||||
|
||||
|
||||
// search for user, set id.
|
||||
importedUsersUnique.forEach(function (user) {
|
||||
if (user.first_name == first_name &&
|
||||
@ -1478,7 +1478,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
||||
// add category
|
||||
if (!motion.category_id && motion.category) {
|
||||
var name = motion.category;
|
||||
|
||||
|
||||
// search for category, set id.
|
||||
importedCategoriesUnique.forEach(function (category) {
|
||||
if (category.name == name) {
|
||||
|
@ -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
|
||||
|
@ -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'):
|
||||
|
@ -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');
|
||||
},
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user