Compare commits

..

No commits in common. "master" and "v0.3.1" have entirely different histories.

71 changed files with 800 additions and 3769 deletions

74
.gitignore vendored
View File

@ -1,35 +1,26 @@
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class
# C extensions # C extensions
*.so *.so
# Distribution / packaging # Distribution / packaging
.Python .Python
env/
bin/
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
downloads/
eggs/ eggs/
.eggs/
lib/ lib/
lib64/ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
wheels/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs # Installer logs
pip-log.txt pip-log.txt
@ -39,66 +30,27 @@ pip-delete-this-directory.txt
htmlcov/ htmlcov/
.tox/ .tox/
.coverage .coverage
.coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations # Translations
*.mo *.mo
*.pot
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff: # Django stuff:
*.log *.log
local_settings.py *.pot
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
# PyBuilder .DS_Store
target/ db.sqlite3
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/

View File

@ -1,26 +1,20 @@
language: python language: python
python: python:
- "2.6"
- "2.7" - "2.7"
- "3.5"
- "3.6"
env: env:
- DJANGO=1.11.* DRF=3.7.* - DJANGO=1.5.10
- DJANGO=1.11.* DRF=3.8.* - DJANGO=1.6.7
- DJANGO=2.0.* DRF=3.7.* - DJANGO=1.7
- DJANGO=2.0.* DRF=3.8.* matrix:
exclude:
- python: "2.6"
env: DJANGO=1.7
install: install:
- pip install -q Django==$DJANGO djangorestframework==$DRF - pip install -q Django==$DJANGO --use-mirrors
- pip install coveralls - pip install coveralls
- pip install -r rest_auth/tests/requirements.pip - pip install -r test_requirements.pip
script: script:
- coverage run --source=rest_auth setup.py test - coverage run --source=rest_auth setup.py test
after_success: after_success:
- coveralls - coveralls
before_script:
- flake8 . --config=flake8
matrix:
exclude:
- python: "2.7"
env: DJANGO=2.0.* DRF=3.7.*
- python: "2.7"
env: DJANGO=2.0.* DRF=3.8.*

View File

@ -2,4 +2,3 @@ include AUTHORS
include LICENSE include LICENSE
include MANIFEST.in include MANIFEST.in
include README.md include README.md
graft rest_auth

View File

@ -1,20 +1,18 @@
===========
Deprecated
===========
Please use https://github.com/iMerica/dj-rest-auth as this project is no longer maintained. Thanks!
Welcome to django-rest-auth Welcome to django-rest-auth
=========================== ===========================
.. image:: https://travis-ci.org/Tivix/django-rest-auth.svg .. image:: https://travis-ci.org/Tivix/django-rest-auth.png
:target: https://travis-ci.org/Tivix/django-rest-auth :target: https://travis-ci.org/Tivix/django-rest-auth
.. image:: https://coveralls.io/repos/Tivix/django-rest-auth/badge.svg .. image:: https://coveralls.io/repos/Tivix/django-rest-auth/badge.png
: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
@ -31,7 +29,3 @@ 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

View File

@ -10,9 +10,9 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
@ -22,8 +22,17 @@ SECRET_KEY = 'ma3c@7uu!%e0=tynp+i6+q%$)9v@$t(eulqurym_b=48z82&5n'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = []
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
"django.core.context_processors.request",
"allauth.account.context_processors.account",
"allauth.socialaccount.context_processors.socialaccount",
)
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = (
@ -42,27 +51,23 @@ INSTALLED_APPS = (
'allauth', 'allauth',
'allauth.account', 'allauth.account',
'rest_auth.registration', 'rest_auth.registration',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'rest_framework_swagger',
) )
MIDDLEWARE = ( MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
) )
# For backwards compatibility for Django 1.8
MIDDLEWARE_CLASSES = MIDDLEWARE
ROOT_URLCONF = 'demo.urls' ROOT_URLCONF = 'demo.urls'
WSGI_APPLICATION = 'demo.wsgi.application' WSGI_APPLICATION = 'demo.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
@ -86,44 +91,17 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/ # https://docs.djangoproject.com/en/1.7/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
# TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
TEMPLATES = [ REST_SESSION_LOGIN = False
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
REST_SESSION_LOGIN = True
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 = False ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD = 'username' ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'optional' ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
SWAGGER_SETTINGS = {
'LOGIN_URL': 'login',
'LOGOUT_URL': 'logout',
}

View File

@ -1,10 +1,8 @@
from django.conf.urls import include, url from django.conf.urls import patterns, 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
from rest_framework_swagger.views import get_swagger_view 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'),
@ -13,8 +11,6 @@ urlpatterns = [
name='email-verification'), name='email-verification'),
url(r'^login/$', TemplateView.as_view(template_name="login.html"), url(r'^login/$', TemplateView.as_view(template_name="login.html"),
name='login'), name='login'),
url(r'^logout/$', TemplateView.as_view(template_name="logout.html"),
name='logout'),
url(r'^password-reset/$', url(r'^password-reset/$',
TemplateView.as_view(template_name="password_reset.html"), TemplateView.as_view(template_name="password_reset.html"),
name='password-reset'), name='password-reset'),
@ -37,8 +33,5 @@ urlpatterns = [
url(r'^rest-auth/', include('rest_auth.urls')), url(r'^rest-auth/', include('rest_auth.urls')),
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'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls), )
url(r'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'),
url(r'^docs/$', get_swagger_view(title='API Docs'), name='api_docs')
]

View File

@ -1,6 +1,3 @@
django>=1.9.0 django==1.7.1
django-rest-auth==0.9.5 django-rest-auth==0.3.0
djangorestframework>=3.7.0 django-allauth==0.18.0
django-allauth>=0.24.1
six==1.9.0
django-rest-swagger==2.0.7

View File

@ -7,7 +7,7 @@
<meta name="description" content="Django-rest-auth demo"> <meta name="description" content="Django-rest-auth demo">
<meta name="author" content="Tivix, Inc."> <meta name="author" content="Tivix, Inc.">
<title>django-rest-auth demo</title> <title>Starter Template for Bootstrap</title>
<!-- Latest compiled and minified CSS --> <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
@ -40,7 +40,6 @@
<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>
@ -60,7 +59,6 @@
<li class="active"><a href="/">Demo</a></li> <li class="active"><a href="/">Demo</a></li>
<li><a target="_blank" href="http://django-rest-auth.readthedocs.org/en/latest/">Documentation</a></li> <li><a target="_blank" href="http://django-rest-auth.readthedocs.org/en/latest/">Documentation</a></li>
<li><a target="_blank" href="https://github.com/Tivix/django-rest-auth">Source code</a></li> <li><a target="_blank" href="https://github.com/Tivix/django-rest-auth">Source code</a></li>
<li><a target="_blank" href="{% url 'api_docs' %}">API Docs</a></li>
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_verify_email' %}">{% csrf_token %} <form class="form-horizontal ajax-post" role="form" action="{% url 'verify_email' %}">
<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">

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_login' %}">{% csrf_token %} <form class="form-horizontal ajax-post" role="form" action="{% url 'rest_login' %}">
<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">

View File

@ -1,20 +0,0 @@
{% 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">Logout</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>
{% endblock %}

View File

@ -1,5 +1,6 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_change' %}">{% csrf_token %} <form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_change' %}">
<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">

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset_confirm' %}">{% csrf_token %} <form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset_confirm' %}">
<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">

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset' %}">{% csrf_token %} <form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset' %}">
<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">

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal ajax-post" id="signup" role="form" action="{% url 'rest_register' %}">{% csrf_token %} <form class="form-horizontal ajax-post" id="signup" role="form" action="{% url 'rest_register' %}">
<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">

View File

@ -1,5 +1,5 @@
<!-- Signup form --> <!-- Signup form -->
<form class="form-horizontal" id="signup" role="form" action="{% url 'rest_user_details' %}">{% csrf_token %} <form class="form-horizontal" id="signup" role="form" action="{% url 'rest_user_details' %}">
<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>

View File

@ -1,8 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<h3>Logout</h3><hr/>
{% include "fragments/logout_form.html" %}
</div>
{% endblock %}

View File

@ -1,4 +0,0 @@
--editable .
responses>=0.5.0
djangorestframework-jwt
django-allauth

View File

@ -6,16 +6,12 @@ Basic
- /rest-auth/login/ (POST) - /rest-auth/login/ (POST)
- username - username (string)
- email - password (string)
- password
Returns Token key
- /rest-auth/logout/ (POST) - /rest-auth/logout/ (POST)
.. note:: ``ACCOUNT_LOGOUT_ON_GET = True`` to allow logout using GET - this is the exact same configuration from allauth. NOT recommended, see: http://django-allauth.readthedocs.io/en/latest/views.html#logout
- /rest-auth/password/reset/ (POST) - /rest-auth/password/reset/ (POST)
- email - email
@ -33,18 +29,15 @@ Basic
- new_password1 - new_password1
- new_password2 - new_password2
- old_password
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password. - /rest-auth/user/ (GET)
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
- /rest-auth/user/ (GET, PUT, PATCH) - /rest-auth/user/ (PUT/PATCH)
- username - username
- first_name - first_name
- last_name - last_name
- email
Returns pk, username, email, first_name, last_name
Registration Registration
@ -57,6 +50,16 @@ 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
@ -70,11 +73,3 @@ Basing on example from installation section :doc:`Installation </installation>`
- /rest-auth/facebook/ (POST) - /rest-auth/facebook/ (POST)
- access_token - access_token
- code
.. note:: ``access_token`` OR ``code`` can be used as standalone arguments, see https://github.com/Tivix/django-rest-auth/blob/master/rest_auth/registration/views.py
- /rest-auth/twitter/ (POST)
- access_token
- token_secret

View File

@ -1,102 +1,6 @@
Changelog Changelog
========= =========
0.9.5
-----
- fixed package distribution issue
0.9.4
-----
- Compatibility fixes (#437, #506)
- JWT auth cookie fix (#345)
- config & packaging fixes
- updated docs
- added new translations (Czech, Chinese, Turkish, Korean)
0.9.3
-----
- added social connect views
- added check for pre-existing accounts in social login
- prevent double-validation in LoginSerializer
- unit tests and demo project changes for Django 2.0
0.9.2
-----
- added permission classes configuration for registration
- added more info to JWT docs
- added Polish translations
0.9.1
-----
- fixed import error when extending rest_auth serializers
- added sensitive fields decorator
- added Spanish translations
0.9.0
-----
- allowed using custom UserDetailsSerializer with JWTSerializer
- fixed error with logout on GET
- updated api endpoints and configuration docs
- bugfixes
- minor text fixes
0.8.2
-----
- fixed allauth import error
- added swagger docs to demo project
0.8.1
-----
- added support for django-allauth hmac email confirmation pattern
0.8.0
-----
- added support for django-rest-framework-jwt
- bugfixes
0.7.0
-----
- Wrapped API returned strings in ugettext_lazy
- Fixed not using ``get_username`` which caused issues when using custom user model without username field
- Django 1.9 support
- Added ``TwitterLoginSerializer``
0.6.0
-----
- 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
-----
- Django 1.8 compatiblity fixes
0.3.4
-----
- fixed bug in PasswordResetConfirmation serializer (token field wasn't validated)
- fixed bug in Register view
0.3.3
-----
- support django-rest-framework v3.0
0.3.2
-----
- fixed few minor bugs
0.3.1 0.3.1
----- -----

View File

@ -44,16 +44,16 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'django-rest-auth' project = u'django-rest-auth'
copyright = u'2018, Tivix Inc.' copyright = u'2014, Tivix Inc.'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.9.5' version = '0.3.0'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.9.5' release = '0.3.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -6,19 +6,17 @@ Configuration
You can define your custom serializers for each endpoint without overriding urls and views by adding ``REST_AUTH_SERIALIZERS`` dictionary in your django settings. You can define your custom serializers for each endpoint without overriding urls and views by adding ``REST_AUTH_SERIALIZERS`` dictionary in your django settings.
Possible key values: Possible key values:
- LOGIN_SERIALIZER - serializer class in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.LoginSerializer`` - LOGIN_SERIALIZER - serializer class in ``rest_auth.views.Login``, default value ``rest_auth.serializers.LoginSerializer``
- TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer`` - TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.Login``, default value ``rest_auth.serializers.TokenSerializer``
- JWT_SERIALIZER - (Using REST_USE_JWT=True) response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.JWTSerializer`` - USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetails``, default value ``rest_auth.serializers.UserDetailsSerializer``
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer`` - PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordReset``, default value ``rest_auth.serializers.PasswordResetSerializer``
- PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer`` - PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirm``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer``
- PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirmView``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer`` - PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChange``, default value ``rest_auth.serializers.PasswordChangeSerializer``
- PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChangeView``, default value ``rest_auth.serializers.PasswordChangeSerializer``
Example configuration: Example configuration:
@ -31,23 +29,8 @@ Configuration
... ...
} }
- **REST_AUTH_REGISTER_SERIALIZERS**
You can define your custom serializers for registration endpoint.
Possible key values:
- REGISTER_SERIALIZER - serializer class in ``rest_auth.registration.views.RegisterView``, default value ``rest_auth.registration.serializers.RegisterSerializer``
.. note:: The custom REGISTER_SERIALIZER must define a ``def save(self, request)`` method that returns a user model instance
- **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)
- **REST_USE_JWT** - Enable JWT Authentication instead of Token/Session based. This is built on top of django-rest-framework-jwt http://getblimp.github.io/django-rest-framework-jwt/, which must also be installed. (default: False)
- **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

