From a9111c44d7f2302e0bb53f8634c52022a0fcbd46 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 14 Dec 2018 00:07:23 +0100 Subject: [PATCH 4/4] api: manage verified attributes (fixes #28962) --- src/authentic2/api_views.py | 28 +++++++++++++- src/authentic2/custom_user/models.py | 20 ++++++++++ tests/test_api.py | 55 ++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py index d8f5e4aa..54e3a626 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -363,6 +363,8 @@ class BaseUserSerializer(serializers.ModelSerializer): kwargs): kwargs['allow_blank'] = True self.fields[at.name] = field_class(**kwargs) + self.fields[at.name + '_verified'] = serializers.BooleanField( + source='is_verified.%s' % at.name, required=False) for key in self.fields: if key in app_settings.A2_REQUIRED_FIELDS: self.fields[key].required = True @@ -383,10 +385,18 @@ class BaseUserSerializer(serializers.ModelSerializer): force_password_reset = validated_data.pop('force_password_reset', False) attributes = validated_data.pop('attributes', {}) + is_verified = validated_data.pop('is_verified', {}) self.check_perm('custom_user.add_user', validated_data.get('ou')) instance = super(BaseUserSerializer, self).create(validated_data) for key, value in attributes.iteritems(): - setattr(instance.attributes, key, value) + if is_verified.get(key): + setattr(instance.verified_attributes, key, value) + else: + setattr(instance.attributes, key, value) + if is_verified.get('first_name'): + instance.verified_attributes.first_name = instance.first_name + if is_verified.get('last_name'): + instance.verified_attributes.last_name = instance.last_name if 'password' in validated_data: instance.set_password(validated_data['password']) instance.save() @@ -414,13 +424,27 @@ class BaseUserSerializer(serializers.ModelSerializer): validated_data.pop('send_registration_email', False) validated_data.pop('send_registration_email_next_url', None) attributes = validated_data.pop('attributes', {}) + is_verified = validated_data.pop('is_verified', {}) # Double check: to move an user from one ou into another you must be administrator of both self.check_perm('custom_user.change_user', instance.ou) if 'ou' in validated_data: self.check_perm('custom_user.change_user', validated_data.get('ou')) super(BaseUserSerializer, self).update(instance, validated_data) for key, value in attributes.iteritems(): - setattr(instance.attributes, key, value) + if is_verified.get(key): + setattr(instance.verified_attributes, key, value) + else: + setattr(instance.attributes, key, value) + for key in is_verified: + if key not in attributes: + if is_verified.get(key): + setattr(instance.verified_attributes, key, getattr(instance.attributes, key)) + else: + setattr(instance.attributes, key, getattr(instance.attributes, key)) + if is_verified.get('first_name'): + instance.verified_attributes.first_name = instance.first_name + if is_verified.get('last_name'): + instance.verified_attributes.last_name = instance.last_name if 'password' in validated_data: instance.set_password(validated_data['password']) instance.save() diff --git a/src/authentic2/custom_user/models.py b/src/authentic2/custom_user/models.py index 6ac08702..5c006c3a 100644 --- a/src/authentic2/custom_user/models.py +++ b/src/authentic2/custom_user/models.py @@ -36,6 +36,8 @@ class Attributes(object): values = {} super(Attributes, self).__setattr__('values', values) for atv in self.owner.attribute_values.all(): + if verified and not atv.verified: + continue try: attribute = at_map[atv.attribute_id] except KeyError: @@ -73,6 +75,23 @@ class AttributesDescriptor(object): return getattr(obj, cache_name) +class IsVerified(object): + def __init__(self, user): + self.user = user + + def __getattr__(self, name): + v = getattr(self.user.attributes, name, None) + return ( + v is not None and + v == getattr(self.user.verified_attributes, name, None) + ) + + +class IsVerifiedDescriptor(object): + def __get__(self, obj, objtype): + return IsVerified(obj) + + class User(AbstractBaseUser, PermissionMixin): """ An abstract base class implementing a fully featured User model with @@ -111,6 +130,7 @@ class User(AbstractBaseUser, PermissionMixin): objects = UserManager.from_queryset(UserQuerySet)() attributes = AttributesDescriptor() verified_attributes = AttributesDescriptor(verified=True) + is_verified = IsVerifiedDescriptor() attribute_values = GenericRelation('authentic2.AttributeValue') diff --git a/tests/test_api.py b/tests/test_api.py index 5a1742ce..527bbad8 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -249,9 +249,11 @@ def test_api_users_create(settings, app, api_user): resp = app.post_json('/api/users/', params=payload, status=status) 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()) + assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser', + 'first_name', 'first_name_verified', 'last_name', + 'last_name_verified', 'date_joined', 'last_login', + 'username', 'password', 'email', 'is_active', 'title', + 'title_verified', 'modified', 'email_verified']) == 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'] @@ -260,6 +262,9 @@ def test_api_users_create(settings, app, api_user): assert resp.json['uuid'] assert resp.json['id'] assert resp.json['date_joined'] + assert not resp.json['first_name_verified'] + assert not resp.json['last_name_verified'] + assert not resp.json['title_verified'] if api_user.is_superuser: assert resp.json['ou'] == 'default' elif api_user.roles.exists(): @@ -271,6 +276,7 @@ def test_api_users_create(settings, app, api_user): assert new_user.first_name == resp.json['first_name'] assert new_user.last_name == resp.json['last_name'] assert AttributeValue.objects.with_owner(new_user).count() == 3 + assert AttributeValue.objects.with_owner(new_user).filter(verified=True).count() == 0 assert AttributeValue.objects.with_owner(new_user).filter(attribute=at).exists() assert (AttributeValue.objects.with_owner(new_user).get(attribute=at).content == payload['title']) @@ -286,6 +292,49 @@ def test_api_users_create(settings, app, api_user): resp = app.get('/api/users/1234567890/') assert 'title' not in resp.json + at.disabled = False + at.save() + payload = { + 'username': 'john.doe2', + 'first_name': 'John', + 'first_name_verified': True, + 'last_name': 'Doe', + 'last_name_verified': True, + 'email': 'john.doe@example.net', + 'password': 'password', + 'title': 'Mr', + 'title_verified': True, + } + if api_user.is_superuser: + status = 201 + elif api_user.roles.exists(): + status = 201 + payload['ou'] = api_user.ou.slug + else: + status = 403 + + resp = app.post_json('/api/users/', params=payload, status=status) + if api_user.is_superuser or api_user.roles.exists(): + assert set(['ou', 'id', 'uuid', 'is_staff', 'is_superuser', + 'first_name', 'first_name_verified', 'last_name', + 'last_name_verified', 'date_joined', 'last_login', + 'username', 'password', 'email', 'is_active', 'title', + 'title_verified', 'modified', 'email_verified']) == set(resp.json.keys()) + user = get_user_model().objects.get(pk=resp.json['id']) + assert AttributeValue.objects.with_owner(user).filter(verified=True).count() == 3 + assert AttributeValue.objects.with_owner(user).filter(verified=False).count() == 0 + assert user.verified_attributes.first_name == 'John' + assert user.verified_attributes.last_name == 'Doe' + assert user.verified_attributes.title == 'Mr' + assert resp.json['first_name_verified'] + assert resp.json['last_name_verified'] + assert resp.json['title_verified'] + resp2 = app.patch_json('/api/users/%s/' % resp.json['uuid'], + params={'title_verified': False}) + assert resp.json['first_name_verified'] + assert resp.json['last_name_verified'] + assert not resp2.json['title_verified'] + def test_api_users_create_email_is_unique(settings, app, superuser): from django.contrib.auth import get_user_model -- 2.18.0