Compare commits

..

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

53 changed files with 296 additions and 1875 deletions

View File

@ -1,21 +1,8 @@
[bdist_wheel] # .coveragerc to control coverage.py
universal = 1 [run]
[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*
[coverage:report] [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
@ -35,5 +22,5 @@ exclude_lines =
ignore_errors = True ignore_errors = True
[coverage:html] [html]
directory = coverage_html directory = coverage_html

77
.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,28 @@ pip-delete-this-directory.txt
htmlcov/ htmlcov/
.tox/ .tox/
.coverage .coverage
.coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover coverage_html
.hypothesis/
.pytest_cache/
# Translations # Mr Developer
*.mo .mr.developer.cfg
*.pot .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 # IntelliJ IDE files
.ipynb_checkpoints .idea
# 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,25 +2,20 @@ language: python
python: python:
- "2.7" - "2.7"
- "3.5" - "3.5"
- "3.6"
env: env:
- DJANGO=1.11.* DRF=3.7.* - DJANGO=1.8.13
- DJANGO=1.11.* DRF=3.8.* - DJANGO=1.9.7
- DJANGO=2.0.* DRF=3.7.*
- DJANGO=2.0.* DRF=3.8.*
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 rest_auth/tests/requirements.pip
matrix:
exclude:
- python: "3.5"
env: DJANGO=1.8.13
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: 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,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,17 +1,11 @@
===========
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
@ -34,4 +28,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

@ -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,19 @@ 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",
# Disabling due to alluth>=0.21.0 changes
# "allauth.account.context_processors.account",
# "allauth.socialaccount.context_processors.socialaccount",
)
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = (
@ -44,25 +55,23 @@ INSTALLED_APPS = (
'rest_auth.registration', 'rest_auth.registration',
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', '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,30 +95,15 @@ 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 = False
@ -122,8 +116,3 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
) )
} }
SWAGGER_SETTINGS = {
'LOGIN_URL': 'login',
'LOGOUT_URL': 'logout',
}

View File

@ -2,8 +2,6 @@ from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.views.generic import TemplateView, RedirectView from django.views.generic import TemplateView, RedirectView
from rest_framework_swagger.views import get_swagger_view
urlpatterns = [ 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"),
@ -38,7 +36,6 @@ 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/', admin.site.urls), url(r'^admin/', include(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')
] ]

View File

@ -1,6 +1,4 @@
django>=1.9.0 django>=1.8.0
django-rest-auth==0.9.5 django-rest-auth==0.8.0
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

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">
@ -60,7 +60,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

@ -11,7 +11,7 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Logout</button> <button type="submit" class="btn btn-default">Login</button>
</div> </div>
</div> </div>

View File

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

View File

@ -6,15 +6,16 @@ Basic
- /rest-auth/login/ (POST) - /rest-auth/login/ (POST)
- username - username (string)
- email - email (string)
- password - password (string)
Returns Token key
- /rest-auth/logout/ (POST) - /rest-auth/logout/ (POST, GET)
.. note:: ``ACCOUNT_LOGOUT_ON_GET = True`` to allow logout using GET - this is the exact same configuration from allauth. NOT recommended, see: http://django-allauth.readthedocs.io/en/latest/views.html#logout .. note:: ``ACCOUNT_LOGOUT_ON_GET = True`` to allow logout using GET (this is the exact same conf from allauth)
- token
- /rest-auth/password/reset/ (POST) - /rest-auth/password/reset/ (POST)
@ -35,16 +36,18 @@ Basic
- new_password2 - new_password2
- old_password - old_password
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password. .. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change .. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
- /rest-auth/user/ (GET, PUT, PATCH) - /rest-auth/user/ (GET)
- /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
@ -72,8 +75,6 @@ Basing on example from installation section :doc:`Installation </installation>`
- access_token - access_token
- code - 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) - /rest-auth/twitter/ (POST)
- access_token - access_token

View File

@ -1,54 +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 0.8.0
----- -----
- added support for django-rest-framework-jwt - added support for django-rest-framework-jwt

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

