mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2024-11-25 10:33:45 +03:00
Merge branch 'master' into jwt-support
# Conflicts: # docs/changelog.rst # rest_auth/registration/views.py
This commit is contained in:
commit
e3901516cb
|
@ -2,8 +2,9 @@ language: python
|
||||||
python:
|
python:
|
||||||
- "2.7"
|
- "2.7"
|
||||||
env:
|
env:
|
||||||
- DJANGO=1.7.7
|
- DJANGO=1.7.11
|
||||||
- DJANGO=1.8
|
- DJANGO=1.8.9
|
||||||
|
- DJANGO=1.9.2
|
||||||
install:
|
install:
|
||||||
- pip install -q Django==$DJANGO --use-mirrors
|
- pip install -q Django==$DJANGO --use-mirrors
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
|
|
|
@ -11,6 +11,8 @@ urlpatterns = [
|
||||||
name='email-verification'),
|
name='email-verification'),
|
||||||
url(r'^login/$', TemplateView.as_view(template_name="login.html"),
|
url(r'^login/$', TemplateView.as_view(template_name="login.html"),
|
||||||
name='login'),
|
name='login'),
|
||||||
|
url(r'^logout/$', TemplateView.as_view(template_name="logout.html"),
|
||||||
|
name='logout'),
|
||||||
url(r'^password-reset/$',
|
url(r'^password-reset/$',
|
||||||
TemplateView.as_view(template_name="password_reset.html"),
|
TemplateView.as_view(template_name="password_reset.html"),
|
||||||
name='password-reset'),
|
name='password-reset'),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
django>=1.7.0
|
django>=1.7.0
|
||||||
django-rest-auth==0.6.0
|
django-rest-auth==0.7.0
|
||||||
django-allauth==0.24.1
|
django-allauth==0.24.1
|
||||||
six==1.9.0
|
six==1.9.0
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<!-- these pages require user token -->
|
<!-- these pages require user token -->
|
||||||
<li><a href="{% url 'user-details' %}">User details</a></li>
|
<li><a href="{% url 'user-details' %}">User details</a></li>
|
||||||
|
<li><a href="{% url 'logout' %}">Logout</a></li>
|
||||||
<li><a href="{% url 'password-change' %}">Password change</a></li>
|
<li><a href="{% url 'password-change' %}">Password change</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
20
demo/templates/fragments/logout_form.html
Normal file
20
demo/templates/fragments/logout_form.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_logout' %}">{% csrf_token %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="token" class="col-sm-2 control-label">User Token</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
|
||||||
|
<p class="help-block">Token received after login</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-default">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group api-response"></div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
8
demo/templates/logout.html
Normal file
8
demo/templates/logout.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<h3>Logout</h3><hr/>
|
||||||
|
{% include "fragments/logout_form.html" %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -11,7 +11,11 @@ Basic
|
||||||
- password (string)
|
- password (string)
|
||||||
|
|
||||||
|
|
||||||
- /rest-auth/logout/ (POST)
|
- /rest-auth/logout/ (POST, GET)
|
||||||
|
|
||||||
|
.. note:: ``ACCOUNT_LOGOUT_ON_GET = True`` to allow logout using GET (this is the exact same conf from allauth)
|
||||||
|
|
||||||
|
- token
|
||||||
|
|
||||||
- /rest-auth/password/reset/ (POST)
|
- /rest-auth/password/reset/ (POST)
|
||||||
|
|
||||||
|
@ -70,3 +74,8 @@ Basing on example from installation section :doc:`Installation </installation>`
|
||||||
|
|
||||||
- access_token
|
- access_token
|
||||||
- code
|
- code
|
||||||
|
|
||||||
|
- /rest-auth/twitter/ (POST)
|
||||||
|
|
||||||
|
- access_token
|
||||||
|
- token_secret
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
0.7.0
|
0.8.0
|
||||||
-----
|
-----
|
||||||
- added support for django-rest-framework-jwt
|
- added support for django-rest-framework-jwt
|
||||||
|
0.7.0
|
||||||
|
-----
|
||||||
|
- Wrapped API returned strings in ugettext_lazy
|
||||||
|
- Fixed not using ``get_username`` which caused issues when using custom user model without username field
|
||||||
|
- Django 1.9 support
|
||||||
|
- Added ``TwitterLoginSerializer``
|
||||||
|
|
||||||
0.6.0
|
0.6.0
|
||||||
-----
|
-----
|
||||||
|
@ -61,4 +67,4 @@ Changelog
|
||||||
- changed password reset confim url - uid and token should be sent in POST
|
- changed password reset confim url - uid and token should be sent in POST
|
||||||
- increase test coverage
|
- increase test coverage
|
||||||
- made compatibile with django 1.7
|
- made compatibile with django 1.7
|
||||||
- removed user profile support
|
- removed user profile support
|
|
@ -31,7 +31,7 @@ Configuration
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
- **REST_AUTH_REGISTRATION_SERIALIZERS**
|
- **REST_AUTH_REGISTER_SERIALIZERS**
|
||||||
|
|
||||||
You can define your custom serializers for registration endpoint.
|
You can define your custom serializers for registration endpoint.
|
||||||
Possible key values:
|
Possible key values:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
Welcome to django-rest-auth's documentation!
|
Welcome to django-rest-auth's documentation!
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
.. warning:: Updating django-rest-auth to version **0.3.4** is highly recommended because of a security issue in PasswordResetConfirmation validation method.
|
.. warning:: Updating django-rest-auth from version **0.3.3** is highly recommended because of a security issue in PasswordResetConfirmation validation method.
|
||||||
|
|
||||||
.. note:: django-rest-auth from v0.3.3 supports django-rest-framework v3.0
|
.. note:: django-rest-auth from v0.3.3 supports django-rest-framework v3.0
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,11 @@ Registration (optional)
|
||||||
Social Authentication (optional)
|
Social Authentication (optional)
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creating social media authentication view. Below is an example with Facebook authentication.
|
Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creating social media authentication view.
|
||||||
|
|
||||||
.. note:: Points 1, 2 and 3 are related with ``django-allauth`` configuration, so if you have already configured social authentication, then please go to step 4. See ``django-allauth`` documentation for more details.
|
.. note:: Points 1 and 2 are related to ``django-allauth`` configuration, so if you have already configured social authentication, then please go to step 3. See ``django-allauth`` documentation for more details.
|
||||||
|
|
||||||
1. Add ``allauth.socialaccount`` and ``allauth.socialaccount.providers.facebook`` apps to INSTALLED_APPS in your django settings.py:
|
1. Add ``allauth.socialaccount`` and ``allauth.socialaccount.providers.facebook`` or ``allauth.socialaccount.providers.twitter`` apps to INSTALLED_APPS in your django settings.py:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -85,10 +85,15 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati
|
||||||
...,
|
...,
|
||||||
'allauth.socialaccount',
|
'allauth.socialaccount',
|
||||||
'allauth.socialaccount.providers.facebook',
|
'allauth.socialaccount.providers.facebook',
|
||||||
|
'allauth.socialaccount.providers.twitter',
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
2. Add Social Application in django admin panel
|
2. Add Social Application in django admin panel
|
||||||
|
|
||||||
|
Facebook
|
||||||
|
########
|
||||||
|
|
||||||
3. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``FacebookOAuth2Adapter`` adapter as an attribute:
|
3. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``FacebookOAuth2Adapter`` adapter as an attribute:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -108,6 +113,32 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati
|
||||||
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
|
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Twitter
|
||||||
|
#######
|
||||||
|
|
||||||
|
If you are using Twitter for your social authentication, it is a bit different since Twitter uses OAuth 1.0.
|
||||||
|
|
||||||
|
3. Create new view as a subclass of ``rest_auth.views.LoginView`` with ``TwitterOAuthAdapter`` adapter and ``TwitterLoginSerializer`` as an attribute:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
|
||||||
|
from rest_auth.views import LoginView
|
||||||
|
from rest_auth.social_serializers import TwitterLoginSerializer
|
||||||
|
|
||||||
|
class TwitterLogin(LoginView):
|
||||||
|
serializer_class = TwitterLoginSerializer
|
||||||
|
adapter_class = TwitterOAuthAdapter
|
||||||
|
|
||||||
|
4. Create url for TwitterLogin view:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
urlpatterns += pattern('',
|
||||||
|
...,
|
||||||
|
url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login')
|
||||||
|
)
|
||||||
.. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details.
|
.. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
99
rest_auth/locale/de/LC_MESSAGES/django.po
Normal file
99
rest_auth/locale/de/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2016-02-02 14:11+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: registration/serializers.py:54
|
||||||
|
msgid "View is not defined, pass it as a context variable"
|
||||||
|
msgstr "\"View\" ist nicht definiert, übergib es als Contextvariable"
|
||||||
|
|
||||||
|
#: registration/serializers.py:59
|
||||||
|
msgid "Define adapter_class in view"
|
||||||
|
msgstr "Definier \"adapter_class\" in view"
|
||||||
|
|
||||||
|
#: registration/serializers.py:78
|
||||||
|
msgid "Define callback_url in view"
|
||||||
|
msgstr "Definier \"callback_url\" in view"
|
||||||
|
|
||||||
|
#: registration/serializers.py:82
|
||||||
|
msgid "Define client_class in view"
|
||||||
|
msgstr "Definier \"client_class\" in view"
|
||||||
|
|
||||||
|
#: registration/serializers.py:102
|
||||||
|
msgid "Incorrect input. access_token or code is required."
|
||||||
|
msgstr "Falsche Eingabe. \"access_token\" oder \"code\" erforderlich."
|
||||||
|
|
||||||
|
#: registration/serializers.py:111
|
||||||
|
msgid "Incorrect value"
|
||||||
|
msgstr "Falscher Wert."
|
||||||
|
|
||||||
|
#: registration/serializers.py:140
|
||||||
|
msgid "A user is already registered with this e-mail address."
|
||||||
|
msgstr "Ein User mit dieser E-Mail Adresse ist schon registriert."
|
||||||
|
|
||||||
|
#: registration/serializers.py:148
|
||||||
|
msgid "The two password fields didn't match."
|
||||||
|
msgstr "Die beiden Passwörter sind nicht identisch."
|
||||||
|
|
||||||
|
#: registration/views.py:64
|
||||||
|
msgid "ok"
|
||||||
|
msgstr "Ok"
|
||||||
|
|
||||||
|
#: serializers.py:29
|
||||||
|
msgid "Must include \"email\" and \"password\"."
|
||||||
|
msgstr "Muss \"email\" und \"password\" enthalten."
|
||||||
|
|
||||||
|
#: serializers.py:40
|
||||||
|
msgid "Must include \"username\" and \"password\"."
|
||||||
|
msgstr "Muss \"username\" und \"password\" enthalten."
|
||||||
|
|
||||||
|
#: serializers.py:53
|
||||||
|
msgid "Must include either \"username\" or \"email\" and \"password\"."
|
||||||
|
msgstr "Muss entweder \"username\" oder \"email\" und password \"password\""
|
||||||
|
|
||||||
|
#: serializers.py:94
|
||||||
|
msgid "User account is disabled."
|
||||||
|
msgstr "Der Useraccount ist deaktiviert."
|
||||||
|
|
||||||
|
#: serializers.py:97
|
||||||
|
msgid "Unable to log in with provided credentials."
|
||||||
|
msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden."
|
||||||
|
|
||||||
|
#: serializers.py:106
|
||||||
|
msgid "E-mail is not verified."
|
||||||
|
msgstr "E-Mail Adresse ist nicht verifiziert."
|
||||||
|
|
||||||
|
#: serializers.py:152
|
||||||
|
msgid "Error"
|
||||||
|
msgstr "Fehler"
|
||||||
|
|
||||||
|
#: views.py:71
|
||||||
|
msgid "Successfully logged out."
|
||||||
|
msgstr "Erfolgreich ausgeloggt."
|
||||||
|
|
||||||
|
#: views.py:111
|
||||||
|
msgid "Password reset e-mail has been sent."
|
||||||
|
msgstr "Die E-Mail zum Zurücksetzen des Passwortes wurde verschickt."
|
||||||
|
|
||||||
|
#: views.py:132
|
||||||
|
msgid "Password has been reset with the new password."
|
||||||
|
msgstr "Das Passwort wurde mit dem neuen Passwort ersetzt."
|
||||||
|
|
||||||
|
#: views.py:150
|
||||||
|
msgid "New password has been saved."
|
||||||
|
msgstr "Das neue Passwort wurde gespeichert."
|
|
@ -1,5 +1,6 @@
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from allauth.account import app_settings as allauth_settings
|
from allauth.account import app_settings as allauth_settings
|
||||||
|
@ -22,7 +23,6 @@ if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SocialLoginSerializer(serializers.Serializer):
|
class SocialLoginSerializer(serializers.Serializer):
|
||||||
access_token = serializers.CharField(required=False, allow_blank=True)
|
access_token = serializers.CharField(required=False, allow_blank=True)
|
||||||
code = serializers.CharField(required=False, allow_blank=True)
|
code = serializers.CharField(required=False, allow_blank=True)
|
||||||
|
@ -53,12 +53,12 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
|
|
||||||
if not view:
|
if not view:
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
'View is not defined, pass it as a context variable'
|
_('View is not defined, pass it as a context variable')
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter_class = getattr(view, 'adapter_class', None)
|
adapter_class = getattr(view, 'adapter_class', None)
|
||||||
if not adapter_class:
|
if not adapter_class:
|
||||||
raise serializers.ValidationError('Define adapter_class in view')
|
raise serializers.ValidationError(_('Define adapter_class in view'))
|
||||||
|
|
||||||
adapter = adapter_class()
|
adapter = adapter_class()
|
||||||
app = adapter.get_provider().get_app(request)
|
app = adapter.get_provider().get_app(request)
|
||||||
|
@ -77,11 +77,11 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
|
|
||||||
if not self.callback_url:
|
if not self.callback_url:
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
'Define callback_url in view'
|
_('Define callback_url in view')
|
||||||
)
|
)
|
||||||
if not self.client_class:
|
if not self.client_class:
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
'Define client_class in view'
|
_('Define client_class in view')
|
||||||
)
|
)
|
||||||
|
|
||||||
code = attrs.get('code')
|
code = attrs.get('code')
|
||||||
|
@ -101,7 +101,7 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
access_token = token['access_token']
|
access_token = token['access_token']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise serializers.ValidationError('Incorrect input. access_token or code is required.')
|
raise serializers.ValidationError(_('Incorrect input. access_token or code is required.'))
|
||||||
|
|
||||||
token = adapter.parse_token({'access_token': access_token})
|
token = adapter.parse_token({'access_token': access_token})
|
||||||
token.app = app
|
token.app = app
|
||||||
|
@ -110,7 +110,7 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
login = self.get_social_login(adapter, app, token, access_token)
|
login = self.get_social_login(adapter, app, token, access_token)
|
||||||
complete_social_login(request, login)
|
complete_social_login(request, login)
|
||||||
except HTTPError:
|
except HTTPError:
|
||||||
raise serializers.ValidationError('Incorrect value')
|
raise serializers.ValidationError(_('Incorrect value'))
|
||||||
|
|
||||||
if not login.is_existing:
|
if not login.is_existing:
|
||||||
login.lookup()
|
login.lookup()
|
||||||
|
@ -139,7 +139,7 @@ class RegisterSerializer(serializers.Serializer):
|
||||||
if allauth_settings.UNIQUE_EMAIL:
|
if allauth_settings.UNIQUE_EMAIL:
|
||||||
if email and email_address_exists(email):
|
if email and email_address_exists(email):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
"A user is already registered with this e-mail address.")
|
_("A user is already registered with this e-mail address."))
|
||||||
return email
|
return email
|
||||||
|
|
||||||
def validate_password1(self, password):
|
def validate_password1(self, password):
|
||||||
|
@ -147,7 +147,7 @@ class RegisterSerializer(serializers.Serializer):
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
if data['password1'] != data['password2']:
|
if data['password1'] != data['password2']:
|
||||||
raise serializers.ValidationError("The two password fields didn't match.")
|
raise serializers.ValidationError(_("The two password fields didn't match."))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def custom_signup(self, request, user):
|
def custom_signup(self, request, user):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.conf import settings
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -75,7 +75,7 @@ class VerifyEmailView(APIView, ConfirmEmailView):
|
||||||
self.kwargs['key'] = serializer.validated_data['key']
|
self.kwargs['key'] = serializer.validated_data['key']
|
||||||
confirmation = self.get_object()
|
confirmation = self.get_object()
|
||||||
confirmation.confirm(self.request)
|
confirmation.confirm(self.request)
|
||||||
return Response({'message': 'ok'}, status=status.HTTP_200_OK)
|
return Response({'message': _('ok')}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class SocialLoginView(LoginView):
|
class SocialLoginView(LoginView):
|
||||||
|
@ -102,4 +102,4 @@ class SocialLoginView(LoginView):
|
||||||
-------------
|
-------------
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SocialLoginSerializer
|
serializer_class = SocialLoginSerializer
|
|
@ -81,7 +81,7 @@ class LoginSerializer(serializers.Serializer):
|
||||||
# Authentication without using allauth
|
# Authentication without using allauth
|
||||||
if email:
|
if email:
|
||||||
try:
|
try:
|
||||||
username = UserModel.objects.get(email__iexact=email).username
|
username = UserModel.objects.get(email__iexact=email).get_username()
|
||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ class LoginSerializer(serializers.Serializer):
|
||||||
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
||||||
email_address = user.emailaddress_set.get(email=user.email)
|
email_address = user.emailaddress_set.get(email=user.email)
|
||||||
if not email_address.verified:
|
if not email_address.verified:
|
||||||
raise serializers.ValidationError('E-mail is not verified.')
|
raise serializers.ValidationError(_('E-mail is not verified.'))
|
||||||
|
|
||||||
attrs['user'] = user
|
attrs['user'] = user
|
||||||
return attrs
|
return attrs
|
||||||
|
|
78
rest_auth/social_serializers.py
Normal file
78
rest_auth/social_serializers.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from rest_framework import serializers
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
# Import is needed only if we are using social login, in which
|
||||||
|
# case the allauth.socialaccount will be declared
|
||||||
|
try:
|
||||||
|
from allauth.socialaccount.helpers import complete_social_login
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from allauth.socialaccount.models import SocialToken
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterLoginSerializer(serializers.Serializer):
|
||||||
|
access_token = serializers.CharField(required=True)
|
||||||
|
token_secret = serializers.CharField(required=True)
|
||||||
|
|
||||||
|
def _get_request(self):
|
||||||
|
request = self.context.get('request')
|
||||||
|
if not isinstance(request, HttpRequest):
|
||||||
|
request = request._request
|
||||||
|
return request
|
||||||
|
|
||||||
|
def get_social_login(self, adapter, app, token, response):
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param adapter: allauth.socialaccount Adapter subclass. Usually OAuthAdapter or Auth2Adapter
|
||||||
|
:param app: `allauth.socialaccount.SocialApp` instance
|
||||||
|
:param token: `allauth.socialaccount.SocialToken` instance
|
||||||
|
:param response: Provider's response for OAuth1. Not used in the
|
||||||
|
:return: :return: A populated instance of the `allauth.socialaccount.SocialLoginView` instance
|
||||||
|
"""
|
||||||
|
request = self._get_request()
|
||||||
|
social_login = adapter.complete_login(request, app, token, response=response)
|
||||||
|
social_login.token = token
|
||||||
|
return social_login
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
view = self.context.get('view')
|
||||||
|
request = self._get_request()
|
||||||
|
|
||||||
|
if not view:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'View is not defined, pass it as a context variable'
|
||||||
|
)
|
||||||
|
|
||||||
|
adapter_class = getattr(view, 'adapter_class', None)
|
||||||
|
if not adapter_class:
|
||||||
|
raise serializers.ValidationError('Define adapter_class in view')
|
||||||
|
|
||||||
|
adapter = adapter_class()
|
||||||
|
app = adapter.get_provider().get_app(request)
|
||||||
|
|
||||||
|
if('access_token' in attrs) and ('token_secret' in attrs):
|
||||||
|
access_token = attrs.get('access_token')
|
||||||
|
token_secret = attrs.get('token_secret')
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError('Incorrect input. access_token and token_secret are required.')
|
||||||
|
|
||||||
|
request.session['oauth_api.twitter.com_access_token'] = {
|
||||||
|
'oauth_token': access_token,
|
||||||
|
'oauth_token_secret': token_secret,
|
||||||
|
}
|
||||||
|
token = SocialToken(token=access_token, token_secret=token_secret)
|
||||||
|
token.app = app
|
||||||
|
|
||||||
|
try:
|
||||||
|
login = self.get_social_login(adapter, app, token, access_token)
|
||||||
|
complete_social_login(request, login)
|
||||||
|
except HTTPError:
|
||||||
|
raise serializers.ValidationError('Incorrect value')
|
||||||
|
|
||||||
|
if not login.is_existing:
|
||||||
|
login.lookup()
|
||||||
|
login.save(request, connect=True)
|
||||||
|
attrs['user'] = login.account.user
|
||||||
|
|
||||||
|
return attrs
|
|
@ -421,3 +421,29 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# try to login again
|
# try to login again
|
||||||
self._login()
|
self._login()
|
||||||
self._logout()
|
self._logout()
|
||||||
|
|
||||||
|
@override_settings(ACCOUNT_LOGOUT_ON_GET=True)
|
||||||
|
def test_logout_on_get(self):
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
# create user
|
||||||
|
user = get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
|
||||||
|
self.post(self.login_url, data=payload, status_code=200)
|
||||||
|
self.get(self.logout_url, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
@override_settings(ACCOUNT_LOGOUT_ON_GET=False)
|
||||||
|
def test_logout_on_post_only(self):
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
# create user
|
||||||
|
user = get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
|
||||||
|
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
self.get(self.logout_url, status_code=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib.auth import login, logout
|
from django.contrib.auth import login, logout
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
@ -9,6 +10,8 @@ from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
|
from allauth.account import app_settings as allauth_settings
|
||||||
|
|
||||||
from .app_settings import (
|
from .app_settings import (
|
||||||
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
||||||
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
||||||
|
@ -84,7 +87,23 @@ class LogoutView(APIView):
|
||||||
"""
|
"""
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
if allauth_settings.LOGOUT_ON_GET:
|
||||||
|
response = self.logout(request)
|
||||||
|
else:
|
||||||
|
response = self.http_method_not_allowed(request, *args, **kwargs)
|
||||||
|
except Exception as exc:
|
||||||
|
response = self.handle_exception(exc)
|
||||||
|
|
||||||
|
return self.finalize_response(request, response, *args, **kwargs)
|
||||||
|
self.response = self.finalize_response(request, response, *args, **kwargs)
|
||||||
|
return self.response
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
|
return self.logout(request)
|
||||||
|
|
||||||
|
def logout(self, request):
|
||||||
try:
|
try:
|
||||||
request.user.auth_token.delete()
|
request.user.auth_token.delete()
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
|
@ -92,12 +111,11 @@ class LogoutView(APIView):
|
||||||
|
|
||||||
logout(request)
|
logout(request)
|
||||||
|
|
||||||
return Response({"success": "Successfully logged out."},
|
return Response({"success": _("Successfully logged out.")},
|
||||||
status=status.HTTP_200_OK)
|
status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class UserDetailsView(RetrieveUpdateAPIView):
|
class UserDetailsView(RetrieveUpdateAPIView):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns User's details in JSON format.
|
Returns User's details in JSON format.
|
||||||
|
|
||||||
|
@ -134,13 +152,12 @@ class PasswordResetView(GenericAPIView):
|
||||||
serializer.save()
|
serializer.save()
|
||||||
# Return the success message with OK HTTP status
|
# Return the success message with OK HTTP status
|
||||||
return Response(
|
return Response(
|
||||||
{"success": "Password reset e-mail has been sent."},
|
{"success": _("Password reset e-mail has been sent.")},
|
||||||
status=status.HTTP_200_OK
|
status=status.HTTP_200_OK
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirmView(GenericAPIView):
|
class PasswordResetConfirmView(GenericAPIView):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Password reset e-mail link is confirmed, therefore this resets the user's password.
|
Password reset e-mail link is confirmed, therefore this resets the user's password.
|
||||||
|
|
||||||
|
@ -156,11 +173,10 @@ class PasswordResetConfirmView(GenericAPIView):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response({"success": "Password has been reset with the new password."})
|
return Response({"success": _("Password has been reset with the new password.")})
|
||||||
|
|
||||||
|
|
||||||
class PasswordChangeView(GenericAPIView):
|
class PasswordChangeView(GenericAPIView):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Calls Django Auth SetPasswordForm save method.
|
Calls Django Auth SetPasswordForm save method.
|
||||||
|
|
||||||
|
@ -175,4 +191,4 @@ class PasswordChangeView(GenericAPIView):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response({"success": "New password has been saved."})
|
return Response({"success": _("New password has been saved.")})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user