From 99aa9252e8bb8479b23e14d4e6d7db84995dbf36 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 2 Oct 2015 09:36:25 +0200 Subject: [PATCH] api_views: finish implementing the registration API with no mail validation (fixes #8480) You have to specify at least username, email or first name and last name. --- src/authentic2/api_views.py | 137 +++++++++++++++++++++++++-------------- src/authentic2/tests/test_all.py | 60 ++++++++++++++--- 2 files changed, 139 insertions(+), 58 deletions(-) diff --git a/src/authentic2/api_views.py b/src/authentic2/api_views.py index b404837..a1b4f59 100644 --- a/src/authentic2/api_views.py +++ b/src/authentic2/api_views.py @@ -16,6 +16,7 @@ from rest_framework.response import Response from rest_framework import authentication, permissions, status from rest_framework.exceptions import PermissionDenied +from .custom_user.models import User from . import utils, decorators @@ -28,7 +29,8 @@ class HasUserAddPermission(permissions.BasePermission): class RegistrationSerializer(serializers.Serializer): '''Register RPC payload''' - email = serializers.EmailField() + email = serializers.EmailField( + required=False, allow_blank=True) ou = serializers.SlugRelatedField( queryset=get_ou_model().objects.all(), slug_field='slug', @@ -43,13 +45,14 @@ class RegistrationSerializer(serializers.Serializer): required=False, allow_null=True) no_email_validation = serializers.BooleanField( required=False) - return_url = serializers.URLField() + return_url = serializers.URLField(required=False, allow_blank=True) def validate(self, data): request = self.context.get('request') + ou = data.get('ou') if request: perm = 'custom_user.add_user' - if data['ou']: + if ou: authorized = request.user.has_ou_perm(perm, data['ou']) else: authorized = request.user.has_perm(perm) @@ -58,19 +61,20 @@ class RegistrationSerializer(serializers.Serializer): 'to create users in ' 'this ou')) User = get_user_model() - if data['ou'] and data['ou'].email_is_unique and \ - User.objects.filter(ou=data['ou'], - email__iexact=data['email']).exists(): - raise serializers.ValidationError( - _('You already have an account')) - if data['ou'] and data['ou'].username_is_unique and not \ - 'username' in data: - raise serializers.ValidationError( - _('Username is required in this ou')) - if data['ou'] and data['ou'].username_is_unique and \ - User.objects.filter(ou=data['ou'], - username=data['username']).exists(): - raise serializers.ValidationError( + if ou: + if ou.email_is_unique and \ + User.objects.filter(ou=ou, + email__iexact=data['email']).exists(): + raise serializers.ValidationError( + _('You already have an account')) + if ou.username_is_unique and not \ + 'username' in data: + raise serializers.ValidationError( + _('Username is required in this ou')) + if ou.username_is_unique and \ + User.objects.filter(ou=data['ou'], + username=data['username']).exists(): + raise serializers.ValidationError( _('You already have an account')) return data @@ -109,16 +113,7 @@ class Register(BaseRpcView): def rpc(self, request, serializer): validated_data = serializer.validated_data - data = serializer.data - email = validated_data['email'] - token = utils.get_hex_uuid()[:16] - final_return_url = utils.make_url(validated_data['return_url'], - params={'token': token}) - - registration_template = ['authentic2/activation_email'] - if validated_data['ou']: - registration_template.insert(0, 'authentic2/activation_email_%s' % - validated_data['ou'].slug) + email = validated_data.get('email') registration_data = {} for field in ('first_name', 'last_name', 'password', 'username', 'ou'): if field in validated_data: @@ -126,32 +121,78 @@ class Register(BaseRpcView): registration_data[field] = validated_data[field].pk else: registration_data[field] = validated_data[field] - ctx = { 'registration_data': registration_data, } - try: - utils.send_registration_mail(self.request, email, - registration_template, - next_url=final_return_url, - ctx=ctx, - **registration_data) - except smtplib.SMTPException, e: - response = { - 'result': 0, - 'errors': { - '__all__': ['Mail sending failed'] - }, - 'exception': unicode(e), - } - response_status = status.HTTP_503_SERVICE_UNAVAILABLE + + if email and not validated_data.get('no_email_validation'): + token = utils.get_hex_uuid()[:16] + if not validated_data.get('return_url'): + response = { + 'result': 0, + 'errors': { + 'return_url': ['Required'] + }, + 'exception': unicode(e), + } + response_status = status.HTTP_400_BAD_REQUEST + return response, response_status + final_return_url = utils.make_url(validated_data['return_url'], + params={'token': token}) + + registration_template = ['authentic2/activation_email'] + if validated_data['ou']: + registration_template.insert(0, 'authentic2/activation_email_%s' % + validated_data['ou'].slug) + + try: + utils.send_registration_mail(self.request, email, + registration_template, + next_url=final_return_url, + ctx=ctx, + **registration_data) + except smtplib.SMTPException, e: + response = { + 'result': 0, + 'errors': { + '__all__': ['Mail sending failed'] + }, + 'exception': unicode(e), + } + response_status = status.HTTP_503_SERVICE_UNAVAILABLE + else: + response = { + 'result': 1, + 'token': token, + } + response_status = status.HTTP_202_ACCEPTED else: - response = { - 'result': 1, - 'token': token, - 'request': data, - } - response_status = status.HTTP_202_ACCEPTED + username = validated_data.get('username') + first_name = validated_data.get('first_name') + last_name = validated_data.get('last_name') + password = validated_data.get('password') + if not email and \ + not username and \ + not (first_name and last_name): + response = { + 'result': 0, + 'errors': { + '__all__': ['You must set at least a username, an email or a first name and a last name'] + }, + } + response_status = status.HTTP_400_BAD_REQUEST + else: + new_user = User(email=email, username=username, + first_name=first_name, last_name=last_name) + if password: + new_user.set_password(password) + new_user.save() + validated_data['uuid'] = new_user.uuid + response = { + 'result': 1, + 'user': validated_data, + } + response_status = status.HTTP_201_CREATED return response, response_status register = Register.as_view() diff --git a/src/authentic2/tests/test_all.py b/src/authentic2/tests/test_all.py index a821032..e7eafcc 100644 --- a/src/authentic2/tests/test_all.py +++ b/src/authentic2/tests/test_all.py @@ -23,6 +23,7 @@ from rest_framework import status from django_rbac.utils import get_role_model, get_ou_model from authentic2 import hashers, utils, models, attribute_kinds +from authentic2.custom_user.models import User from . import Authentic2TestCase, get_response_form @@ -786,8 +787,6 @@ class APITest(TestCase): self.assertEqual(response.data['result'], 1) self.assertIn('token', response.data) token = response.data['token'] - self.assertIn('request', response.data) - self.assertEqual(response.data['request'], payload) self.assertEqual(len(mail.outbox), outbox_level+1) # User side @@ -902,8 +901,6 @@ class APITest(TestCase): self.assertEqual(response.data['result'], 1) self.assertIn('token', response.data) token = response.data['token'] - self.assertIn('request', response.data) - self.assertEqual(response.data['request'], payload) self.assertEqual(len(mail.outbox), outbox_level+1) outbox_level = len(mail.outbox) @@ -916,8 +913,6 @@ class APITest(TestCase): self.assertEqual(response2.data['result'], 1) self.assertIn('token', response2.data) token2 = response2.data['token'] - self.assertIn('request', response2.data) - self.assertEqual(response2.data['request'], payload) self.assertEqual(len(mail.outbox), outbox_level+1) # User side - user click on first email @@ -1033,8 +1028,6 @@ class APITest(TestCase): self.assertEqual(response.data['result'], 1) self.assertIn('token', response.data) token = response.data['token'] - self.assertIn('request', response.data) - self.assertEqual(response.data['request'], payload) self.assertEqual(len(mail.outbox), outbox_level+1) outbox_level = len(mail.outbox) @@ -1048,8 +1041,6 @@ class APITest(TestCase): self.assertEqual(response2.data['result'], 1) self.assertIn('token', response2.data) token2 = response2.data['token'] - self.assertIn('request', response2.data) - self.assertEqual(response2.data['request'], payload) self.assertEqual(len(mail.outbox), outbox_level+1) # User side - user click on first email @@ -1128,6 +1119,55 @@ class APITest(TestCase): self.assertTrue(User.objects.get(username='john.doe') .check_password('password2')) + def test_register_no_email_validation(self): + client = test.APIClient() + password = '12XYab' + username = 'john.doe' + email = 'john.doe@example.com' + first_name = 'John' + last_name = 'Doe' + return_url = 'http://sp.org/register/' + + # invalid payload + payload = { + 'last_name': last_name, + } + client.credentials(HTTP_AUTHORIZATION='Basic %s' % self.reguser3_cred) + response = client.post(reverse('a2-api-register'), + content_type='application/json', + data=json.dumps(payload)) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('errors', response.data) + self.assertEquals(response.data['result'], 0) + self.assertEquals(response.data['errors'], { + '__all__': ['You must set at least a username, an email or a first name and a last name'], + }) + payload = { + 'username': username, + 'email': email, + 'first_name': first_name, + 'last_name': last_name, + 'password': password, + 'no_email_validation': True, + } + response = client.post(reverse('a2-api-register'), + content_type='application/json', + data=json.dumps(payload)) + self.assertEquals(response.status_code, status.HTTP_201_CREATED) + self.assertEquals(response.data['result'], 1) + self.assertEquals(response.data['user']['username'], username) + self.assertEquals(response.data['user']['email'], email) + self.assertEquals(response.data['user']['first_name'], first_name) + self.assertEquals(response.data['user']['last_name'], last_name) + self.assertEquals(response.data['user']['password'], password) + self.assertEquals(User.objects.count(), 4) + user = User.objects.latest('id') + self.assertEquals(user.username, username) + self.assertEquals(user.email, email) + self.assertEquals(user.first_name, first_name) + self.assertEquals(user.last_name, last_name) + self.assertTrue(user.check_password(password)) + class PasswordResetTest(Authentic2TestCase): def setUp(self): -- 2.1.4