Project

General

Profile

0001-api-include-roles-in-users-API-25645.patch

Paul Marillonnet, 29 Mar 2019 12:15 PM

Download (11.5 KB)

View differences:

Subject: [PATCH] api: include roles in users API (#25645)

 src/authentic2/api_views.py | 132 ++++++++++++++++++++++--------------
 tests/conftest.py           |  17 +++++
 tests/test_api.py           |  65 +++++++++++++++++-
 3 files changed, 162 insertions(+), 52 deletions(-)
src/authentic2/api_views.py
321 321
    return request.user.to_json()
322 322

  
323 323

  
324
class RoleSerializer(serializers.ModelSerializer):
325
    ou = serializers.SlugRelatedField(
326
        many=False,
327
        required=False,
328
        default=CreateOnlyDefault(get_default_ou),
329
        queryset=get_ou_model().objects.all(),
330
        slug_field='slug')
331

  
332
    @property
333
    def user(self):
334
        return self.context['request'].user
335

  
336
    def __init__(self, instance=None, **kwargs):
337
        super(RoleSerializer, self).__init__(instance, **kwargs)
338
        if self.instance:
339
            self.fields['ou'].read_only = True
340

  
341
    def create(self, validated_data):
342
        ou = validated_data.get('ou')
343
        # Creating roles also means being allowed to within the OU:
344
        if not self.user.has_ou_perm('a2_rbac.add_role', ou):
345
            raise PermissionDenied(u'User %s can\'t create role in OU %s' % (self.user, ou))
346
        return super(RoleSerializer, self).create(validated_data)
347

  
348
    def update(self, instance, validated_data):
349
        # Check role-updating permissions:
350
        if not self.user.has_perm('a2_rbac.change_role', obj=instance):
351
            raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance))
352
        super(RoleSerializer, self).update(instance, validated_data)
353
        return instance
354

  
355
    def partial_update(self, instance, validated_data):
356
        # Check role-updating permissions:
357
        if not self.user.has_perm('a2_rbac.change_role', obj=instance):
358
            raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance))
359
        super(RoleSerializer, self).partial_update(instance, validated_data)
360
        return instance
361

  
362
    class Meta:
363
        model = get_role_model()
364
        fields = ('uuid', 'name', 'slug', 'ou',)
365
        extra_kwargs = {'uuid': {'read_only': True}}
366

  
367

  
368
class BaseOrganizationalUnitSerializer(serializers.ModelSerializer):
369
    class Meta:
370
        model = get_ou_model()
371
        fields = '__all__'
372

  
373

  
374
class OrganizationalUnitConciseSerializer(BaseOrganizationalUnitSerializer):
375
    class Meta(BaseOrganizationalUnitSerializer.Meta):
376
        fields = ('uuid', 'slug', 'name',)
377

  
378

  
379
class ServiceConciseSerializer(serializers.ModelSerializer):
380
    ou = serializers.SlugRelatedField(
381
        many=False,
382
        required=False,
383
        read_only=True,
384
        slug_field='slug')
385

  
386
    class Meta:
387
        model = Service
388
        fields = ('ou', 'slug', 'name')
389

  
390

  
391
class RoleCustomField(RoleSerializer):
392
    ou = OrganizationalUnitConciseSerializer(
393
        many=False,
394
        required=False,
395
        read_only=True)
396
    service = ServiceConciseSerializer(
397
        many=False,
398
        required=False,
399
        read_only=True)
400

  
401
    class Meta(RoleSerializer.Meta):
402
        fields = ('description', 'name', 'ou', 'service', 'slug', 'uuid',)
403

  
404

  
324 405
class BaseUserSerializer(serializers.ModelSerializer):
325 406
    ou = serializers.SlugRelatedField(
326 407
        queryset=get_ou_model().objects.all(),
......
335 416
                                     default=CreateOnlyDefault(utils.generate_password),
336 417
                                     required=False)