@ -36,9 +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.registration.views.RegisterView``, default value ``rest_auth.registration.serializers.RegisterSerializer`` - REGISTER_SERIALIZER - serializer class in ``rest_auth.register.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_MODEL** - model class for tokens, default value ``rest_framework.authtoken.models``
@ -51,3 +49,20 @@ Configuration
- **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 - **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change
Throttling
=============
You may specify custom throttling for ``rest_auth.register.views.RegisterView`` by specifying DRF settings:
.. code-block:: python
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '6/m',
'register_view':'1/h',
},
}

View File

@ -7,14 +7,14 @@ 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. 2. I get an error: Reverse for 'password_reset_confirm' not found.

View File

@ -26,18 +26,12 @@ Installation
.. code-block:: python .. code-block:: python
urlpatterns = [ urlpatterns = patterns('',
..., ...,
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!
@ -66,11 +60,11 @@ Registration (optional)
.. code-block:: python .. code-block:: python
urlpatterns = [ urlpatterns = patterns('',
..., ...,
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)
@ -120,10 +114,10 @@ Facebook
.. code-block:: python .. code-block:: python
urlpatterns += [ urlpatterns += patterns('',
..., ...,
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login') url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
] )
Twitter Twitter
@ -131,15 +125,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.registration.views.SocialLoginView`` with ``TwitterOAuthAdapter`` adapter and ``TwitterLoginSerializer`` as an attribute: 3. Create new view as a subclass of ``rest_auth.views.LoginView`` 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.registration.views import SocialLoginView from rest_auth.views import LoginView
from rest_auth.social_serializers import TwitterLoginSerializer from rest_auth.social_serializers import TwitterLoginSerializer
class TwitterLogin(SocialLoginView): class TwitterLogin(LoginView):
serializer_class = TwitterLoginSerializer serializer_class = TwitterLoginSerializer
adapter_class = TwitterOAuthAdapter adapter_class = TwitterOAuthAdapter
@ -147,114 +141,23 @@ If you are using Twitter for your social authentication, it is a bit different s
.. code-block:: python .. code-block:: python
urlpatterns += [ urlpatterns += patterns('',
..., ...,
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.
GitHub JWT Support (optional)
###### ----------------------
If you are using GitHub for your social authentication, it uses code and not AccessToken directly. By default, ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, you need to install the following:
3. Create new view as a subclass of ``rest_auth.views.SocialLoginView`` with ``GitHubOAuth2Adapter`` adapter, an ``OAuth2Client`` and a callback_url as attributes: 1. Install ``django-rest-framework-jwt`` http://getblimp.github.io/django-rest-framework-jwt/ . Right now this is the only supported JWT library.
.. code-block:: python 2. Add the following to your settings
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 Normal file
View File

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

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

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n" "POT-Creation-Date: 2016-02-02 14:11+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,19 +18,19 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: registration/serializers.py:53 #: registration/serializers.py:54
msgid "View is not defined, pass it as a context variable" msgid "View is not defined, pass it as a context variable"
msgstr "\"View\" ist nicht definiert, übergib es als Contextvariable" msgstr "\"View\" ist nicht definiert, übergib es als Contextvariable"
#: registration/serializers.py:58 #: registration/serializers.py:59
msgid "Define adapter_class in view" msgid "Define adapter_class in view"
msgstr "Definier \"adapter_class\" in view" msgstr "Definier \"adapter_class\" in view"
#: registration/serializers.py:77 #: registration/serializers.py:78
msgid "Define callback_url in view" msgid "Define callback_url in view"
msgstr "Definier \"callback_url\" in view" msgstr "Definier \"callback_url\" in view"
#: registration/serializers.py:81 #: registration/serializers.py:82
msgid "Define client_class in view" msgid "Define client_class in view"
msgstr "Definier \"client_class\" in view" msgstr "Definier \"client_class\" in view"
@ -50,49 +50,50 @@ msgstr "Ein User mit dieser E-Mail Adresse ist schon registriert."
msgid "The two password fields didn't match." msgid "The two password fields didn't match."
msgstr "Die beiden Passwörter sind nicht identisch." msgstr "Die beiden Passwörter sind nicht identisch."
#: registration/views.py:91 #: registration/views.py:64
msgid "ok" msgid "ok"
msgstr "Ok" msgstr "Ok"
#: serializers.py:30 #: serializers.py:29
msgid "Must include \"email\" and \"password\"." msgid "Must include \"email\" and \"password\"."
msgstr "Muss \"email\" und \"password\" enthalten." msgstr "Muss \"email\" und \"password\" enthalten."
#: serializers.py:41 #: serializers.py:40
msgid "Must include \"username\" and \"password\"." msgid "Must include \"username\" and \"password\"."
msgstr "Muss \"username\" und \"password\" enthalten." msgstr "Muss \"username\" und \"password\" enthalten."
#: serializers.py:54 #: serializers.py:53
msgid "Must include either \"username\" or \"email\" and \"password\"." msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Muss entweder \"username\" oder \"email\" und password \"password\"" msgstr "Muss entweder \"username\" oder \"email\" und password \"password\""
#: serializers.py:95 #: serializers.py:94
msgid "User account is disabled." msgid "User account is disabled."
msgstr "Der Useraccount ist deaktiviert." msgstr "Der Useraccount ist deaktiviert."
#: serializers.py:98 #: serializers.py:97
msgid "Unable to log in with provided credentials." msgid "Unable to log in with provided credentials."
msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden." msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden."
#: serializers.py:107 #: serializers.py:106
msgid "E-mail is not verified." msgid "E-mail is not verified."
msgstr "E-Mail Adresse ist nicht verifiziert." msgstr "E-Mail Adresse ist nicht verifiziert."
#: views.py:126 #: serializers.py:152
msgid "Error"
msgstr "Fehler"
#: views.py:71
msgid "Successfully logged out." msgid "Successfully logged out."
msgstr "Erfolgreich ausgeloggt." msgstr "Erfolgreich ausgeloggt."
#: views.py:174 #: views.py:111
msgid "Password reset e-mail has been sent." msgid "Password reset e-mail has been sent."
msgstr "Die E-Mail zum Zurücksetzen des Passwortes wurde verschickt." msgstr "Die E-Mail zum Zurücksetzen des Passwortes wurde verschickt."
#: views.py:200 #: views.py:132
msgid "Password has been reset with the new password." msgid "Password has been reset with the new password."
msgstr "Das Passwort wurde mit dem neuen Passwort ersetzt." msgstr "Das Passwort wurde mit dem neuen Passwort ersetzt."
#: views.py:222 #: views.py:150
msgid "New password has been saved." msgid "New password has been saved."
msgstr "Das neue Passwort wurde gespeichert." 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,6 +1,5 @@
from django.conf import settings from django.conf import settings
from rest_framework.permissions import AllowAny
from rest_auth.registration.serializers import ( from rest_auth.registration.serializers import (
RegisterSerializer as DefaultRegisterSerializer) RegisterSerializer as DefaultRegisterSerializer)
from ..utils import import_callable from ..utils import import_callable
@ -10,10 +9,3 @@ serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {})
RegisterSerializer = import_callable( RegisterSerializer = import_callable(
serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer)) 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,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,29 +8,15 @@ 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
class SocialAccountSerializer(serializers.ModelSerializer): if 'allauth.socialaccount' in settings.INSTALLED_APPS:
""" 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):
@ -45,13 +31,12 @@ class SocialLoginSerializer(serializers.Serializer):
def get_social_login(self, adapter, app, token, response): def get_social_login(self, adapter, app, token, response):
""" """
:param adapter: allauth.socialaccount Adapter subclass.
Usually OAuthAdapter or Auth2Adapter :param adapter: allauth.socialaccount Adapter subclass. Usually OAuthAdapter or Auth2Adapter
:param app: `allauth.socialaccount.SocialApp` instance :param app: `allauth.socialaccount.SocialApp` instance
:param token: `allauth.socialaccount.SocialToken` instance :param token: `allauth.socialaccount.SocialToken` instance
:param response: Provider's response for OAuth1. Not used in the :param response: Provider's response for OAuth1. Not used in the
:returns: A populated instance of the :return: :return: A populated instance of the `allauth.socialaccount.SocialLoginView` instance
`allauth.socialaccount.SocialLoginView` instance
""" """
request = self._get_request() request = self._get_request()
social_login = adapter.complete_login(request, app, token, response=response) social_login = adapter.complete_login(request, app, token, response=response)
@ -64,12 +49,12 @@ class SocialLoginSerializer(serializers.Serializer):
if not view: if not view:
raise serializers.ValidationError( raise serializers.ValidationError(
_("View is not defined, pass it as a context variable") _('View is not defined, pass it as a context variable')
) )
adapter_class = getattr(view, 'adapter_class', None) adapter_class = getattr(view, 'adapter_class', None)
if not adapter_class: if not adapter_class:
raise serializers.ValidationError(_("Define adapter_class in view")) raise serializers.ValidationError(_('Define adapter_class in view'))
adapter = adapter_class(request) adapter = adapter_class(request)
app = adapter.get_provider().get_app(request) app = adapter.get_provider().get_app(request)
@ -78,21 +63,21 @@ class SocialLoginSerializer(serializers.Serializer):
# http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token # http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token
# Case 1: We received the access_token # Case 1: We received the access_token
if attrs.get('access_token'): if('access_token' in attrs):
access_token = attrs.get('access_token') access_token = attrs.get('access_token')
# Case 2: We received the authorization code # Case 2: We received the authorization code
elif attrs.get('code'): elif('code' in attrs):
self.callback_url = getattr(view, 'callback_url', None) self.callback_url = getattr(view, 'callback_url', None)
self.client_class = getattr(view, 'client_class', None) self.client_class = getattr(view, 'client_class', None)
if not self.callback_url: if not self.callback_url:
raise serializers.ValidationError( raise serializers.ValidationError(
_("Define callback_url in view") _('Define callback_url in view')
) )
if not self.client_class: if not self.client_class:
raise serializers.ValidationError( raise serializers.ValidationError(
_("Define client_class in view") _('Define client_class in view')
) )
code = attrs.get('code') code = attrs.get('code')
@ -112,57 +97,25 @@ class SocialLoginSerializer(serializers.Serializer):
access_token = token['access_token'] access_token = token['access_token']
else: else:
raise serializers.ValidationError( raise serializers.ValidationError(_('Incorrect input. access_token or code is required.'))
_("Incorrect input. access_token or code is required."))
social_token = adapter.parse_token({'access_token': access_token}) token = adapter.parse_token({'access_token': access_token})
social_token.app = app token.app = app
try: try:
login = self.get_social_login(adapter, app, social_token, access_token) login = self.get_social_login(adapter, app, token, access_token)
complete_social_login(request, login) complete_social_login(request, login)
except HTTPError: except HTTPError:
raise serializers.ValidationError(_("Incorrect value")) raise serializers.ValidationError(_('Incorrect value'))
if not login.is_existing: if not login.is_existing:
# 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(),
@ -170,8 +123,8 @@ class RegisterSerializer(serializers.Serializer):
required=allauth_settings.USERNAME_REQUIRED required=allauth_settings.USERNAME_REQUIRED
) )
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED) email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
password1 = serializers.CharField(write_only=True) password1 = serializers.CharField(required=True, write_only=True)
password2 = serializers.CharField(write_only=True) password2 = serializers.CharField(required=True, write_only=True)
def validate_username(self, username): def validate_username(self, username):
username = get_adapter().clean_username(username) username = get_adapter().clean_username(username)

View File

@ -17,7 +17,7 @@ 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 # django-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,54 +1,39 @@
from django.conf import settings
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters from django.conf import settings
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
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 (TokenSerializer, from rest_auth.app_settings import (TokenSerializer,
JWTSerializer, JWTSerializer,
create_token) create_token)
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.views import LoginView from rest_auth.views import LoginView
from .app_settings import RegisterSerializer, register_permission_classes from rest_auth.models import TokenModel
from .app_settings import RegisterSerializer
sensitive_post_parameters_m = method_decorator( from rest_auth.utils import jwt_encode
sensitive_post_parameters('password1', 'password2')
)
class RegisterView(CreateAPIView): class RegisterView(CreateAPIView):
serializer_class = RegisterSerializer serializer_class = RegisterSerializer
permission_classes = register_permission_classes() permission_classes = (AllowAny, )
token_model = TokenModel token_model = TokenModel
throttle_scope = 'register_view'
@sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(RegisterView, self).dispatch(*args, **kwargs)
def get_response_data(self, user): def get_response_data(self, user):
if allauth_settings.EMAIL_VERIFICATION == \ if allauth_settings.EMAIL_VERIFICATION == \
allauth_settings.EmailVerificationMethod.MANDATORY: allauth_settings.EmailVerificationMethod.MANDATORY:
return {"detail": _("Verification e-mail sent.")} return {}
if getattr(settings, 'REST_USE_JWT', False): if getattr(settings, 'REST_USE_JWT', False):
data = { data = {
@ -65,9 +50,7 @@ class RegisterView(CreateAPIView):
user = self.perform_create(serializer) user = self.perform_create(serializer)
headers = self.get_success_headers(serializer.data) headers = self.get_success_headers(serializer.data)
return Response(self.get_response_data(user), return Response(self.get_response_data(user), status=status.HTTP_201_CREATED, headers=headers)
status=status.HTTP_201_CREATED,
headers=headers)
def perform_create(self, serializer): def perform_create(self, serializer):
user = serializer.save(self.request) user = serializer.save(self.request)
@ -83,19 +66,17 @@ class RegisterView(CreateAPIView):
class VerifyEmailView(APIView, ConfirmEmailView): class VerifyEmailView(APIView, ConfirmEmailView):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
allowed_methods = ('POST', 'OPTIONS', 'HEAD') allowed_methods = ('POST', 'OPTIONS', 'HEAD')
def get_serializer(self, *args, **kwargs):
return VerifyEmailSerializer(*args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = VerifyEmailSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
self.kwargs['key'] = serializer.validated_data['key'] self.kwargs['key'] = serializer.validated_data['key']
confirmation = self.get_object() confirmation = self.get_object()
confirmation.confirm(self.request) confirmation.confirm(self.request)
return Response({'detail': _('ok')}, status=status.HTTP_200_OK) return Response({'message': _('ok')}, status=status.HTTP_200_OK)
class SocialLoginView(LoginView): class SocialLoginView(LoginView):
@ -117,70 +98,12 @@ 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

@ -6,12 +6,11 @@ from django.utils.http import urlsafe_base64_decode as uid_decoder
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import force_text from django.utils.encoding import force_text
from .models import TokenModel
from rest_framework import serializers, exceptions from rest_framework import serializers, exceptions
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from .models import TokenModel
from .utils import import_callable
# Get the UserModel # Get the UserModel
UserModel = get_user_model() UserModel = get_user_model()
@ -21,14 +20,11 @@ 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 = self.authenticate(email=email, password=password) user = authenticate(email=email, password=password)
else: else:
msg = _('Must include "email" and "password".') msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
@ -39,7 +35,7 @@ class LoginSerializer(serializers.Serializer):
user = None user = None
if username and password: if username and password:
user = self.authenticate(username=username, password=password) user = authenticate(username=username, password=password)
else: else:
msg = _('Must include "username" and "password".') msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
@ -50,9 +46,9 @@ class LoginSerializer(serializers.Serializer):
user = None user = None
if email and password: if email and password:
user = self.authenticate(email=email, password=password) user = authenticate(email=email, password=password)
elif username and password: elif username and password:
user = self.authenticate(username=username, password=password) user = 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)
@ -74,7 +70,7 @@ class LoginSerializer(serializers.Serializer):
user = self._validate_email(email, password) user = self._validate_email(email, password)
# Authentication through username # Authentication through username
elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME: if 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
@ -124,12 +120,13 @@ class TokenSerializer(serializers.ModelSerializer):
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 = UserModel
fields = ('pk', 'username', 'email', 'first_name', 'last_name') fields = ('username', 'email', 'first_name', 'last_name')
read_only_fields = ('email', ) read_only_fields = ('email', )
@ -138,31 +135,22 @@ class JWTSerializer(serializers.Serializer):
Serializer for JWT authentication. Serializer for JWT authentication.
""" """
token = serializers.CharField() token = serializers.CharField()
user = serializers.SerializerMethodField() user = UserDetailsSerializer()
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 get_email_options(self):
"""Override this method to change default e-mail options""" """ Override this method to change default e-mail options
"""
return {} return {}
def validate_email(self, value): def validate_email(self, value):
@ -190,10 +178,12 @@ 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
@ -223,10 +213,11 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
return attrs return attrs
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)
@ -256,8 +247,7 @@ class PasswordChangeSerializer(serializers.Serializer):
) )
if all(invalid_password_conditions): if all(invalid_password_conditions):
err_msg = _("Your old password was entered incorrectly. Please enter it again.") raise serializers.ValidationError('Invalid password')
raise serializers.ValidationError(err_msg)
return value return value
def validate(self, attrs): def validate(self, attrs):

View File

@ -8,12 +8,10 @@ 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(required=True)
token_secret = serializers.CharField() token_secret = serializers.CharField(required=True)
def _get_request(self): def _get_request(self):
request = self.context.get('request') request = self.context.get('request')
@ -23,17 +21,15 @@ class TwitterLoginSerializer(serializers.Serializer):
def get_social_login(self, adapter, app, token, response): def get_social_login(self, adapter, app, token, response):
""" """
:param adapter: allauth.socialaccount Adapter subclass.
Usually OAuthAdapter or Auth2Adapter :param adapter: allauth.socialaccount Adapter subclass. Usually OAuthAdapter or Auth2Adapter
:param app: `allauth.socialaccount.SocialApp` instance :param app: `allauth.socialaccount.SocialApp` instance
:param token: `allauth.socialaccount.SocialToken` instance :param token: `allauth.socialaccount.SocialToken` instance
:param response: Provider's response for OAuth1. Not used in the :param response: Provider's response for OAuth1. Not used in the
:returns: A populated instance of the :return: :return: A populated instance of the `allauth.socialaccount.SocialLoginView` instance
`allauth.socialaccount.SocialLoginView` instance
""" """
request = self._get_request() request = self._get_request()
social_login = adapter.complete_login(request, app, token, social_login = adapter.complete_login(request, app, token, response=response)
response=response)
social_login.token = token social_login.token = token
return social_login return social_login
@ -43,12 +39,12 @@ class TwitterLoginSerializer(serializers.Serializer):
if not view: if not view:
raise serializers.ValidationError( raise serializers.ValidationError(
"View is not defined, pass it as a context variable" 'View is not defined, pass it as a context variable'
) )
adapter_class = getattr(view, 'adapter_class', None) adapter_class = getattr(view, 'adapter_class', None)
if not adapter_class: if not adapter_class:
raise serializers.ValidationError("Define adapter_class in view") raise serializers.ValidationError('Define adapter_class in view')
adapter = adapter_class(request) adapter = adapter_class(request)
app = adapter.get_provider().get_app(request) app = adapter.get_provider().get_app(request)
@ -75,7 +71,3 @@ 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,5 +1,4 @@
django-allauth>=0.25.0 django-allauth>=0.19.1
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 = [ 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 +33,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',

View File

@ -1,26 +1,17 @@
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 allauth.account import app_settings as account_app_settings
from rest_framework import status from rest_framework import status
from rest_framework.test import APIRequestFactory from allauth.account import app_settings as account_app_settings
from .test_base import BaseAPITestCase
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 APIBasicTests(TestsMixin, TestCase): class APITestCase1(TestCase, BaseAPITestCase):
""" """
Case #1: Case #1:
- user profile: defined - user profile: defined
@ -407,20 +398,6 @@ class APIBasicTests(TestsMixin, TestCase):
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()
@ -482,7 +459,7 @@ class APIBasicTests(TestsMixin, TestCase):
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\ email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
.emailconfirmation_set.order_by('-created')[0] .emailconfirmation_set.order_by('-created')[0]
self.post( self.post(
self.verify_email_url, self.veirfy_email_url,
data={"key": email_confirmation.key}, data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK status_code=status.HTTP_200_OK
) )

View File

@ -1,23 +1,11 @@
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):
@ -29,7 +17,8 @@ class APIClient(Client):
return self.generic('OPTIONS', path, data, content_type, **extra) return self.generic('OPTIONS', path, data, content_type, **extra)
class TestsMixin(object): class BaseAPITestCase(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)
@ -75,6 +64,29 @@ class TestsMixin(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()
@ -85,14 +97,11 @@ class TestsMixin(object):
self.register_url = reverse('rest_register') self.register_url = reverse('rest_register')
self.password_reset_url = reverse('rest_password_reset') self.password_reset_url = reverse('rest_password_reset')
self.user_url = reverse('rest_user_details') self.user_url = reverse('rest_user_details')
self.verify_email_url = reverse('rest_verify_email') self.veirfy_email_url = reverse('rest_verify_email')
self.fb_login_url = reverse('fb_login') self.fb_login_url = reverse('fb_login')
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

@ -5,22 +5,17 @@ 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 .mixins import TestsMixin from .test_base import BaseAPITestCase
@override_settings(ROOT_URLCONF="tests.urls") @override_settings(ROOT_URLCONF="tests.urls")
class TestSocialAuth(TestsMixin, TestCase): class TestSocialAuth(TestCase, BaseAPITestCase):
USERNAME = 'person' USERNAME = 'person'
PASS = 'person' PASS = 'person'
@ -230,7 +225,7 @@ class TestSocialAuth(TestsMixin, TestCase):
REST_SESSION_LOGIN=False, REST_SESSION_LOGIN=False,
ACCOUNT_EMAIL_CONFIRMATION_HMAC=False ACCOUNT_EMAIL_CONFIRMATION_HMAC=False
) )
def test_email_clash_with_existing_account(self): def test_edge_case(self):
resp_body = { resp_body = {
"id": "123123123123", "id": "123123123123",
"first_name": "John", "first_name": "John",
@ -256,8 +251,6 @@ class TestSocialAuth(TestsMixin, TestCase):
# 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,
@ -270,7 +263,7 @@ class TestSocialAuth(TestsMixin, TestCase):
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\ email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
.emailconfirmation_set.order_by('-created')[0] .emailconfirmation_set.order_by('-created')[0]
self.post( self.post(
self.verify_email_url, self.veirfy_email_url,
data={"key": email_confirmation.key}, data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK status_code=status.HTTP_200_OK
) )
@ -278,11 +271,12 @@ class TestSocialAuth(TestsMixin, TestCase):
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(
@ -308,140 +302,3 @@ class TestSocialAuth(TestsMixin, TestCase):
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,13 +8,8 @@ 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 ( from rest_auth.registration.views import SocialLoginView
SocialLoginView, SocialConnectView, SocialAccountListView, from rest_auth.social_serializers import TwitterLoginSerializer
SocialAccountDisconnectView
)
from rest_auth.social_serializers import (
TwitterLoginSerializer, TwitterConnectSerializer
)
class FacebookLogin(SocialLoginView): class FacebookLogin(SocialLoginView):
@ -26,15 +21,6 @@ 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
@ -63,10 +49,5 @@ 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

@ -20,7 +20,7 @@ def jwt_encode(user):
try: try:
from rest_framework_jwt.settings import api_settings from rest_framework_jwt.settings import api_settings
except ImportError: except ImportError:
raise ImportError("djangorestframework_jwt needs to be installed") raise ImportError('rest_framework_jwt needs to be installed')
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

View File

@ -3,17 +3,17 @@ from django.contrib.auth import (
logout as django_logout 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.core.exceptions import ObjectDoesNotExist
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ 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.generics import RetrieveUpdateAPIView
from allauth.account import app_settings as allauth_settings
from .app_settings import ( from .app_settings import (
TokenSerializer, UserDetailsSerializer, LoginSerializer, TokenSerializer, UserDetailsSerializer, LoginSerializer,
@ -21,16 +21,12 @@ from .app_settings import (
PasswordChangeSerializer, JWTSerializer, create_token PasswordChangeSerializer, JWTSerializer, create_token
) )
from .models import TokenModel from .models import TokenModel
from .utils import jwt_encode
sensitive_post_parameters_m = method_decorator( from .utils import jwt_encode
sensitive_post_parameters(
'password', 'old_password', 'new_password1', 'new_password2'
)
)
class LoginView(GenericAPIView): class LoginView(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.
@ -44,10 +40,6 @@ class LoginView(GenericAPIView):
serializer_class = LoginSerializer serializer_class = LoginSerializer
token_model = TokenModel token_model = TokenModel
@sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(LoginView, self).dispatch(*args, **kwargs)
def process_login(self): def process_login(self):
django_login(self.request, self.user) django_login(self.request, self.user)
@ -64,8 +56,7 @@ class LoginView(GenericAPIView):
if getattr(settings, 'REST_USE_JWT', False): if getattr(settings, 'REST_USE_JWT', False):
self.token = jwt_encode(self.user) self.token = jwt_encode(self.user)
else: else:
self.token = create_token(self.token_model, self.user, self.token = create_token(self.token_model, self.user, self.serializer)
self.serializer)
if getattr(settings, 'REST_SESSION_LOGIN', True): if getattr(settings, 'REST_SESSION_LOGIN', True):
self.process_login() self.process_login()
@ -78,28 +69,15 @@ class LoginView(GenericAPIView):
'user': self.user, 'user': self.user,
'token': self.token 'token': self.token
} }
serializer = serializer_class(instance=data, serializer = serializer_class(instance=data, context={'request': self.request})
context={'request': self.request})
else: else:
serializer = serializer_class(instance=self.token, serializer = serializer_class(instance=self.token, context={'request': self.request})
context={'request': self.request})
response = Response(serializer.data, status=status.HTTP_200_OK) return 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()
@ -107,6 +85,7 @@ class LoginView(GenericAPIView):
class LogoutView(APIView): class LogoutView(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.
@ -116,14 +95,17 @@ class LogoutView(APIView):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if getattr(settings, 'ACCOUNT_LOGOUT_ON_GET', False): try:
response = self.logout(request) if allauth_settings.LOGOUT_ON_GET:
else: response = self.logout(request)
response = self.http_method_not_allowed(request, *args, **kwargs) else:
response = self.http_method_not_allowed(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
return self.finalize_response(request, response, *args, **kwargs) return self.finalize_response(request, response, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request):
return self.logout(request) return self.logout(request)
def logout(self, request): def logout(self, request):
@ -131,28 +113,22 @@ 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)
response = Response({"detail": _("Successfully logged out.")}, django_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 UserDetailsView(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,22 +136,16 @@ 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 PasswordResetView(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,)
@ -187,34 +157,28 @@ class PasswordResetView(GenericAPIView):
serializer.save() serializer.save()
# Return the success message with OK HTTP status # Return the success message with OK HTTP status
return Response( return Response(
{"detail": _("Password reset e-mail has been sent.")}, {"success": _("Password reset e-mail has been sent.")},
status=status.HTTP_200_OK status=status.HTTP_200_OK
) )
class PasswordResetConfirmView(GenericAPIView): class PasswordResetConfirmView(GenericAPIView):
""" """
Password reset e-mail link is confirmed, therefore 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):
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs)
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()
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 PasswordChangeView(GenericAPIView):
@ -224,15 +188,12 @@ class PasswordChangeView(GenericAPIView):
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):
return super(PasswordChangeView, self).dispatch(*args, **kwargs)
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()
return Response({"detail": _("New password has been saved.")}) return Response({"success": _("New password has been saved.")})

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.8.0',
author='Sumit Chachra', author='Sumit Chachra',
author_email='chachra@tivix.com', author_email='chachra@tivix.com',
url='http://github.com/Tivix/django-rest-auth', url='http://github.com/Tivix/django-rest-auth',
@ -23,7 +29,7 @@ setup(
zip_safe=False, zip_safe=False,
install_requires=[ install_requires=[
'Django>=1.8.0', 'Django>=1.8.0',
'djangorestframework>=3.1.3', 'djangorestframework>=3.1.0',
'six>=1.9.0', 'six>=1.9.0',
], ],
extras_require={ extras_require={
@ -32,10 +38,10 @@ setup(
tests_require=[ tests_require=[
'responses>=0.5.0', 'responses>=0.5.0',
'django-allauth>=0.25.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',