Project

General

Profile

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

Paul Marillonnet, 22 Mar 2019 05:33 PM

Download (9.96 KB)

View differences:

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

 src/authentic2/api_views.py | 122 +++++++++++++++++++++---------------
 tests/test_api.py           |  64 ++++++++++++++++++-
 2 files changed, 134 insertions(+), 52 deletions(-)
src/authentic2/api_views.py
321 321
    return request.user.to_json()
322 322

  
323 323

  
324
def attributes_hash(attributes):
325
    attributes = sorted(attributes, key=lambda at: at.name)
326
    return hash(tuple((at.name, at.required) for at in attributes))
327

  
328

  
329
class RoleSerializer(serializers.ModelSerializer):
330
    ou = serializers.SlugRelatedField(
331
        many=False,
332
        required=False,
333
        default=CreateOnlyDefault(get_default_ou),
334
        queryset=get_ou_model().objects.all(),
335
        slug_field='slug')
336

  
337
    @property
338
    def user(self):
339
        return self.context['request'].user
340

  
341
    def __init__(self, instance=None, **kwargs):
342
        super(RoleSerializer, self).__init__(instance, **kwargs)
343
        if self.instance:
344
            self.fields['ou'].read_only = True
345

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

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

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

  
367
    class Meta:
368
        model = get_role_model()
369
        fields = ('uuid', 'name', 'slug', 'ou',)
370
        extra_kwargs = {'uuid': {'read_only': True}}
371

  
372

  
373
class BaseOrganizationalUnitSerializer(serializers.ModelSerializer):
374
    class Meta:
375
        model = get_ou_model()
376
        fields = '__all__'
377

  
378

  
379
class OrganizationalUnitConciseSerializer(BaseOrganizationalUnitSerializer):
380
    class Meta(BaseOrganizationalUnitSerializer.Meta):
381
        fields = ('uuid', 'slug', 'name',)
382

  
383

  
384
class RoleCustomField(RoleSerializer):
385
    ou = OrganizationalUnitConciseSerializer(
386
        many=False,
387
        required=False,
388
        read_only=True)
389

  
390
    class Meta(RoleSerializer.Meta):
391
        fields = ('description', 'external_id', 'name', 'ou', 'service', 'slug',
392
                'uuid',)
393

  
394

  
324 395
class BaseUserSerializer(serializers.ModelSerializer):
325 396
    ou = serializers.SlugRelatedField(
326 397
        queryset=get_ou_model().objects.all(),
......
335 406
                                     default=CreateOnlyDefault(utils.generate_password),
336 407
                                     required=False)
337 408
    force_password_reset = serializers.BooleanField(write_only=True, required=False, default=False)
409
    roles = RoleCustomField(many=True, read_only=True)
338 410

  
339 411
    def __init__(self, *args, **kwargs):
340 412
        super(BaseUserSerializer, self).__init__(*args, **kwargs)
......
490 562
        exclude = ('date_joined', 'user_permissions', 'groups', 'last_login')
491 563

  
492 564

  
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 565
class UsersFilter(FilterSet):
538 566
    class Meta:
539 567
        model = get_user_model()
......
732 760
role_memberships = RoleMembershipsAPI.as_view()
733 761

  
734 762

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

  
740

  
741 763
class OrganizationalUnitAPI(ExceptionHandlerMixin, ModelViewSet):
742 764
    permission_classes = (DjangoPermission('a2_rbac.search_organizationalunit'),)
743 765
    serializer_class = BaseOrganizationalUnitSerializer
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):
1113

  
1114
    User = get_user_model()
1115
    user1 = User(username='john.doe', email='john.doe@example.com')
1116
    user1.set_password('password')
1117
    user1.save()
1118
    user2 = User(username='bob.smith', email='bob.smith@example.com')
1119
    user2.set_password('password')
1120
    user2.save()
1121

  
1122
    Role = get_role_model()
1123
    role1 = Role.objects.create(name='Role1')
1124
    role1.ou = ou1
1125
    role1.members.add(user1)
1126
    role1.save()
1127
    role2 = Role.objects.create(name='Role2')
1128
    role2.ou = ou2
1129
    role2.members.add(user1)
1130
    role2.members.add(user2)
1131
    role2.save()
1132
    role3 = Role.objects.create(name='Role3')
1133
    role3.ou = get_default_ou()
1134
    role3.members.add(user2)
1135
    role3.save()
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
                    'external_id', '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

  
1150
    url = u'/api/users/%s/' % admin.uuid
1151
    response = app.get(url, status=200)
1152
    assert len(response.json['roles']) == 1
1153
    assert response.json['roles'][0]['slug'] == '_a2-manager'
1154

  
1155
    url = u'/api/users/%s/' % user1.uuid
1156
    response = app.get(url, status=200)
1157
    assert len(response.json['roles']) == 2
1158
    user_roles = ['Role1', 'Role2']
1159
    for role in response.json['roles']:
1160
        assert role['name'] in user_roles
1161
        user_roles.remove(role['name'])
1162

  
1163
    url = u'/api/users/%s/' % user2.uuid
1164
    response = app.get(url, status=200)
1165
    assert len(response.json['roles']) == 2
1166
    user_roles = ['Role2', 'Role3']
1167
    for role in response.json['roles']:
1168
        assert role['name'] in user_roles
1169
        user_roles.remove(role['name'])
1110
-