Merge pull request #2336 from normanjaeckel/SerializerRefactoring
Refactored user serializers for different client permissions. See #1871.
This commit is contained in:
commit
54dd21dce0
@ -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
|
||||||
|
@ -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'):
|
||||||
|
@ -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');
|
||||||
},
|
},
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user