View File

@ -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 migrate --settings=demo.settings --noinput python manage.py syncdb --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.

View File

@ -7,66 +7,11 @@ FAQ
.. code-block:: python .. code-block:: python
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'),
This url is used by django-allauth. Empty TemplateView is defined just to allow reverse() call inside app - when email with verification link is being sent. This url is used by django-allauth. Empty TemplateView is defined just to allow reverse() call inside app - when email with verification link is being sent.
You should override this view/url to handle it in your API client somehow and then, send post to /verify-email/ endpoint with proper key. You should override this view/url to handle it in your API client somehow and then, send post to /verify-email/ endpoint with proper key.
If you don't want to use API on that step, then just use ConfirmEmailView view from: If you don't want to use API on that step, then just use ConfirmEmailView view from:
django-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
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
.. code-block:: python
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
# custom fields for user
company_name = models.CharField(max_length=100)
To allow update user details within one request send to rest_auth.views.UserDetailsView view, create serializer like this:
.. code-block:: python
from rest_framework import serializers
from rest_auth.serializers import UserDetailsSerializer
class UserSerializer(UserDetailsSerializer):
company_name = serializers.CharField(source="userprofile.company_name")
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('company_name',)
def update(self, instance, validated_data):
profile_data = validated_data.pop('userprofile', {})
company_name = profile_data.get('company_name')
instance = super(UserSerializer, self).update(instance, validated_data)
# get and update user profile
profile = instance.userprofile
if profile_data and company_name:
profile.company_name = company_name
profile.save()
return instance
And setup USER_DETAILS_SERIALIZER in django settings:
.. code-block:: python
REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'demo.serializers.UserSerializer'
}

View File

@ -6,9 +6,7 @@
Welcome to django-rest-auth's documentation! Welcome to django-rest-auth's documentation!
============================================ ============================================
.. warning:: Updating django-rest-auth from version **0.3.3** is highly recommended because of a security issue in PasswordResetConfirmation validation method. .. warning:: Version 0.3.0 is not compatible with any of previous versions, see :doc:`Changelog </changelog>` section for a list of changes.
.. note:: django-rest-auth from v0.3.3 supports django-rest-framework v3.0
|build-status| |coverage-status| |requirements-status| |docs| |build-status| |coverage-status| |requirements-status| |docs|

View File

@ -26,61 +26,50 @@ Installation
.. code-block:: python .. code-block:: python
urlpatterns = [ urlpatterns = patterns('',
..., ...,
url(r'^rest-auth/', include('rest_auth.urls')) (r'^rest-auth/', include('rest_auth.urls'))
] )
4. Migrate your database
.. code-block:: python
python manage.py migrate
You're good to go now! 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`` by using ``pip install django-rest-auth[with_social]``. 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.
2. Add ``django.contrib.sites``, ``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:
3. Add ``SITE_ID = 1`` to your django settings.py
.. code-block:: python .. code-block:: python
INSTALLED_APPS = ( INSTALLED_APPS = (
..., ...,
'django.contrib.sites',
'allauth', 'allauth',
'allauth.account', 'allauth.account',
'rest_auth.registration', 'rest_auth.registration',
) )
SITE_ID = 1
3. Add rest_auth.registration urls: 3. Add rest_auth.registration urls:
.. code-block:: python .. code-block:: python
urlpatterns = [ urlpatterns = patterns('',
..., ...,
url(r'^rest-auth/', include('rest_auth.urls')), (r'^rest-auth/', include('rest_auth.urls'))
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')) (r'^rest-auth/registration/', include('rest_auth.registration.urls'))
] )
Social Authentication (optional) Social Authenitcation (optional)
-------------------------------- --------------------------------
Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creating social media authentication view. Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creating social media authentication view. Below is an example with Facebook authentication.
.. note:: Points 1 and 2 are related to ``django-allauth`` configuration, so if you have already configured social authentication, then please go to step 3. See ``django-allauth`` documentation for more details. .. note:: Points 1, 2 and 3 are related with ``django-allauth`` configuration, so if you have already configured social authentication, then please go to step 4. See ``django-allauth`` documentation for more details.
1. Add ``allauth.socialaccount`` and ``allauth.socialaccount.providers.facebook`` or ``allauth.socialaccount.providers.twitter`` apps to INSTALLED_APPS in your django settings.py: 1. Add ``allauth.socialaccount`` and ``allauth.socialaccount.providers.facebook`` apps to INSTALLED_APPS in your django settings.py:
.. code-block:: python .. code-block:: python
@ -90,171 +79,33 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati
'rest_framework.authtoken', 'rest_framework.authtoken',
'rest_auth' 'rest_auth'
..., ...,
'django.contrib.sites',
'allauth', 'allauth',
'allauth.account', 'allauth.account',
'rest_auth.registration', 'rest_auth.registration',
..., ...,
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.twitter',
) )
2. Add Social Application in django admin panel 2. Add ``allauth.socialaccount.context_processors.socialaccount`` to TEMPLATE_CONTEXT_PROCESSORS in django settings
Facebook 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.SocialLogin`` with ``FacebookOAuth2Adapter`` adapter as an attribute:
.. code-block:: python .. code-block:: python
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from rest_auth.registration.views import SocialLoginView from rest_auth.registration.views import SocialLogin
class FacebookLogin(SocialLoginView): class FacebookLogin(SocialLogin):
adapter_class = FacebookOAuth2Adapter adapter_class = FacebookOAuth2Adapter
4. Create url for FacebookLogin view: 5. Create url for FacebookLogin view:
.. code-block:: python .. code-block:: python
urlpatterns += [ urlpatterns += pattern('',
..., ...,
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login') url(r'^/rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
]
Twitter
#######
If you are using Twitter for your social authentication, it is a bit different since Twitter uses OAuth 1.0.
3. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``TwitterOAuthAdapter`` adapter and ``TwitterLoginSerializer`` as an attribute:
.. code-block:: python
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from rest_auth.registration.views import SocialLoginView
from rest_auth.social_serializers import TwitterLoginSerializer
class TwitterLogin(SocialLoginView):
serializer_class = TwitterLoginSerializer
adapter_class = TwitterOAuthAdapter
4. Create url for TwitterLogin view:
.. code-block:: python
urlpatterns += [
...,
url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login')
]
.. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details.
GitHub
######
If you are using GitHub for your social authentication, it uses code and not AccessToken directly.
3. Create new view as a subclass of ``rest_auth.views.SocialLoginView`` with ``GitHubOAuth2Adapter`` adapter, an ``OAuth2Client`` and a callback_url as attributes:
.. code-block:: python
from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from rest_auth.registration.views import SocialLoginView
class GithubLogin(SocialLoginView):
adapter_class = GitHubOAuth2Adapter
callback_url = CALLBACK_URL_YOU_SET_ON_GITHUB
client_class = OAuth2Client
4. Create url for GitHubLogin view:
.. code-block:: python
urlpatterns += [
...,
url(r'^rest-auth/github/$', GitHubLogin.as_view(), name='github_login')
]
Additional Social Connect Views
###############################
If you want to allow connecting existing accounts in addition to login, you can use connect views:
.. code-block:: python
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from rest_auth.registration.views import SocialConnectView
from rest_auth.social_serializers import TwitterConnectSerializer
class FacebookConnect(SocialConnectView):
adapter_class = FacebookOAuth2Adapter
class TwitterConnect(SocialConnectView):
serializer_class = TwitterConnectSerializer
adapter_class = TwitterOAuthAdapter
class GithubConnect(SocialConnectView):
adapter_class = GitHubOAuth2Adapter
callback_url = CALLBACK_URL_YOU_SET_ON_GITHUB
client_class = OAuth2Client
In urls.py:
.. code-block:: python
urlpatterns += [
...,
url(r'^rest-auth/facebook/connect/$', FacebookConnect.as_view(), name='fb_connect')
url(r'^rest-auth/twitter/connect/$', TwitterConnect.as_view(), name='twitter_connect')
url(r'^rest-auth/github/connect/$', GithubConnect.as_view(), name='github_connect')
]
You can also use the following views to check all social accounts attached to the current authenticated user and disconnect selected social accounts:
.. code-block:: python
from rest_auth.registration.views import (
SocialAccountListView, SocialAccountDisconnectView
) )
urlpatterns += [
...,
url(
r'^socialaccounts/$',
SocialAccountListView.as_view(),
name='social_account_list'
),
url(
r'^socialaccounts/(?P<pk>\d+)/disconnect/$',
SocialAccountDisconnectView.as_view(),
name='social_account_disconnect'
)
]
JSON Web Token (JWT) Support (optional)
---------------------------------------
By default ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, follow these steps:
1. Install `djangorestframework-jwt <http://getblimp.github.io/django-rest-framework-jwt/>`_
- ``djangorestframework-jwt`` is currently the only supported JWT library.
2. The ``JWT_PAYLOAD_HANDLER`` and ``JWT_ENCODE_HANDLER`` settings are imported from the ``django-rest-framework-jwt`` settings object.
- Refer to `the library's documentation <http://getblimp.github.io/django-rest-framework-jwt/#additional-settings>`_ for information on using different encoders.
3. Add the following configuration value to your settings file to enable JWT authentication.
.. code-block:: python
REST_USE_JWT = True

View File

@ -2,25 +2,19 @@ from django.conf import settings
from rest_auth.serializers import ( from rest_auth.serializers import (
TokenSerializer as DefaultTokenSerializer, TokenSerializer as DefaultTokenSerializer,
JWTSerializer as DefaultJWTSerializer,
UserDetailsSerializer as DefaultUserDetailsSerializer, UserDetailsSerializer as DefaultUserDetailsSerializer,
LoginSerializer as DefaultLoginSerializer, LoginSerializer as DefaultLoginSerializer,
PasswordResetSerializer as DefaultPasswordResetSerializer, PasswordResetSerializer as DefaultPasswordResetSerializer,
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer, PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
PasswordChangeSerializer as DefaultPasswordChangeSerializer) PasswordChangeSerializer as DefaultPasswordChangeSerializer)
from .utils import import_callable, default_create_token from .utils import import_callable
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', {})
TokenSerializer = import_callable( TokenSerializer = import_callable(
serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer)) serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer))
JWTSerializer = import_callable(
serializers.get('JWT_SERIALIZER', DefaultJWTSerializer))
UserDetailsSerializer = import_callable( UserDetailsSerializer = import_callable(
serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer) serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer)
) )
@ -30,22 +24,16 @@ LoginSerializer = import_callable(
) )
PasswordResetSerializer = import_callable( PasswordResetSerializer = import_callable(
serializers.get( serializers.get('PASSWORD_RESET_SERIALIZER',
'PASSWORD_RESET_SERIALIZER', DefaultPasswordResetSerializer)
DefaultPasswordResetSerializer
)
) )
PasswordResetConfirmSerializer = import_callable( PasswordResetConfirmSerializer = import_callable(
serializers.get( serializers.get('PASSWORD_RESET_CONFIRM_SERIALIZER',
'PASSWORD_RESET_CONFIRM_SERIALIZER', DefaultPasswordResetConfirmSerializer)
DefaultPasswordResetConfirmSerializer
)
) )
PasswordChangeSerializer = import_callable( PasswordChangeSerializer = import_callable(
serializers.get( serializers.get('PASSWORD_CHANGE_SERIALIZER',
'PASSWORD_CHANGE_SERIALIZER', DefaultPasswordChangeSerializer)
DefaultPasswordChangeSerializer
)
) )

View File