337 418
    force_password_reset = serializers.BooleanField(write_only=True, required=False, default=False)
419
    roles = RoleCustomField(many=True, read_only=True, source='roles_and_parents')
338 420

  
339 421
    def __init__(self, *args, **kwargs):
340 422
        super(BaseUserSerializer, self).__init__(*args, **kwargs)
......
490 572
        exclude = ('date_joined', 'user_permissions', 'groups', 'last_login')
491 573

  
492 574

  
493
class RoleSerializer(serializers.ModelSerializer):
494
    ou = serializers.SlugRelatedField(
495
        many=False,
496
        required=False,
497
        default=CreateOnlyDefault(get_default_ou),
498
        queryset=get_ou_model().objects.all(),
499
        slug_field='slug')
500

  
501
    @property
502
    def user(self):
503
        return self.context['request'].user
504

  
505
    def __init__(self, instance=None, **kwargs):
506
        super(RoleSerializer, self).__init__(instance, **kwargs)
507
        if self.instance:
508
            self.fields['ou'].read_only = True
509

  
510
    def create(self, validated_data):
511
        ou = validated_data.get('ou')
512
        # Creating roles also means being allowed to within the OU:
513
        if not self.user.has_ou_perm('a2_rbac.add_role', ou):
514
            raise PermissionDenied(u'User %s can\'t create role in OU %s' % (self.user, ou))
515
        return super(RoleSerializer, self).create(validated_data)
516

  
517
    def update(self, instance, validated_data):
518
        # Check role-updating permissions:
519
        if not self.user.has_perm('a2_rbac.change_role', obj=instance):
520
            raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance))
521
        super(RoleSerializer, self).update(instance, validated_data)
522
        return instance
523

  
524
    def partial_update(self, instance, validated_data):
525
        # Check role-updating permissions:
526
        if not self.user.has_perm('a2_rbac.change_role', obj=instance):
527
            raise PermissionDenied(u'User %s can\'t change role %s' % (self.user, instance))
528
        super(RoleSerializer, self).partial_update(instance, validated_data)
529
        return instance
530

  
531
    class Meta:
532
        model = get_role_model()
533
        fields = ('uuid', 'name', 'slug', 'ou',)
534
        extra_kwargs = {'uuid': {'read_only': True}}
535

  
536

  
537 575
class UsersFilter(FilterSet):
538 576
    class Meta:
539 577
        model = get_user_model()
......
732 770
role_memberships = RoleMembershipsAPI.as_view()
733 771

  
734 772

  
735
class BaseOrganizationalUnitSerializer(serializers.ModelSerializer):
736
    class Meta:
737
        model = get_ou_model()
738
        fields = '__all__'
739

  
740

  
741 773
class OrganizationalUnitAPI(ExceptionHandlerMixin, ModelViewSet):
742 774
    permission_classes = (DjangoPermission('a2_rbac.search_organizationalunit'),)
743 775
    serializer_class = BaseOrganizationalUnitSerializer
tests/conftest.py
15 15
from authentic2.a2_rbac.utils import get_default_ou
16 16
from authentic2_idp_oidc.models import OIDCClient
17 17
from authentic2.authentication import OIDCUser
18
from authentic2. models import Service
18 19

  
19 20
import utils
20 21

  
......
41 42
    return OU.objects.create(name='OU2', slug='ou2')
42 43

  
43 44

  
45
@pytest.fixture
46
def service1(db):
47
    return Service.objects.create(
48
        ou=get_default_ou(),
49
        name='Service 1',
50
        slug='service1')
51

  
52

  
53
@pytest.fixture
54
def service2(db):
55
    return Service.objects.create(
56
        ou=get_default_ou(),
57
        name='Service 2',
58
        slug='service2')
59

  
60

  
44 61
@pytest.fixture
45 62
def ou_rando(db):
46 63
    OU = get_ou_model()
tests/test_api.py
365 365

  
366 366
    resp = app.post_json('/api/users/', params=payload, status=status)
