Merge branch 'Tivix:master' into AddOptionReturnUserDetailsOnLogin

This commit is contained in:
Egor 2021-07-07 23:29:49 +07:00 committed by GitHub
commit bd7d3f0a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1438 additions and 137 deletions

77
.gitignore vendored
View File

@ -1,26 +1,35 @@
# 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
@ -30,28 +39,66 @@ pip-delete-this-directory.txt
htmlcov/ htmlcov/
.tox/ .tox/
.coverage .coverage
.coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
coverage_html *.cover
.hypothesis/
.pytest_cache/
# Mr Developer # Translations
.mr.developer.cfg *.mo
.project *.pot
.pydevproject
# Rope
.ropeproject
# Django stuff: # Django stuff:
*.log *.log
*.pot local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
.DS_Store # PyBuilder
db.sqlite3 target/
# IntelliJ IDE files # Jupyter Notebook
.idea .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

@ -2,12 +2,14 @@ language: python
python: python:
- "2.7" - "2.7"
- "3.5" - "3.5"
- "3.6"
env: env:
- DJANGO=1.8.17 - DJANGO=1.11.* DRF=3.7.*
- DJANGO=1.9.12 - DJANGO=1.11.* DRF=3.8.*
- DJANGO=1.10.4 - DJANGO=2.0.* DRF=3.7.*
- DJANGO=2.0.* DRF=3.8.*
install: install:
- pip install -q Django==$DJANGO - pip install -q Django==$DJANGO djangorestframework==$DRF
- pip install coveralls - pip install coveralls
- pip install -r rest_auth/tests/requirements.pip - pip install -r rest_auth/tests/requirements.pip
script: script:
@ -16,3 +18,9 @@ after_success:
- coveralls - coveralls
before_script: before_script:
- flake8 . --config=flake8 - 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,3 +2,4 @@ include AUTHORS
include LICENSE include LICENSE
include MANIFEST.in include MANIFEST.in
include README.md include README.md
graft rest_auth

View File

@ -1,11 +1,13 @@
Welcome to django-rest-auth Welcome to django-rest-auth
=========================== ===========================
.. image:: https://travis-ci.org/Tivix/django-rest-auth.png Repository is unmaintained at the moment (on pause). More info can be found on this issue page: https://github.com/Tivix/django-rest-auth/issues/568
.. image:: https://travis-ci.org/Tivix/django-rest-auth.svg
: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.png .. image:: https://coveralls.io/repos/Tivix/django-rest-auth/badge.svg
:target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master :target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master
@ -28,4 +30,4 @@ https://github.com/Tivix/django-rest-auth
Stack Overflow Stack Overflow
----------- -----------
http://stackoverflow.com/questions/tagged/django-rest-auth http://stackoverflow.com/questions/tagged/django-rest-auth

View File

@ -47,16 +47,18 @@ INSTALLED_APPS = (
'rest_framework_swagger', 'rest_framework_swagger',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE = (
'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'

View File

@ -38,7 +38,7 @@ 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'^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'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'),
url(r'^docs/$', get_swagger_view(title='API Docs'), name='api_docs') url(r'^docs/$', get_swagger_view(title='API Docs'), name='api_docs')
] ]

View File

@ -1,6 +1,6 @@
django>=1.8.0 django>=1.9.0
django-rest-auth==0.9.1 django-rest-auth==0.9.5
djangorestframework==3.5.1 djangorestframework>=3.7.0
django-allauth>=0.24.1 django-allauth>=0.24.1
six==1.9.0 six==1.9.0
django-rest-swagger==2.0.7 django-rest-swagger==2.0.7

View File

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

View File

@ -1,6 +1,31 @@
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 0.9.1
----- -----
- fixed import error when extending rest_auth serializers - fixed import error when extending rest_auth serializers

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'2014, Tivix Inc.' copyright = u'2018, 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.3.0' version = '0.9.5'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.3.0' release = '0.9.5'
# 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