@ -1,102 +0,0 @@
# Czech translations of Tivix/django-rest-auth
#
# This file is distributed under the same license as the Tivix/django-rest-auth package.
#
msgid ""
msgstr ""
"Project-Id-Version: Tivix/django-rest-auth\n"
"Report-Msgid-Bugs-To: https://github.com/Tivix/django-rest-auth/issues\n"
"POT-Creation-Date: 2018-06-27 23:05+0200\n"
"PO-Revision-Date: 2018-06-27 23:22+0200\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"Last-Translator: Václav Dohnal <vaclav.dohnal@gmail.com>\n"
"Language-Team: N/A\n"
"X-Generator: Poedit 2.0.8\n"
#: .\registration\serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr "View není definováno, předejte jej jako proměnnou kontextu"
#: .\registration\serializers.py:72
msgid "Define adapter_class in view"
msgstr "Definujte adapter_class ve view"
#: .\registration\serializers.py:91
msgid "Define callback_url in view"
msgstr "Definujte callback_url ve view"
#: .\registration\serializers.py:95
msgid "Define client_class in view"
msgstr "Definujte client_class ve view"
#: .\registration\serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr "Nesprávný vstup. access_token je povinný."
#: .\registration\serializers.py:125
msgid "Incorrect value"
msgstr "Nesprávná hodnota"
#: .\registration\serializers.py:139
msgid "User is already registered with this e-mail address."
msgstr "Uživatel s touto adresou je již registrován."
#: .\registration\serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr "Uživatel s touto adresou je již registrován."
#: .\registration\serializers.py:193
msgid "The two password fields didn't match."
msgstr "Zadaná hesla se neshodují."
#: .\registration\views.py:51
msgid "Verification e-mail sent."
msgstr "Ověřovací e-mail odeslán."
#: .\registration\views.py:98
msgid "ok"
msgstr "ok"
#: .\serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Musí obsahovat \"e-mail\" a \"heslo\"."
#: .\serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Musí obsahovat \"uživatelské jméno\" a \"heslo\"."
#: .\serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Musí obsahovat \"uživatelské jméno\" nebo \"e-mail\" a \"heslo\"."
#: .\serializers.py:95
msgid "User account is disabled."
msgstr "Uživatelský účet je zakázán."
#: .\serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Pomocí zadaných údajů se nelze přihlásit."
#: .\serializers.py:107
msgid "E-mail is not verified."
msgstr "E-mail není ověřený."
#: .\views.py:127
msgid "Successfully logged out."
msgstr "Byli jste úspěšně odhlášeni."
#: .\views.py:175
msgid "Password reset e-mail has been sent."
msgstr "E-mail pro resetování hesla byl odeslán."
#: .\views.py:201
msgid "Password has been reset with the new password."
msgstr "Vaše heslo bylo resetováno."
#: .\views.py:223
msgid "New password has been saved."
msgstr "Nové heslo bylo uloženo."

View File

@ -1,98 +0,0 @@
# 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: 2017-03-05 21:56-0800\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:53
msgid "View is not defined, pass it as a context variable"
msgstr "\"View\" ist nicht definiert, übergib es als Contextvariable"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Definier \"adapter_class\" in view"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Definier \"callback_url\" in view"
#: registration/serializers.py:81
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:91
msgid "ok"
msgstr "Ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Muss \"email\" und \"password\" enthalten."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Muss \"username\" und \"password\" enthalten."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Muss entweder \"username\" oder \"email\" und password \"password\""
#: serializers.py:95
msgid "User account is disabled."
msgstr "Der Useraccount ist deaktiviert."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "E-Mail Adresse ist nicht verifiziert."
#: views.py:126
msgid "Successfully logged out."
msgstr "Erfolgreich ausgeloggt."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "Die E-Mail zum Zurücksetzen des Passwortes wurde verschickt."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "Das Passwort wurde mit dem neuen Passwort ersetzt."
#: views.py:222
msgid "New password has been saved."
msgstr "Das neue Passwort wurde gespeichert."
#~ msgid "Error"
#~ msgstr "Fehler"

View File

@ -1,99 +0,0 @@
# 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: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Carlos de las Heras <cahersan@gmail.com>\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:53
msgid "View is not defined, pass it as a context variable"
msgstr "\"View\" no está definida, pásala como una variable de contexto"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Defina \"adapter_class\" en view"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Defina \"callback_url\" en view"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Defina \"client_class\" en view"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Entrada incorrecta. Se requiere \"access_token\" o \"code\"."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Valor incorrecto"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Ya existe un usuario registrado con esa dirección de correo electrónico."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Las contraseñas no coinciden"
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "Se ha enviado un correo de verificación."
#: registration/views.py:91
msgid "ok"
msgstr "Ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Debe incluir \"correo electrónico\" y \"contraseña\"."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Debe incluir \"nombre de usuario\" y \"contraseña\"."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Debe incluir \"nombre de usuario\" o \"correo electrónico\" y \"contraseña\"."
#: serializers.py:95
msgid "User account is disabled."
msgstr "Cuenta de usuario deshabilitada"
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "No puede iniciar sesión con las credenciales proporcionadas."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "El correo electrónico no ha sido verificado."
#: views.py:126
msgid "Successfully logged out."
msgstr "Sesión cerrada con éxito."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "Se ha enviado un correo electrónico para restablecer la contraseña."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "La contraseña ha sido restablecida con la nueva contraseña."
#: views.py:222
msgid "New password has been saved."
msgstr "La nueva contraseña ha sido guardada."

View File

@ -1,98 +0,0 @@
# 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.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-12-22 11:37-0800\n"
"PO-Revision-Date: 2017-02-14 13:27+0100\n"
"Language: fr\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"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 1.8.11\n"
#: registration/serializers.py:53
msgid "View is not defined, pass it as a context variable"
msgstr "La “View” nest pas définie, passez la en variable contextuelle"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Définissez “adapter_class” dans la vue"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Définissez “callback_url” dans la vue"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Définissez “client_class” dans la vue"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Paramètres incorrects. Il faut “access_token” ou “code”."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Paramètre incorrect"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Un utilisateur existe déjà avec cette adresse email."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Les deux mots de passes ne sont pas les mêmes."
#: registration/views.py:82
msgid "ok"
msgstr "Ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Doit inclure “email” et “password”."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Doit inclure “username” et “password”."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Doit inclure un “username” ou “email”, et un “password”."
#: serializers.py:95
msgid "User account is disabled."
msgstr "Le compte utilisateur est désactivé."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Connexion impossible avec les informations fournies."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "Ladresse email na pas été vérifiée."
#: views.py:114
msgid "Successfully logged out."
msgstr "Déconnexion effectuée avec succès."
#: views.py:162
msgid "Password reset e-mail has been sent."
msgstr "Lemail de réinitialisation du mot de passe a été envoyé."
#: views.py:184
msgid "Password has been reset with the new password."
msgstr "Le mot de passe a été réinitialisé."
#: views.py:202
msgid "New password has been saved."
msgstr "Le nouveau mot de passe est sauvé."
#~ msgid "Error"
#~ msgstr "Fehler"

View File

@ -1,99 +0,0 @@
# 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: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 2018-03-20 17:52+0900\n"
"Last-Translator: Jeonsgoo Park <toracle@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: ko\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:53
msgid "View is not defined, pass it as a context variable"
msgstr "View가 정의되지 않았습니다. 컨텍스트 변수에 포함해주세요"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "view에 adapter_class를 정의하세요"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "view에 callback_url을 정의하세요"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "view에 client_class를 정의하세요"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "올바르지 않은 입력입니다. access_token이나 code가 필요합니다."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "올바르지 않은 값"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "이미 이 이메일 주소로 등록된 사용자가 있습니다."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "두 개의 패스워드 필드가 서로 맞지 않습니다."
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "확인 이메일을 발송했습니다."
#: registration/views.py:91
msgid "ok"
msgstr "ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "\"email\"과 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "\"username\"과 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "\"username\"이나 \"email\", 그리고 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:95
msgid "User account is disabled."
msgstr "사용자 계정이 비활성화 되어있습니다."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "주어진 자격 증명으로 로그인이 불가능합니다."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "이메일 주소가 확인되지 않았습니다."
#: views.py:126
msgid "Successfully logged out."
msgstr "로그아웃되었습니다."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "패스워드 초기화 이메일이 발송되었습니다."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "새로운 패스워드로 패스워드가 초기화 되었습니다."
#: views.py:222
msgid "New password has been saved."
msgstr "새로운 패스워드가 저장되었습니다."

View File

@ -1,99 +0,0 @@
# 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.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 2017-03-11 12:10+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"Language-Team: \n"
"X-Generator: Poedit 1.8.11\n"
"Last-Translator: \n"
"Language: pl\n"
#: registration/serializers.py:53
msgid "View is not defined, pass it as a context variable"
msgstr "Widok nie został zdefiniowany, przekaż go przez zmienną \"context\""
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Zdefiniuj \"adapter_class\" w widoku"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Zdefiniuj \"callback_url\" w widoku"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Zdefiniuj \"client_class\" w widoku"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Podano błędne dane. \"access_token\" lub \"code\" są wymagne."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Niepoprawna wartość."
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Istnieje już użytkownik z takim adresem email."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Hasła nie są identyczne."
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "Email weryfikacyjny został wysłany."
#: registration/views.py:91
msgid "ok"
msgstr "ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Musisz podać email i hasło."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Musisz podać nazwę użytkownika i hasło."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Musisz podać nazwę użytkownika (lub email) i hasło."
#: serializers.py:95
msgid "User account is disabled."
msgstr "Konto użytkownika zostało wyłączone."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Podane dane do logowania są niepoprawne."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "Email nie został zweryfikowany."
#: views.py:126
msgid "Successfully logged out."
msgstr "Wylogowano."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "Email z linkiem do resetu hasła został wysłany."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "Hasło zostało zresetowane."
#: views.py:222
msgid "New password has been saved."
msgstr "Nowe hasło zostało zapisane."

View File

@ -1,99 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Bruno Barreto Freitas <brunobarretofreitas@outlook.com>, 2019.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-04-16 09:48-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Bruno Barreto Freitas <brunobarretofreitas@outlook.com>\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:53
msgid "View is not defined, pass it as a context variable"
msgstr "\"View\" não está definida, passe-a como uma variável de contexto"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Defina \"adapter_class\" na view"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Defina \"callback_url\" na view"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Defina \"client_class\" na view"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Entrada incorreta. \"access_token\" ou \"code\" são obrigatórios."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Valor incorreto"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Já existe um usuário cadastrado com este endereço de e-mail."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Os dois campos de senha não correspondem."
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "E-mail de verificação enviado."
#: registration/views.py:91
msgid "ok"
msgstr "Ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Deve-se incluir \"email\" e \"password\"."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Deve-se incluir \"username\" e \"password\"."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Deve-se incluir \"username\" ou \"email\" e \"password\"."
#: serializers.py:95
msgid "User account is disabled."
msgstr "Conta de usuário está desativada"
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Não foi possível realizar o login com as credenciais fornecidas."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "E-mail não foi verificado."
#: views.py:126
msgid "Successfully logged out."
msgstr "Logout realizado com sucesso."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "E-mail de redefinição de senha foi enviado."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "Senha foi redefinida com a nova senha."
#: views.py:222
msgid "New password has been saved."
msgstr "Nova senha foi salva com sucesso."

View File

@ -1,101 +0,0 @@
# 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.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 2016-08-01 07:48+0300\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
"X-Generator: Poedit 1.8.5\n"
#: registration/serializers.py:53
msgid "View is not defined, pass it as a context variable"
msgstr "View неизвестен, передайте его как переменную контекста"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Установите adapter_class в view"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Установите callback_url в view"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Установите client_class в view"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Некорректный ввод. Необходим access_token или code."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Некорректное значение"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Пользователь с таким e-mail адресом уже зарегистрирован."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Пароли не совпадают."
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "Письмо с подтверждением выслано."
#: registration/views.py:91
msgid "ok"
msgstr "ок"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Должно включать \"email\" и \"password\"."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Должно включать \"username\" и \"password\"."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Должно включать либо \"username\" либо \"email\" и \"password\"."
#: serializers.py:95
msgid "User account is disabled."
msgstr "Пользовательский аккаунт отключён."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Невозможно войти в систему с указанными учётными данными."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "E-mail не подтверждён."
#: views.py:126
msgid "Successfully logged out."
msgstr "Успешно вышли."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "Письмо с инструкциями по восстановлению пароля выслано."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "Пароль изменён на новый."
#: views.py:222
msgid "New password has been saved."
msgstr "Новый пароль сохранён."

View File

