mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2024-11-10 19:26:35 +03:00
Merge pull request #169 from Tivix/master
Update develop with changes from master
This commit is contained in:
commit
09f6ad4169
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -33,6 +33,7 @@ htmlcov/
|
||||||
.cache
|
.cache
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
coverage_html
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
|
16
.travis.yml
16
.travis.yml
|
@ -1,22 +1,14 @@
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "2.6"
|
|
||||||
- "2.7"
|
- "2.7"
|
||||||
env:
|
env:
|
||||||
- DJANGO=1.5.12
|
- DJANGO=1.7.11
|
||||||
- DJANGO=1.6.11
|
- DJANGO=1.8.9
|
||||||
- DJANGO=1.7.7
|
- DJANGO=1.9.2
|
||||||
- DJANGO=1.8
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- python: "2.6"
|
|
||||||
env: DJANGO=1.7.7
|
|
||||||
- python: "2.6"
|
|
||||||
env: DJANGO=1.8
|
|
||||||
install:
|
install:
|
||||||
- pip install -q Django==$DJANGO --use-mirrors
|
- pip install -q Django==$DJANGO --use-mirrors
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
- pip install -r test_requirements.pip
|
- pip install -r rest_auth/tests/requirements.pip
|
||||||
script:
|
script:
|
||||||
- coverage run --source=rest_auth setup.py test
|
- coverage run --source=rest_auth setup.py test
|
||||||
after_success:
|
after_success:
|
||||||
|
|
|
@ -9,10 +9,6 @@ Welcome to django-rest-auth
|
||||||
:target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master
|
:target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://requires.io/github/Tivix/django-rest-auth/requirements.png?branch=master
|
|
||||||
:target: https://requires.io/github/Tivix/django-rest-auth/requirements/?branch=master
|
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://readthedocs.org/projects/django-rest-auth/badge/?version=latest
|
.. image:: https://readthedocs.org/projects/django-rest-auth/badge/?version=latest
|
||||||
:target: https://readthedocs.org/projects/django-rest-auth/?badge=latest
|
:target: https://readthedocs.org/projects/django-rest-auth/?badge=latest
|
||||||
|
|
||||||
|
@ -29,3 +25,7 @@ Source code
|
||||||
-----------
|
-----------
|
||||||
https://github.com/Tivix/django-rest-auth
|
https://github.com/Tivix/django-rest-auth
|
||||||
|
|
||||||
|
|
||||||
|
Stack Overflow
|
||||||
|
-----------
|
||||||
|
http://stackoverflow.com/questions/tagged/django-rest-auth
|
|
@ -29,8 +29,10 @@ ALLOWED_HOSTS = []
|
||||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
"django.core.context_processors.request",
|
"django.core.context_processors.request",
|
||||||
"allauth.account.context_processors.account",
|
|
||||||
"allauth.socialaccount.context_processors.socialaccount",
|
# Disabling due to alluth>=0.21.0 changes
|
||||||
|
# "allauth.account.context_processors.account",
|
||||||
|
# "allauth.socialaccount.context_processors.socialaccount",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
@ -51,6 +53,8 @@ INSTALLED_APPS = (
|
||||||
'allauth',
|
'allauth',
|
||||||
'allauth.account',
|
'allauth.account',
|
||||||
'rest_auth.registration',
|
'rest_auth.registration',
|
||||||
|
'allauth.socialaccount',
|
||||||
|
'allauth.socialaccount.providers.facebook',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
MIDDLEWARE_CLASSES = (
|
||||||
|
@ -102,9 +106,9 @@ TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
|
||||||
REST_SESSION_LOGIN = False
|
REST_SESSION_LOGIN = False
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = False
|
||||||
ACCOUNT_AUTHENTICATION_METHOD = 'email'
|
ACCOUNT_AUTHENTICATION_METHOD = 'username'
|
||||||
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
|
ACCOUNT_EMAIL_VERIFICATION = 'optional'
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView, RedirectView
|
from django.views.generic import TemplateView, RedirectView
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = [
|
||||||
url(r'^$', TemplateView.as_view(template_name="home.html"), name='home'),
|
url(r'^$', TemplateView.as_view(template_name="home.html"), name='home'),
|
||||||
url(r'^signup/$', TemplateView.as_view(template_name="signup.html"),
|
url(r'^signup/$', TemplateView.as_view(template_name="signup.html"),
|
||||||
name='signup'),
|
name='signup'),
|
||||||
|
@ -11,6 +11,8 @@ urlpatterns = patterns('',
|
||||||
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'),
|
||||||
|
@ -35,5 +37,5 @@ urlpatterns = patterns('',
|
||||||
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
|
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
|
||||||
url(r'^account/', include('allauth.urls')),
|
url(r'^account/', include('allauth.urls')),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
url(r'^accounts/profile/$', RedirectView.as_view(url='/'), name='profile-redirect'),
|
url(r'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'),
|
||||||
)
|
]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
django>=1.5.0
|
django>=1.7.0
|
||||||
django-rest-auth==0.4.0
|
django-rest-auth==0.6.0
|
||||||
django-allauth==0.19.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>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_verify_email' %}">
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_verify_email' %}">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="key" class="col-sm-2 control-label">Key</label>
|
<label for="key" class="col-sm-2 control-label">Key</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_login' %}">
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_login' %}">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username" class="col-sm-2 control-label">Username</label>
|
<label for="username" class="col-sm-2 control-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
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 %}
|
|
@ -1,6 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_change' %}">
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_change' %}">{% csrf_token %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="new_password1" class="col-sm-2 control-label">Password</label>
|
<label for="new_password1" class="col-sm-2 control-label">Password</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset_confirm' %}">
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset_confirm' %}">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="uid" class="col-sm-2 control-label">Uid</label>
|
<label for="uid" class="col-sm-2 control-label">Uid</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset' %}">
|
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset' %}">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">E-mail</label>
|
<label for="email" class="col-sm-2 control-label">E-mail</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal ajax-post" id="signup" role="form" action="{% url 'rest_register' %}">
|
<form class="form-horizontal ajax-post" id="signup" role="form" action="{% url 'rest_register' %}">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">Email</label>
|
<label for="email" class="col-sm-2 control-label">Email</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- Signup form -->
|
<!-- Signup form -->
|
||||||
<form class="form-horizontal" id="signup" role="form" action="{% url 'rest_user_details' %}">
|
<form class="form-horizontal" id="signup" role="form" action="{% url 'rest_user_details' %}">{% csrf_token %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">Email</label>
|
<label for="email" class="col-sm-2 control-label">Email</label>
|
||||||
|
|
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 %}
|
|
@ -7,11 +7,14 @@ Basic
|
||||||
- /rest-auth/login/ (POST)
|
- /rest-auth/login/ (POST)
|
||||||
|
|
||||||
- username (string)
|
- username (string)
|
||||||
|
- email (string)
|
||||||
- password (string)
|
- password (string)
|
||||||
|
|
||||||
|
|
||||||
- /rest-auth/logout/ (POST)
|
- /rest-auth/logout/ (POST)
|
||||||
|
|
||||||
|
- token
|
||||||
|
|
||||||
- /rest-auth/password/reset/ (POST)
|
- /rest-auth/password/reset/ (POST)
|
||||||
|
|
||||||
- email
|
- email
|
||||||
|
@ -33,6 +36,7 @@ Basic
|
||||||
|
|
||||||
|
|
||||||
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
|
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
|
||||||
|
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
|
||||||
|
|
||||||
- /rest-auth/user/ (GET)
|
- /rest-auth/user/ (GET)
|
||||||
|
|
||||||
|
@ -54,16 +58,6 @@ Registration
|
||||||
- password2
|
- password2
|
||||||
- email
|
- email
|
||||||
|
|
||||||
.. note:: This endpoint is based on ``allauth.account.views.SignupView`` and uses the same form as in this view. To override fields you have to create custom Signup Form and define it in django settings:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
ACCOUNT_FORMS = {
|
|
||||||
'signup': 'path.to.custom.SignupForm'
|
|
||||||
}
|
|
||||||
|
|
||||||
See allauth documentation for more details.
|
|
||||||
|
|
||||||
- /rest-auth/registration/verify-email/ (POST)
|
- /rest-auth/registration/verify-email/ (POST)
|
||||||
|
|
||||||
- key
|
- key
|
||||||
|
|
|
@ -1,6 +1,23 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.6.0
|
||||||
|
-----
|
||||||
|
- dropped support for Python 2.6
|
||||||
|
- dropped support for Django 1.6
|
||||||
|
- fixed demo code
|
||||||
|
- added better validation support for serializers
|
||||||
|
- added optional logout after password change
|
||||||
|
- compatibility fixes
|
||||||
|
- bugfixes
|
||||||
|
|
||||||
|
0.5.0
|
||||||
|
-----
|
||||||
|
- replaced request.DATA with request.data for compatibility with DRF 3.2
|
||||||
|
- authorization codes for social login
|
||||||
|
- view classes rename (appended "View" to all of them)
|
||||||
|
- bugfixes
|
||||||
|
|
||||||
0.4.0
|
0.4.0
|
||||||
-----
|
-----
|
||||||
- Django 1.8 compatiblity fixes
|
- Django 1.8 compatiblity fixes
|
||||||
|
|
|
@ -29,8 +29,19 @@ Configuration
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- **REST_AUTH_REGISTRATION_SERIALIZERS**
|
||||||
|
|
||||||
|
You can define your custom serializers for registration endpoint.
|
||||||
|
Possible key values:
|
||||||
|
|
||||||
|
- REGISTER_SERIALIZER - serializer class in ``rest_auth.register.views.RegisterView``, default value ``rest_auth.register.serializers.RegisterSerializer``
|
||||||
|
|
||||||
|
- **REST_AUTH_TOKEN_MODEL** - model class for tokens, default value ``rest_framework.authtoken.models``
|
||||||
|
|
||||||
|
- **REST_AUTH_TOKEN_CREATOR** - callable to create tokens, default value ``rest_auth.utils.default_create_token``.
|
||||||
|
|
||||||
- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
|
- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
|
||||||
|
|
||||||
|
|
||||||
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
||||||
|
|
||||||
|
- **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change
|
||||||
|
|
|
@ -11,7 +11,7 @@ Do these steps to make it running (ideally in virtualenv).
|
||||||
git clone https://github.com/Tivix/django-rest-auth.git
|
git clone https://github.com/Tivix/django-rest-auth.git
|
||||||
cd django-rest-auth/demo/
|
cd django-rest-auth/demo/
|
||||||
pip install -r requirements.pip
|
pip install -r requirements.pip
|
||||||
python manage.py syncdb --settings=demo.settings --noinput
|
python manage.py migrate --settings=demo.settings --noinput
|
||||||
python manage.py runserver --settings=demo.settings
|
python manage.py runserver --settings=demo.settings
|
||||||
|
|
||||||
Now, go to ``http://127.0.0.1:8000/`` in your browser.
|
Now, go to ``http://127.0.0.1:8000/`` in your browser.
|
||||||
|
|
|
@ -17,7 +17,12 @@ FAQ
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
2. How can I update UserProfile assigned to User model?
|
2. I get an error: Reverse for 'password_reset_confirm' not found.
|
||||||
|
|
||||||
|
You need to add `password_reset_confirm` url into your ``urls.py`` (at the top of any other included urls). Please check the ``urls.py`` module inside demo app example for more details.
|
||||||
|
|
||||||
|
|
||||||
|
3. How can I update UserProfile assigned to User model?
|
||||||
|
|
||||||
Assuming you already have UserProfile model defined like this
|
Assuming you already have UserProfile model defined like this
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ You're good to go now!
|
||||||
Registration (optional)
|
Registration (optional)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
1. If you want to enable standard registration process you will need to install ``django-allauth`` - see this doc for installation http://django-allauth.readthedocs.org/en/latest/installation.html.
|
1. If you want to enable standard registration process you will need to install ``django-allauth`` by using ``pip install django-rest-auth[extras]`` or ``pip install django-rest-auth[with_social]``.
|
||||||
|
|
||||||
2. Add ``allauth``, ``allauth.account`` and ``rest_auth.registration`` apps to INSTALLED_APPS in your django settings.py:
|
2. Add ``allauth``, ``allauth.account`` and ``rest_auth.registration`` apps to INSTALLED_APPS in your django settings.py:
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ Registration (optional)
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
...,
|
...,
|
||||||
(r'^rest-auth/', include('rest_auth.urls')),
|
url(r'^rest-auth/', include('rest_auth.urls')),
|
||||||
(r'^rest-auth/registration/', include('rest_auth.registration.urls'))
|
url(r'^rest-auth/registration/', include('rest_auth.registration.urls'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,11 +87,9 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati
|
||||||
'allauth.socialaccount.providers.facebook',
|
'allauth.socialaccount.providers.facebook',
|
||||||
)
|
)
|
||||||
|
|
||||||
2. Add ``allauth.socialaccount.context_processors.socialaccount`` to TEMPLATE_CONTEXT_PROCESSORS in django settings
|
2. Add Social Application in django admin panel
|
||||||
|
|
||||||
3. Add Social Application in django admin panel
|
3. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``FacebookOAuth2Adapter`` adapter as an attribute:
|
||||||
|
|
||||||
4. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``FacebookOAuth2Adapter`` adapter as an attribute:
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -101,7 +99,7 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati
|
||||||
class FacebookLogin(SocialLoginView):
|
class FacebookLogin(SocialLoginView):
|
||||||
adapter_class = FacebookOAuth2Adapter
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
|
||||||
5. Create url for FacebookLogin view:
|
4. Create url for FacebookLogin view:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -109,3 +107,5 @@ 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')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.. 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.
|
||||||
|
|
|
@ -7,8 +7,10 @@ from rest_auth.serializers import (
|
||||||
PasswordResetSerializer as DefaultPasswordResetSerializer,
|
PasswordResetSerializer as DefaultPasswordResetSerializer,
|
||||||
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
|
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
|
||||||
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
|
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
|
||||||
from .utils import import_callable
|
from .utils import import_callable, default_create_token
|
||||||
|
|
||||||
|
create_token = import_callable(
|
||||||
|
getattr(settings, 'REST_AUTH_TOKEN_CREATOR', default_create_token))
|
||||||
|
|
||||||
serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
|
serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
|
||||||
|
|
||||||
|
|
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,3 +1,10 @@
|
||||||
# from django.db import models
|
from django.conf import settings
|
||||||
|
|
||||||
|
from rest_framework.authtoken.models import Token as DefaultTokenModel
|
||||||
|
|
||||||
|
from .utils import import_callable
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
|
||||||
|
TokenModel = import_callable(
|
||||||
|
getattr(settings, 'REST_AUTH_TOKEN_MODEL', DefaultTokenModel))
|
||||||
|
|
11
rest_auth/registration/app_settings.py
Normal file
11
rest_auth/registration/app_settings.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from rest_auth.registration.serializers import (
|
||||||
|
RegisterSerializer as DefaultRegisterSerializer)
|
||||||
|
from ..utils import import_callable
|
||||||
|
|
||||||
|
|
||||||
|
serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {})
|
||||||
|
|
||||||
|
RegisterSerializer = import_callable(
|
||||||
|
serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer))
|
|
@ -1,17 +1,31 @@
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
try:
|
||||||
|
from allauth.account import app_settings as allauth_settings
|
||||||
|
from allauth.utils import (email_address_exists,
|
||||||
|
get_username_max_length)
|
||||||
|
from allauth.account.adapter import get_adapter
|
||||||
|
from allauth.account.utils import setup_user_email
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError('allauth needs to be added to INSTALLED_APPS.')
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
# Import is needed only if we are using social login, in which
|
# Import is needed only if we are using social login, in which
|
||||||
# case the allauth.socialaccount will be declared
|
# case the allauth.socialaccount will be declared
|
||||||
try:
|
|
||||||
|
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||||
|
try:
|
||||||
from allauth.socialaccount.helpers import complete_social_login
|
from allauth.socialaccount.helpers import complete_social_login
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SocialLoginSerializer(serializers.Serializer):
|
class SocialLoginSerializer(serializers.Serializer):
|
||||||
access_token = serializers.CharField(required=False)
|
access_token = serializers.CharField(required=False, allow_blank=True)
|
||||||
code = serializers.CharField(required=False)
|
code = serializers.CharField(required=False, allow_blank=True)
|
||||||
|
|
||||||
def _get_request(self):
|
def _get_request(self):
|
||||||
request = self.context.get('request')
|
request = self.context.get('request')
|
||||||
|
@ -39,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)
|
||||||
|
@ -63,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')
|
||||||
|
@ -87,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
|
||||||
|
@ -96,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()
|
||||||
|
@ -104,3 +118,57 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
attrs['user'] = login.account.user
|
attrs['user'] = login.account.user
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField(
|
||||||
|
max_length=get_username_max_length(),
|
||||||
|
min_length=allauth_settings.USERNAME_MIN_LENGTH,
|
||||||
|
required=allauth_settings.USERNAME_REQUIRED
|
||||||
|
)
|
||||||
|
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
|
||||||
|
password1 = serializers.CharField(required=True, write_only=True)
|
||||||
|
password2 = serializers.CharField(required=True, write_only=True)
|
||||||
|
|
||||||
|
def validate_username(self, username):
|
||||||
|
username = get_adapter().clean_username(username)
|
||||||
|
return username
|
||||||
|
|
||||||
|
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(
|
||||||
|
_("A user is already registered with this e-mail address."))
|
||||||
|
return email
|
||||||
|
|
||||||
|
def validate_password1(self, password):
|
||||||
|
return get_adapter().clean_password(password)
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
if data['password1'] != data['password2']:
|
||||||
|
raise serializers.ValidationError(_("The two password fields didn't match."))
|
||||||
|
return data
|
||||||
|
|
||||||
|
def custom_signup(self, request, user):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_cleaned_data(self):
|
||||||
|
return {
|
||||||
|
'username': self.validated_data.get('username', ''),
|
||||||
|
'password1': self.validated_data.get('password1', ''),
|
||||||
|
'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)
|
||||||
|
self.custom_signup(request, user)
|
||||||
|
setup_user_email(request, user, [])
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyEmailSerializer(serializers.Serializer):
|
||||||
|
key = serializers.CharField()
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import RegisterView, VerifyEmailView
|
from .views import RegisterView, VerifyEmailView
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = [
|
||||||
'',
|
|
||||||
url(r'^$', RegisterView.as_view(), name='rest_register'),
|
url(r'^$', RegisterView.as_view(), name='rest_register'),
|
||||||
url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'),
|
url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'),
|
||||||
|
|
||||||
|
@ -21,4 +20,4 @@ urlpatterns = patterns(
|
||||||
# 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-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'),
|
||||||
)
|
]
|
||||||
|
|
|
@ -1,72 +1,52 @@
|
||||||
from django.http import HttpRequest
|
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
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
|
from rest_framework.generics import CreateAPIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.exceptions import MethodNotAllowed
|
||||||
|
|
||||||
from allauth.account.views import SignupView, ConfirmEmailView
|
from allauth.account.views import ConfirmEmailView
|
||||||
from allauth.account.utils import complete_signup
|
from allauth.account.utils import complete_signup
|
||||||
from allauth.account import app_settings
|
from allauth.account import app_settings as allauth_settings
|
||||||
|
|
||||||
from rest_auth.app_settings import TokenSerializer
|
from rest_auth.app_settings import (TokenSerializer,
|
||||||
from rest_auth.registration.serializers import SocialLoginSerializer
|
create_token)
|
||||||
|
from rest_auth.registration.serializers import (SocialLoginSerializer,
|
||||||
|
VerifyEmailSerializer)
|
||||||
from rest_auth.views import LoginView
|
from rest_auth.views import LoginView
|
||||||
|
from rest_auth.models import TokenModel
|
||||||
|
from .app_settings import RegisterSerializer
|
||||||
|
|
||||||
|
|
||||||
class RegisterView(APIView, SignupView):
|
class RegisterView(CreateAPIView):
|
||||||
"""
|
serializer_class = RegisterSerializer
|
||||||
Accepts the credentials and creates a new user
|
permission_classes = (AllowAny, )
|
||||||
if user does not exist already
|
token_model = TokenModel
|
||||||
Return the REST Token if the credentials are valid and authenticated.
|
|
||||||
Calls allauth complete_signup method
|
|
||||||
|
|
||||||
Accept the following POST parameters: username, email, password
|
def get_response_data(self, user):
|
||||||
Return the REST Framework Token Object's key.
|
if allauth_settings.EMAIL_VERIFICATION == \
|
||||||
"""
|
allauth_settings.EmailVerificationMethod.MANDATORY:
|
||||||
|
return {}
|
||||||
|
|
||||||
permission_classes = (AllowAny,)
|
return TokenSerializer(user.auth_token).data
|
||||||
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
|
||||||
token_model = Token
|
|
||||||
serializer_class = TokenSerializer
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
user = self.perform_create(serializer)
|
||||||
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
|
||||||
def put(self, *args, **kwargs):
|
return Response(self.get_response_data(user), status=status.HTTP_201_CREATED, headers=headers)
|
||||||
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def perform_create(self, serializer):
|
||||||
self.user = form.save(self.request)
|
user = serializer.save(self.request)
|
||||||
self.token, created = self.token_model.objects.get_or_create(
|
create_token(self.token_model, user, serializer)
|
||||||
user=self.user
|
complete_signup(self.request._request, user,
|
||||||
)
|
allauth_settings.EMAIL_VERIFICATION,
|
||||||
if isinstance(self.request, HttpRequest):
|
None)
|
||||||
request = self.request
|
return user
|
||||||
else:
|
|
||||||
request = self.request._request
|
|
||||||
return complete_signup(request, self.user,
|
|
||||||
app_settings.EMAIL_VERIFICATION,
|
|
||||||
self.get_success_url())
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
self.initial = {}
|
|
||||||
self.request.POST = self.request.data.copy()
|
|
||||||
form_class = self.get_form_class()
|
|
||||||
self.form = self.get_form(form_class)
|
|
||||||
if self.form.is_valid():
|
|
||||||
self.form_valid(self.form)
|
|
||||||
return self.get_response()
|
|
||||||
else:
|
|
||||||
return self.get_response_with_errors()
|
|
||||||
|
|
||||||
def get_response(self):
|
|
||||||
# serializer = self.user_serializer_class(instance=self.user)
|
|
||||||
serializer = self.serializer_class(instance=self.token)
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
||||||
|
|
||||||
def get_response_with_errors(self):
|
|
||||||
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class VerifyEmailView(APIView, ConfirmEmailView):
|
class VerifyEmailView(APIView, ConfirmEmailView):
|
||||||
|
@ -75,13 +55,15 @@ class VerifyEmailView(APIView, ConfirmEmailView):
|
||||||
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
raise MethodNotAllowed('GET')
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.kwargs['key'] = self.request.data.get('key', '')
|
serializer = VerifyEmailSerializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
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):
|
||||||
|
|
|
@ -1,47 +1,50 @@
|
||||||
from django.contrib.auth import get_user_model, authenticate
|
from django.contrib.auth import get_user_model, authenticate
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
||||||
try:
|
|
||||||
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
|
||||||
except:
|
|
||||||
# make compatible with django 1.5
|
|
||||||
from django.utils.http import base36_to_int as uid_decoder
|
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
from .models import TokenModel
|
||||||
|
|
||||||
from rest_framework import serializers, exceptions
|
from rest_framework import serializers, exceptions
|
||||||
from rest_framework.authtoken.models import Token
|
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
# Get the UserModel
|
||||||
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class LoginSerializer(serializers.Serializer):
|
class LoginSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(required=False)
|
username = serializers.CharField(required=False, allow_blank=True)
|
||||||
email = serializers.EmailField(required=False)
|
email = serializers.EmailField(required=False, allow_blank=True)
|
||||||
password = serializers.CharField(style={'input_type': 'password'})
|
password = serializers.CharField(style={'input_type': 'password'})
|
||||||
|
|
||||||
def validate(self, attrs):
|
def _validate_email(self, email, password):
|
||||||
username = attrs.get('username')
|
user = None
|
||||||
email = attrs.get('email')
|
|
||||||
password = attrs.get('password')
|
|
||||||
|
|
||||||
if 'allauth' in settings.INSTALLED_APPS:
|
|
||||||
from allauth.account import app_settings
|
|
||||||
# Authentication through email
|
|
||||||
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
|
|
||||||
if email and password:
|
if email and password:
|
||||||
user = authenticate(email=email, password=password)
|
user = authenticate(email=email, password=password)
|
||||||
else:
|
else:
|
||||||
msg = _('Must include "email" and "password".')
|
msg = _('Must include "email" and "password".')
|
||||||
raise exceptions.ValidationError(msg)
|
raise exceptions.ValidationError(msg)
|
||||||
# Authentication through username
|
|
||||||
elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
|
return user
|
||||||
|
|
||||||
|
def _validate_username(self, username, password):
|
||||||
|
user = None
|
||||||
|
|
||||||
if username and password:
|
if username and password:
|
||||||
user = authenticate(username=username, password=password)
|
user = authenticate(username=username, password=password)
|
||||||
else:
|
else:
|
||||||
msg = _('Must include "username" and "password".')
|
msg = _('Must include "username" and "password".')
|
||||||
raise exceptions.ValidationError(msg)
|
raise exceptions.ValidationError(msg)
|
||||||
# Authentication through either username or email
|
|
||||||
else:
|
return user
|
||||||
|
|
||||||
|
def _validate_username_email(self, username, email, password):
|
||||||
|
user = None
|
||||||
|
|
||||||
if email and password:
|
if email and password:
|
||||||
user = authenticate(email=email, password=password)
|
user = authenticate(email=email, password=password)
|
||||||
elif username and password:
|
elif username and password:
|
||||||
|
@ -50,12 +53,40 @@ class LoginSerializer(serializers.Serializer):
|
||||||
msg = _('Must include either "username" or "email" and "password".')
|
msg = _('Must include either "username" or "email" and "password".')
|
||||||
raise exceptions.ValidationError(msg)
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
elif username and password:
|
return user
|
||||||
user = authenticate(username=username, password=password)
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
username = attrs.get('username')
|
||||||
|
email = attrs.get('email')
|
||||||
|
password = attrs.get('password')
|
||||||
|
|
||||||
|
user = None
|
||||||
|
|
||||||
|
if 'allauth' in settings.INSTALLED_APPS:
|
||||||
|
from allauth.account import app_settings
|
||||||
|
|
||||||
|
# Authentication through email
|
||||||
|
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
|
||||||
|
user = self._validate_email(email, password)
|
||||||
|
|
||||||
|
# Authentication through username
|
||||||
|
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
|
||||||
|
user = self._validate_username(username, password)
|
||||||
|
|
||||||
|
# Authentication through either username or email
|
||||||
|
else:
|
||||||
|
user = self._validate_username_email(username, email, password)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = _('Must include "username" and "password".')
|
# Authentication without using allauth
|
||||||
raise exceptions.ValidationError(msg)
|
if email:
|
||||||
|
try:
|
||||||
|
username = UserModel.objects.get(email__iexact=email).get_username()
|
||||||
|
except UserModel.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if username:
|
||||||
|
user = self._validate_username_email(username, '', password)
|
||||||
|
|
||||||
# Did we get back an active user?
|
# Did we get back an active user?
|
||||||
if user:
|
if user:
|
||||||
|
@ -72,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
|
||||||
|
@ -84,7 +115,7 @@ class TokenSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Token
|
model = TokenModel
|
||||||
fields = ('key',)
|
fields = ('key',)
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +125,7 @@ class UserDetailsSerializer(serializers.ModelSerializer):
|
||||||
User model w/o password
|
User model w/o password
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = get_user_model()
|
model = UserModel
|
||||||
fields = ('username', 'email', 'first_name', 'last_name')
|
fields = ('username', 'email', 'first_name', 'last_name')
|
||||||
read_only_fields = ('email', )
|
read_only_fields = ('email', )
|
||||||
|
|
||||||
|
@ -109,11 +140,17 @@ class PasswordResetSerializer(serializers.Serializer):
|
||||||
|
|
||||||
password_reset_form_class = PasswordResetForm
|
password_reset_form_class = PasswordResetForm
|
||||||
|
|
||||||
|
def get_email_options(self):
|
||||||
|
""" Override this method to change default e-mail options
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
def validate_email(self, value):
|
def validate_email(self, value):
|
||||||
# Create PasswordResetForm with the serializer
|
# Create PasswordResetForm with the serializer
|
||||||
self.reset_form = self.password_reset_form_class(data=self.initial_data)
|
self.reset_form = self.password_reset_form_class(data=self.initial_data)
|
||||||
if not self.reset_form.is_valid():
|
if not self.reset_form.is_valid():
|
||||||
raise serializers.ValidationError('Error')
|
raise serializers.ValidationError(_('Error'))
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
@ -124,6 +161,8 @@ class PasswordResetSerializer(serializers.Serializer):
|
||||||
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
|
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
|
||||||
'request': request,
|
'request': request,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.update(self.get_email_options())
|
||||||
self.reset_form.save(**opts)
|
self.reset_form.save(**opts)
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,11 +184,10 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
self._errors = {}
|
self._errors = {}
|
||||||
# Get the UserModel
|
|
||||||
UserModel = get_user_model()
|
|
||||||
# Decode the uidb64 to uid to get User object
|
# Decode the uidb64 to uid to get User object
|
||||||
try:
|
try:
|
||||||
uid = uid_decoder(attrs['uid'])
|
uid = force_text(uid_decoder(attrs['uid']))
|
||||||
self.user = UserModel._default_manager.get(pk=uid)
|
self.user = UserModel._default_manager.get(pk=uid)
|
||||||
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
|
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
|
||||||
raise ValidationError({'uid': ['Invalid value']})
|
raise ValidationError({'uid': ['Invalid value']})
|
||||||
|
@ -182,6 +220,9 @@ class PasswordChangeSerializer(serializers.Serializer):
|
||||||
self.old_password_field_enabled = getattr(
|
self.old_password_field_enabled = getattr(
|
||||||
settings, 'OLD_PASSWORD_FIELD_ENABLED', False
|
settings, 'OLD_PASSWORD_FIELD_ENABLED', False
|
||||||
)
|
)
|
||||||
|
self.logout_on_password_change = getattr(
|
||||||
|
settings, 'LOGOUT_ON_PASSWORD_CHANGE', False
|
||||||
|
)
|
||||||
super(PasswordChangeSerializer, self).__init__(*args, **kwargs)
|
super(PasswordChangeSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
if not self.old_password_field_enabled:
|
if not self.old_password_field_enabled:
|
||||||
|
@ -212,3 +253,6 @@ class PasswordChangeSerializer(serializers.Serializer):
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
self.set_password_form.save()
|
self.set_password_form.save()
|
||||||
|
if not self.logout_on_password_change:
|
||||||
|
from django.contrib.auth import update_session_auth_hash
|
||||||
|
update_session_auth_hash(self.request, self.user)
|
||||||
|
|
0
rest_auth/tests/__init__.py
Normal file
0
rest_auth/tests/__init__.py
Normal file
|
@ -1,8 +1,8 @@
|
||||||
import django
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0])
|
PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0])
|
||||||
|
|
||||||
ROOT_URLCONF = 'urls'
|
ROOT_URLCONF = 'urls'
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
STATIC_ROOT = '%s/staticserve' % PROJECT_ROOT
|
STATIC_ROOT = '%s/staticserve' % PROJECT_ROOT
|
||||||
|
@ -18,15 +18,12 @@ IS_STAGING = False
|
||||||
IS_PROD = False
|
IS_PROD = False
|
||||||
IS_TEST = 'test' in sys.argv or 'test_coverage' in sys.argv
|
IS_TEST = 'test' in sys.argv or 'test_coverage' in sys.argv
|
||||||
|
|
||||||
if django.VERSION[:2] >= (1, 3):
|
DATABASES = {
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': ':memory:',
|
'NAME': ':memory:',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
DATABASE_ENGINE = 'sqlite3'
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = [
|
MIDDLEWARE_CLASSES = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
@ -73,7 +70,3 @@ INSTALLED_APPS = [
|
||||||
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
||||||
ACCOUNT_ACTIVATION_DAYS = 1
|
ACCOUNT_ACTIVATION_DAYS = 1
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
MIGRATION_MODULES = {
|
|
||||||
'authtoken': 'authtoken.migrations',
|
|
||||||
}
|
|
|
@ -1,122 +1,14 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test.client import Client, MULTIPART_CONTENT
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
from django.conf import settings
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.contrib.sites.models import Site
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from allauth.socialaccount.models import SocialApp
|
|
||||||
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
|
|
||||||
import responses
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
|
from .test_base import BaseAPITestCase
|
||||||
class APIClient(Client):
|
|
||||||
|
|
||||||
def patch(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
|
|
||||||
return self.generic('PATCH', path, data, content_type, **extra)
|
|
||||||
|
|
||||||
def options(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
|
|
||||||
return self.generic('OPTIONS', path, data, content_type, **extra)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAPITestCase(object):
|
|
||||||
|
|
||||||
"""
|
|
||||||
base for API tests:
|
|
||||||
* easy request calls, f.e.: self.post(url, data), self.get(url)
|
|
||||||
* easy status check, f.e.: self.post(url, data, status_code=200)
|
|
||||||
"""
|
|
||||||
def send_request(self, request_method, *args, **kwargs):
|
|
||||||
request_func = getattr(self.client, request_method)
|
|
||||||
status_code = None
|
|
||||||
if 'content_type' not in kwargs and request_method != 'get':
|
|
||||||
kwargs['content_type'] = 'application/json'
|
|
||||||
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
|
|
||||||
data = kwargs.get('data', '')
|
|
||||||
kwargs['data'] = json.dumps(data) # , cls=CustomJSONEncoder
|
|
||||||
if 'status_code' in kwargs:
|
|
||||||
status_code = kwargs.pop('status_code')
|
|
||||||
|
|
||||||
# check_headers = kwargs.pop('check_headers', True)
|
|
||||||
if hasattr(self, 'token'):
|
|
||||||
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
|
|
||||||
|
|
||||||
self.response = request_func(*args, **kwargs)
|
|
||||||
is_json = bool(
|
|
||||||
filter(lambda x: 'json' in x, self.response._headers['content-type']))
|
|
||||||
if is_json and self.response.content:
|
|
||||||
self.response.json = json.loads(self.response.content)
|
|
||||||
else:
|
|
||||||
self.response.json = {}
|
|
||||||
if status_code:
|
|
||||||
self.assertEqual(self.response.status_code, status_code)
|
|
||||||
return self.response
|
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
|
||||||
return self.send_request('post', *args, **kwargs)
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
return self.send_request('get', *args, **kwargs)
|
|
||||||
|
|
||||||
def patch(self, *args, **kwargs):
|
|
||||||
return self.send_request('patch', *args, **kwargs)
|
|
||||||
|
|
||||||
# def put(self, *args, **kwargs):
|
|
||||||
# return self.send_request('put', *args, **kwargs)
|
|
||||||
|
|
||||||
# def delete(self, *args, **kwargs):
|
|
||||||
# return self.send_request('delete', *args, **kwargs)
|
|
||||||
|
|
||||||
# def options(self, *args, **kwargs):
|
|
||||||
# return self.send_request('options', *args, **kwargs)
|
|
||||||
|
|
||||||
# def post_file(self, *args, **kwargs):
|
|
||||||
# kwargs['content_type'] = MULTIPART_CONTENT
|
|
||||||
# return self.send_request('post', *args, **kwargs)
|
|
||||||
|
|
||||||
# def get_file(self, *args, **kwargs):
|
|
||||||
# content_type = None
|
|
||||||
# if 'content_type' in kwargs:
|
|
||||||
# content_type = kwargs.pop('content_type')
|
|
||||||
# response = self.send_request('get', *args, **kwargs)
|
|
||||||
# if content_type:
|
|
||||||
# self.assertEqual(
|
|
||||||
# bool(filter(lambda x: content_type in x, response._headers['content-type'])), True)
|
|
||||||
# return response
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
settings.DEBUG = True
|
|
||||||
self.client = APIClient()
|
|
||||||
|
|
||||||
self.login_url = reverse('rest_login')
|
|
||||||
self.logout_url = reverse('rest_logout')
|
|
||||||
self.password_change_url = reverse('rest_password_change')
|
|
||||||
self.register_url = reverse('rest_register')
|
|
||||||
self.password_reset_url = reverse('rest_password_reset')
|
|
||||||
self.user_url = reverse('rest_user_details')
|
|
||||||
self.veirfy_email_url = reverse('rest_verify_email')
|
|
||||||
self.fb_login_url = reverse('fb_login')
|
|
||||||
|
|
||||||
def _login(self):
|
|
||||||
payload = {
|
|
||||||
"username": self.USERNAME,
|
|
||||||
"password": self.PASS
|
|
||||||
}
|
|
||||||
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
def _logout(self):
|
|
||||||
self.post(self.logout_url, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------
|
|
||||||
# T E S T H E R E
|
|
||||||
# -----------------------
|
|
||||||
|
|
||||||
|
|
||||||
class APITestCase1(TestCase, BaseAPITestCase):
|
class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
|
@ -126,7 +18,7 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
- custom registration: backend defined
|
- custom registration: backend defined
|
||||||
"""
|
"""
|
||||||
|
|
||||||
urls = 'rest_auth.test_urls'
|
urls = 'tests.urls'
|
||||||
|
|
||||||
USERNAME = 'person'
|
USERNAME = 'person'
|
||||||
PASS = 'person'
|
PASS = 'person'
|
||||||
|
@ -159,12 +51,8 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
result = {}
|
result = {}
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
from django import VERSION
|
|
||||||
if VERSION[1] == 5:
|
|
||||||
from django.utils.http import int_to_base36
|
|
||||||
result['uid'] = int_to_base36(user.pk)
|
|
||||||
else:
|
|
||||||
from django.utils.http import urlsafe_base64_encode
|
from django.utils.http import urlsafe_base64_encode
|
||||||
|
|
||||||
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
|
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
|
||||||
result['token'] = default_token_generator.make_token(user)
|
result['token'] = default_token_generator.make_token(user)
|
||||||
return result
|
return result
|
||||||
|
@ -203,6 +91,51 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# test empty payload
|
# test empty payload
|
||||||
self.post(self.login_url, data={}, status_code=400)
|
self.post(self.login_url, data={}, status_code=400)
|
||||||
|
|
||||||
|
def test_login_by_email(self):
|
||||||
|
# starting test without allauth app
|
||||||
|
settings.INSTALLED_APPS.remove('allauth')
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"email": self.EMAIL.lower(),
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
# there is no users in db so it should throw error (400)
|
||||||
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
|
self.post(self.password_change_url, status_code=403)
|
||||||
|
|
||||||
|
# create user
|
||||||
|
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
|
|
||||||
|
# test auth by email
|
||||||
|
self.post(self.login_url, data=payload, status_code=200)
|
||||||
|
self.assertEqual('key' in self.response.json.keys(), True)
|
||||||
|
self.token = self.response.json['key']
|
||||||
|
|
||||||
|
# test auth by email in different case
|
||||||
|
payload = {
|
||||||
|
"email": self.EMAIL.upper(),
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
self.post(self.login_url, data=payload, status_code=200)
|
||||||
|
self.assertEqual('key' in self.response.json.keys(), True)
|
||||||
|
self.token = self.response.json['key']
|
||||||
|
|
||||||
|
# test inactive user
|
||||||
|
user.is_active = False
|
||||||
|
user.save()
|
||||||
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
|
# test wrong email/password
|
||||||
|
payload = {
|
||||||
|
"email": 't' + self.EMAIL,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
|
# test empty payload
|
||||||
|
self.post(self.login_url, data={}, status_code=400)
|
||||||
|
|
||||||
def test_password_change(self):
|
def test_password_change(self):
|
||||||
login_payload = {
|
login_payload = {
|
||||||
"username": self.USERNAME,
|
"username": self.USERNAME,
|
||||||
|
@ -298,7 +231,7 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
data = {
|
data = {
|
||||||
'new_password1': self.NEW_PASS,
|
'new_password1': self.NEW_PASS,
|
||||||
'new_password2': self.NEW_PASS,
|
'new_password2': self.NEW_PASS,
|
||||||
'uid': url_kwargs['uid'],
|
'uid': force_text(url_kwargs['uid']),
|
||||||
'token': '-wrong-token-'
|
'token': '-wrong-token-'
|
||||||
}
|
}
|
||||||
self.post(url, data=data, status_code=400)
|
self.post(url, data=data, status_code=400)
|
||||||
|
@ -325,7 +258,7 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
data = {
|
data = {
|
||||||
'new_password1': self.NEW_PASS,
|
'new_password1': self.NEW_PASS,
|
||||||
'new_password2': self.NEW_PASS,
|
'new_password2': self.NEW_PASS,
|
||||||
'uid': url_kwargs['uid'],
|
'uid': force_text(url_kwargs['uid']),
|
||||||
'token': url_kwargs['token']
|
'token': url_kwargs['token']
|
||||||
}
|
}
|
||||||
url = reverse('rest_password_reset_confirm')
|
url = reverse('rest_password_reset_confirm')
|
||||||
|
@ -337,6 +270,27 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
}
|
}
|
||||||
self.post(self.login_url, data=payload, status_code=200)
|
self.post(self.login_url, data=payload, status_code=200)
|
||||||
|
|
||||||
|
def test_password_reset_with_email_in_different_case(self):
|
||||||
|
get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS)
|
||||||
|
|
||||||
|
# call password reset in upper case
|
||||||
|
mail_count = len(mail.outbox)
|
||||||
|
payload = {'email': self.EMAIL.upper()}
|
||||||
|
self.post(self.password_reset_url, data=payload, status_code=200)
|
||||||
|
self.assertEqual(len(mail.outbox), mail_count + 1)
|
||||||
|
|
||||||
|
def test_password_reset_with_invalid_email(self):
|
||||||
|
"""
|
||||||
|
Invalid email should not raise error, as this would leak users
|
||||||
|
"""
|
||||||
|
get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
|
|
||||||
|
# call password reset
|
||||||
|
mail_count = len(mail.outbox)
|
||||||
|
payload = {'email': 'nonexisting@email.com'}
|
||||||
|
self.post(self.password_reset_url, data=payload, status_code=200)
|
||||||
|
self.assertEqual(len(mail.outbox), mail_count)
|
||||||
|
|
||||||
def test_user_details(self):
|
def test_user_details(self):
|
||||||
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
payload = {
|
payload = {
|
||||||
|
@ -359,14 +313,22 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# test empty payload
|
# test empty payload
|
||||||
self.post(self.register_url, data={}, status_code=400)
|
self.post(self.register_url, data={}, status_code=400)
|
||||||
|
|
||||||
self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
|
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)
|
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
|
||||||
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
new_user = get_user_model().objects.latest('id')
|
||||||
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
||||||
|
|
||||||
self._login()
|
self._login()
|
||||||
self._logout()
|
self._logout()
|
||||||
|
|
||||||
|
def test_registration_with_invalid_password(self):
|
||||||
|
data = self.REGISTRATION_DATA.copy()
|
||||||
|
data['password2'] = 'foobar'
|
||||||
|
|
||||||
|
self.post(self.register_url, data=data, status_code=400)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
ACCOUNT_EMAIL_VERIFICATION='mandatory',
|
ACCOUNT_EMAIL_VERIFICATION='mandatory',
|
||||||
ACCOUNT_EMAIL_REQUIRED=True
|
ACCOUNT_EMAIL_REQUIRED=True
|
||||||
|
@ -382,11 +344,12 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
status_code=status.HTTP_400_BAD_REQUEST
|
status_code=status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
|
|
||||||
self.post(
|
result = self.post(
|
||||||
self.register_url,
|
self.register_url,
|
||||||
data=self.REGISTRATION_DATA_WITH_EMAIL,
|
data=self.REGISTRATION_DATA_WITH_EMAIL,
|
||||||
status_code=status.HTTP_201_CREATED
|
status_code=status.HTTP_201_CREATED
|
||||||
)
|
)
|
||||||
|
self.assertNotIn('key', result.data)
|
||||||
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
|
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
|
||||||
self.assertEqual(len(mail.outbox), mail_count + 1)
|
self.assertEqual(len(mail.outbox), mail_count + 1)
|
||||||
new_user = get_user_model().objects.latest('id')
|
new_user = get_user_model().objects.latest('id')
|
||||||
|
@ -415,119 +378,3 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# try to login again
|
# try to login again
|
||||||
self._login()
|
self._login()
|
||||||
self._logout()
|
self._logout()
|
||||||
|
|
||||||
|
|
||||||
class TestSocialAuth(TestCase, BaseAPITestCase):
|
|
||||||
|
|
||||||
urls = 'rest_auth.test_urls'
|
|
||||||
|
|
||||||
USERNAME = 'person'
|
|
||||||
PASS = 'person'
|
|
||||||
EMAIL = "person1@world.com"
|
|
||||||
REGISTRATION_DATA = {
|
|
||||||
"username": USERNAME,
|
|
||||||
"password1": PASS,
|
|
||||||
"password2": PASS,
|
|
||||||
"email": EMAIL
|
|
||||||
}
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.init()
|
|
||||||
|
|
||||||
social_app = SocialApp.objects.create(
|
|
||||||
provider='facebook',
|
|
||||||
name='Facebook',
|
|
||||||
client_id='123123123',
|
|
||||||
secret='321321321',
|
|
||||||
)
|
|
||||||
site = Site.objects.get_current()
|
|
||||||
social_app.sites.add(site)
|
|
||||||
self.graph_api_url = GRAPH_API_URL + '/me'
|
|
||||||
|
|
||||||
@responses.activate
|
|
||||||
def test_failed_social_auth(self):
|
|
||||||
# fake response
|
|
||||||
responses.add(
|
|
||||||
responses.GET,
|
|
||||||
self.graph_api_url,
|
|
||||||
body='',
|
|
||||||
status=400,
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
'access_token': 'abc123'
|
|
||||||
}
|
|
||||||
self.post(self.fb_login_url, data=payload, status_code=400)
|
|
||||||
|
|
||||||
@responses.activate
|
|
||||||
def test_social_auth(self):
|
|
||||||
# fake response for facebook call
|
|
||||||
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}' # noqa
|
|
||||||
responses.add(
|
|
||||||
responses.GET,
|
|
||||||
self.graph_api_url,
|
|
||||||
body=resp_body,
|
|
||||||
status=200,
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
|
|
||||||
users_count = get_user_model().objects.all().count()
|
|
||||||
payload = {
|
|
||||||
'access_token': 'abc123'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.post(self.fb_login_url, data=payload, status_code=200)
|
|
||||||
self.assertIn('key', self.response.json.keys())
|
|
||||||
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
|
|
||||||
|
|
||||||
# make sure that second request will not create a new user
|
|
||||||
self.post(self.fb_login_url, data=payload, status_code=200)
|
|
||||||
self.assertIn('key', self.response.json.keys())
|
|
||||||
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
|
|
||||||
|
|
||||||
@responses.activate
|
|
||||||
@override_settings(
|
|
||||||
ACCOUNT_EMAIL_VERIFICATION='mandatory',
|
|
||||||
ACCOUNT_EMAIL_REQUIRED=True,
|
|
||||||
REST_SESSION_LOGIN=False
|
|
||||||
)
|
|
||||||
def test_edge_case(self):
|
|
||||||
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true,"email":"%s"}' # noqa
|
|
||||||
responses.add(
|
|
||||||
responses.GET,
|
|
||||||
self.graph_api_url,
|
|
||||||
body=resp_body % self.EMAIL,
|
|
||||||
status=200,
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
|
|
||||||
# test empty payload
|
|
||||||
self.post(self.register_url, data={}, status_code=400)
|
|
||||||
|
|
||||||
self.post(
|
|
||||||
self.register_url,
|
|
||||||
data=self.REGISTRATION_DATA,
|
|
||||||
status_code=201
|
|
||||||
)
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
|
||||||
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
|
||||||
|
|
||||||
# verify email
|
|
||||||
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
|
|
||||||
.emailconfirmation_set.order_by('-created')[0]
|
|
||||||
self.post(
|
|
||||||
self.veirfy_email_url,
|
|
||||||
data={"key": email_confirmation.key},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
|
|
||||||
self._login()
|
|
||||||
self._logout()
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
'access_token': 'abc123'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.post(self.fb_login_url, data=payload, status_code=200)
|
|
||||||
self.assertIn('key', self.response.json.keys())
|
|
106
rest_auth/tests/test_base.py
Normal file
106
rest_auth/tests/test_base.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test.client import Client, MULTIPART_CONTENT
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
|
||||||
|
class APIClient(Client):
|
||||||
|
|
||||||
|
def patch(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
|
||||||
|
return self.generic('PATCH', path, data, content_type, **extra)
|
||||||
|
|
||||||
|
def options(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
|
||||||
|
return self.generic('OPTIONS', path, data, content_type, **extra)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAPITestCase(object):
|
||||||
|
|
||||||
|
"""
|
||||||
|
base for API tests:
|
||||||
|
* easy request calls, f.e.: self.post(url, data), self.get(url)
|
||||||
|
* easy status check, f.e.: self.post(url, data, status_code=200)
|
||||||
|
"""
|
||||||
|
def send_request(self, request_method, *args, **kwargs):
|
||||||
|
request_func = getattr(self.client, request_method)
|
||||||
|
status_code = None
|
||||||
|
if 'content_type' not in kwargs and request_method != 'get':
|
||||||
|
kwargs['content_type'] = 'application/json'
|
||||||
|
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
|
||||||
|
data = kwargs.get('data', '')
|
||||||
|
kwargs['data'] = json.dumps(data) # , cls=CustomJSONEncoder
|
||||||
|
if 'status_code' in kwargs:
|
||||||
|
status_code = kwargs.pop('status_code')
|
||||||
|
|
||||||
|
# check_headers = kwargs.pop('check_headers', True)
|
||||||
|
if hasattr(self, 'token'):
|
||||||
|
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
|
||||||
|
|
||||||
|
self.response = request_func(*args, **kwargs)
|
||||||
|
is_json = bool(
|
||||||
|
[x for x in self.response._headers['content-type'] if 'json' in x])
|
||||||
|
if is_json and self.response.content:
|
||||||
|
self.response.json = json.loads(force_text(self.response.content))
|
||||||
|
else:
|
||||||
|
self.response.json = {}
|
||||||
|
if status_code:
|
||||||
|
self.assertEqual(self.response.status_code, status_code)
|
||||||
|
return self.response
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
return self.send_request('post', *args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
return self.send_request('get', *args, **kwargs)
|
||||||
|
|
||||||
|
def patch(self, *args, **kwargs):
|
||||||
|
return self.send_request('patch', *args, **kwargs)
|
||||||
|
|
||||||
|
# def put(self, *args, **kwargs):
|
||||||
|
# return self.send_request('put', *args, **kwargs)
|
||||||
|
|
||||||
|
# def delete(self, *args, **kwargs):
|
||||||
|
# return self.send_request('delete', *args, **kwargs)
|
||||||
|
|
||||||
|
# def options(self, *args, **kwargs):
|
||||||
|
# return self.send_request('options', *args, **kwargs)
|
||||||
|
|
||||||
|
# def post_file(self, *args, **kwargs):
|
||||||
|
# kwargs['content_type'] = MULTIPART_CONTENT
|
||||||
|
# return self.send_request('post', *args, **kwargs)
|
||||||
|
|
||||||
|
# def get_file(self, *args, **kwargs):
|
||||||
|
# content_type = None
|
||||||
|
# if 'content_type' in kwargs:
|
||||||
|
# content_type = kwargs.pop('content_type')
|
||||||
|
# response = self.send_request('get', *args, **kwargs)
|
||||||
|
# if content_type:
|
||||||
|
# self.assertEqual(
|
||||||
|
# bool(filter(lambda x: content_type in x, response._headers['content-type'])), True)
|
||||||
|
# return response
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
settings.DEBUG = True
|
||||||
|
self.client = APIClient()
|
||||||
|
|
||||||
|
self.login_url = reverse('rest_login')
|
||||||
|
self.logout_url = reverse('rest_logout')
|
||||||
|
self.password_change_url = reverse('rest_password_change')
|
||||||
|
self.register_url = reverse('rest_register')
|
||||||
|
self.password_reset_url = reverse('rest_password_reset')
|
||||||
|
self.user_url = reverse('rest_user_details')
|
||||||
|
self.veirfy_email_url = reverse('rest_verify_email')
|
||||||
|
self.fb_login_url = reverse('fb_login')
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def _logout(self):
|
||||||
|
self.post(self.logout_url, status=status.HTTP_200_OK)
|
127
rest_auth/tests/test_social.py
Normal file
127
rest_auth/tests/test_social.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
|
from allauth.socialaccount.models import SocialApp
|
||||||
|
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
|
||||||
|
import responses
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from .test_base import BaseAPITestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestSocialAuth(TestCase, BaseAPITestCase):
|
||||||
|
|
||||||
|
urls = 'tests.urls'
|
||||||
|
|
||||||
|
USERNAME = 'person'
|
||||||
|
PASS = 'person'
|
||||||
|
EMAIL = "person1@world.com"
|
||||||
|
REGISTRATION_DATA = {
|
||||||
|
"username": USERNAME,
|
||||||
|
"password1": PASS,
|
||||||
|
"password2": PASS,
|
||||||
|
"email": EMAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
social_app = SocialApp.objects.create(
|
||||||
|
provider='facebook',
|
||||||
|
name='Facebook',
|
||||||
|
client_id='123123123',
|
||||||
|
secret='321321321',
|
||||||
|
)
|
||||||
|
site = Site.objects.get_current()
|
||||||
|
social_app.sites.add(site)
|
||||||
|
self.graph_api_url = GRAPH_API_URL + '/me'
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_failed_social_auth(self):
|
||||||
|
# fake response
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
self.graph_api_url,
|
||||||
|
body='',
|
||||||
|
status=400,
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'access_token': 'abc123'
|
||||||
|
}
|
||||||
|
self.post(self.fb_login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_social_auth(self):
|
||||||
|
# fake response for facebook call
|
||||||
|
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}' # noqa
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
self.graph_api_url,
|
||||||
|
body=resp_body,
|
||||||
|
status=200,
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
users_count = get_user_model().objects.all().count()
|
||||||
|
payload = {
|
||||||
|
'access_token': 'abc123'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.post(self.fb_login_url, data=payload, status_code=200)
|
||||||
|
self.assertIn('key', self.response.json.keys())
|
||||||
|
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
|
||||||
|
|
||||||
|
# make sure that second request will not create a new user
|
||||||
|
self.post(self.fb_login_url, data=payload, status_code=200)
|
||||||
|
self.assertIn('key', self.response.json.keys())
|
||||||
|
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
@override_settings(
|
||||||
|
ACCOUNT_EMAIL_VERIFICATION='mandatory',
|
||||||
|
ACCOUNT_EMAIL_REQUIRED=True,
|
||||||
|
REST_SESSION_LOGIN=False
|
||||||
|
)
|
||||||
|
def test_edge_case(self):
|
||||||
|
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true,"email":"%s"}' # noqa
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
self.graph_api_url,
|
||||||
|
body=resp_body % self.EMAIL,
|
||||||
|
status=200,
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
|
||||||
|
# test empty payload
|
||||||
|
self.post(self.register_url, data={}, status_code=400)
|
||||||
|
self.post(
|
||||||
|
self.register_url,
|
||||||
|
data=self.REGISTRATION_DATA,
|
||||||
|
status_code=201
|
||||||
|
)
|
||||||
|
new_user = get_user_model().objects.latest('id')
|
||||||
|
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
||||||
|
|
||||||
|
# verify email
|
||||||
|
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
|
||||||
|
.emailconfirmation_set.order_by('-created')[0]
|
||||||
|
self.post(
|
||||||
|
self.veirfy_email_url,
|
||||||
|
data={"key": email_confirmation.key},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
|
||||||
|
self._login()
|
||||||
|
self._logout()
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'access_token': 'abc123'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.post(self.fb_login_url, data=payload, status_code=200)
|
||||||
|
self.assertIn('key', self.response.json.keys())
|
|
@ -1,6 +1,6 @@
|
||||||
from django.conf.urls import patterns, url, include
|
from django.conf.urls import url, include
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
import rest_auth.django_test_urls
|
from . import django_urls
|
||||||
|
|
||||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||||
|
|
||||||
|
@ -11,14 +11,13 @@ from rest_auth.registration.views import SocialLoginView
|
||||||
class FacebookLogin(SocialLoginView):
|
class FacebookLogin(SocialLoginView):
|
||||||
adapter_class = FacebookOAuth2Adapter
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
|
||||||
urlpatterns += patterns(
|
urlpatterns += [
|
||||||
'',
|
|
||||||
url(r'^rest-registration/', include('rest_auth.registration.urls')),
|
url(r'^rest-registration/', include('rest_auth.registration.urls')),
|
||||||
url(r'^test-admin/', include(rest_auth.django_test_urls)),
|
url(r'^test-admin/', include(django_urls)),
|
||||||
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
|
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
|
||||||
name='account_email_verification_sent'),
|
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'),
|
||||||
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'),
|
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'),
|
||||||
url(r'^accounts/', include('allauth.socialaccount.urls'))
|
url(r'^accounts/', include('allauth.socialaccount.urls'))
|
||||||
)
|
]
|
|
@ -1,12 +1,11 @@
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from rest_auth.views import (
|
from rest_auth.views import (
|
||||||
LoginView, LogoutView, UserDetailsView, PasswordChangeView,
|
LoginView, LogoutView, UserDetailsView, PasswordChangeView,
|
||||||
PasswordResetView, PasswordResetConfirmView
|
PasswordResetView, PasswordResetConfirmView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = [
|
||||||
'',
|
|
||||||
# URLs that do not require a session or valid token
|
# URLs that do not require a session or valid token
|
||||||
url(r'^password/reset/$', PasswordResetView.as_view(),
|
url(r'^password/reset/$', PasswordResetView.as_view(),
|
||||||
name='rest_password_reset'),
|
name='rest_password_reset'),
|
||||||
|
@ -18,4 +17,4 @@ urlpatterns = patterns(
|
||||||
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
|
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
|
||||||
url(r'^password/change/$', PasswordChangeView.as_view(),
|
url(r'^password/change/$', PasswordChangeView.as_view(),
|
||||||
name='rest_password_change'),
|
name='rest_password_change'),
|
||||||
)
|
]
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
from six import string_types
|
from six import string_types
|
||||||
import sys
|
from importlib import import_module
|
||||||
if sys.version_info < (2, 7):
|
|
||||||
from django.utils.importlib import import_module
|
|
||||||
else:
|
|
||||||
from importlib import import_module
|
|
||||||
|
|
||||||
|
|
||||||
def import_callable(path_or_callable):
|
def import_callable(path_or_callable):
|
||||||
|
@ -13,3 +9,8 @@ def import_callable(path_or_callable):
|
||||||
assert isinstance(path_or_callable, string_types)
|
assert isinstance(path_or_callable, string_types)
|
||||||
package, attr = path_or_callable.rsplit('.', 1)
|
package, attr = path_or_callable.rsplit('.', 1)
|
||||||
return getattr(import_module(package), attr)
|
return getattr(import_module(package), attr)
|
||||||
|
|
||||||
|
|
||||||
|
def default_create_token(token_model, user, serializer):
|
||||||
|
token, _ = token_model.objects.get_or_create(user=user)
|
||||||
|
return token
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
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.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
|
||||||
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.authtoken.models import Token
|
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
from .app_settings import (
|
from .app_settings import (
|
||||||
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
||||||
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
||||||
PasswordChangeSerializer
|
PasswordChangeSerializer, create_token
|
||||||
)
|
)
|
||||||
|
from .models import TokenModel
|
||||||
|
|
||||||
|
|
||||||
class LoginView(GenericAPIView):
|
class LoginView(GenericAPIView):
|
||||||
|
@ -29,13 +31,12 @@ class LoginView(GenericAPIView):
|
||||||
"""
|
"""
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
serializer_class = LoginSerializer
|
serializer_class = LoginSerializer
|
||||||
token_model = Token
|
token_model = TokenModel
|
||||||
response_serializer = TokenSerializer
|
response_serializer = TokenSerializer
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
self.user = self.serializer.validated_data['user']
|
self.user = self.serializer.validated_data['user']
|
||||||
self.token, created = self.token_model.objects.get_or_create(
|
self.token = create_token(self.token_model, self.user, self.serializer)
|
||||||
user=self.user)
|
|
||||||
if getattr(settings, 'REST_SESSION_LOGIN', True):
|
if getattr(settings, 'REST_SESSION_LOGIN', True):
|
||||||
login(self.request, self.user)
|
login(self.request, self.user)
|
||||||
|
|
||||||
|
@ -44,15 +45,9 @@ class LoginView(GenericAPIView):
|
||||||
self.response_serializer(self.token).data, status=status.HTTP_200_OK
|
self.response_serializer(self.token).data, status=status.HTTP_200_OK
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_error_response(self):
|
|
||||||
return Response(
|
|
||||||
self.serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
|
||||||
)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.serializer = self.get_serializer(data=self.request.data)
|
self.serializer = self.get_serializer(data=self.request.data)
|
||||||
if not self.serializer.is_valid():
|
self.serializer.is_valid(raise_exception=True)
|
||||||
return self.get_error_response()
|
|
||||||
self.login()
|
self.login()
|
||||||
return self.get_response()
|
return self.get_response()
|
||||||
|
|
||||||
|
@ -70,17 +65,16 @@ class LogoutView(APIView):
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
try:
|
try:
|
||||||
request.user.auth_token.delete()
|
request.user.auth_token.delete()
|
||||||
except:
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -112,20 +106,17 @@ class PasswordResetView(GenericAPIView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
# Create a serializer with request.data
|
# Create a serializer with request.data
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
if not serializer.is_valid():
|
|
||||||
return Response(serializer.errors,
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -139,16 +130,12 @@ class PasswordResetConfirmView(GenericAPIView):
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
if not serializer.is_valid():
|
serializer.is_valid(raise_exception=True)
|
||||||
return Response(
|
|
||||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
|
||||||
)
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -161,9 +148,6 @@ class PasswordChangeView(GenericAPIView):
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
if not serializer.is_valid():
|
serializer.is_valid(raise_exception=True)
|
||||||
return Response(
|
|
||||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
|
||||||
)
|
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response({"success": "New password has been saved."})
|
return Response({"success": _("New password has been saved.")})
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings'
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
|
||||||
test_dir = os.path.dirname(__file__)
|
test_dir = os.path.join(os.path.dirname(__file__), 'rest_auth')
|
||||||
sys.path.insert(0, test_dir)
|
sys.path.insert(0, test_dir)
|
||||||
|
|
||||||
import django
|
import django
|
||||||
|
|
13
setup.py
13
setup.py
|
@ -18,7 +18,7 @@ f.close()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-rest-auth',
|
name='django-rest-auth',
|
||||||
version='0.4.0',
|
version='0.6.0',
|
||||||
author='Sumit Chachra',
|
author='Sumit Chachra',
|
||||||
author_email='chachra@tivix.com',
|
author_email='chachra@tivix.com',
|
||||||
url='http://github.com/Tivix/django-rest-auth',
|
url='http://github.com/Tivix/django-rest-auth',
|
||||||
|
@ -28,10 +28,17 @@ setup(
|
||||||
keywords='django rest auth registration rest-framework django-registration api',
|
keywords='django rest auth registration rest-framework django-registration api',
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'Django>=1.5.0',
|
'Django>=1.7.0',
|
||||||
'djangorestframework>=3.0',
|
'djangorestframework>=3.1.0',
|
||||||
'six>=1.9.0',
|
'six>=1.9.0',
|
||||||
],
|
],
|
||||||
|
extras_require={
|
||||||
|
'with_social': ['django-allauth>=0.24.1'],
|
||||||
|
},
|
||||||
|
tests_require=[
|
||||||
|
'responses>=0.5.0',
|
||||||
|
'django-allauth>=0.24.1',
|
||||||
|
],
|
||||||
test_suite='runtests.runtests',
|
test_suite='runtests.runtests',
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
# cmdclass={},
|
# cmdclass={},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user