Skip to content

Commit

Permalink
Allow Registration with no password (#369)
Browse files Browse the repository at this point in the history
* Allow Registration with no password

[`django-allauth` account adapters allow creating a user with no password](https://github.com/pennersr/django-allauth/blob/ced1ddc730c36eca3551406c60e1577e30e01cbd/allauth/account/adapter.py#L241). 

#277 broke using this functionality. This PR re-adds it by only running password validation if a password is present in cleaned data

* refactor: change style to match that in allauth

* test: add tests

* test: fix tests

* test: add extra testing to account for coveralls complaints
  • Loading branch information
Julian Early authored Feb 6, 2022
1 parent fa5f33d commit 6701beb
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 6 deletions.
11 changes: 6 additions & 5 deletions dj_rest_auth/registration/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,12 @@ def save(self, request):
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
user = adapter.save_user(request, user, self, commit=False)
try:
adapter.clean_password(self.cleaned_data['password1'], user=user)
except DjangoValidationError as exc:
raise serializers.ValidationError(
detail=serializers.as_serializer_error(exc)
if "password1" in self.cleaned_data:
try:
adapter.clean_password(self.cleaned_data['password1'], user=user)
except DjangoValidationError as exc:
raise serializers.ValidationError(
detail=serializers.as_serializer_error(exc)
)
user.save()
self.custom_signup(request, user)
Expand Down
1 change: 1 addition & 0 deletions dj_rest_auth/tests/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def init(self):
self.logout_url = reverse('rest_logout')
self.password_change_url = reverse('rest_password_change')
self.register_url = reverse('rest_register')
self.no_password_register_url = reverse('no_password_rest_register')
self.password_reset_url = reverse('rest_password_reset')
self.user_url = reverse('rest_user_details')
self.verify_email_url = reverse('rest_verify_email')
Expand Down
32 changes: 32 additions & 0 deletions dj_rest_auth/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,38 @@ class CustomRegisterView(RegisterView):
self.assertEqual(response.data['detail'], CustomPermissionClass.message)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_registration_allowed_with_custom_no_password_serializer(self):
payload = {
"username": "test_username",
"email": "test@email.com",
}
user_count = get_user_model().objects.all().count()

# test empty payload
self.post(self.no_password_register_url, data={}, status_code=400)

result = self.post(self.no_password_register_url, data=payload, status_code=201)
self.assertIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)

new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, payload['username'])
self.assertFalse(new_user.has_usable_password())

## Also check that regular registration also works
user_count = get_user_model().objects.all().count()

# test empty payload
self.post(self.register_url, data={}, status_code=400)

result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)

new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])


@override_settings(REST_USE_JWT=True)
def test_registration_with_jwt(self):
user_count = get_user_model().objects.all().count()
Expand Down
24 changes: 23 additions & 1 deletion dj_rest_auth/tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.serializers import CharField
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenVerifyView

from dj_rest_auth.jwt_auth import get_refresh_view
from dj_rest_auth.registration.views import (
SocialAccountDisconnectView, SocialAccountListView, SocialConnectView,
SocialLoginView,
SocialLoginView, RegisterView
)
from dj_rest_auth.registration.serializers import RegisterSerializer
from dj_rest_auth.social_serializers import (
TwitterConnectSerializer, TwitterLoginSerializer,
)
Expand Down Expand Up @@ -54,6 +56,25 @@ class TwitterLoginSerializerFoo(TwitterLoginSerializer):
pass


class NoPassowrdRegisterSerializer(RegisterSerializer):
password1 = CharField(write_only=True, default=None)
password2 = CharField(write_only=True, default=None)

def get_cleaned_data(self):
return {
"username": self.validated_data.get("username", ""),
"email": self.validated_data.get("email", ""),
}

def validate_password1(self, password):
if password:
return super().validate_password1(password)
return None

class NoPasswordRegisterView(RegisterView):
serializer_class = NoPassowrdRegisterSerializer


@api_view(['POST'])
def twitter_login_view(request):
serializer = TwitterLoginSerializerFoo(
Expand All @@ -75,6 +96,7 @@ def get_csrf_cookie(request):

urlpatterns += [
re_path(r'^rest-registration/', include('dj_rest_auth.registration.urls')),
re_path(r'^rest-registration-no-password/', NoPasswordRegisterView.as_view(), name="no_password_rest_register"),
re_path(r'^test-admin/', include(django_urls)),
re_path(
r'^account-email-verification-sent/$', TemplateView.as_view(),
Expand Down

0 comments on commit 6701beb

Please sign in to comment.