@ -1,95 +0,0 @@
# 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.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 2018-10-13 19:37+0300\n"
"Language: tr\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"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 2.2\n"
#: registration/serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr "“View” tanımlanmadı, “context” değişkeni olarak tanımla"
#: registration/serializers.py:72
msgid "Define adapter_class in view"
msgstr "“view” içerisinde “adapter_class” tanımla"
#: registration/serializers.py:91
msgid "Define callback_url in view"
msgstr "“view” içerisinde “callback_url” tanımla"
#: registration/serializers.py:95
msgid "Define client_class in view"
msgstr "“view” içerisinde “client_class” tanımla"
#: registration/serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr "Geçersiz girdi. “access_token” veya “code” gerekli."
#: registration/serializers.py:125
msgid "Incorrect value"
msgstr "Geçersiz değer"
#: registration/serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr "Bu e-posta adresi ile bir kullanıcı zaten kayıt olmuştu."
#: registration/serializers.py:193
msgid "The two password fields didn't match."
msgstr "İki şifre alanı eşleşmiyor."
#: registration/views.py:98
msgid "ok"
msgstr "tamam"
#: serializers.py:33
msgid "Must include \"email\" and \"password\"."
msgstr "\"email\" ve \"password\" içermelidir."
#: serializers.py:44
msgid "Must include \"username\" and \"password\"."
msgstr "“username\" und \"password\" içermelidir."
#: serializers.py:57
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Ya ”username\" yada \"email\" ve \"password\" içermelidir."
#: serializers.py:98
msgid "User account is disabled."
msgstr "Kullanıcı hesap pasiftir."
#: serializers.py:101
msgid "Unable to log in with provided credentials."
msgstr "Sağlanan kimlik bilgileri ile giriş yapılamıyor."
#: serializers.py:110
msgid "E-mail is not verified."
msgstr "E-posta adresi doğrulanmadı."
#: views.py:127
msgid "Successfully logged out."
msgstr "Başarılı bir şekilde çıkış yapıldı."
#: views.py:175
msgid "Password reset e-mail has been sent."
msgstr "Şifre sıfırlama e-postası gönderildi."
#: views.py:201
msgid "Password has been reset with the new password."
msgstr "Yeni şifre ile şifre sıfırlandı."
#: views.py:223
msgid "New password has been saved."
msgstr "Yeni şifre kaydedildi."

View File

@ -1,104 +0,0 @@
# 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: 2018-10-28 11:41+0800\n"
"PO-Revision-Date: 2018-10-28 11:45+0806\n"
"Last-Translator: b' <admin@xx.com>'\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=1; plural=0;\n"
"X-Translated-Using: django-rosetta 0.9.0\n"
#: registration/serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr "View未定义请通过context变量传入"
#: registration/serializers.py:72
msgid "Define adapter_class in view"
msgstr "请在View中定义adapter_class"
#: registration/serializers.py:91
msgid "Define callback_url in view"
msgstr "请在view中定义callback_url"
#: registration/serializers.py:95
msgid "Define client_class in view"
msgstr "请在view中定义client_class"
#: registration/serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr "输入错误。access_token或code是必填项。"
#: registration/serializers.py:125
msgid "Incorrect value"
msgstr "错误的值"
#: registration/serializers.py:139
msgid "User is already registered with this e-mail address."
msgstr "该邮箱地址已被注册。"
#: registration/serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr "该邮箱地址已被注册。"
#: registration/serializers.py:193
msgid "The two password fields didn't match."
msgstr "两次输入的密码不相同"
#: registration/views.py:51
msgid "Verification e-mail sent."
msgstr "验证邮件已发送。"
#: registration/views.py:98
msgid "ok"
msgstr "好的"
#: serializers.py:33
msgid "Must include \"email\" and \"password\"."
msgstr "比如包含\"email\"和\"password\"。"
#: serializers.py:44
msgid "Must include \"username\" and \"password\"."
msgstr "比如包含\"username\"和\"password\"。"
#: serializers.py:57
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "比如包含\"username\"\"email\"\"password\"其中一个。"
#: serializers.py:98
msgid "User account is disabled."
msgstr "用户账号已被禁用。"
#: serializers.py:101
msgid "Unable to log in with provided credentials."
msgstr "无法使用提供的信息登录。"
#: serializers.py:110
msgid "E-mail is not verified."
msgstr "邮箱未验证。"
#: views.py:127
msgid "Successfully logged out."
msgstr "已成功登出。"
#: views.py:175
msgid "Password reset e-mail has been sent."
msgstr "密码重置邮件已发送。"
#: views.py:201
msgid "Password has been reset with the new password."
msgstr "密码重置成功。"
#: views.py:223
msgid "New password has been saved."
msgstr "新密码已设置成功。"

View File

@ -1,103 +0,0 @@
# 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: 2018-10-28 11:41+0800\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=1; plural=0;\n"
#: registration/serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr ""
#: registration/serializers.py:72
msgid "Define adapter_class in view"
msgstr ""
#: registration/serializers.py:91
msgid "Define callback_url in view"
msgstr ""
#: registration/serializers.py:95
msgid "Define client_class in view"
msgstr ""
#: registration/serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr ""
#: registration/serializers.py:125
msgid "Incorrect value"
msgstr ""
#: registration/serializers.py:139
msgid "User is already registered with this e-mail address."
msgstr ""
#: registration/serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr ""
#: registration/serializers.py:193
msgid "The two password fields didn't match."
msgstr ""
#: registration/views.py:51
msgid "Verification e-mail sent."
msgstr ""
#: registration/views.py:98
msgid "ok"
msgstr ""
#: serializers.py:33
msgid "Must include \"email\" and \"password\"."
msgstr ""
#: serializers.py:44
msgid "Must include \"username\" and \"password\"."
msgstr ""
#: serializers.py:57
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr ""
#: serializers.py:98
msgid "User account is disabled."
msgstr ""
#: serializers.py:101
msgid "Unable to log in with provided credentials."
msgstr ""
#: serializers.py:110
msgid "E-mail is not verified."
msgstr ""
#: views.py:127
msgid "Successfully logged out."
msgstr ""
#: views.py:175
msgid "Password reset e-mail has been sent."
msgstr ""
#: views.py:201
msgid "Password has been reset with the new password."
msgstr ""
#: views.py:223
msgid "New password has been saved."
msgstr ""

View File

@ -1,10 +1,3 @@
from django.conf import settings # from django.db import models
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))

View File

@ -1,19 +0,0 @@
from django.conf import settings
from rest_framework.permissions import AllowAny
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))
def register_permission_classes():
permission_classes = [AllowAny, ]
for klass in getattr(settings, 'REST_AUTH_REGISTER_PERMISSION_CLASSES', tuple()):
permission_classes.append(import_callable(klass))
return tuple(permission_classes)

View File

