0001-api-include-roles-in-users-API-25645.patch
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 |
- |