367 367
    if api_user.is_superuser or api_user.roles.exists():
368
        assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser',
368
        assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser', 'roles',
369 369
                    'first_name', 'first_name_verified', 'last_name',
370 370
                    'last_name_verified', 'date_joined', 'last_login',
371 371
                    'username', 'password', 'email', 'is_active', 'title',
......
431 431

  
432 432
    resp = app.post_json('/api/users/', params=payload, status=status)
433 433
    if api_user.is_superuser or api_user.roles.exists():
434
        assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser',
434
        assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser', 'roles',
435 435
                    'first_name', 'first_name_verified', 'last_name',
436 436
                    'last_name_verified', 'date_joined', 'last_login',
437 437
                    'username', 'password', 'email', 'is_active', 'title',
......
1107 1107
    assert response.json['checks'][3]['result'] is True
1108 1108
    assert response.json['checks'][4]['label'] == 'must contain "ok"'
1109 1109
    assert response.json['checks'][4]['result'] is True
1110

  
1111

  
1112
def test_roles_in_users_api(app, admin, ou1, ou2, service1, service2):
1113
    User = get_user_model()
1114
    user1 = User(username='john.doe', email='john.doe@example.com')
1115
    user1.set_password('password')
1116
    user1.save()
1117
    user2 = User(username='bob.smith', email='bob.smith@example.com')
1118
    user2.set_password('password')
1119
    user2.save()
1120

  
1121
    Role = get_role_model()
1122
    role1 = Role.objects.create(name='Role1')
1123
    role1.ou = ou1
1124
    role1.service = service1
1125
    role1.members.add(user1)
1126
    role2 = Role.objects.create(name='Role2')
1127
    role2.ou = ou2
1128
    role2.service = service2
1129
    role2.members.add(user1)
1130
    role2.members.add(user2)
1131
    role2.add_parent(role1)
1132
    role3 = Role.objects.create(name='Role3')
1133
    role3.ou = get_default_ou()
1134
    role3.members.add(user2)
1135
    role3.add_parent(role2)
1136

  
1137
    app.authorization = ('Basic', (admin.username, admin.username))
1138
    response = app.get(u'/api/users/', status=200)
1139
    assert len(response.json['results']) == 3
1140
    for user in response.json['results']:
1141
        assert user['roles']
1142
        for role in user['roles']:
1143
            assert set(['slug', 'name', 'uuid', 'service', 'description',
1144
                    'ou']) == set(role.keys())
1145
            role_object = Role.objects.get(uuid=role['uuid'])
1146
            if getattr(role_object, 'ou', None) is not None:
1147
                for ou_attr in ('uuid', 'slug', 'name'):
1148
                    assert getattr(role_object.ou, ou_attr) == role['ou'][ou_attr]
1149
            if getattr(role_object, 'service', None) is not None:
1150
                for service_attr in ('slug','name'):
1151
                    assert getattr(role_object.service, service_attr) == role['service'][attr]
1152
                # Service OUs identified by their slug:
1153
                if getattr(role_object.service, 'ou', None) is not None:
1154
                    assert role_object.service.ou.slug == role['service']['ou']
1155

  
1156
    url = u'/api/users/%s/' % admin.uuid
1157
    response = app.get(url, status=200)
1158
    assert len(response.json['roles']) == 4
1159
    assert set(role['slug'] for role in response.json['roles']) == set([
1160
        '_a2-manager',
1161
        '_a2-manager-of-users',
1162
        '_a2-manager-of-roles',
1163
        '_a2-manager-of-organizational-units'])
1164

  
1165
    for user in [user1, user2]:
1166
        url = u'/api/users/%s/' % user.uuid
1167
        response = app.get(url, status=200)
1168
        roles = user.roles_and_parents().all()
1169
        assert len(response.json['roles']) == roles.count()
1170
        assert set(role['name'] for role in response.json['roles']) == set(roles.values_list('name', flat=True))
1110
-