@ -36,7 +36,7 @@ Configuration
You can define your custom serializers for registration endpoint. You can define your custom serializers for registration endpoint.
Possible key values: Possible key values:
- REGISTER_SERIALIZER - serializer class in ``rest_auth.register.views.RegisterView``, default value ``rest_auth.registration.serializers.RegisterSerializer`` - 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 .. note:: The custom REGISTER_SERIALIZER must define a ``def save(self, request)`` method that returns a user model instance

View File

@ -26,12 +26,18 @@ Installation
.. code-block:: python .. code-block:: python
urlpatterns = patterns('', urlpatterns = [
..., ...,
url(r'^rest-auth/', include('rest_auth.urls')) url(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!
@ -60,11 +66,11 @@ Registration (optional)
.. code-block:: python .. code-block:: python
urlpatterns = patterns('', 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'))
) ]
Social Authentication (optional) Social Authentication (optional)
@ -114,10 +120,10 @@ Facebook
.. code-block:: python .. code-block:: python
urlpatterns += patterns('', urlpatterns += [
..., ...,
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login') url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
) ]
Twitter Twitter
@ -125,15 +131,15 @@ Twitter
If you are using Twitter for your social authentication, it is a bit different since Twitter uses OAuth 1.0. If you are using Twitter for your social authentication, it is a bit different since Twitter uses OAuth 1.0.
3. Create new view as a subclass of ``rest_auth.views.LoginView`` with ``TwitterOAuthAdapter`` adapter and ``TwitterLoginSerializer`` as an attribute: 3. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``TwitterOAuthAdapter`` adapter and ``TwitterLoginSerializer`` as an attribute:
.. code-block:: python .. code-block:: python
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from rest_auth.views import LoginView from rest_auth.registration.views import SocialLoginView
from rest_auth.social_serializers import TwitterLoginSerializer from rest_auth.social_serializers import TwitterLoginSerializer
class TwitterLogin(LoginView): class TwitterLogin(SocialLoginView):
serializer_class = TwitterLoginSerializer serializer_class = TwitterLoginSerializer
adapter_class = TwitterOAuthAdapter adapter_class = TwitterOAuthAdapter
@ -141,23 +147,114 @@ If you are using Twitter for your social authentication, it is a bit different s
.. code-block:: python .. code-block:: python
urlpatterns += patterns('', urlpatterns += [
..., ...,
url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login') url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login')
) ]
.. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details. .. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details.
JWT Support (optional) GitHub
---------------------- ######
By default, ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, you need to install the following: If you are using GitHub for your social authentication, it uses code and not AccessToken directly.
1. Install ``django-rest-framework-jwt`` http://getblimp.github.io/django-rest-framework-jwt/ . Right now this is the only supported JWT library. 3. Create new view as a subclass of ``rest_auth.views.SocialLoginView`` with ``GitHubOAuth2Adapter`` adapter, an ``OAuth2Client`` and a callback_url as attributes:
2. Add the following to your settings .. 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 .. code-block:: python
REST_USE_JWT = True REST_USE_JWT = True

4
flake8
View File

@ -1,4 +0,0 @@
[flake8]
max-line-length = 120
exclude = docs/*,demo/*
ignore = F403

Binary file not shown.

View File

@ -0,0 +1,102 @@
# 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."

Binary file not shown.

View File

@ -0,0 +1,98 @@
# 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"

Binary file not shown.

View File

@ -0,0 +1,99 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 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 "새로운 패스워드가 저장되었습니다."

Binary file not shown.

View File

@ -0,0 +1,99 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
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

@ -0,0 +1,99 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# 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

@ -0,0 +1,95 @@
# 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."

Binary file not shown.

View File

@ -0,0 +1,104 @@
# 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 "新密码已设置成功。"

Binary file not shown.

View File

@ -0,0 +1,103 @@
# 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,6 +1,6 @@
from django.http import HttpRequest from django.http import HttpRequest
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model
try: try:
from allauth.account import app_settings as allauth_settings from allauth.account import app_settings as allauth_settings
@ -8,15 +8,29 @@ try:
get_username_max_length) get_username_max_length)
from allauth.account.adapter import get_adapter from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email 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: except ImportError:
raise ImportError("allauth needs to be added to INSTALLED_APPS.") raise ImportError("allauth needs to be added to INSTALLED_APPS.")
from rest_framework import serializers from rest_framework import serializers
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
# Import is needed only if we are using social login, in which
# case the allauth.socialaccount will be declared
if 'allauth.socialaccount' in settings.INSTALLED_APPS: class SocialAccountSerializer(serializers.ModelSerializer):
from allauth.socialaccount.helpers import complete_social_login """
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):
@ -108,16 +122,47 @@ class SocialLoginSerializer(serializers.Serializer):
login = self.get_social_login(adapter, app, social_token, access_token) login = self.get_social_login(adapter, app, social_token, access_token)
complete_social_login(request, login) complete_social_login(request, login)
except HTTPError: except HTTPError:
raise serializers.ValidationError(_('Incorrect value')) raise serializers.ValidationError(_("Incorrect value"))
if not login.is_existing: if not login.is_existing:
# 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)
attrs['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): class RegisterSerializer(serializers.Serializer):
username = serializers.CharField( username = serializers.CharField(
max_length=get_username_max_length(), max_length=get_username_max_length(),

View File

@ -5,22 +5,29 @@ 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,
from rest_framework.generics import CreateAPIView 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.adapter import get_adapter
from allauth.account.views import 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 as allauth_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 (UserDetailsSerializer, from rest_auth.app_settings import (UserDetailsSerializer,
TokenSerializer, TokenSerializer,
JWTSerializer, JWTSerializer,
create_token) create_token)
from rest_auth.models import TokenModel from rest_auth.models import TokenModel
from rest_auth.registration.serializers import (SocialLoginSerializer, from rest_auth.registration.serializers import (VerifyEmailSerializer,
VerifyEmailSerializer) SocialLoginSerializer,
SocialAccountSerializer,
SocialConnectSerializer)
from rest_auth.utils import jwt_encode from rest_auth.utils import jwt_encode
from rest_auth.views import LoginView from rest_auth.views import LoginView
from .app_settings import RegisterSerializer, register_permission_classes from .app_settings import RegisterSerializer, register_permission_classes
@ -113,12 +120,70 @@ class SocialLoginView(LoginView):
class FacebookLogin(SocialLoginView): class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter adapter_class = FacebookOAuth2Adapter
client_class = OAuth2Client client_class = OAuth2Client
callback_url = 'localhost:8000' callback_url = 'localhost:8000'
------------- -------------
""" """
serializer_class = SocialLoginSerializer serializer_class = SocialLoginSerializer
def process_login(self): def process_login(self):
get_adapter(self.request).login(self.request, self.user) 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

@ -21,11 +21,14 @@ class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(required=False, allow_blank=True) email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={'input_type': 'password'}) password = serializers.CharField(style={'input_type': 'password'})
def authenticate(self, **kwargs):
return authenticate(self.context['request'], **kwargs)
def _validate_email(self, email, password): def _validate_email(self, email, password):
user = None user = None
if email and password: if email and password:
user = authenticate(email=email, password=password) user = self.authenticate(email=email, password=password)
else: else:
msg = _('Must include "email" and "password".') msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
@ -36,7 +39,7 @@ class LoginSerializer(serializers.Serializer):
user = None user = None
if username and password: if username and password:
user = authenticate(username=username, password=password) user = self.authenticate(username=username, password=password)
else: else:
msg = _('Must include "username" and "password".') msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
@ -47,9 +50,9 @@ class LoginSerializer(serializers.Serializer):
user = None user = None
if email and password: if email and password:
user = authenticate(email=email, password=password) user = self.authenticate(email=email, password=password)
elif username and password: elif username and password:
user = authenticate(username=username, password=password) user = self.authenticate(username=username, password=password)
else: else:
msg = _('Must include either "username" or "email" and "password".') msg = _('Must include either "username" or "email" and "password".')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
@ -71,7 +74,7 @@ class LoginSerializer(serializers.Serializer):
user = self._validate_email(email, password) user = self._validate_email(email, password)
# Authentication through username # Authentication through username
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME: elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
user = self._validate_username(username, password) user = self._validate_username(username, password)
# Authentication through either username or email # Authentication through either username or email
@ -146,7 +149,7 @@ class JWTSerializer(serializers.Serializer):
JWTUserDetailsSerializer = import_callable( JWTUserDetailsSerializer = import_callable(
rest_auth_serializers.get('USER_DETAILS_SERIALIZER', UserDetailsSerializer) rest_auth_serializers.get('USER_DETAILS_SERIALIZER', UserDetailsSerializer)
) )
user_data = JWTUserDetailsSerializer(obj['user']).data user_data = JWTUserDetailsSerializer(obj['user'], context=self.context).data
return user_data return user_data
@ -220,7 +223,7 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
return attrs return attrs
def save(self): def save(self):
self.set_password_form.save() return self.set_password_form.save()
class PasswordChangeSerializer(serializers.Serializer): class PasswordChangeSerializer(serializers.Serializer):
@ -253,7 +256,8 @@ class PasswordChangeSerializer(serializers.Serializer):
) )
if all(invalid_password_conditions): if all(invalid_password_conditions):
raise serializers.ValidationError('Invalid password') err_msg = _("Your old password was entered incorrectly. Please enter it again.")
raise serializers.ValidationError(err_msg)
return value return value
def validate(self, attrs): def validate(self, attrs):

View File

@ -8,6 +8,8 @@ if 'allauth.socialaccount' in settings.INSTALLED_APPS:
from allauth.socialaccount.models import SocialToken from allauth.socialaccount.models import SocialToken
from allauth.socialaccount.providers.oauth.client import OAuthError from allauth.socialaccount.providers.oauth.client import OAuthError
from rest_auth.registration.serializers import SocialConnectMixin
class TwitterLoginSerializer(serializers.Serializer): class TwitterLoginSerializer(serializers.Serializer):
access_token = serializers.CharField() access_token = serializers.CharField()
@ -73,3 +75,7 @@ class TwitterLoginSerializer(serializers.Serializer):
attrs['user'] = login.account.user attrs['user'] = login.account.user
return attrs return attrs
class TwitterConnectSerializer(SocialConnectMixin, TwitterLoginSerializer):
pass

View File

@ -1,11 +1,23 @@
import json import json
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.client import Client, MULTIPART_CONTENT from django.test.client import Client, MULTIPART_CONTENT
from django.utils.encoding import force_text from django.utils.encoding import force_text
from rest_framework import status 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): class APIClient(Client):
@ -17,8 +29,7 @@ class APIClient(Client):
return self.generic('OPTIONS', path, data, content_type, **extra) return self.generic('OPTIONS', path, data, content_type, **extra)
class BaseAPITestCase(object): class TestsMixin(object):
""" """
base for API tests: base for API tests:
* easy request calls, f.e.: self.post(url, data), self.get(url) * easy request calls, f.e.: self.post(url, data), self.get(url)
@ -64,29 +75,6 @@ class BaseAPITestCase(object):
def patch(self, *args, **kwargs): def patch(self, *args, **kwargs):
return self.send_request('patch', *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): def init(self):
settings.DEBUG = True settings.DEBUG = True
self.client = APIClient() self.client = APIClient()
@ -102,6 +90,9 @@ class BaseAPITestCase(object):
self.tw_login_url = reverse('tw_login') self.tw_login_url = reverse('tw_login')
self.tw_login_no_view_url = reverse('tw_login_no_view') self.tw_login_no_view_url = reverse('tw_login_no_view')
self.tw_login_no_adapter_url = reverse('tw_login_no_adapter') 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): def _login(self):
payload = { payload = {

View File

@ -2,3 +2,4 @@ django-allauth>=0.25.0
responses>=0.3.0 responses>=0.3.0
flake8==2.4.0 flake8==2.4.0
djangorestframework-jwt>=1.7.2 djangorestframework-jwt>=1.7.2
djangorestframework>=3.6.4

View File

@ -25,7 +25,7 @@ DATABASES = {
} }
} }
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
'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,6 +33,9 @@ MIDDLEWARE_CLASSES = [
'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',

View File

@ -1,17 +1,26 @@
from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core import mail from django.core import mail
from django.conf import settings from django.conf import settings
from django.utils.encoding import force_text from django.utils.encoding import force_text
from rest_framework import status
from allauth.account import app_settings as account_app_settings from allauth.account import app_settings as account_app_settings
from .test_base import BaseAPITestCase 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") @override_settings(ROOT_URLCONF="tests.urls")
class APITestCase1(TestCase, BaseAPITestCase): class APIBasicTests(TestsMixin, TestCase):
""" """
Case #1: Case #1:
- user profile: defined - user profile: defined
@ -413,6 +422,20 @@ class APITestCase1(TestCase, BaseAPITestCase):
self._login() self._login()
self._logout() 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) @override_settings(REST_USE_JWT=True)
def test_registration_with_jwt(self): def test_registration_with_jwt(self):
user_count = get_user_model().objects.all().count() user_count = get_user_model().objects.all().count()

View File

@ -5,17 +5,22 @@ from django.contrib.auth import get_user_model
from django.test.utils import override_settings from django.test.utils import override_settings
from django.contrib.sites.models import Site 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.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
import responses import responses
from rest_framework import status from rest_framework import status
from .test_base import BaseAPITestCase from .mixins import TestsMixin
@override_settings(ROOT_URLCONF="tests.urls") @override_settings(ROOT_URLCONF="tests.urls")
class TestSocialAuth(TestCase, BaseAPITestCase): class TestSocialAuth(TestsMixin, TestCase):
USERNAME = 'person' USERNAME = 'person'
PASS = 'person' PASS = 'person'
@ -225,7 +230,7 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
REST_SESSION_LOGIN=False, REST_SESSION_LOGIN=False,
ACCOUNT_EMAIL_CONFIRMATION_HMAC=False ACCOUNT_EMAIL_CONFIRMATION_HMAC=False
) )
def test_edge_case(self): def test_email_clash_with_existing_account(self):
resp_body = { resp_body = {
"id": "123123123123", "id": "123123123123",
"first_name": "John", "first_name": "John",
@ -251,6 +256,8 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
# test empty payload # test empty payload
self.post(self.register_url, data={}, status_code=400) self.post(self.register_url, data={}, status_code=400)
# register user and send email confirmation
self.post( self.post(
self.register_url, self.register_url,
data=self.REGISTRATION_DATA, data=self.REGISTRATION_DATA,
@ -271,12 +278,11 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
self._login() self._login()
self._logout() self._logout()
# fb log in with already existing email
payload = { payload = {
'access_token': 'abc123' 'access_token': 'abc123'
} }
self.post(self.fb_login_url, data=payload, status_code=400)
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
@responses.activate @responses.activate
@override_settings( @override_settings(
@ -302,3 +308,140 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
self.assertIn('user', self.response.json.keys()) self.assertIn('user', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1) 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

@ -8,8 +8,13 @@ from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_auth.urls import urlpatterns from rest_auth.urls import urlpatterns
from rest_auth.registration.views import SocialLoginView from rest_auth.registration.views import (
from rest_auth.social_serializers import TwitterLoginSerializer SocialLoginView, SocialConnectView, SocialAccountListView,
SocialAccountDisconnectView
)
from rest_auth.social_serializers import (
TwitterLoginSerializer, TwitterConnectSerializer
)
class FacebookLogin(SocialLoginView): class FacebookLogin(SocialLoginView):
@ -21,6 +26,15 @@ class TwitterLogin(SocialLoginView):
serializer_class = TwitterLoginSerializer serializer_class = TwitterLoginSerializer
class FacebookConnect(SocialConnectView):
adapter_class = FacebookOAuth2Adapter
class TwitterConnect(SocialConnectView):
adapter_class = TwitterOAuthAdapter
serializer_class = TwitterConnectSerializer
class TwitterLoginSerializerFoo(TwitterLoginSerializer): class TwitterLoginSerializerFoo(TwitterLoginSerializer):
pass pass
@ -49,5 +63,10 @@ urlpatterns += [
url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_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-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/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')) url(r'^accounts/', include('allauth.socialaccount.urls'))
] ]

View File

@ -89,11 +89,22 @@ class LoginView(GenericAPIView):
serializer = serializer_class(instance=self.user, serializer = serializer_class(instance=self.user,
context={'request': self.request}) context={'request': self.request})
return Response(serializer.data, status=status.HTTP_200_OK) 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.request = request
self.serializer = self.get_serializer(data=self.request.data) self.serializer = self.get_serializer(data=self.request.data,
context={'request': request})
self.serializer.is_valid(raise_exception=True) self.serializer.is_valid(raise_exception=True)
self.login() self.login()
@ -117,7 +128,7 @@ class LogoutView(APIView):
return self.finalize_response(request, response, *args, **kwargs) return self.finalize_response(request, response, *args, **kwargs)
def post(self, request): def post(self, request, *args, **kwargs):
return self.logout(request) return self.logout(request)
def logout(self, request): def logout(self, request):
@ -125,11 +136,16 @@ class LogoutView(APIView):
request.user.auth_token.delete() request.user.auth_token.delete()
except (AttributeError, ObjectDoesNotExist): except (AttributeError, ObjectDoesNotExist):
pass pass
if getattr(settings, 'REST_SESSION_LOGIN', True):
django_logout(request)
django_logout(request) response = Response({"detail": _("Successfully logged out.")},
status=status.HTTP_200_OK)
return Response({"detail": _("Successfully logged out.")}, if getattr(settings, 'REST_USE_JWT', False):
status=status.HTTP_200_OK) from rest_framework_jwt.settings import api_settings as jwt_settings
if jwt_settings.JWT_AUTH_COOKIE:
response.delete_cookie(jwt_settings.JWT_AUTH_COOKIE)
return response
class UserDetailsView(RetrieveUpdateAPIView): class UserDetailsView(RetrieveUpdateAPIView):
@ -197,7 +213,7 @@ class PasswordResetConfirmView(GenericAPIView):
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs) return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs)
def post(self, request): def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
@ -220,7 +236,7 @@ class PasswordChangeView(GenericAPIView):
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
return super(PasswordChangeView, self).dispatch(*args, **kwargs) return super(PasswordChangeView, self).dispatch(*args, **kwargs)
def post(self, request): def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()

View File

@ -1,8 +1,21 @@
# .coveragerc to control coverage.py [bdist_wheel]
[run] universal = 1
[metadata]
license_file = LICENSE
[flake8]
max-line-length = 120
exclude = docs/*,demo/*
ignore = F403
[coverage:run]
omit=*site-packages*,*distutils*,*migrations* omit=*site-packages*,*distutils*,*migrations*
[report] [coverage:report]
# Regexes for lines to exclude from consideration # Regexes for lines to exclude from consideration
exclude_lines = exclude_lines =
# Have to re-enable the standard pragma # Have to re-enable the standard pragma
@ -22,5 +35,5 @@ exclude_lines =
ignore_errors = True ignore_errors = True
[html] [coverage:html]
directory = coverage_html directory = coverage_html

View File

@ -1,14 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
import os import os
from setuptools import setup, find_packages
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'))
@ -18,7 +12,7 @@ f.close()
setup( setup(
name='django-rest-auth', name='django-rest-auth',
version='0.9.1', version='0.9.5',
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',
@ -29,7 +23,7 @@ setup(
zip_safe=False, zip_safe=False,
install_requires=[ install_requires=[
'Django>=1.8.0', 'Django>=1.8.0',
'djangorestframework>=3.1.0', 'djangorestframework>=3.1.3',
'six>=1.9.0', 'six>=1.9.0',
], ],
extras_require={ extras_require={