@ -1,217 +1,43 @@
from django.http import HttpRequest
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model
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
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.providers.base import AuthProcess
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
from allauth.socialaccount.helpers import complete_social_login
class SocialAccountSerializer(serializers.ModelSerializer):
"""
serialize allauth SocialAccounts for use with a REST API
"""
class Meta:
model = SocialAccount
fields = (
'id',
'provider',
'uid',
'last_login',
'date_joined',
)
class SocialLoginSerializer(serializers.Serializer): class SocialLoginSerializer(serializers.Serializer):
access_token = serializers.CharField(required=False, allow_blank=True)
code = serializers.CharField(required=False, allow_blank=True)
def _get_request(self): access_token = serializers.CharField(required=True)
request = self.context.get('request')
if not isinstance(request, HttpRequest):
request = request._request
return request
def get_social_login(self, adapter, app, token, response): def validate_access_token(self, attrs, source):
""" access_token = attrs[source]
:param adapter: allauth.socialaccount Adapter subclass.
Usually OAuthAdapter or Auth2Adapter
:param app: `allauth.socialaccount.SocialApp` instance
:param token: `allauth.socialaccount.SocialToken` instance
:param response: Provider's response for OAuth1. Not used in the
:returns: A populated instance of the
`allauth.socialaccount.SocialLoginView` instance
"""
request = self._get_request()
social_login = adapter.complete_login(request, app, token, response=response)
social_login.token = token
return social_login
def validate(self, attrs):
view = self.context.get('view') view = self.context.get('view')
request = self._get_request() request = self.context.get('request')
if not view: if not view:
raise serializers.ValidationError( raise serializers.ValidationError('View is not defined, pass it ' +
_("View is not defined, pass it as a context variable") 'as a context variable')
) self.adapter_class = getattr(view, 'adapter_class', None)
adapter_class = getattr(view, 'adapter_class', None) if not self.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(request) self.adapter = self.adapter_class()
app = adapter.get_provider().get_app(request) app = self.adapter.get_provider().get_app(request)
token = self.adapter.parse_token({'access_token': access_token})
# More info on code vs access_token token.app = app
# http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token
# Case 1: We received the access_token
if attrs.get('access_token'):
access_token = attrs.get('access_token')
# Case 2: We received the authorization code
elif attrs.get('code'):
self.callback_url = getattr(view, 'callback_url', None)
self.client_class = getattr(view, 'client_class', None)
if not self.callback_url:
raise serializers.ValidationError(
_("Define callback_url in view")
)
if not self.client_class:
raise serializers.ValidationError(
_("Define client_class in view")
)
code = attrs.get('code')
provider = adapter.get_provider()
scope = provider.get_scope(request)
client = self.client_class(
request,
app.client_id,
app.secret,
adapter.access_token_method,
adapter.access_token_url,
self.callback_url,
scope
)
token = client.get_access_token(code)
access_token = token['access_token']
else:
raise serializers.ValidationError(
_("Incorrect input. access_token or code is required."))
social_token = adapter.parse_token({'access_token': access_token})
social_token.app = app
try: try:
login = self.get_social_login(adapter, app, social_token, access_token) login = self.adapter.complete_login(request, app, token,
response=access_token)
token.account = login.account
login.token = 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:
# We have an account already signed up in a different flow
# with the same email address: raise an exception.
# This needs to be handled in the frontend. We can not just
# link up the accounts due to security constraints
if allauth_settings.UNIQUE_EMAIL:
# Do we have an account already with this email address?
account_exists = get_user_model().objects.filter(
email=login.user.email,
).exists()
if account_exists:
raise serializers.ValidationError(
_("User is already registered with this e-mail address.")
)
login.lookup() login.lookup()
login.save(request, connect=True) login.save(request, connect=True)
self.object = {'user': login.account.user}
attrs['user'] = login.account.user
return attrs return attrs
class SocialConnectMixin(object):
def get_social_login(self, *args, **kwargs):
"""
Set the social login process state to connect rather than login
Refer to the implementation of get_social_login in base class and to the
allauth.socialaccount.helpers module complete_social_login function.
"""
social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
social_login.state['process'] = AuthProcess.CONNECT
return social_login
class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
pass
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(write_only=True)
password2 = serializers.CharField(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()

View File

@ -1,11 +1,11 @@
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.conf.urls import url from django.conf.urls import patterns, url
from .views import RegisterView, VerifyEmailView from .views import Register, VerifyEmail
urlpatterns = [ urlpatterns = patterns('',
url(r'^$', RegisterView.as_view(), name='rest_register'), url(r'^$', Register.as_view(), name='rest_register'),
url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'), url(r'^verify-email/$', VerifyEmail.as_view(), name='rest_verify_email'),
# This url is used by django-allauth and empty TemplateView is # This url is used by django-allauth and empty TemplateView is
# defined just to allow reverse() call inside app, for example when email # defined just to allow reverse() call inside app, for example when email
@ -17,7 +17,8 @@ urlpatterns = [
# with proper key. # with proper key.
# If you don't want to use API on that step, then just use ConfirmEmailView # If you don't want to use API on that step, then just use ConfirmEmailView
# view from: # view from:
# django-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py # 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'),
] )

View File

@ -1,186 +1,77 @@
from django.conf import settings
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters
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
IsAuthenticated)
from rest_framework.generics import CreateAPIView, ListAPIView, GenericAPIView
from rest_framework.exceptions import NotFound
from rest_framework import status from rest_framework import status
from allauth.account.adapter import get_adapter 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 as allauth_settings from allauth.account import app_settings
from allauth.socialaccount import signals
from allauth.socialaccount.adapter import get_adapter as get_social_adapter
from allauth.socialaccount.models import SocialAccount
from rest_auth.app_settings import (TokenSerializer, from rest_auth.serializers import UserDetailsSerializer
JWTSerializer, from rest_auth.registration.serializers import SocialLoginSerializer
create_token) from rest_auth.views import Login
from rest_auth.models import TokenModel
from rest_auth.registration.serializers import (VerifyEmailSerializer,
SocialLoginSerializer,
SocialAccountSerializer,
SocialConnectSerializer)
from rest_auth.utils import jwt_encode
from rest_auth.views import LoginView
from .app_settings import RegisterSerializer, register_permission_classes
sensitive_post_parameters_m = method_decorator(
sensitive_post_parameters('password1', 'password2')
)
class RegisterView(CreateAPIView): class Register(APIView, SignupView):
serializer_class = RegisterSerializer
permission_classes = register_permission_classes()
token_model = TokenModel
@sensitive_post_parameters_m permission_classes = (AllowAny,)
def dispatch(self, *args, **kwargs): user_serializer_class = UserDetailsSerializer
return super(RegisterView, self).dispatch(*args, **kwargs) allowed_methods = ('POST', 'OPTIONS', 'HEAD')
def get_response_data(self, user): def get(self, *args, **kwargs):
if allauth_settings.EMAIL_VERIFICATION == \ return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
allauth_settings.EmailVerificationMethod.MANDATORY:
return {"detail": _("Verification e-mail sent.")}
if getattr(settings, 'REST_USE_JWT', False): def put(self, *args, **kwargs):
data = { return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
'user': user,
'token': self.token def form_valid(self, form):
} self.user = form.save(self.request)
return JWTSerializer(data).data return complete_signup(self.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: else:
return TokenSerializer(user.auth_token).data return self.get_response_with_errors()
def create(self, request, *args, **kwargs): def get_response(self):
serializer = self.get_serializer(data=request.data) serializer = self.user_serializer_class(instance=self.user)
serializer.is_valid(raise_exception=True) return Response(serializer.data, status=status.HTTP_201_CREATED)
user = self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(self.get_response_data(user), def get_response_with_errors(self):
status=status.HTTP_201_CREATED, return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
headers=headers)
def perform_create(self, serializer):
user = serializer.save(self.request)
if getattr(settings, 'REST_USE_JWT', False):
self.token = jwt_encode(user)
else:
create_token(self.token_model, user, serializer)
complete_signup(self.request._request, user,
allauth_settings.EMAIL_VERIFICATION,
None)
return user
class VerifyEmailView(APIView, ConfirmEmailView): class VerifyEmail(APIView, ConfirmEmailView):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
allowed_methods = ('POST', 'OPTIONS', 'HEAD') allowed_methods = ('POST', 'OPTIONS', 'HEAD')
def get_serializer(self, *args, **kwargs): def get(self, *args, **kwargs):
return VerifyEmailSerializer(*args, **kwargs) return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) self.kwargs['key'] = self.request.DATA.get('key', '')
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({'detail': _('ok')}, status=status.HTTP_200_OK) return Response({'message': 'ok'}, status=status.HTTP_200_OK)
class SocialLoginView(LoginView): class SocialLogin(Login):
""" """
class used for social authentications class used for social authentications
example usage for facebook with access_token example usage for facebook
-------------
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
class FacebookLogin(SocialLogin):
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter adapter_class = FacebookOAuth2Adapter
-------------
example usage for facebook with code
-------------
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
client_class = OAuth2Client
callback_url = 'localhost:8000'
-------------
""" """
serializer_class = SocialLoginSerializer serializer_class = SocialLoginSerializer
def process_login(self):
get_adapter(self.request).login(self.request, self.user)
class SocialConnectView(LoginView):
"""
class used for social account linking
example usage for facebook with access_token
-------------
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
class FacebookConnect(SocialConnectView):
adapter_class = FacebookOAuth2Adapter
-------------
"""
serializer_class = SocialConnectSerializer
permission_classes = (IsAuthenticated,)
def process_login(self):
get_adapter(self.request).login(self.request, self.user)
class SocialAccountListView(ListAPIView):
"""
List SocialAccounts for the currently logged in user
"""
serializer_class = SocialAccountSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return SocialAccount.objects.filter(user=self.request.user)
class SocialAccountDisconnectView(GenericAPIView):
"""
Disconnect SocialAccount from remote service for
the currently logged in user
"""
serializer_class = SocialConnectSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return SocialAccount.objects.filter(user=self.request.user)
def post(self, request, *args, **kwargs):
accounts = self.get_queryset()
account = accounts.filter(pk=kwargs['pk']).first()
if not account:
raise NotFound
get_social_adapter(self.request).validate_disconnect(account, accounts)
account.delete()
signals.social_account_removed.send(
sender=SocialAccount,
request=self.request,
socialaccount=account
)
return Response(self.get_serializer(account).data)

View File

@ -1,115 +1,30 @@
from django.contrib.auth import get_user_model, authenticate from django.contrib.auth import get_user_model
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.encoding import force_text
from rest_framework import serializers, exceptions from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
from .models import TokenModel
from .utils import import_callable
# Get the UserModel
UserModel = get_user_model()
class LoginSerializer(serializers.Serializer): class LoginSerializer(AuthTokenSerializer):
username = serializers.CharField(required=False, allow_blank=True)
email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={'input_type': 'password'})
def authenticate(self, **kwargs):
return authenticate(self.context['request'], **kwargs)
def _validate_email(self, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
else:
msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg)
return user
def _validate_username(self, username, password):
user = None
if username and password:
user = self.authenticate(username=username, password=password)
else:
msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg)
return user
def _validate_username_email(self, username, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
elif username and password:
user = self.authenticate(username=username, password=password)
else:
msg = _('Must include either "username" or "email" and "password".')
raise exceptions.ValidationError(msg)
return user
def validate(self, attrs): def validate(self, attrs):
username = attrs.get('username') attrs = super(LoginSerializer, self).validate(attrs)
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
elif 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:
# Authentication without using allauth
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?
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise exceptions.ValidationError(msg)
else:
msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationError(msg)
# If required, is the email verified?
if 'rest_auth.registration' in settings.INSTALLED_APPS: if 'rest_auth.registration' in settings.INSTALLED_APPS:
from allauth.account import app_settings from allauth.account import app_settings
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY: if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
user = attrs['user']
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
return attrs return attrs
@ -119,59 +34,36 @@ class TokenSerializer(serializers.ModelSerializer):
""" """
class Meta: class Meta:
model = TokenModel model = Token
fields = ('key',) fields = ('key',)
class UserDetailsSerializer(serializers.ModelSerializer): class UserDetailsSerializer(serializers.ModelSerializer):
""" """
User model w/o password User model w/o password
""" """
class Meta: class Meta:
model = UserModel model = get_user_model()
fields = ('pk', 'username', 'email', 'first_name', 'last_name') fields = ('username', 'email', 'first_name', 'last_name')
read_only_fields = ('email', )
class JWTSerializer(serializers.Serializer):
"""
Serializer for JWT authentication.
"""
token = serializers.CharField()
user = serializers.SerializerMethodField()
def get_user(self, obj):
"""
Required to allow using custom USER_DETAILS_SERIALIZER in
JWTSerializer. Defining it here to avoid circular imports
"""
rest_auth_serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
JWTUserDetailsSerializer = import_callable(
rest_auth_serializers.get('USER_DETAILS_SERIALIZER', UserDetailsSerializer)
)
user_data = JWTUserDetailsSerializer(obj['user'], context=self.context).data
return user_data
class PasswordResetSerializer(serializers.Serializer): class PasswordResetSerializer(serializers.Serializer):
""" """
Serializer for requesting a password reset e-mail. Serializer for requesting a password reset e-mail.
""" """
email = serializers.EmailField() email = serializers.EmailField()
password_reset_form_class = PasswordResetForm password_reset_form_class = PasswordResetForm
def get_email_options(self): def validate_email(self, attrs, source):
"""Override this method to change default e-mail options"""
return {}
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=attrs)
if not self.reset_form.is_valid(): if not self.reset_form.is_valid():
raise serializers.ValidationError(self.reset_form.errors) raise serializers.ValidationError('Error')
return attrs
return value
def save(self): def save(self):
request = self.context.get('request') request = self.context.get('request')
@ -181,19 +73,20 @@ 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)
class PasswordResetConfirmSerializer(serializers.Serializer): class PasswordResetConfirmSerializer(serializers.Serializer):
""" """
Serializer for requesting a password reset e-mail. Serializer for requesting a password reset e-mail.
""" """
new_password1 = serializers.CharField(max_length=128) new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128) new_password2 = serializers.CharField(max_length=128)
uid = serializers.CharField()
token = serializers.CharField() uid = serializers.CharField(required=True)
token = serializers.CharField(required=True)
set_password_form_class = SetPasswordForm set_password_form_class = SetPasswordForm
@ -202,31 +95,32 @@ 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 = force_text(uid_decoder(attrs['uid'])) uid = 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']}) self._errors['uid'] = ['Invalid value']
self.custom_validation(attrs) self.custom_validation(attrs)
# Construct SetPasswordForm instance
self.set_password_form = self.set_password_form_class(
user=self.user, data=attrs
)
if not self.set_password_form.is_valid():
raise serializers.ValidationError(self.set_password_form.errors)
if not default_token_generator.check_token(self.user, attrs['token']):
raise ValidationError({'token': ['Invalid value']})
return attrs # Construct SetPasswordForm instance
self.set_password_form = self.set_password_form_class(user=self.user,
data=attrs)
if not self.set_password_form.is_valid():
self._errors['token'] = ['Invalid value']
if not default_token_generator.check_token(self.user, attrs['token']):
self._errors['token'] = ['Invalid value']
def save(self): def save(self):
return self.set_password_form.save() self.set_password_form.save()
class PasswordChangeSerializer(serializers.Serializer): class PasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(max_length=128) old_password = serializers.CharField(max_length=128)
new_password1 = serializers.CharField(max_length=128) new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128) new_password2 = serializers.CharField(max_length=128)
@ -234,43 +128,30 @@ class PasswordChangeSerializer(serializers.Serializer):
set_password_form_class = SetPasswordForm set_password_form_class = SetPasswordForm
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.old_password_field_enabled = getattr( self.old_password_field_enabled = getattr(settings,
settings, 'OLD_PASSWORD_FIELD_ENABLED', False '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:
self.fields.pop('old_password') self.fields.pop('old_password')
self.request = self.context.get('request') self.request = self.context.get('request')
self.user = getattr(self.request, 'user', None) self.user = self.request.user
def validate_old_password(self, value): def validate_old_password(self, attrs, source):
invalid_password_conditions = ( if self.old_password_field_enabled and \
self.old_password_field_enabled, not self.user.check_password(attrs.get(source, '')):
self.user, raise serializers.ValidationError('Invalid password')
not self.user.check_password(value) return attrs
)
if all(invalid_password_conditions):
err_msg = _("Your old password was entered incorrectly. Please enter it again.")
raise serializers.ValidationError(err_msg)
return value
def validate(self, attrs): def validate(self, attrs):
self.set_password_form = self.set_password_form_class( self.set_password_form = self.set_password_form_class(user=self.user,
user=self.user, data=attrs data=attrs)
)
if not self.set_password_form.is_valid(): if not self.set_password_form.is_valid():
raise serializers.ValidationError(self.set_password_form.errors) self._errors = self.set_password_form.errors
return None
return attrs return attrs
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)

View File

@ -1,81 +0,0 @@
from django.conf import settings
from django.http import HttpRequest
from rest_framework import serializers
# Import is needed only if we are using social login, in which
# case the allauth.socialaccount will be declared
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount.models import SocialToken
from allauth.socialaccount.providers.oauth.client import OAuthError
from rest_auth.registration.serializers import SocialConnectMixin
class TwitterLoginSerializer(serializers.Serializer):
access_token = serializers.CharField()
token_secret = serializers.CharField()
def _get_request(self):
request = self.context.get('request')
if not isinstance(request, HttpRequest):
request = request._request
return request
def get_social_login(self, adapter, app, token, response):
"""
:param adapter: allauth.socialaccount Adapter subclass.
Usually OAuthAdapter or Auth2Adapter
:param app: `allauth.socialaccount.SocialApp` instance
:param token: `allauth.socialaccount.SocialToken` instance
:param response: Provider's response for OAuth1. Not used in the
:returns: A populated instance of the
`allauth.socialaccount.SocialLoginView` instance
"""
request = self._get_request()
social_login = adapter.complete_login(request, app, token,
response=response)
social_login.token = token
return social_login
def validate(self, attrs):
view = self.context.get('view')
request = self._get_request()
if not view:
raise serializers.ValidationError(
"View is not defined, pass it as a context variable"
)
adapter_class = getattr(view, 'adapter_class', None)
if not adapter_class:
raise serializers.ValidationError("Define adapter_class in view")
adapter = adapter_class(request)
app = adapter.get_provider().get_app(request)
access_token = attrs.get('access_token')
token_secret = attrs.get('token_secret')
request.session['oauth_api.twitter.com_access_token'] = {
'oauth_token': access_token,
'oauth_token_secret': token_secret,
}
token = SocialToken(token=access_token, token_secret=token_secret)
token.app = app
try:
login = self.get_social_login(adapter, app, token, access_token)
complete_social_login(request, login)
except OAuthError as e:
raise serializers.ValidationError(str(e))
if not login.is_existing:
login.lookup()
login.save(request, connect=True)
attrs['user'] = login.account.user
return attrs
class TwitterConnectSerializer(SocialConnectMixin, TwitterLoginSerializer):
pass

22
rest_auth/test_urls.py Normal file
View File

@ -0,0 +1,22 @@
from django.conf.urls import patterns, url, include
from django.views.generic import TemplateView
from django.contrib.auth.tests import urls
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from rest_auth.urls import urlpatterns
from rest_auth.registration.views import SocialLogin
class FacebookLogin(SocialLogin):
adapter_class = FacebookOAuth2Adapter
urlpatterns += patterns('',
url(r'^rest-registration/', include('rest_auth.registration.urls')),
url(r'^test-admin/', include(urls)),
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
name='account_email_verification_sent'),
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
name='account_confirm_email'),
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login')
)

