From 95cdbd3d1e93275805a639813e9198828447111e Mon Sep 17 00:00:00 2001 From: Paul Marillonnet Date: Thu, 30 Aug 2018 14:51:56 +0200 Subject: [PATCH] api: include roles in users API (#25645) --- src/authentic2/api_views.py | 94 ++++++++++++++++++++----------------- tests/test_api.py | 52 +++++++++++++++++++- 2 files changed, 101 insertions(+), 45 deletions(-) diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py index 8023cfad..8fa007df 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -310,6 +310,55 @@ def attributes_hash(attributes): return hash(tuple((at.name, at.required) for at in attributes)) +class RoleSerializer(serializers.ModelSerializer): + ou = serializers.SlugRelatedField( + many=False, + required=False, + default=CreateOnlyDefault(get_default_ou), + queryset=get_ou_model().objects.all(), + slug_field='slug') + + @property + def user(self): + return self.context['request'].user + + def __init__(self, instance=None, **kwargs): + super(RoleSerializer, self).__init__(instance, **kwargs) + if self.instance: + self.fields['ou'].read_only = True + + def create(self, validated_data): + ou = validated_data.get('ou') + # Creating roles also means being allowed to within the OU: + if not self.user.has_ou_perm('a2_rbac.add_role', ou): + raise PermissionDenied(u'User %s can\'t create role in OU %s' % (self.user, ou)) + return super(RoleSerializer, self).create(validated_data) + + def update(self, instance, validated_data): + # Check role-updating permissions: + if not self.user.has_perm('a2_rbac.change_role', obj=instance): + raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance)) + super(RoleSerializer, self).update(instance, validated_data) + return instance + + def partial_update(self, instance, validated_data): + # Check role-updating permissions: + if not self.user.has_perm('a2_rbac.change_role', obj=instance): + raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance)) + super(RoleSerializer, self).partial_update(instance, validated_data) + return instance + + class Meta: + model = get_role_model() + fields = ('uuid', 'name', 'slug', 'ou',) + extra_kwargs = {'uuid': {'read_only': True}} + + +class RoleCustomField(RoleSerializer): + class Meta(RoleSerializer.Meta): + fields = ('uuid', 'name', 'slug',) + + class BaseUserSerializer(serializers.ModelSerializer): ou = serializers.SlugRelatedField( queryset=get_ou_model().objects.all(), @@ -324,6 +373,7 @@ class BaseUserSerializer(serializers.ModelSerializer): default=CreateOnlyDefault(utils.generate_password), required=False) force_password_reset = serializers.BooleanField(write_only=True, required=False, default=False) + roles = RoleCustomField(many=True, read_only=True, source='roles_and_parents') def __init__(self, *args, **kwargs): super(BaseUserSerializer, self).__init__(*args, **kwargs) @@ -452,50 +502,6 @@ class BaseUserSerializer(serializers.ModelSerializer): exclude = ('date_joined', 'user_permissions', 'groups', 'last_login') -class RoleSerializer(serializers.ModelSerializer): - ou = serializers.SlugRelatedField( - many=False, - required=False, - default=CreateOnlyDefault(get_default_ou), - queryset=get_ou_model().objects.all(), - slug_field='slug') - - @property - def user(self): - return self.context['request'].user - - def __init__(self, instance=None, **kwargs): - super(RoleSerializer, self).__init__(instance, **kwargs) - if self.instance: - self.fields['ou'].read_only = True - - def create(self, validated_data): - ou = validated_data.get('ou') - # Creating roles also means being allowed to within the OU: - if not self.user.has_ou_perm('a2_rbac.add_role', ou): - raise PermissionDenied(u'User %s can\'t create role in OU %s' % (self.user, ou)) - return super(RoleSerializer, self).create(validated_data) - - def update(self, instance, validated_data): - # Check role-updating permissions: - if not self.user.has_perm('a2_rbac.change_role', obj=instance): - raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance)) - super(RoleSerializer, self).update(instance, validated_data) - return instance - - def partial_update(self, instance, validated_data): - # Check role-updating permissions: - if not self.user.has_perm('a2_rbac.change_role', obj=instance): - raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance)) - super(RoleSerializer, self).partial_update(instance, validated_data) - return instance - - class Meta: - model = get_role_model() - fields = ('uuid', 'name', 'slug', 'ou',) - extra_kwargs = {'uuid': {'read_only': True}} - - class UsersFilter(FilterSet): class Meta: model = get_user_model() diff --git a/tests/test_api.py b/tests/test_api.py index 5a1742ce..336b6573 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -251,7 +251,7 @@ def test_api_users_create(settings, app, api_user): if api_user.is_superuser or api_user.roles.exists(): assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser', 'first_name', 'last_name', 'date_joined', 'last_login', 'username', 'password', 'email', 'is_active', - 'title', 'modified', 'email_verified']) == set(resp.json.keys()) + 'title', 'modified', 'email_verified', 'roles']) == set(resp.json.keys()) assert resp.json['first_name'] == payload['first_name'] assert resp.json['last_name'] == payload['last_name'] assert resp.json['email'] == payload['email'] @@ -944,3 +944,53 @@ def test_validate_password_regex(app, settings): assert response.json['checks'][3]['result'] is True assert response.json['checks'][4]['label'] == 'must contain "ok"' assert response.json['checks'][4]['result'] is True + + +def test_roles_in_users_api(app, admin): + + User = get_user_model() + user1 = User(username='john.doe', email='john.doe@example.com') + user1.set_password('password') + user1.save() + user2 = User(username='bob.smith', email='bob.smith@example.com') + user2.set_password('password') + user2.save() + + Role = get_role_model() + role1 = Role.objects.create(name='Role1') + role1.members.add(user1) + role2 = Role.objects.create(name='Role2') + role2.members.add(user1) + role2.members.add(user2) + role2.add_parent(role1) + role3 = Role.objects.create(name='Role3') + role3.members.add(user2) + role3.add_parent(role1) + + app.authorization = ('Basic', (admin.username, admin.username)) + response = app.get(u'/api/users/', status=200) + assert len(response.json['results']) == 3 + for user in response.json['results']: + assert user['roles'] + for role in user['roles']: + keys = ['slug', 'name', 'uuid'] + for key, value in role.items(): + assert key in keys + assert value + keys.remove(key) + + url = u'/api/users/%s/' % admin.uuid + response = app.get(url, status=200) + assert len(response.json['roles']) == 4 + assert set(role['slug'] for role in response.json['roles']) == set([ + '_a2-manager', + '_a2-manager-of-users', + '_a2-manager-of-roles', + '_a2-manager-of-organizational-units']) + + for user in [user1, user2]: + url = u'/api/users/%s/' % user.uuid + response = app.get(url, status=200) + roles = user.roles_and_parents().all() + assert len(response.json['roles']) == roles.count() + assert set(role['name'] for role in response.json['roles']) == set(roles.values_list('name', flat=True)) -- 2.18.0