mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2024-11-22 17:16:34 +03:00
commit
86eb741b34
|
@ -16,7 +16,7 @@ Basic
|
||||||
|
|
||||||
- email
|
- email
|
||||||
|
|
||||||
- /rest-auth/password/reset/confim/ (POST)
|
- /rest-auth/password/reset/confirm/ (POST)
|
||||||
|
|
||||||
- uid
|
- uid
|
||||||
- token
|
- token
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
Demo project
|
Demo project
|
||||||
============
|
============
|
||||||
|
|
||||||
To run demo project (ideally in virtualenv):
|
The idea of creating demo project was to show how you can potentially use
|
||||||
|
django-rest-auth app with jQuery on frontend.
|
||||||
|
Do these steps to make it running (ideally in virtualenv).
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
17
docs/faq.rst
Normal file
17
docs/faq.rst
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
FAQ
|
||||||
|
===
|
||||||
|
|
||||||
|
1. Why account_confirm_email url is defined but it is not usable?
|
||||||
|
|
||||||
|
In /rest_auth/registration/urls.py we can find something like this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
|
||||||
|
name='account_confirm_email'),
|
||||||
|
|
||||||
|
This url is used by django-allauth. Empty TemplateView is defined just to allow reverse() call inside app - when email with verification link is being sent.
|
||||||
|
|
||||||
|
You should override this view/url to handle it in your API client somehow and then, send post to /verify-email/ endpoint with proper key.
|
||||||
|
If you don't want to use API on that step, then just use ConfirmEmailView view from:
|
||||||
|
djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
|
|
@ -22,6 +22,7 @@ Contents
|
||||||
API endpoints <api_endpoints>
|
API endpoints <api_endpoints>
|
||||||
Configuration <configuration>
|
Configuration <configuration>
|
||||||
Demo project <demo>
|
Demo project <demo>
|
||||||
|
FAQ <faq>
|
||||||
Changelog <changelog>
|
Changelog <changelog>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,21 @@ Features
|
||||||
* Password reset via e-mail
|
* Password reset via e-mail
|
||||||
* Social Media authentication
|
* Social Media authentication
|
||||||
|
|
||||||
|
|
||||||
Apps structure
|
Apps structure
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
* ``rest_auth`` has basic auth functionality like login, logout, password reset and password change
|
* ``rest_auth`` has basic auth functionality like login, logout, password reset and password change
|
||||||
* ``rest_auth.registration`` has logic related with registration and social media authentication
|
* ``rest_auth.registration`` has logic related with registration and social media authentication
|
||||||
|
|
||||||
|
|
||||||
|
Angular app
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- Tivix has also created angular module which uses API endpoints from this app - `angular-django-registration-auth <https://github.com/Tivix/angular-django-registration-auth>`_
|
||||||
|
|
||||||
|
|
||||||
|
Demo project
|
||||||
|
------------
|
||||||
|
|
||||||
|
- You can also check our :doc:`Demo Project </demo>` which is using jQuery on frontend.
|
||||||
|
|
|
@ -37,5 +37,3 @@ PasswordChangeSerializer = import_callable(
|
||||||
serializers.get('PASSWORD_CHANGE_SERIALIZER',
|
serializers.get('PASSWORD_CHANGE_SERIALIZER',
|
||||||
DefaultPasswordChangeSerializer)
|
DefaultPasswordChangeSerializer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ urlpatterns = patterns('',
|
||||||
url(r'^$', Register.as_view(), name='rest_register'),
|
url(r'^$', Register.as_view(), name='rest_register'),
|
||||||
url(r'^verify-email/$', VerifyEmail.as_view(), name='rest_verify_email'),
|
url(r'^verify-email/$', VerifyEmail.as_view(), name='rest_verify_email'),
|
||||||
|
|
||||||
# These two views are used in django-allauth and empty TemplateView were
|
# This url is used by django-allauth and empty TemplateView is
|
||||||
# defined just to allow reverse() call inside app, for example when email
|
# defined just to allow reverse() call inside app, for example when email
|
||||||
# with verification link is being sent, then it's required to render email
|
# with verification link is being sent, then it's required to render email
|
||||||
# content.
|
# content.
|
||||||
|
@ -18,8 +18,6 @@ urlpatterns = patterns('',
|
||||||
# If you don't want to use API on that step, then just use ConfirmEmailView
|
# If you don't want to use API on that step, then just use ConfirmEmailView
|
||||||
# view from:
|
# view from:
|
||||||
# djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
|
# djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
|
||||||
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
|
|
||||||
name='account_email_verification_sent'),
|
|
||||||
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
|
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
|
||||||
name='account_confirm_email'),
|
name='account_confirm_email'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,6 +16,13 @@ class Register(APIView, SignupView):
|
||||||
|
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
user_serializer_class = UserDetailsSerializer
|
user_serializer_class = UserDetailsSerializer
|
||||||
|
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
|
||||||
|
def put(self, *args, **kwargs):
|
||||||
|
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.user = form.save(self.request)
|
self.user = form.save(self.request)
|
||||||
|
@ -45,6 +52,10 @@ class Register(APIView, SignupView):
|
||||||
class VerifyEmail(APIView, ConfirmEmailView):
|
class VerifyEmail(APIView, ConfirmEmailView):
|
||||||
|
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.kwargs['key'] = self.request.DATA.get('key', '')
|
self.kwargs['key'] = self.request.DATA.get('key', '')
|
||||||
|
|
|
@ -121,14 +121,31 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
|
||||||
|
|
||||||
class PasswordChangeSerializer(serializers.Serializer):
|
class PasswordChangeSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
old_password = serializers.CharField(max_length=128)
|
||||||
new_password1 = serializers.CharField(max_length=128)
|
new_password1 = serializers.CharField(max_length=128)
|
||||||
new_password2 = serializers.CharField(max_length=128)
|
new_password2 = serializers.CharField(max_length=128)
|
||||||
|
|
||||||
set_password_form_class = SetPasswordForm
|
set_password_form_class = SetPasswordForm
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.old_password_field_enabled = getattr(settings,
|
||||||
|
'OLD_PASSWORD_FIELD_ENABLED', False)
|
||||||
|
super(PasswordChangeSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if not self.old_password_field_enabled:
|
||||||
|
self.fields.pop('old_password')
|
||||||
|
|
||||||
|
self.request = self.context.get('request')
|
||||||
|
self.user = self.request.user
|
||||||
|
|
||||||
|
def validate_old_password(self, attrs, source):
|
||||||
|
if self.old_password_field_enabled and \
|
||||||
|
not self.user.check_password(attrs.get(source, '')):
|
||||||
|
raise serializers.ValidationError('Invalid password')
|
||||||
|
return attrs
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
request = self.context.get('request')
|
self.set_password_form = self.set_password_form_class(user=self.user,
|
||||||
self.set_password_form = self.set_password_form_class(user=request.user,
|
|
||||||
data=attrs)
|
data=attrs)
|
||||||
|
|
||||||
if not self.set_password_form.is_valid():
|
if not self.set_password_form.is_valid():
|
||||||
|
|
|
@ -248,6 +248,40 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# send empty payload
|
# send empty payload
|
||||||
self.post(self.password_change_url, data={}, status_code=400)
|
self.post(self.password_change_url, data={}, status_code=400)
|
||||||
|
|
||||||
|
@override_settings(OLD_PASSWORD_FIELD_ENABLED=True)
|
||||||
|
def test_password_change_with_old_password(self):
|
||||||
|
login_payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
User.objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
|
self.token = self.response.json['key']
|
||||||
|
|
||||||
|
new_password_payload = {
|
||||||
|
"old_password": "%s!" % self.PASS, # wrong password
|
||||||
|
"new_password1": "new_person",
|
||||||
|
"new_password2": "new_person"
|
||||||
|
}
|
||||||
|
self.post(self.password_change_url, data=new_password_payload,
|
||||||
|
status_code=400)
|
||||||
|
|
||||||
|
new_password_payload = {
|
||||||
|
"old_password": self.PASS,
|
||||||
|
"new_password1": "new_person",
|
||||||
|
"new_password2": "new_person"
|
||||||
|
}
|
||||||
|
self.post(self.password_change_url, data=new_password_payload,
|
||||||
|
status_code=200)
|
||||||
|
|
||||||
|
# user should not be able to login using old password
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=400)
|
||||||
|
|
||||||
|
# new password should work
|
||||||
|
login_payload['password'] = new_password_payload['new_password1']
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
|
|
||||||
|
|
||||||
def test_password_reset(self):
|
def test_password_reset(self):
|
||||||
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@ from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.generics import GenericAPIView
|
from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
from rest_framework.authentication import SessionAuthentication, \
|
|
||||||
TokenAuthentication
|
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
|
@ -32,10 +30,6 @@ class Login(GenericAPIView):
|
||||||
token_model = Token
|
token_model = Token
|
||||||
response_serializer = TokenSerializer
|
response_serializer = TokenSerializer
|
||||||
|
|
||||||
def get_serializer(self):
|
|
||||||
return self.serializer_class(data=self.request.DATA,
|
|
||||||
context={'request': self.request, 'view': self})
|
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
self.user = self.serializer.object['user']
|
self.user = self.serializer.object['user']
|
||||||
self.token, created = self.token_model.objects.get_or_create(
|
self.token, created = self.token_model.objects.get_or_create(
|
||||||
|
@ -52,7 +46,7 @@ class Login(GenericAPIView):
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.serializer = self.get_serializer()
|
self.serializer = self.get_serializer(data=self.request.DATA)
|
||||||
if not self.serializer.is_valid():
|
if not self.serializer.is_valid():
|
||||||
return self.get_error_response()
|
return self.get_error_response()
|
||||||
self.login()
|
self.login()
|
||||||
|
@ -67,7 +61,7 @@ class Logout(APIView):
|
||||||
|
|
||||||
Accepts/Returns nothing.
|
Accepts/Returns nothing.
|
||||||
"""
|
"""
|
||||||
permissions_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user