416
rest_auth/tests.py Normal file
View File

@ -0,0 +1,416 @@
import json
from datetime import datetime, date, time
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.client import Client, MULTIPART_CONTENT
from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.core import mail
from django.test.utils import override_settings
from django.contrib.sites.models import Site
from allauth.socialaccount.models import SocialApp
import responses
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 CustomJSONEncoder(json.JSONEncoder):
# """
# Convert datetime/date objects into isoformat
# """
# def default(self, obj):
# if isinstance(obj, (datetime, date, time)):
# return obj.isoformat()
# else:
# return super(CustomJSONEncoder, self).default(obj)
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 not 'content_type' 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()
# -----------------------
# T E S T H E R E
# -----------------------
class APITestCase1(TestCase, BaseAPITestCase):
"""
Case #1:
- user profile: defined
- custom registration: backend defined
"""
urls = 'rest_auth.test_urls'
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
NEW_PASS = 'new-test-pass'
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
# data without user profile
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS
}
REGISTRATION_DATA_WITH_EMAIL = REGISTRATION_DATA.copy()
REGISTRATION_DATA_WITH_EMAIL['email'] = EMAIL
BASIC_USER_DATA = {
'first_name': "John",
'last_name': 'Smith',
'email': EMAIL
}
USER_DATA = BASIC_USER_DATA.copy()
USER_DATA['newsletter_subscribe'] = True
def setUp(self):
self.init()
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')
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)
def _generate_uid_and_token(self, user):
result = {}
from django.utils.encoding import force_bytes
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
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
result['token'] = default_token_generator.make_token(user)
return result
def test_login(self):
payload = {
"username": self.USERNAME,
"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 = User.objects.create_user(self.USERNAME, '', 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']
self.post(self.password_change_url, status_code=400)
# test inactive user
user.is_active = False
user.save()
self.post(self.login_url, data=payload, status_code=400)
# test wrong username/password
payload = {
"username": self.USERNAME + '?',
"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):
login_payload = {
"username": self.USERNAME,
"password": self.PASS
}
User.objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=login_payload, status_code=200)
self.token = self.response.json['key']
new_password_payload = {
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=200)
# user should not be able to login using old password
self.post(self.login_url, data=login_payload, status_code=400)
# new password should work
login_payload['password'] = new_password_payload['new_password1']
self.post(self.login_url, data=login_payload, status_code=200)
# pass1 and pass2 are not equal
new_password_payload = {
"new_password1": "new_person1",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=400)
# send empty payload
self.post(self.password_change_url, data={}, status_code=400)
@override_settings(OLD_PASSWORD_FIELD_ENABLED=True)
def test_password_change_with_old_password(self):
login_payload = {
"username": self.USERNAME,
"password": self.PASS
}
User.objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=login_payload, status_code=200)
self.token = self.response.json['key']
new_password_payload = {
"old_password": "%s!" % self.PASS, # wrong password
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=400)
new_password_payload = {
"old_password": self.PASS,
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=200)
# user should not be able to login using old password
self.post(self.login_url, data=login_payload, status_code=400)
# new password should work
login_payload['password'] = new_password_payload['new_password1']
self.post(self.login_url, data=login_payload, status_code=200)
def test_password_reset(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
# call password reset
mail_count = len(mail.outbox)
payload = {'email': self.EMAIL}
self.post(self.password_reset_url, data=payload, status_code=200)
self.assertEqual(len(mail.outbox), mail_count + 1)
url_kwargs = self._generate_uid_and_token(user)
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': url_kwargs['uid'],
'token': url_kwargs['token']
}
url = reverse('rest_password_reset_confirm')
self.post(url, data=data, status_code=200)
payload = {
"username": self.USERNAME,
"password": self.NEW_PASS
}
self.post(self.login_url, data=payload, status_code=200)
def test_user_details(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=200)
self.token = self.response.json['key']
self.get(self.user_url, status_code=200)
self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
user = User.objects.get(pk=user.pk)
self.assertEqual(user.first_name, self.response.json['first_name'])
self.assertEqual(user.last_name, self.response.json['last_name'])
self.assertEqual(user.email, self.response.json['email'])
def test_registration(self):
user_count = User.objects.all().count()
# test empty payload
self.post(self.register_url, data={}, status_code=400)
self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertEqual(User.objects.all().count(), user_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
self._login()
self._logout()
@override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True
)
def test_registration_with_email_verification(self):
user_count = User.objects.all().count()
mail_count = len(mail.outbox)
# test empty payload
self.post(self.register_url, data={},
status_code=status.HTTP_400_BAD_REQUEST)
self.post(self.register_url, data=self.REGISTRATION_DATA_WITH_EMAIL,
status_code=status.HTTP_201_CREATED)
self.assertEqual(User.objects.all().count(), user_count + 1)
self.assertEqual(len(mail.outbox), mail_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
# email is not verified yet
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload,
status=status.HTTP_400_BAD_REQUEST)
# veirfy 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)
# try to login again
self._login()
self._logout()
class TestSocialAuth(TestCase, BaseAPITestCase):
urls = 'rest_auth.test_urls'
def setUp(self):
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.fb_login_url = reverse('fb_login')
@responses.activate
def test_failed_social_auth(self):
# fake response
responses.add(responses.GET, 'https://graph.facebook.com/me',
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}'
responses.add(responses.GET, 'https://graph.facebook.com/me',
body=resp_body, status=200, content_type='application/json')
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())

View File

@ -1,30 +0,0 @@
# Moved in Django 1.8 from django to tests/auth_tests/urls.py
from django.conf.urls import url
from django.contrib.auth import views
from django.contrib.auth.decorators import login_required
from django.contrib.auth.urls import urlpatterns
# special urls for auth test cases
urlpatterns += [
url(r'^logout/custom_query/$', views.logout, dict(redirect_field_name='follow')),
url(r'^logout/next_page/$', views.logout, dict(next_page='/somewhere/')),
url(r'^logout/next_page/named/$', views.logout, dict(next_page='password_reset')),
url(r'^password_reset_from_email/$', views.password_reset, dict(from_email='staffmember@example.com')),
url(r'^password_reset/custom_redirect/$', views.password_reset, dict(post_reset_redirect='/custom/')),
url(r'^password_reset/custom_redirect/named/$', views.password_reset, dict(post_reset_redirect='password_reset')),
url(r'^password_reset/html_email_template/$', views.password_reset,
dict(html_email_template_name='registration/html_password_reset_email.html')),
url(r'^reset/custom/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.password_reset_confirm,
dict(post_reset_redirect='/custom/')),
url(r'^reset/custom/named/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.password_reset_confirm,
dict(post_reset_redirect='password_reset')),
url(r'^password_change/custom/$', views.password_change, dict(post_change_redirect='/custom/')),
url(r'^password_change/custom/named/$', views.password_change, dict(post_change_redirect='password_reset')),
url(r'^admin_password_reset/$', views.password_reset, dict(is_admin_site=True)),
url(r'^login_required/$', login_required(views.password_reset)),
url(r'^login_required_login_url/$', login_required(views.password_reset, login_url='/somewhere/')),
]

View File

@ -1,105 +0,0 @@
import json
from django.conf import settings
from django.test.client import Client, MULTIPART_CONTENT
from django.utils.encoding import force_text
from rest_framework import status
from rest_framework import permissions
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
class CustomPermissionClass(permissions.BasePermission):
message = 'You shall not pass!'
def has_permission(self, request, view):
return False
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 TestsMixin(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'):
if getattr(settings, 'REST_USE_JWT', False):
kwargs['HTTP_AUTHORIZATION'] = 'JWT %s' % self.token
else:
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])
self.response.json = {}
if is_json and self.response.content:
self.response.json = json.loads(force_text(self.response.content))
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 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.verify_email_url = reverse('rest_verify_email')
self.fb_login_url = reverse('fb_login')
self.tw_login_url = reverse('tw_login')
self.tw_login_no_view_url = reverse('tw_login_no_view')
self.tw_login_no_adapter_url = reverse('tw_login_no_adapter')
self.fb_connect_url = reverse('fb_connect')
self.tw_connect_url = reverse('tw_connect')
self.social_account_list_url = reverse('social_account_list')
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)

View File

@ -1,5 +0,0 @@
django-allauth>=0.25.0
responses>=0.3.0
flake8==2.4.0
djangorestframework-jwt>=1.7.2
djangorestframework>=3.6.4

View File

