diff --git a/requirements.lock b/requirements.lock index e819026d0..a45571e47 100644 --- a/requirements.lock +++ b/requirements.lock @@ -1,6 +1,18 @@ +certifi==2018.8.24 +chardet==3.0.4 +defusedxml==0.5.0 Django==2.1.1 -django-countries==5.3.2 +django-countries==5.3.2 +django-allauth==0.37.1 +django-rest-auth==0.9.3 djangorestframework==3.8.2 +idna==2.7 +oauthlib==2.1.0 psycopg2==2.7.5 +python3-openid==3.1.0 pytz==2018.5 -PyYAML==3.13 +PyYAML==3.13 +requests==2.19.1 +requests-oauthlib==1.0.0 +six==1.11.0 +urllib3==1.23 diff --git a/requirements.txt b/requirements.txt index 5e35675ea..2ba518b26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,18 @@ -Django>=2.1,<2.2 +certifi +chardet +defusedxml +Django => 2.1, < 2.2 django-countries +django-allauth +django-rest-auth djangorestframework +idna +oauthlib psycopg2 +python3-openid pytz PyYAML +requests +requests-oauthlib +six +urllib3 diff --git a/time_monkey/settings/base.py b/time_monkey/settings/base.py index bfc57ff3e..b6043da15 100644 --- a/time_monkey/settings/base.py +++ b/time_monkey/settings/base.py @@ -42,6 +42,13 @@ 'django_countries', 'users', 'rest_framework', + 'rest_framework.authtoken', + 'rest_auth', + 'django.contrib.sites', + 'allauth', + 'allauth.account', + 'rest_auth.registration', + 'allauth.socialaccount', 'managers.apps.ManagersConfig', ] @@ -131,6 +138,26 @@ AUTH_USER_MODEL = 'users.CustomUser' + +ACCOUNT_USERNAME_REQUIRED = False + +ACCOUNT_USER_MODEL_USERNAME_FIELD = None +ACCOUNT_AUTHENTICATION_METHOD = 'email' + +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_UNIQUE_EMAIL = True +ACCOUNT_USERNAME_REQUIRED = False +ACCOUNT_USER_EMAIL_FIELD = 'email' +ACCOUNT_LOGOUT_ON_GET = True + +REST_AUTH_REGISTER_SERIALIZERS = { + "REGISTER_SERIALIZER": "users.serializers.CustomRegisterSerializer", +} + + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +SITE_ID = 1 FIXTURE_DIRS = [ 'users/fixtures/', ] diff --git a/time_monkey/urls.py b/time_monkey/urls.py index 5ecf79839..a1d2d15c7 100644 --- a/time_monkey/urls.py +++ b/time_monkey/urls.py @@ -12,11 +12,13 @@ """ from django.conf.urls import url from django.contrib import admin -from django.conf.urls import include +from django.urls import include +from django.urls import path urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^', include('users.urls')), - url(r'^api-auth/', include('rest_framework.urls')), url(r'^managers/', include('managers.urls')), + path('rest-auth/', include('rest_framework.urls')), + path('rest-auth/registration/', include('rest_auth.registration.urls')), ] diff --git a/users/common/strings.py b/users/common/strings.py index c57d1f145..69f084be8 100644 --- a/users/common/strings.py +++ b/users/common/strings.py @@ -28,6 +28,8 @@ class CustomUserModelText: class CustomValidationErrorText: VALIDATION_ERROR_EMAIL_MESSAGE = 'The given email must be set' VALIDATION_ERROR_PASSWORD_MESSAGE = 'The given password must be set' + VALIDATION_ERROR_SIGNUP_EMAIL_MESSAGE = ugettext_lazy("A user is already registered with this e-mail address.") + VALIDATION_ERROR_SIGNUP_PASSWORD_MESSAGE = ugettext_lazy("The two password fields didn't match.") class CustomUserCountryText: diff --git a/users/serializers.py b/users/serializers.py index fc31e0473..a5d7f76f9 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -1,10 +1,15 @@ +from allauth.account import app_settings as allauth_settings +from allauth.account.adapter import get_adapter as allauth_get_adapter +from allauth.account.utils import setup_user_email +from allauth.utils import email_address_exists + from django_countries.serializers import CountryFieldMixin -from django_countries.serializer_fields import CountryField + from rest_framework import serializers +from users.common.strings import CustomValidationErrorText from users.models import CustomUser - class UserSerializer(CountryFieldMixin, serializers.ModelSerializer): class Meta: model = CustomUser @@ -66,3 +71,45 @@ class Meta: 'is_superuser', 'is_active', ) + + +class CustomRegisterSerializer(serializers.Serializer): + email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED) + first_name = serializers.CharField(required=False, write_only=True) + last_name = serializers.CharField(required=False, write_only=True) + password = serializers.CharField(required=True, write_only=True) + password_confirmation = serializers.CharField(required=True, write_only=True) + + def validate_email(self, email): + email = get_adapter().clean_email(email) + if allauth_settings.UNIQUE_EMAIL: + if email and email_address_exists(email): + raise serializers.ValidationError( + CustomValidationErrorText.VALIDATION_ERROR_SIGNUP_EMAIL_MESSAGE) + return email + + def validate_password(self, password): + return get_adapter().clean_password(password) + + def validate(self, data): + if data['password'] != data['password_confirmation']: + raise serializers.ValidationError( + CustomValidationErrorText.VALIDATION_ERROR_SIGNUP_PASSWORD_MESSAGE) + return data + + def get_cleaned_data(self): + return { + 'first_name': self.validated_data.get('first_name', ''), + 'last_name': self.validated_data.get('last_name', ''), + 'password': self.validated_data.get('password', ''), + 'email': self.validated_data.get('email', ''), + } + + def save(self, request): + adapter = get_adapter() + user = adapter.new_user(request) + self.cleaned_data = self.get_cleaned_data() + adapter.save_user(request, user, self) + setup_user_email(request, user, []) + user.save() + return user diff --git a/users/views.py b/users/views.py index 9df0e44ac..3b673b7cc 100644 --- a/users/views.py +++ b/users/views.py @@ -42,6 +42,11 @@ def api_root(request, format=None): }) else: return Response({ + 'registration': reverse( + 'rest_register', + request=request, + format=format, + ), })