@ -1,518 +0,0 @@
from django.test import TestCase, override_settings
from django.contrib.auth import get_user_model
from django.core import mail
from django.conf import settings
from django.utils.encoding import force_text
from allauth.account import app_settings as account_app_settings
from rest_framework import status
from rest_framework.test import APIRequestFactory
from rest_auth.registration.views import RegisterView
from rest_auth.registration.app_settings import register_permission_classes
from .mixins import TestsMixin, CustomPermissionClass
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
@override_settings(ROOT_URLCONF="tests.urls")
class APIBasicTests(TestsMixin, TestCase):
"""
Case #1:
- user profile: defined
- custom registration: backend defined
"""
# urls = 'tests.urls'
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
NEW_PASS = 'new-test-pass'
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
# data without user profile
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS
}
REGISTRATION_DATA_WITH_EMAIL = REGISTRATION_DATA.copy()
REGISTRATION_DATA_WITH_EMAIL['email'] = EMAIL
BASIC_USER_DATA = {
'first_name': "John",
'last_name': 'Smith',
'email': EMAIL
}
USER_DATA = BASIC_USER_DATA.copy()
USER_DATA['newsletter_subscribe'] = True
def setUp(self):
self.init()
def _generate_uid_and_token(self, user):
result = {}
from django.utils.encoding import force_bytes
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
result['token'] = default_token_generator.make_token(user)
return result
@override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.EMAIL)
def test_login_failed_email_validation(self):
payload = {
"email": '',
"password": self.PASS
}
resp = self.post(self.login_url, data=payload, status_code=400)
self.assertEqual(resp.json['non_field_errors'][0], u'Must include "email" and "password".')
@override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.USERNAME)
def test_login_failed_username_validation(self):
payload = {
"username": '',
"password": self.PASS
}
resp = self.post(self.login_url, data=payload, status_code=400)
self.assertEqual(resp.json['non_field_errors'][0], u'Must include "username" and "password".')
@override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.USERNAME_EMAIL)
def test_login_failed_username_email_validation(self):
payload = {
"password": self.PASS
}
resp = self.post(self.login_url, data=payload, status_code=400)
self.assertEqual(resp.json['non_field_errors'][0], u'Must include either "username" or "email" and "password".')
def test_allauth_login_with_username(self):
payload = {
"username": self.USERNAME,
"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.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']
self.post(self.password_change_url, status_code=400)
# test inactive user
user.is_active = False
user.save()
self.post(self.login_url, data=payload, status_code=400)
# test wrong username/password
payload = {
"username": self.USERNAME + '?',
"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)
@override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.EMAIL)
def test_allauth_login_with_email(self):
payload = {
"email": self.EMAIL,
"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
get_user_model().objects.create_user(self.EMAIL, email=self.EMAIL, password=self.PASS)
self.post(self.login_url, data=payload, status_code=200)
@override_settings(REST_USE_JWT=True)
def test_login_jwt(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=payload, status_code=200)
self.assertEqual('token' in self.response.json.keys(), True)
self.token = self.response.json['token']
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)
# bring back allauth
settings.INSTALLED_APPS.append('allauth')
def test_password_change(self):
login_payload = {
"username": self.USERNAME,
"password": self.PASS
}
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=login_payload, status_code=200)
self.token = self.response.json['key']
new_password_payload = {
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(
self.password_change_url,
data=new_password_payload,
status_code=200
)
# user should not be able to login using old password
self.post(self.login_url, data=login_payload, status_code=400)
# new password should work
login_payload['password'] = new_password_payload['new_password1']
self.post(self.login_url, data=login_payload, status_code=200)
# pass1 and pass2 are not equal
new_password_payload = {
"new_password1": "new_person1",
"new_password2": "new_person"
}
self.post(
self.password_change_url,
data=new_password_payload,
status_code=400
)
# send empty payload
self.post(self.password_change_url, data={}, status_code=400)
@override_settings(OLD_PASSWORD_FIELD_ENABLED=True)
def test_password_change_with_old_password(self):
login_payload = {
"username": self.USERNAME,
"password": self.PASS
}
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=login_payload, status_code=200)
self.token = self.response.json['key']
new_password_payload = {
"old_password": "%s!" % self.PASS, # wrong password
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(
self.password_change_url,
data=new_password_payload,
status_code=400
)
new_password_payload = {
"old_password": self.PASS,
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(
self.password_change_url,
data=new_password_payload,
status_code=200
)
# user should not be able to login using old password
self.post(self.login_url, data=login_payload, status_code=400)
# new password should work
login_payload['password'] = new_password_payload['new_password1']
self.post(self.login_url, data=login_payload, status_code=200)
def test_password_reset(self):
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
# call password reset
mail_count = len(mail.outbox)
payload = {'email': self.EMAIL}
self.post(self.password_reset_url, data=payload, status_code=200)
self.assertEqual(len(mail.outbox), mail_count + 1)
url_kwargs = self._generate_uid_and_token(user)
url = reverse('rest_password_reset_confirm')
# wrong token
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': force_text(url_kwargs['uid']),
'token': '-wrong-token-'
}
self.post(url, data=data, status_code=400)
# wrong uid
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': '-wrong-uid-',
'token': url_kwargs['token']
}
self.post(url, data=data, status_code=400)
# wrong token and uid
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': '-wrong-uid-',
'token': '-wrong-token-'
}
self.post(url, data=data, status_code=400)
# valid payload
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': force_text(url_kwargs['uid']),
'token': url_kwargs['token']
}
url = reverse('rest_password_reset_confirm')
self.post(url, data=data, status_code=200)
payload = {
"username": self.USERNAME,
"password": self.NEW_PASS
}
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):
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=200)
self.token = self.response.json['key']
self.get(self.user_url, status_code=200)
self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
user = get_user_model().objects.get(pk=user.pk)
self.assertEqual(user.first_name, self.response.json['first_name'])
self.assertEqual(user.last_name, self.response.json['last_name'])
self.assertEqual(user.email, self.response.json['email'])
@override_settings(REST_USE_JWT=True)
def test_user_details_using_jwt(self):
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=200)
self.token = self.response.json['token']
self.get(self.user_url, status_code=200)
self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
user = get_user_model().objects.get(pk=user.pk)
self.assertEqual(user.email, self.response.json['email'])
def test_registration(self):
user_count = get_user_model().objects.all().count()
# test empty payload
self.post(self.register_url, data={}, status_code=400)
result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
self._login()
self._logout()
@override_settings(REST_AUTH_REGISTER_PERMISSION_CLASSES=(CustomPermissionClass,))
def test_registration_with_custom_permission_class(self):
class CustomRegisterView(RegisterView):
permission_classes = register_permission_classes()
authentication_classes = ()
factory = APIRequestFactory()
request = factory.post('/customer/details', self.REGISTRATION_DATA, format='json')
response = CustomRegisterView.as_view()(request)
self.assertEqual(response.data['detail'], CustomPermissionClass.message)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@override_settings(REST_USE_JWT=True)
def test_registration_with_jwt(self):
user_count = get_user_model().objects.all().count()
self.post(self.register_url, data={}, status_code=400)
result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertIn('token', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
self._login()
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(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True,
ACCOUNT_EMAIL_CONFIRMATION_HMAC=False
)
def test_registration_with_email_verification(self):
user_count = get_user_model().objects.all().count()
mail_count = len(mail.outbox)
# test empty payload
self.post(
self.register_url,
data={},
status_code=status.HTTP_400_BAD_REQUEST
)
result = self.post(
self.register_url,
data=self.REGISTRATION_DATA_WITH_EMAIL,
status_code=status.HTTP_201_CREATED
)
self.assertNotIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
self.assertEqual(len(mail.outbox), mail_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
# email is not verified yet
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(
self.login_url,
data=payload,
status=status.HTTP_400_BAD_REQUEST
)
# verify email
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
.emailconfirmation_set.order_by('-created')[0]
self.post(
self.verify_email_url,
data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK
)
# try to login again
self._login()
self._logout()
@override_settings(ACCOUNT_LOGOUT_ON_GET=True)
def test_logout_on_get(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
# create user
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=payload, status_code=200)
self.get(self.logout_url, status=status.HTTP_200_OK)
@override_settings(ACCOUNT_LOGOUT_ON_GET=False)
def test_logout_on_post_only(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
# create user
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
self.get(self.logout_url, status_code=status.HTTP_405_METHOD_NOT_ALLOWED)

View File

@ -1,447 +0,0 @@
import json
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
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
import responses
from rest_framework import status
from .mixins import TestsMixin
@override_settings(ROOT_URLCONF="tests.urls")
class TestSocialAuth(TestsMixin, TestCase):
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',
)
twitter_social_app = SocialApp.objects.create(
provider='twitter',
name='Twitter',
client_id='11223344',
secret='55667788',
)
site = Site.objects.get_current()
social_app.sites.add(site)
twitter_social_app.sites.add(site)
self.graph_api_url = GRAPH_API_URL + '/me'
self.twitter_url = 'http://twitter.com/foobarme'
@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
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(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)
def _twitter_social_auth(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_url, data=payload)
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.tw_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(SOCIALACCOUNT_AUTO_SIGNUP=True)
def test_twitter_social_auth(self):
self._twitter_social_auth()
@responses.activate
@override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False)
def test_twitter_social_auth_without_auto_singup(self):
self._twitter_social_auth()
@responses.activate
def test_twitter_social_auth_request_error(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_url, data=payload, status_code=400)
self.assertNotIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
def test_twitter_social_auth_no_view_in_context(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_no_view_url, data=payload, status_code=400)
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
def test_twitter_social_auth_no_adapter(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_no_adapter_url, data=payload, status_code=400)
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
@override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True,
REST_SESSION_LOGIN=False,
ACCOUNT_EMAIL_CONFIRMATION_HMAC=False
)
def test_email_clash_with_existing_account(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": self.EMAIL
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
# test empty payload
self.post(self.register_url, data={}, status_code=400)
# register user and send email confirmation
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.verify_email_url,
data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK
)
self._login()
self._logout()
# fb log in with already existing email
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=400)
@responses.activate
@override_settings(
REST_USE_JWT=True
)
def test_jwt(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}' # 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('token', self.response.json.keys())
self.assertIn('user', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
@override_settings(ROOT_URLCONF="tests.urls")
class TestSocialConnectAuth(TestsMixin, TestCase):
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS,
"email": EMAIL
}
def setUp(self):
self.init()
facebook_social_app = SocialApp.objects.create(
provider='facebook',
name='Facebook',
client_id='123123123',
secret='321321321',
)
twitter_social_app = SocialApp.objects.create(
provider='twitter',
name='Twitter',
client_id='11223344',
secret='55667788',
)
site = Site.objects.get_current()
facebook_social_app.sites.add(site)
twitter_social_app.sites.add(site)
self.graph_api_url = GRAPH_API_URL + '/me'
self.twitter_url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
@responses.activate
def test_social_connect_no_auth(self):
responses.add(
responses.GET,
self.graph_api_url,
body='',
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123'
}
self.post(self.fb_connect_url, data=payload, status_code=403)
self.post(self.tw_connect_url, data=payload, status_code=403)
@responses.activate
def test_social_connect(self):
# register user
self.post(
self.register_url,
data=self.REGISTRATION_DATA,
status_code=201
)
# Test Facebook
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
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123'
}
self.post(self.fb_connect_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
# Test Twitter
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
self.twitter_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_connect_url, data=payload)
self.assertIn('key', self.response.json.keys())
# Check current social accounts
self.get(self.social_account_list_url)
self.assertEqual(len(self.response.json), 2)
self.assertEqual(self.response.json[0]['provider'], 'facebook')
self.assertEqual(self.response.json[1]['provider'], 'twitter')
facebook_social_account_id = self.response.json[0]['id']
# Try disconnecting accounts
self.incorrect_disconnect_url = reverse(
'social_account_disconnect', args=[999999999]
)
self.post(self.incorrect_disconnect_url, status_code=404)
self.disconnect_url = reverse(
'social_account_disconnect', args=[facebook_social_account_id]
)
self.post(self.disconnect_url, status_code=200)
# Check social accounts after disconnecting
self.get(self.social_account_list_url)
self.assertEqual(len(self.response.json), 1)
self.assertEqual(self.response.json[0]['provider'], 'twitter')

View File

@ -1,72 +0,0 @@
from django.conf.urls import url, include
from django.views.generic import TemplateView
from . import django_urls
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from rest_framework.decorators import api_view
from rest_auth.urls import urlpatterns
from rest_auth.registration.views import (
SocialLoginView, SocialConnectView, SocialAccountListView,
SocialAccountDisconnectView
)
from rest_auth.social_serializers import (
TwitterLoginSerializer, TwitterConnectSerializer
)
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
class TwitterLogin(SocialLoginView):
adapter_class = TwitterOAuthAdapter
serializer_class = TwitterLoginSerializer
class FacebookConnect(SocialConnectView):
adapter_class = FacebookOAuth2Adapter
class TwitterConnect(SocialConnectView):
adapter_class = TwitterOAuthAdapter
serializer_class = TwitterConnectSerializer
class TwitterLoginSerializerFoo(TwitterLoginSerializer):
pass
@api_view(['POST'])
def twitter_login_view(request):
serializer = TwitterLoginSerializerFoo(
data={'access_token': '11223344', 'token_secret': '55667788'},
context={'request': request}
)
serializer.is_valid(raise_exception=True)
class TwitterLoginNoAdapter(SocialLoginView):
serializer_class = TwitterLoginSerializer
urlpatterns += [
url(r'^rest-registration/', include('rest_auth.registration.urls')),
url(r'^test-admin/', include(django_urls)),
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
name='account_email_verification_sent'),
url(r'^account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
name='account_confirm_email'),
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'),
url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_login'),
url(r'^social-login/twitter-no-view/$', twitter_login_view, name='tw_login_no_view'),
url(r'^social-login/twitter-no-adapter/$', TwitterLoginNoAdapter.as_view(), name='tw_login_no_adapter'),
url(r'^social-login/facebook/connect/$', FacebookConnect.as_view(), name='fb_connect'),
url(r'^social-login/twitter/connect/$', TwitterConnect.as_view(), name='tw_connect'),
url(r'^socialaccounts/$', SocialAccountListView.as_view(), name='social_account_list'),
url(r'^socialaccounts/(?P<pk>\d+)/disconnect/$', SocialAccountDisconnectView.as_view(),
name='social_account_disconnect'),
url(r'^accounts/', include('allauth.socialaccount.urls'))
]

View File

@ -1,20 +1,18 @@
from django.conf.urls import url from django.conf.urls import patterns, url
from rest_auth.views import ( from rest_auth.views import (Login, Logout, UserDetails, PasswordChange,
LoginView, LogoutView, UserDetailsView, PasswordChangeView, PasswordReset, PasswordResetConfirm)
PasswordResetView, PasswordResetConfirmView
)
urlpatterns = [ urlpatterns = patterns('',
# 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/$', PasswordReset.as_view(),
name='rest_password_reset'), name='rest_password_reset'),
url(r'^password/reset/confirm/$', PasswordResetConfirmView.as_view(), url(r'^password/reset/confirm/$', PasswordResetConfirm.as_view(),
name='rest_password_reset_confirm'), name='rest_password_reset_confirm'),
url(r'^login/$', LoginView.as_view(), name='rest_login'), url(r'^login/$', Login.as_view(), name='rest_login'),
# URLs that require a user to be logged in with a valid session / token. # URLs that require a user to be logged in with a valid session / token.
url(r'^logout/$', LogoutView.as_view(), name='rest_logout'), url(r'^logout/$', Logout.as_view(), name='rest_logout'),
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'), url(r'^user/$', UserDetails.as_view(), name='rest_user_details'),
url(r'^password/change/$', PasswordChangeView.as_view(), url(r'^password/change/$', PasswordChange.as_view(),
name='rest_password_change'), name='rest_password_change'),
] )

View File

@ -1,5 +1,5 @@
from six import string_types from six import string_types
from importlib import import_module from django.utils.importlib import import_module
def import_callable(path_or_callable): def import_callable(path_or_callable):
@ -10,20 +10,3 @@ def import_callable(path_or_callable):
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
def jwt_encode(user):
try:
from rest_framework_jwt.settings import api_settings
except ImportError:
raise ImportError("djangorestframework_jwt needs to be installed")
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
return jwt_encode_handler(payload)

View File

@ -1,36 +1,21 @@
from django.contrib.auth import ( from django.contrib.auth import login, logout
login as django_login,
logout as django_logout
)
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters
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, RetrieveUpdateAPIView 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 .app_settings import ( from .app_settings import (TokenSerializer, UserDetailsSerializer,
TokenSerializer, UserDetailsSerializer, LoginSerializer, LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer,
PasswordResetSerializer, PasswordResetConfirmSerializer, PasswordChangeSerializer)
PasswordChangeSerializer, JWTSerializer, create_token
)
from .models import TokenModel
from .utils import jwt_encode
sensitive_post_parameters_m = method_decorator(
sensitive_post_parameters(
'password', 'old_password', 'new_password1', 'new_password2'
)
)
class LoginView(GenericAPIView): class Login(GenericAPIView):
""" """
Check the credentials and return the REST Token Check the credentials and return the REST Token
if the credentials are valid and authenticated. if the credentials are valid and authenticated.
@ -42,71 +27,34 @@ class LoginView(GenericAPIView):
""" """
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
serializer_class = LoginSerializer serializer_class = LoginSerializer
token_model = TokenModel token_model = Token
response_serializer = TokenSerializer
@sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(LoginView, self).dispatch(*args, **kwargs)
def process_login(self):
django_login(self.request, self.user)
def get_response_serializer(self):
if getattr(settings, 'REST_USE_JWT', False):
response_serializer = JWTSerializer
else:
response_serializer = TokenSerializer
return response_serializer
def login(self): def login(self):
self.user = self.serializer.validated_data['user'] self.user = self.serializer.object['user']
self.token, created = self.token_model.objects.get_or_create(
if getattr(settings, 'REST_USE_JWT', False): user=self.user)
self.token = jwt_encode(self.user)
else:
self.token = create_token(self.token_model, self.user,
self.serializer)
if getattr(settings, 'REST_SESSION_LOGIN', True): if getattr(settings, 'REST_SESSION_LOGIN', True):
self.process_login() login(self.request, self.user)
def get_response(self): def get_response(self):
serializer_class = self.get_response_serializer() return Response(self.response_serializer(self.token).data,
status=status.HTTP_200_OK)
if getattr(settings, 'REST_USE_JWT', False): def get_error_response(self):
data = { return Response(self.serializer.errors,
'user': self.user, status=status.HTTP_400_BAD_REQUEST)
'token': self.token
}
serializer = serializer_class(instance=data,
context={'request': self.request})
else:
serializer = serializer_class(instance=self.token,
context={'request': self.request})
response = Response(serializer.data, status=status.HTTP_200_OK)
if getattr(settings, 'REST_USE_JWT', False):
from rest_framework_jwt.settings import api_settings as jwt_settings
if jwt_settings.JWT_AUTH_COOKIE:
from datetime import datetime
expiration = (datetime.utcnow() + jwt_settings.JWT_EXPIRATION_DELTA)
response.set_cookie(jwt_settings.JWT_AUTH_COOKIE,
self.token,
expires=expiration,
httponly=True)
return response
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.request = request self.serializer = self.get_serializer(data=self.request.DATA)
self.serializer = self.get_serializer(data=self.request.data, if not self.serializer.is_valid():
context={'request': request}) return self.get_error_response()
self.serializer.is_valid(raise_exception=True)
self.login() self.login()
return self.get_response() return self.get_response()
class LogoutView(APIView): class Logout(APIView):
""" """
Calls Django logout method and delete the Token object Calls Django logout method and delete the Token object
assigned to the current User object. assigned to the current User object.
@ -115,44 +63,28 @@ class LogoutView(APIView):
""" """
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs): def post(self, request):
if getattr(settings, 'ACCOUNT_LOGOUT_ON_GET', False):
response = self.logout(request)
else:
response = self.http_method_not_allowed(request, *args, **kwargs)
return self.finalize_response(request, response, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.logout(request)
def logout(self, request):
try: try:
request.user.auth_token.delete() request.user.auth_token.delete()
except (AttributeError, ObjectDoesNotExist): except:
pass pass
if getattr(settings, 'REST_SESSION_LOGIN', True):
django_logout(request)
response = Response({"detail": _("Successfully logged out.")}, logout(request)
status=status.HTTP_200_OK)
if getattr(settings, 'REST_USE_JWT', False): return Response({"success": "Successfully logged out."},
from rest_framework_jwt.settings import api_settings as jwt_settings status=status.HTTP_200_OK)
if jwt_settings.JWT_AUTH_COOKIE:
response.delete_cookie(jwt_settings.JWT_AUTH_COOKIE)
return response
class UserDetailsView(RetrieveUpdateAPIView): class UserDetails(RetrieveUpdateAPIView):
""" """
Reads and updates UserModel fields Returns User's details in JSON format.
Accepts GET, PUT, PATCH methods.
Default accepted fields: username, first_name, last_name Accepts the following GET parameters: token
Default display fields: pk, username, email, first_name, last_name Accepts the following POST parameters:
Read-only fields: pk, email Required: token
Optional: email, first_name, last_name and UserProfile fields
Returns UserModel fields. Returns the updated UserProfile and/or User object.
""" """
serializer_class = UserDetailsSerializer serializer_class = UserDetailsSerializer
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@ -160,79 +92,70 @@ class UserDetailsView(RetrieveUpdateAPIView):
def get_object(self): def get_object(self):
return self.request.user return self.request.user
def get_queryset(self):
"""
Adding this method since it is sometimes called when using
django-rest-swagger
https://github.com/Tivix/django-rest-auth/issues/275
"""
return get_user_model().objects.none()
class PasswordReset(GenericAPIView):
class PasswordResetView(GenericAPIView):
""" """
Calls Django Auth PasswordResetForm save method. Calls Django Auth PasswordResetForm save method.
Accepts the following POST parameters: email Accepts the following POST parameters: email
Returns the success/fail message. Returns the success/fail message.
""" """
serializer_class = PasswordResetSerializer serializer_class = PasswordResetSerializer
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
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."},
{"detail": _("Password reset e-mail has been sent.")}, status=status.HTTP_200_OK)
status=status.HTTP_200_OK
)
class PasswordResetConfirmView(GenericAPIView): class PasswordResetConfirm(GenericAPIView):
""" """
Password reset e-mail link is confirmed, therefore Password reset e-mail link is confirmed, therefore this resets the user's password.
this resets the user's password.
Accepts the following POST parameters: token, uid, Accepts the following POST parameters: new_password1, new_password2
new_password1, new_password2 Accepts the following Django URL arguments: token, uid
Returns the success/fail message. Returns the success/fail message.
""" """
serializer_class = PasswordResetConfirmSerializer serializer_class = PasswordResetConfirmSerializer
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
@sensitive_post_parameters_m def post(self, request):
def dispatch(self, *args, **kwargs): serializer = self.get_serializer(data=request.DATA)
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs) if not serializer.is_valid():
return Response(serializer.errors,
def post(self, request, *args, **kwargs): status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return Response( return Response({"success": "Password has been reset with the new password."})
{"detail": _("Password has been reset with the new password.")}
)
class PasswordChangeView(GenericAPIView): class PasswordChange(GenericAPIView):
""" """
Calls Django Auth SetPasswordForm save method. Calls Django Auth SetPasswordForm save method.
Accepts the following POST parameters: new_password1, new_password2 Accepts the following POST parameters: new_password1, new_password2
Returns the success/fail message. Returns the success/fail message.
""" """
serializer_class = PasswordChangeSerializer serializer_class = PasswordChangeSerializer
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@sensitive_post_parameters_m def post(self, request):
def dispatch(self, *args, **kwargs): serializer = self.get_serializer(data=request.DATA)
return super(PasswordChangeView, self).dispatch(*args, **kwargs) if not serializer.is_valid():
return Response(serializer.errors,
def post(self, request, *args, **kwargs): status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return Response({"detail": _("New password has been saved.")}) return Response({"success": "New password has been saved."})

View File

@ -1,10 +1,9 @@
# This file mainly exists to allow python setup.py test to work. # This file mainly exists to allow python setup.py test to work.
# flake8: noqa
import os import os
import sys import sys
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings'
test_dir = os.path.join(os.path.dirname(__file__), 'rest_auth') test_dir = os.path.dirname(__file__)
sys.path.insert(0, test_dir) sys.path.insert(0, test_dir)
import django import django

View File

@ -1,39 +0,0 @@
[bdist_wheel]
universal = 1
[metadata]
license_file = LICENSE
[flake8]
max-line-length = 120
exclude = docs/*,demo/*
ignore = F403
[coverage:run]
omit=*site-packages*,*distutils*,*migrations*
[coverage:report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
ignore_errors = True
[coverage:html]
directory = coverage_html

View File

@ -1,9 +1,15 @@
#!/usr/bin/env python #!/usr/bin/env python
import os try:
from setuptools import setup, find_packages from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
import os
here = os.path.dirname(os.path.abspath(__file__)) here = os.path.dirname(os.path.abspath(__file__))
f = open(os.path.join(here, 'README.rst')) f = open(os.path.join(here, 'README.rst'))
long_description = f.read().strip() long_description = f.read().strip()
@ -12,7 +18,7 @@ f.close()
setup( setup(
name='django-rest-auth', name='django-rest-auth',
version='0.9.5', version='0.3.1',
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',
@ -22,20 +28,12 @@ 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.8.0', 'Django>=1.5.0',
'djangorestframework>=3.1.3', 'djangorestframework>=2.3.13',
'six>=1.9.0',
],
extras_require={
'with_social': ['django-allauth>=0.25.0'],
},
tests_require=[
'responses>=0.5.0',
'django-allauth>=0.25.0',
'djangorestframework-jwt>=1.9.0',
], ],
test_suite='runtests.runtests', test_suite='runtests.runtests',
include_package_data=True, include_package_data=True,
# cmdclass={},
classifiers=[ classifiers=[
'Framework :: Django', 'Framework :: Django',
'Intended Audience :: Developers', 'Intended Audience :: Developers',

2
test_requirements.pip Normal file
View File

@ -0,0 +1,2 @@
django-allauth>=0.18.0
responses>=0.2.2

View 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,14 +18,17 @@ 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
DATABASES = { if django.VERSION[:2] >= (1, 3):
'default': { DATABASES = {
'ENGINE': 'django.db.backends.sqlite3', 'default': {
'NAME': ':memory:', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
} }
} else:
DATABASE_ENGINE = 'sqlite3'
MIDDLEWARE = [ MIDDLEWARE_CLASSES = [
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
@ -33,9 +36,6 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware' 'django.contrib.messages.middleware.MessageMiddleware'
] ]
# Adding for backwards compatibility for Django 1.8 tests
MIDDLEWARE_CLASSES = MIDDLEWARE
TEMPLATE_CONTEXT_PROCESSORS = [ TEMPLATE_CONTEXT_PROCESSORS = [
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug', 'django.core.context_processors.debug',
@ -48,27 +48,6 @@ TEMPLATE_CONTEXT_PROCESSORS = [
"allauth.socialaccount.context_processors.socialaccount", "allauth.socialaccount.context_processors.socialaccount",
] ]
# avoid deprecation warnings during tests
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
# insert your TEMPLATE_DIRS here
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': TEMPLATE_CONTEXT_PROCESSORS,
},
},
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
@ -83,24 +62,18 @@ INSTALLED_APPS = [
'allauth.account', 'allauth.account',
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.twitter',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'rest_auth', 'rest_auth',
'rest_auth.registration', 'rest_auth.registration'
'rest_framework_jwt'
] ]
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
AUTHENTICATION_BACKENDS = ( MIGRATION_MODULES = {
# Needed to login by username in Django admin, regardless of `allauth` 'authtoken': 'authtoken.migrations',
'django.contrib.auth.backends.ModelBackend', }
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)