Compare commits

..

443 Commits

Author SHA1 Message Date
Adam Lord
cdd04aa9be
Merge pull request #657 from weslord/patch-1
Update deprecation notice with link to fork
2022-02-25 13:28:51 -08:00
Wes Lord
146777283e Update deprecation notice with link to fork
A deprecation notice meant for this repo was accidentally applied to
https://github.com/Tivix/django-cron instead.

I've directly copied the text from that commit.
2022-02-24 15:07:25 -08:00
Sumit Chachra
3c36004c44
Merge pull request #603 from raunaqss/patch-1
Update ReadMe with state of repo
2021-03-07 21:54:38 -08:00
Raunaq Singh
0ad457cea6
Update ReadMe with state of repo
According to this issue: https://github.com/Tivix/django-rest-auth/issues/568. This repository is currently unmaintained. This information should be obvious to new users planning to use this package. Hence making this PR.

Thanks.
2020-02-25 12:20:57 +05:30
mario
624ad01afb
Merge pull request #313 from aspyct/feature/fr_localization
Added French translations
2019-04-18 20:02:52 +02:00
mario
04c692bba6
Merge pull request #468 from kneufeld/master
expand JWT acronym in docs
2019-04-18 20:01:15 +02:00
mario
4cf813262f
Merge pull request #481 from OskarPersson/old-password-error-msg
Use translated msg when providing incorrect old password
2019-04-18 19:58:54 +02:00
mario
bd843f9460
Merge pull request #526 from brunobarretofreitas/portugueseBrazilTranslation
Brazilian Portuguese Translation
2019-04-18 19:56:04 +02:00
Bruno Barreto Freitas
07d2bd5c56 Brazilian Portuguese Translation
- django.po file
2019-04-16 10:27:43 -03:00
mario
998b2b1fbc feat: prep for new release (0.9.5) 2019-04-01 09:51:08 +02:00
mario
c7ff94ced7 feat: prep for new release (0.9.4) 2019-04-01 07:51:51 +02:00
mario
ac44fbe07a
Merge pull request #483 from vthorey/vthorey/installation-doc
Vthorey/installation doc
2019-03-29 19:28:05 +01:00
mario
39252820f7
Merge pull request #521 from toracle/master
Added a korean translation
2019-03-29 19:25:34 +01:00
mario
043cb85374
Merge pull request #522 from gsheni/patch-1
Update installation.rst
2019-03-29 19:24:36 +01:00
Gaurav Sheni
f39d93042e
Update installation.rst 2019-03-26 11:07:27 -04:00
Jeongsoo Park
d2cd31a560 update revision date 2019-03-20 17:52:51 +09:00
Jeongsoo Park
11e877ba50 add a korean translation mo file 2019-03-20 17:51:09 +09:00
Jeongsoo Park
64b5572172 add a korean translation 2019-03-20 17:44:18 +09:00
mario
bb3b082895
Merge pull request #345 from gbezyuk/master
LoginView.get_response modified in order to respect rest_framework_jwt.JWT_AUTH_COOKIE setting
2019-03-16 14:57:01 +01:00
mario
5c556cd09a
Merge pull request #463 from erdtsksn/feat/translation-tr
feat: Add Turkish(tr) translation
2019-02-03 20:00:03 +01:00
mario
22a76d91ac
Merge pull request #470 from magnunleno/fix/add-missing-allauth-requirement
Adds django-allauth to dev-requirements.txt
2019-02-03 19:47:02 +01:00
mario
51d6bf4542
Merge pull request #472 from yihuang/master
add translation for Chinese
2019-02-03 19:02:42 +01:00
mario
c7cc41e07c
Merge pull request #506 from Jasper0819X/master
disable django_logout if REST_SESSION_LOGIN is False
2019-02-03 19:02:25 +01:00
Jasper Wan
a3d38f4c59 disable django_logout if REST_SESSION_LOGIN is False 2019-02-03 11:42:24 +08:00
Valentino
e891a76c3f Add example of github API in installation doc. 2018-11-12 16:58:07 +01:00
vthorey
25b94db0cc
Update installation.rst 2018-11-12 16:49:36 +01:00
Oskar Persson
c437ca22ff Translate msg when providing incorrect old password 2018-11-09 15:58:09 +01:00
yihuang
9913f8eb03 add translation for Chinese 2018-10-28 11:46:19 +08:00
Magnun Leno
ca39ba7ea9 Adds django-allauth to dev-requirements.txt 2018-10-27 02:14:25 -03:00
Kurt Neufeld
f5fe62ce91 expand JWT acronym in docs 2018-10-25 11:28:24 -06:00
erdtsksn
5d318b3a4b feat: Add Turkish(tr) translation
oth: There is no .mo file because there is a .gitignore entry for that.
2018-10-22 11:38:21 +03:00
mario
479a40d2cc
Merge pull request #437 from dgilge/request
Pass request to authenticate
2018-09-08 17:07:43 +02:00
mario
25263b3e4a
Merge pull request #442 from illagrenan/master
Add Czech (cs) translations
2018-09-08 17:03:22 +02:00
mario
834e4c4747
Merge pull request #434 from dgilge/DRF3.8
Add DRF 3.8 to Travis config
2018-09-08 17:00:13 +02:00
mario
f5fcf9f083
Merge pull request #452 from rpkilby/gardening
Minor package maintenance
2018-09-08 16:54:51 +02:00
Ryan P Kilby
4b07c3ca92 Add wheel & metdata config 2018-08-21 21:29:15 -07:00
Ryan P Kilby
691c73d556 Update .gitignore from GH 2018-08-21 21:18:41 -07:00
Ryan P Kilby
f0941b4cc2 Merge tools configs into setup.cfg 2018-08-21 21:16:52 -07:00
Ryan P Kilby
033ee8f483 Remove deprecated EZ setup 2018-08-21 21:12:28 -07:00
Václav Dohnal
53f901b3c8 refresh .mo file 2018-06-27 23:22:38 +02:00
Vašek Dohnal
3af4f1a343
fix typo 2018-06-27 23:21:56 +02:00
Vašek Dohnal
b778a5085b
add full project name 2018-06-27 23:20:10 +02:00
Vašek Dohnal
98212a83f5
fix typo 2018-06-27 23:19:48 +02:00
Václav Dohnal
fa3000e8bd add cs translations 2018-06-27 23:18:39 +02:00
Václav Dohnal
b9fd4aba96 add czech (cs) translations stub 2018-06-27 23:06:34 +02:00
Daniel
a1845aef43 Pass request to authenticate 2018-06-14 00:06:20 +02:00
Daniel
db60e43397 Exclude Python 2.7 for Django 2.0 2018-06-11 09:02:02 +02:00
Daniel
90760548f6 Add DRF 3.8 to Travis config 2018-06-11 08:46:59 +02:00
Maxim Kukhtenkov
95fafe5e0f Remove Django 1.8 from Travis config 2018-06-06 17:10:43 -04:00
Maxim Kukhtenkov
801bad7c61 Update copyright and version number in Docs config 2018-06-06 15:01:22 -04:00
Maxim Kukhtenkov
a3057b7aa1 Flake8 fix - unused import 2018-01-20 20:25:58 -05:00
Maxim Kukhtenkov
c009bb9975 Change conditional import from allauth.socialaccount 2018-01-20 20:10:18 -05:00
Maxim Kukhtenkov
bcd6ab4401 Update configs and changelog for version 0.9.3 + additional fixes 2018-01-19 19:44:31 -05:00
Maxim Kukhtenkov
6a84d85d91 Add change in documentation and tests for social connect 2018-01-19 18:29:38 -05:00
Maxim Kukhtenkov
3eb546f633 Add extra info to docs for social connect
#347
2018-01-18 21:11:36 -05:00
Maxim Kukhtenkov
fed6b9840c Refactor social connect views and serializers
# 347
2018-01-18 21:08:41 -05:00
Maxim Kukhtenkov
41ae498be0
Merge pull request #387 from aleksihakli/master
Implement connect social accounts functionality
2018-01-18 21:02:45 -05:00
Aleksi Häkli
8a4afe746c
Implement connect social accounts functionality
Add serializers and views for connecting accounts with minimal
specialization of the existing django-rest-auth interfaces.

Also add viewset which enables listing social account infmration
via the REST API for social authentication driven client applications.

Resolves #347 in GitHub.
2018-01-18 16:56:42 +02:00
Maxim Kukhtenkov
a892ca3fa5 Upgrade requirements for demo project 2017-12-16 16:48:34 -05:00
Maxim Kukhtenkov
5e6c5a75ea Changes in demo project settings for Django 2.0 compatibility 2017-12-05 12:39:57 -05:00
Maxim Kukhtenkov
98d5ce314f Fix unit tests 2017-12-05 00:39:32 -05:00
Maxim Kukhtenkov
7ad440f7a2 Remove Django 1.10 from tests and move imports to top of the file 2017-12-05 00:23:37 -05:00
Maxim Kukhtenkov
fcca39643e
Merge pull request #391 from michael-k/patch-1
Run tests against Django 2.0
2017-12-05 00:20:31 -05:00
Michael K
3a2a256821 Fixes import path for django's reverse 2017-12-04 15:02:36 +01:00
Michael
864aa60f92 Run tests against Django 2.0
https://www.djangoproject.com/weblog/2017/dec/02/django-20-released/
2017-12-04 15:02:36 +01:00
mario
ed16455712
Merge pull request #386 from ducheneaut/patch-1
Prevent double-validation in LoginSerializer
2017-11-27 08:15:33 +01:00
Nic Ducheneaut
8bd8d604ff
Prevent double-validation in LoginSerializer
Modified branching statement to prevent double-validation when using "email" as the authentication method with Allauth.
2017-11-25 14:10:44 -08:00
mario
658664a7d4 Merge branch 'master' into pr/367 2017-11-12 10:36:13 +01:00
mario
ee791a402e
Merge pull request #367 from philippeluickx/d/pre_existing_acounts_check
Checking for pre-existing accounts
2017-11-12 10:35:36 +01:00
mario
f89471f980 pep8 cleanups 2017-11-12 10:32:27 +01:00
mario
a670fa9687
Merge pull request #372 from sloria/test-py36
Test against Python 3.6
2017-10-31 21:20:05 +01:00
mario
1b51eb8732
Merge pull request #380 from Tivix/travis-conf-fixes
Travis conf fixes
2017-10-31 18:27:42 +01:00
mario
2c1649daf1 fix: revert setup.py changes 2017-10-31 17:13:57 +01:00
mario
be2ac99a34 fix: changed travis.yml and setup.py configuration 2017-10-31 16:34:29 +01:00
mario
e4c04528a2 Merge pull request #373 from sloria/versioning-compat
Handle extra args and kwargs in all POST endpoints
2017-10-25 14:56:40 +02:00
Steven Loria
b34250ec94
Handle extra args and kwargs in all POST endpoints
This fixes compatibility with DRF's versioning
2017-10-24 20:14:12 -04:00
Steven Loria
05e5c647fa
Test against Python 3.6 2017-10-24 19:51:45 -04:00
Philippe Luickx
945008d326 Checking for pre-existing accounts from a different flow when using social connect 2017-10-05 12:06:05 +03:00
Maxim Kukhtenkov
42d039b473 Fix urlpatterns format in docs 2017-10-03 10:17:48 -04:00
Maxim Kukhtenkov
7e708d9ca7 Update configs and changelog for version 0.9.2 2017-10-02 15:52:33 -04:00
mario
30c89b1cc6 chore: added new test case for custom permission classes + general test module cleanups 2017-10-02 21:38:06 +02:00
Maxim Kukhtenkov
8afba8ca16 Merge pull request #341 from bIgBV/add-request-to-context
Pass request in the context when initializing `LoginSerializer`
2017-10-02 12:57:07 -04:00
Maxim Kukhtenkov
7a0fa017d3 Add MANIFEST.in change from #335 to master branch 2017-10-02 12:31:02 -04:00
Maxim Kukhtenkov
d16ec273e6 Merge pull request #366 from sobolevn/patch-1
Updates README.rst with svg badges
2017-10-02 11:56:54 -04:00
Nikita Sobolev
99b68b2914 Updates README.rst with svg badges 2017-10-02 18:46:47 +03:00
Maxim Kukhtenkov
53398f8727 Merge pull request #327 from blablacio/user-details-serializer-context
Pass context to user defined USER_DETAILS_SERIALIZER
2017-10-02 11:26:37 -04:00
Mateusz Sikora
b124aaf273 Merge pull request #334 from jrief/fixes/bypass-user-object
return user object from upstream method invocation
2017-09-21 09:43:41 +02:00
Grigoriy Beziuk
46fd16700a also for cookie deletion 2017-06-30 14:23:56 +03:00
Grigoriy Beziuk
2672263100 Merge pull request #2 from gbezyuk-on-software/master
LoginView.get_response modified in order to respect rest_framework_jwt.JWT_AUTH_COOKIE setting
2017-06-30 14:04:58 +03:00
Grigoriy Beziuk
18e98d333b Merge pull request #1 from Tivix/master
merge upstream
2017-06-30 14:04:02 +03:00
Grigoriy Beziuk
6da0703b0d LoginView.get_response modified in order to respect rest_framework_jwt.JWT_AUTH_COOKIE setting 2017-06-30 13:59:10 +03:00
David Gunter
9d1f65eedc Merge pull request #338 from verkaufer/jwt_docs_fix
Update JWT Support documentation
2017-06-28 11:36:04 -07:00
bIgBV
98306a35c1 Pass request in the context when initializing LoginSerializer 2017-06-24 18:50:50 +05:30
David Gunter
f0175b4aac Update JWT Support documentation to explain how the library uses django-rest-framework-jwt to handle token encoding. 2017-06-15 16:30:21 -07:00
mario
606858fba7 Merge pull request #336 from Vitiell0/patch-1
Add step to remind user to migrate their database
2017-06-12 17:34:31 +02:00
Daniel Vitiello
8981317b1c Add step to remind user to migrate their database
I didn't do this and spent a while tracking down errors with CSRF and Token Validation.
2017-06-04 16:54:03 -05:00
mario
eb9e6eb05a Merge pull request #331 from nattyg93/patch-1
Fix docs settings typo
2017-05-31 19:10:45 +02:00
Jacob Rief
90219295a4 return user object from upstream method invocation 2017-05-30 09:26:16 +02:00
Nathanael Gordon
937162bd97 Fix docs settings typo 2017-05-17 23:36:09 +10:00
Maxim Kukhtenkov
cbda9ee075 Add Django 1.11 to Travis CI 2017-05-15 10:36:19 -04:00
mario
13863991ca Merge pull request #321 from mpuhacz/polish-translation
polish translation
2017-05-14 21:52:27 +02:00
Vladislav Manchev
052094d43c Pass context to user defined USER_DETAILS_SERIALIZER 2017-04-26 23:20:10 +02:00
Maxim Kukhtenkov
8c58782e30 Change requirements in setup.py: allauth is only required for tests 2017-03-20 19:28:32 -07:00
mario
dd3eb3e8c6 fix: few fixes for #312 2017-03-19 21:21:32 +01:00
mario
e665b95643 Merge pull request #312 from swstack/custom-registration-permissions
Custom registration permissions
2017-03-19 13:24:31 +01:00
mpuhacz
bf96f6ab8e polish translation 2017-03-11 15:50:27 +01:00
mpuhacz
d0d348bf6b polish translation 2017-03-11 12:33:25 +01:00
mpuhacz
50325ddb4f polish translation 2017-03-11 12:10:55 +01:00
Maxim Kukhtenkov
819fbf768f Update configs and changelog for version 0.9.1 2017-03-05 22:05:08 -08:00
Maxim Kukhtenkov
469e4af82b Compile translations 2017-03-05 22:02:28 -08:00
Maxim Kukhtenkov
5b94deb5c5 Merge pull request #300 from Cahersan/master
Added Spanish translations
2017-03-05 21:55:25 -08:00
Maxim Kukhtenkov
48dba428d8 Merge pull request #308 from joaoricardo000/master
Added djangorestframework-jwt as test requirement, and typo
2017-03-05 21:45:56 -08:00
Maxim Kukhtenkov
955ff3a280 Change retrieving user details serializer to avoid import error
#309
2017-03-05 21:37:14 -08:00
Antoine d'Otreppe
3f7035eb68 Added French translations 2017-02-14 13:28:42 +01:00
Stephen Stack
be17865b00 registration: allow custom permission classes 2017-02-12 17:35:28 -06:00
Stephen Stack
9cf4c4e730 deps: dev-requirements.txt to install developer dependencies 2017-02-12 16:47:03 -06:00
Stephen Stack
49eaf0feea deps: require django-allauth>=0.25.0 2017-02-12 16:41:11 -06:00
Joao Ricardo
60a12107ba Add djangorestframework-jwt on test_require 2017-01-29 13:18:44 -02:00
Joao Ricardo
60a585339b Fix typo on test variable name 2017-01-29 13:17:33 -02:00
Carlos
09a30742f7 Added Spanish translations 2017-01-13 11:49:02 +01:00
Maxim Kukhtenkov
beb073f35f Update travis.yml and add latest Django 1.10 to tests 2016-12-31 12:59:49 -08:00
Maxim Kukhtenkov
971072ae37 Add sensitive_post_parameters decorator to several views 2016-12-31 12:55:19 -08:00
Maxim Kukhtenkov
ce58da58b2 Update configs and changelog for version 0.9.0 2016-12-22 13:29:51 -08:00
Maxim Kukhtenkov
951f3ce284 Compile translations and small text fixes 2016-12-22 11:57:23 -08:00
Maxim Kukhtenkov
aa677d51c0 PEP8 cleanup and small text fixes 2016-12-21 14:08:56 -08:00
Maxim Kukhtenkov
5bcf31f545 Remove redundant required=True from serializer fields 2016-12-21 13:47:24 -08:00
Maxim Kukhtenkov
9df9608631 Add get_queryset method to UserDetailsView due to issue with Swagger
#275
2016-12-21 11:40:18 -08:00
Maxim Kukhtenkov
e81ed716ec Add verification e-mail sent message
#240
2016-12-21 11:31:04 -08:00
Maxim Kukhtenkov
a673db87f8 Merge pull request #200 from mariuccio/master
'detail' keyword in success response messages
2016-12-21 11:12:59 -08:00
Maxim Kukhtenkov
592ea78edc Merge pull request #289 from squallcs12/refactor/allow-VerifyEmailView-work-with-swagger
Make VerifyEmailView display data field in swagger
2016-12-21 11:05:28 -08:00
Maxim Kukhtenkov
5ec3b73dec Merge pull request #284 from Akay7/RussianLanguage
Russian language
2016-12-10 13:52:13 -05:00
Maxim Kukhtenkov
d20a601fdd Merge pull request #286 from Tivix/jwt-serializer-with-custom-user-model
JWT serializer with custom user model
2016-12-09 10:12:52 -05:00
Bang Dao
d2917e9f3f refactor allow VerifyEmailView display data field in swagger 2016-12-09 16:53:09 +07:00
Maxim Kukhtenkov
f200b8d6f0 Remove redundant required=True from serializer fields
`required=True` is default setting
2016-12-07 21:47:07 -05:00
Maxim Kukhtenkov
f79537de77 Update API endpoints docs and docstring
https://github.com/Tivix/django-rest-auth/issues/280
2016-12-07 19:12:01 -05:00
Maxim Kukhtenkov
6812deeeb4 Rename variable for clarity
We have two variables named `token` which have different data types
2016-12-02 16:35:13 -08:00
Maxim Kukhtenkov
f09bbaf877 Merge pull request #283 from DigiCred/master
fix: social login using auth code flow
2016-12-02 16:08:13 -08:00
Maxim Kukhtenkov
ca62f44061 Append more information to comment
Explain why we are defining JWTUserDetailsSerializer in registration/serializers.py instead of getting it from app_settings.py
2016-11-30 20:03:34 -08:00
Maxim Kukhtenkov
45bda640e5 Add note to docs for defining custom REGISTER_SERIALIZER
#198
2016-11-30 19:49:25 -08:00
Maxim Kukhtenkov
ebf6a92b17 Merge pull request #288 from Tivix/logout_on_get
Logout on get
2016-11-30 19:41:14 -08:00
Maxim Kukhtenkov
cef8d67968 Move note under info on accepted params 2016-11-30 19:38:47 -08:00
Maxim Kukhtenkov
42ae22152a Remove dependency on allauth for logout on GET 2016-11-30 17:39:57 -08:00
Ankit Popli
139dd4a4e4
refactor: remove redundant check as suggested by @maxim-kht 2016-11-28 11:42:06 +05:30
Maxim Kukhtenkov
dd6db3563f Allow using custom UserDetailsSerializer with JWTSerializer - update 2016-11-27 17:57:55 -08:00
Maxim Kukhtenkov
7fc875a4f5 Change handling for logout on GET
+ Make it require allauth
+ Add a note to docs that it’s not a recommended setting
2016-11-27 03:37:05 -08:00
Egor Poderyagin
49ddf00d57 update messages in agreement to comment of pull request 2016-11-27 09:38:18 +03:00
Egor Poderyagin
89b48ce053 update language file 2016-11-27 09:25:48 +03:00
Egor Poderyagin
a9c6900e26 Merge branch 'RussianLanguage' of https://github.com/Akay7/django-rest-auth into RussianLanguage 2016-11-27 08:05:30 +03:00
Ankit Popli
4599adf92b
fix: social login using auth code flow
'access_token' in attrs always returns True, we need to check whether
the token is empty or not
2016-11-25 20:33:43 +05:30
Maxim Kukhtenkov
667e70c40f Update configs and changelog for version 0.8.2 2016-11-01 11:10:15 -07:00
Maxim Kukhtenkov
a989de8624 Merge pull request #277 from Tivix/demo-site-swagger
Add swagger API docs to demo project
2016-11-01 10:56:14 -07:00
Maxim Kukhtenkov
a907efc06b Allow using custom UserDetailsSerializer with JWTSerializer 2016-10-31 20:45:33 -07:00
Maxim Kukhtenkov
70d03e3e9b Update docstring for UserDetailsView and cleanup 2016-10-31 20:17:31 -07:00
Maxim Kukhtenkov
5330e0cfb1 Add swagger API docs to demo project 2016-10-27 17:42:07 -07:00
Maxim Kukhtenkov
37375461df Add info to docs and comments
+ Add comments by @chubz regarding django-allauth hmac pattern from PR #233
+ Cleanup
2016-10-24 14:23:44 -07:00
Maxim Kukhtenkov
35fe1ae590 Return pk in /rest-auth/user/ instead of id 2016-10-17 10:27:03 -07:00
Maxim Kukhtenkov
e662736e4a Merge pull request #256 from briva/patch-1
Return ID user on /rest-auth/user/
2016-10-17 10:26:22 -07:00
Maxim Kukhtenkov
0472b44241 Merge pull request #273 from Tivix/revert-209-patch-1
Revert "allows registration throttle control"
2016-10-17 10:11:18 -07:00
Maxim Kukhtenkov
83e200e576 Revert "allows registration throttle control" 2016-10-17 10:11:04 -07:00
Le Pogam Brivael
f793447bae Return ID user on /rest-auth/user/ 2016-09-05 12:44:17 +02:00
mario
3b80fcbca9 fix: removed --use-mirrors from pip 2016-08-31 18:09:01 +02:00
mario
4246511fb1 Merge pull request #235 from syamvilakudy/patch-1
Added missing variable "token"
2016-08-31 00:05:32 +02:00
mario
53094041b8 Merge pull request #236 from greenstatic/master
changed login into logout in the templates logout_form.html
2016-08-31 00:01:33 +02:00
mario
8365d729ac Merge pull request #248 from jberends/master
Refactored demo.settings to use TEMPLATE dictionary settings for django
2016-08-31 00:01:01 +02:00
mario
f07429c252 Merge pull request #244 from Akay7/FixImportAllauthError
Fix import allauth error
2016-08-30 23:58:15 +02:00
jberends
8949b1ffde Refactored the settings of the demo to confirm to the use of TEMPLATE_* settings in django. 2016-08-29 14:58:47 +02:00
Poderyagin Egor
2546b17067 fix import allauth error 2016-08-12 10:46:25 +03:00
Egor
5086eebfdb Merge pull request #5 from Tivix/master
Pull updates
2016-08-01 09:00:38 +04:00
Poderyagin Egor
2d54117863 added russian language 2016-08-01 07:51:44 +03:00
greenstatic
abc72e88f0 changed login into logout in the templates logout_form.html 2016-07-29 21:23:52 +02:00
Syam Mohan
acf15b6360 Added missing variable "token" 2016-07-29 18:24:49 +05:30
mario
e9215f4105 Bump to v0.8.1 2016-07-28 22:45:39 +02:00
mario
18f8178ce6 More release changes (0.8.0) 2016-07-28 21:16:35 +02:00
mario
9df528f482 Changed confirm email url path + test fixes 2016-07-28 20:14:26 +02:00
mariodev
0ffc573482 Merge pull request #209 from vsevolod-kolchinsky/patch-1
allows registration throttle control
2016-07-18 08:14:11 +02:00
vsevolod kolchinsky
ff641cf31c Throttling documentation added 2016-07-18 09:09:51 +03:00
mario
235efa4ec1 Dropping django 1.7 support 2016-07-18 07:20:02 +02:00
mariodev
d26661400d Merge pull request #230 from omidraha/patch-1
Fix patterns function name on the installation pgae.
2016-07-18 07:07:19 +02:00
mariodev
c5e0382d25 Increased test coverage (#229)
* Added twitter login test

* pep8

* Fixes missing backend attr issue

* Refactored login process

* pep8

* Added more tests for twitter social login
2016-07-18 07:06:28 +02:00
Omid Raha
8635cec373 Fix patterns function name on the installation pgae.
It's `patterns`,
That used as `from django.conf.urls import patterns`
2016-07-16 16:37:49 +04:30
mariodev
976b3bbe4d Merge pull request #220 from hkraal/docs-installation-include-django-sites
Add django.contrib.sites to INSTALLED_APPS
2016-06-28 23:05:47 +02:00
Henk Kraal
8812ba11af Added `django.contrib.sites and SITE_ID setting requirements of allauth` to installation instructions
Fixes #218
2016-06-28 22:01:01 +02:00
mariodev
c4e7bdc77f pep8 2016-06-28 20:20:53 +02:00
mariodev
118b173282 Merge remote-tracking branch 'origin/master' 2016-06-28 20:14:26 +02:00
mariodev
8a004bb48a Increased test coverage
+ minor fixes
2016-06-28 20:14:05 +02:00
mariodev
2c4f7c2ec5 Merge pull request #202 from frewsxcv/patch-1
Update Travis CI config to run on Python 3.5.
2016-06-27 20:22:04 +02:00
Corey Farwell
8ffb292b70 Update Travis CI config to run on Python 3.5. 2016-06-06 20:37:21 -04:00
vsevolod kolchinsky
2a0fa1ab4e allows registration throttle control 2016-05-05 09:03:34 +03:00
mariodev
3189a5c7a0 Merge pull request #187 from marsam/master
Capture OAuthError in TwitterLoginSerializer
2016-04-14 19:08:18 +02:00
mariodev
5e4d85bca9 Merge pull request #191 from EnTeQuAk/bugfix/fix-social-login-test
Fix social-adatper tests for allauth>=0.25.0
2016-04-14 17:15:16 +02:00
Girish
22667230bb passing on the context/request to serializer 2016-04-14 12:54:50 +01:00
mariuccio
f9b6a6cd9f 'detail' keyword in success response messages 2016-03-31 10:58:14 +02:00
Christopher Grebs
50087549e8 Fix social-adatper tests for allauth>=0.25.0
See 742d114abf for more details.

This unfortunately requires bumping up the version to 0.25.0
2016-03-14 13:20:24 +01:00
mariodev
49ccfe92a9 Merge pull request #190 from rpatterson/master
Documentation fixes
2016-03-11 10:31:50 +01:00
Ross Patterson
4463fb8afa Fix docs settings default value 2016-03-10 14:12:24 -08:00
Ross Patterson
adf22bcc78 Fix reference to missing extras_require 2016-03-09 11:51:52 -08:00
Maxim Kukhtenkov
e8cd780ae2 Remove unreachable code in LogoutView 2016-03-07 18:21:11 -05:00
mariodev
f95c42036b Merge pull request #188 from EnTeQuAk/patch-1
Fix typo in configuration.rst
2016-03-07 18:48:00 +01:00
Christopher Grebs
a661f02b8f Fix typo in configuration.rst 2016-03-07 18:43:18 +01:00
Mario Rodas
0737da0077 Capture OAuthError in TwitterLoginSerializer 2016-03-02 15:00:26 -05:00
Mario Rodas
86a487fe21 Import allauth.socialaccount only when declared in INSTALLED_APPS
Don't silently ignore ImportError
2016-03-02 14:37:27 -05:00
Maciej Jaworski
e3fc4e64e9 Merge pull request #178 from jasinai/UnignoreMoFiles
Unignore mo files
2016-03-01 12:00:57 +00:00
mjaworski
2572475971 fixed code quality 2016-03-01 11:51:01 +00:00
mariodev
eda3d7b6d2 Merge pull request #168 from Haos616/master
Fixed errors messages for PasswordResetSerializer
2016-02-29 21:56:16 +01:00
mariodev
4018d6a52a Merge pull request #147 from jgr3go/jwt-support
JWT support
2016-02-29 16:59:23 +01:00
Jon Gregorowicz
9baeb2883b Docs comment 2016-02-29 08:02:50 -05:00
Jon Gregorowicz
4ba9841bc5 Merge changes 2016-02-29 08:01:39 -05:00
Jon Gregorowicz
1eb4b45e50 Fixing tests 2016-02-29 07:57:54 -05:00
Jon Gregorowicz
85c2fe4661 Merge branch 'master' into jwt-support 2016-02-29 07:48:37 -05:00
Jon Gregorowicz
b4c122ee5d Bump 2016-02-29 07:47:47 -05:00
Jon Gregorowicz
e3901516cb Merge branch 'master' into jwt-support
# Conflicts:
#	docs/changelog.rst
#	rest_auth/registration/views.py
2016-02-29 07:45:43 -05:00
mariodev
b22a55b0fa Merge pull request #183 from be-ndee/patch-1
typo fix
2016-02-29 12:59:00 +01:00
Andreas Bissinger
9a0cbc81eb typo fix 2016-02-29 12:53:08 +01:00
mariodev
ef56efc0c8 Merge pull request #152 from caruccio/logout-on-get
Allow logout on GET
2016-02-26 13:38:01 +01:00
mariodev
837d9a6e46 Merge pull request #182 from jasinai/patch-1
Change the variable name in the doc
2016-02-26 13:30:36 +01:00
Nicola
42b860b8bf Change the variable name in the doc 2016-02-25 21:37:08 +01:00
Nicola Hauke
340f0651c9 Adds the german mo file 2016-02-23 18:27:29 +01:00
Nicola Hauke
ff0664a66e Unignores translation files 2016-02-23 18:24:55 +01:00
Maciej Jaworski
0850fb47ae Update requirements.pip 2016-02-23 15:46:34 +00:00
Maciej Jaworski
ed42925053 Update setup.py 2016-02-23 15:39:26 +00:00
Maciej Jaworski
f01ed78d59 Update changelog.rst 2016-02-23 15:30:53 +00:00
Maciej Jaworski
b6dfd69009 Merge pull request #177 from tevinjoseph/master
fixed pep8 error in social_serializers
2016-02-23 13:27:01 +00:00
Tevin Joseph K O
b2edfffc91 fixed pep8 error in social_serializers
fixed pep8 error in social_serializers which cause documentation build error
2016-02-23 17:52:44 +05:30
Maciej Jaworski
bb7b1270b7 Adjusted phrasing and layout of social integration examples 2016-02-23 12:16:24 +00:00
Maciej Jaworski
b6e991e787 Merge pull request #154 from tevinjoseph/master
Added a Serializer for Twitter oauth
2016-02-23 12:04:38 +00:00
Tevin Joseph K O
ee9e848694 URL for twitter login added. 2016-02-23 17:31:11 +05:30
Tevin Joseph K O
8f05f20051 Updated api_endpoints.rst with twitter login 2016-02-23 17:18:30 +05:30
Tevin Joseph K O
e6c5be4b69 Updated index.rst with twitter login 2016-02-23 17:15:57 +05:30
Maciej Jaworski
b34ef24dd7 Merge pull request #167 from fergyfresh/master
Adds logout to API endpoints demo project
2016-02-23 11:07:26 +00:00
Maciej Jaworski
aa839f97d2 Added missing import 2016-02-23 10:59:47 +00:00
Maciej Jaworski
1a0ab4488e Merge pull request #165 from jasinai/AddTranslation
Adds ugettext_lazy to more texts
2016-02-23 10:55:42 +00:00
Maciej Jaworski
282ac5b517 Merge pull request #175 from Tivix/Update-test-requirements,-added-Django-1.9
Update test requirements, added django 1.9, pepfix
2016-02-23 10:38:59 +00:00
Maciej Jaworski
3bcabe6b17 Update serializers.py
pepfix
2016-02-23 10:35:41 +00:00
Maciej Jaworski
23221dc4d5 Update .travis.yml 2016-02-23 10:30:25 +00:00
Maciej Jaworski
c282a1309e Merge pull request #171 from Akay7/CanLoginWithCustomUserModel
update for accept login users of CustomUserModel objects where doesn'…
2016-02-23 10:24:36 +00:00
Maciej Jaworski
40ac97b847 Update index.rst 2016-02-23 10:20:52 +00:00
Egor
1a964f9056 Merge pull request #4 from Akay7/CanLoginWithCustomUserModel
update for accept login users of CustomUserModel objects where doesn'…
2016-02-17 08:46:50 +03:00
Poderyagin Egor
cc9552adda update for accept login users of CustomUserModel objects where doesn't exist username field and setted USERNAME_FIELD property 2016-02-17 08:35:47 +03:00
Egor
2acf4dd115 Merge pull request #3 from Tivix/master
get new updates
2016-02-16 15:41:33 +03:00
Jon Gregorowicz
511329c30a Fixing merge tool induced file endings 2016-02-16 00:49:41 -05:00
Jon Gregorowicz
8b5e5173d0 Added newline to pip file 2016-02-16 00:43:19 -05:00
Jon Gregorowicz
e3a1ba520e Added tests for JWT, fixed merge issues 2016-02-16 00:42:18 -05:00
Jon Gregorowicz
f8a9cc152d Merge branch 'master' into jwt-support
# Conflicts:
#	docs/configuration.rst
#	rest_auth/registration/views.py
#	rest_auth/tests/test_api.py
#	rest_auth/utils.py
#	rest_auth/views.py
2016-02-15 23:35:32 -05:00
Haos616
dae38d4e10 Fixed errors messages for PasswordResetSerializer
Fixed errors messages for PasswordResetSerializer
2016-02-10 18:45:32 +02:00
Billy Ferguson
37c49e0c86 Adds token to logout API documentation 2016-02-08 17:35:37 -05:00
Billy Ferguson
8d91e1881e Added logout functionality to demo project 2016-02-08 17:14:25 -05:00
mariodev
7c8a34f700 Merge pull request #132 from asudoma/master
fix import complete_social_login
2016-02-08 08:12:52 +01:00
Nicola Hauke
152b0a6fb6 Adds ugettext_lazy to more texts
Also adds a first german translation.
2016-02-02 15:29:16 +01:00
mariodev
00415301d6 Merge pull request #145 from mdentremont/topic/131
#131: Do not raise 400 when resetting password for non-existing account
2016-01-14 23:53:31 +01:00
mariodev
811dc30830 Merge pull request #156 from Tivix/pr/155
Made e-mail options more extendible for `PasswordResetSerializer`
2016-01-14 23:43:28 +01:00
mario
7e85667208 Made e-mail options more extendible for PasswordResetSerializer 2016-01-14 23:42:02 +01:00
mario
af9dcbd79b Added FAQ issue. 2016-01-14 22:36:58 +01:00
Tevin Joseph K O
1af16ae7ba Added a Serializer for Twitter oauth
Added a serializer for twitter OAuth to work. If you are not using this it will cause an error ('TwitterOAuthAdapter' object has no attribute 'parse_token'). It happens because method parse_token() is implemented in OAuth2Adapter, but Twitter uses OAuth 1.0, so TwitterOAuthAdapter inherits from OAuthAdapter, which doesn't have parse_token() method. Example usage is given below:

class TwitterLogin(LoginView):
    serializer_class = TwitterLoginSerializer
    adapter_class = TwitterOAuthAdapter
2016-01-13 12:43:12 +05:30
mariodev
97dbb528a2 Merge pull request #153 from Tivix/pr/128
Return token only when verification is mandatory
2016-01-11 22:47:50 +01:00
mario
ae8a26b708 Return token only when verification is mandatory 2016-01-11 22:33:14 +01:00
Mateus Caruccio
70a4dc9a13 Allow logout on GET 2016-01-09 01:11:35 -02:00
mariodev
f9547f62e8 Merge pull request #144 from caruccio/custom-token
Add support for custom Token model
2016-01-08 08:43:18 +01:00
Mateus Caruccio
c087899311 Merge branch 'master' into custom-token 2016-01-07 19:56:57 -02:00
Mateus Caruccio
ccc261d57f Merge branch 'master' of https://github.com/Tivix/django-rest-auth 2016-01-07 19:56:33 -02:00
mariodev
334a29c4d9 Merge pull request #151 from Tivix/pr/130
Refactored registration logic
2016-01-06 18:26:47 +01:00
mariodev
d63232224e Merge pull request #150 from Tivix/pr/141
Ability to login using e-mail (without allauth)
2016-01-06 18:16:51 +01:00
mario
54eb54ad65 Cleaned up LoginSerializer codebase 2016-01-06 01:18:13 +01:00
mario
b12ed79bb1 Merge branch 'master' into pr/141 2016-01-05 22:32:38 +01:00
Tabatha Memmott
37dda5d7be Merge pull request #149 from Tivix/test_fail_url
url change for tests
2016-01-05 11:50:53 -08:00
Tabatha Memmott
55fb36ec91 url change for tests 2016-01-05 11:46:01 -08:00
Tabatha Memmott
2bddb944cc Merge pull request #148 from Tivix/developDemo
Develop demo
2016-01-05 09:45:17 -08:00
Tabatha Memmott
29669be296 Merge branch 'master' into developDemo 2016-01-05 08:08:17 -08:00
mariodev
ef17cb3165 Merge pull request #146 from mdentremont/topic/86
#86: Add missing dependencies to setup.py
2016-01-05 15:14:28 +01:00
mario
073dd3e765 Fixed flake8 warnings 2016-01-05 15:09:31 +01:00
mario
99c4dc9d05 Brought back pass verification + added test 2016-01-05 14:56:11 +01:00
Tabatha Memmott
0e3fb4a5c9 closes PR #134 and adds same syntax to demo 2016-01-04 16:29:47 -08:00
Jon Gregorowicz
317db1b811 Bump version 2016-01-04 13:57:38 -05:00
Jon Gregorowicz
19e234d1dc * Added support for REST_USE_JWT
* Added JWTSerializer
* Added JWT encoding support, based on django-rest-framework-jwt
* Tests for JWT authentication
2016-01-04 12:45:33 -05:00
Matt d'Entremont
1d9c2d647e #86: Add missing dependencies to setup.py
- Add django-allauth as an extra, and update documentation
- Ensure django-allauth and responses are present when running tests
2016-01-04 10:29:50 -04:00
Matt d'Entremont
d36a9bc1cb #131: Do not raise 400 when resetting password for non-existing account
- Do not raises validation error if email doesn't exist
- Update unit test
2016-01-04 10:17:47 -04:00
Mateus Caruccio
57e7fb998f Merge branch 'custom-token' of github.com:caruccio/django-rest-auth into custom-token 2016-01-02 16:00:55 -02:00
Mateus Caruccio
c9d55f768c Add support for custom Token model 2016-01-02 15:59:06 -02:00
mario
ec91620550 Merge branch 'master' into pr/130 2016-01-02 17:32:40 +01:00
Mateus Caruccio
eb61b24087 Add support for custom Token model 2016-01-02 12:38:05 -02:00
mariodev
4a56a9e7e5 Merge pull request #140 from Akay7/TestResetPass
Test reset pass
2015-12-22 11:57:01 +01:00
mario
4c8db510b0 Fixed test exception. 2015-12-22 09:28:35 +01:00
mario
23eb6e5be5 Added coverage_html to .gitignore 2015-12-22 09:27:17 +01:00
mariodev
7bd7924801 Merge pull request #143 from Tivix/issue_116_fix_csrf_tokens_email_login
fix demo by add csrf and modify account settings
2015-12-22 08:46:01 +01:00
Will Liu
4d9e33e9a8 fix demo by add csrf and modify account settings
In response to Issue 116 at https://github.com/Tivix/django-rest-auth/issues/116
* Add csrf_token tags on demo templates (was returning CSRF page)
* Update settings file for the demo login (was returning message that email was required when template only shows username and password fields)
2015-12-21 16:32:53 -05:00
mariodev
991178a9d5 Merge pull request #142 from Tivix/update-demo-rst
Update demo.rst
2015-12-21 18:56:10 +01:00
Will
411cc298b3 Update demo.rst
With requirements file of django >= 1.7.0, change command of `syncdb` to `migrate` (`syncdb` is deprecated).
2015-12-21 11:01:41 -05:00
ron8mcr
f848c8b6fa Merge remote-tracking branch 'Tivix/django-rest-auth/master' 2015-12-15 17:06:47 +07:00
Poderyagin Egor
bb2fb65f7d Auth by email 2015-12-13 23:43:33 +03:00
Poderyagin Egor
a93b7f5cec Added test case for reset by email in different case 2015-12-13 22:24:27 +03:00
Egor
56773d8618 Merge pull request #1 from Tivix/master
Update
2015-12-13 21:58:58 +03:00
mariodev
4889cb5518 Merge pull request #138 from tomcounsell/patch-1
Update installation.rst
2015-12-11 12:33:37 +01:00
Tom Counsell
fb435fbea4 Update installation.rst 2015-12-11 18:08:39 +07:00
mariodev
41946d16a0 Merge pull request #137 from Akay7/PasswordResetCaseUnsensetive
Make email in password reset serializer Case unsensetive
2015-12-10 13:25:01 +01:00
Poderyagin Egor
9b7ede752b Make email in password reset serializer Case unsensetive 2015-12-10 18:13:28 +06:00
mariodev
f1e96be7e6 Merge pull request #133 from Tivix/cleanup_tests
Reorganized test files
2015-12-04 09:46:33 +01:00
mario
afcf2a5043 Fixed reqs path for travis 2015-12-04 09:43:17 +01:00
mario
72062408aa Reorganized test files 2015-12-04 09:32:03 +01:00
anyone_j
cc963ca1a1 fix import complete_social_login 2015-11-26 14:38:25 +05:00
ron8mcr
52f04ba224 Update tests and fix register serializer 2015-11-24 22:04:57 +07:00
ron8mcr
65b29d3515 None as success_url for complete_signup in RegisterView 2015-11-24 21:16:39 +07:00
ron8mcr
30fd6414ce Explict Allow Any for register view 2015-11-24 21:07:20 +07:00
Roman Gorbil
10ae7acac9 Rewrite registration logic 2015-11-24 17:11:46 +07:00
mario
b8c3ae9c68 version 0.6.0 2015-11-24 08:40:41 +01:00
mario
f3151ad5c0 Fixed try/catch in logout view 2015-11-23 22:52:59 +01:00
mario
d9b8f3faf6 Added non existing e-mail validation on password reset
+ small cleanup
2015-11-23 22:17:32 +01:00
mariodev
52283aed70 Merge pull request #129 from ron8mcr/master
raise_exception=True for views
2015-11-23 20:55:08 +01:00
ron8mcr
fac959ea80 raise_exception=True for views 2015-11-23 21:04:56 +07:00
mario
d90717e9bd Added SO link to README.rst 2015-11-19 10:45:13 +01:00
mario
680bb56e99 Clean up demo codebase 2015-11-19 10:36:55 +01:00
mario
6ace9de268 Compatibility updates
+ removed some legacy code
+ added force_text for py3 support
2015-11-19 09:38:57 +01:00
Mateusz Sikora
315f6f2844 Merge pull request #118 from mdentremont/topic/py3-fix
Alter a statement to make it python3 compatible
2015-11-06 15:16:07 +01:00
Mateusz Sikora
8a6e13c1fe update travis config 2015-11-06 15:12:21 +01:00
Mateusz Sikora
51c7e78c45 disable travis cache 2015-11-06 14:20:30 +01:00
Mateusz Sikora
3eaa491fb1 fix flake8 2015-11-06 14:09:47 +01:00
Mateusz Sikora
57a879f6c0 Merge branch 'master' of github.com:Tivix/django-rest-auth 2015-11-06 14:09:23 +01:00
Mateusz Sikora
60581fc375 fix issue with setting POST attribute in request 2015-11-06 14:07:12 +01:00
Mateusz Sikora
5afed21ab2 Merge pull request #119 from fleischie/master
Bypass AssertionErrors on nested Hyperlinked fields
2015-11-06 13:39:18 +01:00
Mateusz Sikora
77db2d8d64 change auth method demo app 2015-11-06 13:23:55 +01:00
Karl Fleischmann
f1858e4ce4 Bypass AssertionErrors on nested Hyperlinked fields
Send request as context data to the UserDetailSerializer class, when
signing up. This way nested Hyperlinked serializer fields can be
correctly resolved.
2015-10-28 00:55:08 +01:00
Matt d'Entremont
7fbdcff5e0 Alter a statement to make it python3 compatible
- In python 3, filter returns an iterator instead of a list
- Thus bool(filter(...)) always evaluated to true on python3
- Convert the filter to a list comprehension to ensure it evaluated as
  expected on python 3
2015-10-26 10:13:27 -03:00
Mateusz Sikora
ba5edbaf62 fix update_session_auth_hash for django <1.7 2015-10-19 10:12:25 +02:00
Mateusz Sikora
6ba1916c48 Merge pull request #112 from Aerstone/master
Add support for keeping the user logged in after password change (Django 1.7+)
2015-10-19 09:52:22 +02:00
Bhaarat Sharma
48eb40ae47 adding not 2015-10-18 08:23:36 -04:00
Bhaarat Sharma
296a49a04b Don't log the user out after change password - Django 1.7 2015-10-18 00:20:50 -04:00
mario
680f24e43d Fix the optional deps for allauth.socialaccount 2015-10-04 12:41:07 +02:00
Mateusz Sikora
74fa6cdf38 Merge pull request #107 from nickspacek/fixes-optional-socialaccount
Adds check for optional deps in INSTALLED_APPS
2015-10-02 11:45:23 -04:00
Nick Spacek
0ae97701c8 Adds check for optional deps in INSTALLED_APPS
Previously the serializers.py file relied solely on the presence of
allauth.socialaccount in the PYTHON_PATH to determine if its use was
required. This adds another check in the Django INSTALLED_APPS for the
allauth.socialaccount app, and then continues with the import if the app
has been added.
2015-10-01 09:51:25 -03:00
Mateusz Sikora
d25df33a79 version 0.5.0 2015-08-20 11:28:45 +02:00
Mateusz Sikora
316d5c4233 Merge pull request #100 from philippeluickx/master
allow_blank required for optional paramaters in serializers
2015-08-17 12:37:33 +02:00
Philippe Luickx
c22b77724d LoginSerializer allow_blank fix 2015-08-17 13:35:20 +03:00
Philippe Luickx
4cf33ff9fa SocialLoginSerializer allow_blank fix 2015-08-17 13:34:59 +03:00
Mateusz Sikora
3527afccc6 Update README.rst 2015-08-14 15:20:20 +02:00
Mateusz Sikora
7d23048316 Update .travis.yml 2015-08-14 15:12:08 +02:00
Mateusz Sikora
1cdbc95dbb Merge pull request #85 from iraycd/patch-1
Added old password to Password Change
2015-08-14 13:59:53 +02:00
Mateusz Sikora
4d2c302ce2 Merge branch 'master' of github.com:Tivix/django-rest-auth 2015-08-14 13:51:00 +02:00
Mateusz Sikora
f9ef1ff4c5 Merge branch 'philippeluickx-development' 2015-08-14 13:50:09 +02:00
Mateusz Sikora
388314f831 fix flake8 2015-08-14 13:49:47 +02:00
Mateusz Sikora
1b667fa4ab Merge branch 'development' of https://github.com/philippeluickx/django-rest-auth into philippeluickx-development 2015-08-14 13:45:27 +02:00
Mateusz Sikora
cb27b9fef9 Merge pull request #97 from Greyvend/patch-1
Update installation.rst
2015-08-14 13:40:29 +02:00
Philippe Luickx
64ab8be2f0 catching incorrect input 2015-08-13 10:56:25 +03:00
Serge Mosin
17398ab363 Update installation.rst
Fixed urlpatterns section.
2015-08-11 20:22:10 +05:00
Mateusz Sikora
7f86c4ce02 Merge pull request #94 from alacritythief/master
Replaced request.DATA with request.data for compatibility with DRF 3.2
2015-08-10 12:15:14 +02:00
Philippe Luickx
5a6c8f549b bugfix 2015-08-10 11:24:21 +03:00
Philippe Luickx
02bf6fbe5b bugfix, request.DATA is deprecated, replaced with request.data 2015-08-07 14:31:33 +03:00
Philippe Luickx
ad94008503 appending all views with View 2015-08-07 13:54:45 +03:00
Philippe Luickx
4a3ea85f44 well, that was an easy cleanup, now it is nice and consistent 2015-08-07 13:46:18 +03:00
Philippe Luickx
bd193a1401 you can now login with email and password, without username 2015-08-07 13:43:21 +03:00
Philippe Luickx
74f2ffec7f now also accepting authorization codes from social login (e.g. facebook) 2015-08-07 13:26:57 +03:00
Philippe Luickx
8ea935ef40 conditional import 2015-08-07 11:25:40 +03:00
Andy Wong
dd3db28325 replaced request.DATA with request.data for compatibility with DRF 3.2 2015-08-06 13:49:26 -04:00
Nikolay Golub
a9d2a24011 Make names more consistent 2015-07-23 22:33:48 +03:00
Nikolay Golub
f43a6b8d58 move SocialAccount population to the separate method in the SocialLoginSerializer.
It makes easier to get the correct signup for custom user models, because application can subclass SocialLoginSeriaLizer and add required fields to the instance.
2015-07-23 22:22:39 +03:00
Ray Ch
ce81906f59 Note for old_password. 2015-05-23 01:28:40 +05:30
Ray Ch
f4160f6577 Added old password to Password Change 2015-05-23 01:17:06 +05:30
mjaworski
bd97eee65a Added changeog notes and updated requirements in demo app 2015-04-28 11:10:58 +02:00
mjaworski
b228791e3b updated version to 0.4.0 2015-04-28 10:59:23 +02:00
mjaworski
86f55483ee merge fix 2015-04-28 10:57:53 +02:00
mjaworski
d351037555 upgrade flake version 2015-04-28 10:56:42 +02:00
Maciej Jaworski
14261d69ed Merge pull request #82 from Tivix/flake8
Flake8
2015-04-28 10:29:05 +02:00
mjaworski
a60df71c07 Flake8 style fixes 2015-04-28 10:22:08 +02:00
mjaworski
ece4c01ada Merge branch 'master' into flake8 2015-04-28 10:04:42 +02:00
mjaworski
9803d9f941 adding static code analysis 2015-04-28 10:04:20 +02:00
Maciej Jaworski
c1f2bb48dd Merge pull request #81 from itbabu/master
Fix TabError: inconsistent use of tabs and spaces in indentation
2015-04-28 08:19:39 +02:00
Marco Badan
4b9b631744 Fix TabError: inconsistent use of tabs and spaces in indentation 2015-04-27 17:06:48 +02:00
Maciej Jaworski
24af3512de Merge pull request #76 from Antwan86/master
Removes a warning due to the usage of django.utils.importlib which is deprecated. Includes backward compatibility for Django 1.5 & 1.6 + Python 2.6.
2015-04-27 14:12:41 +02:00
Antoine
7aa2fdb18c Fixed version check 2015-04-27 20:05:25 +08:00
Antoine
f31827d642 Merge branch 'master' of github.com:Tivix/django-rest-auth 2015-04-27 19:59:40 +08:00
mjaworski
ea782b83e2 fixed url conf in example 2015-04-27 11:01:19 +02:00
Antoine
ee55380a6c Switched to standard import_module as the Django embedded one is deprecated and will be removed in next version 2015-04-17 16:49:32 +01:00
Mateusz Sikora
92ca2e33d6 Merge pull request #75 from Tivix/django-1.8
Django 1.8 compatibility fixes

...lecisz malina 
2015-04-17 10:03:05 +02:00
mjaworski
80a7988a9b Django 1.8 compatibility fixes 2015-04-17 09:53:46 +02:00
Mateusz Sikora
6eb0146c2b Revert "Update .travis.yml"
This reverts commit 4e2cb5beaf.
2015-04-13 15:58:40 +02:00
Mateusz Sikora
4e2cb5beaf Update .travis.yml 2015-04-13 15:23:55 +02:00
mjaworski
e6d56c9e02 Merge branch 'master' of https://github.com/Tivix/django-rest-auth 2015-04-13 15:19:57 +02:00
mjaworski
bef1074052 Replaced references to contrib.auth.model.User with get_user_model(). Closes #68 2015-04-13 15:19:54 +02:00
Mateusz Sikora
d4d390e74d Merge pull request #56 from safaktrgt/master
change PasswordResetConfirmSerializer setpasswordform exception from token invalid  value error to form validation error
2015-04-13 15:11:42 +02:00
mjaworski
d0c5f0a6d9 Merge branch 'master' of https://github.com/Tivix/django-rest-auth 2015-04-13 15:05:26 +02:00
Mateusz Sikora
32ebd551b3 Update .travis.yml 2015-04-13 15:05:10 +02:00
mjaworski
8d4a227509 Added simple redirect to fix #66 2015-04-13 15:04:14 +02:00
mjaworski
0a98594a11 Updated gitignore 2015-04-13 15:03:46 +02:00
Mateusz Sikora
dddde2d618 Merge pull request #65 from rdragos/patch-1
Update installation.rst
2015-04-03 10:37:13 +02:00
Dragoș Alin Rotaru
c152b39951 Update installation.rst
Fixed small typo
2015-04-02 23:53:19 +03:00
Mateusz Sikora
3e7d72a4a8 Update requirements.pip 2015-04-01 16:05:04 +02:00
Mateusz Sikora
926589ac1d Merge pull request #59 from skylerberg/missing-comma
Fix missing comma in installation settings documentation
2015-03-17 08:27:22 +01:00
Skyler Berg
6c69d7eeda Fix missing comma in installation settings documentation 2015-03-16 20:42:30 -07:00
S.Turgut
fe1eff745e change PasswordResetConfirmSerializer setpasswordform from token invalid value error to form validation error 2015-03-11 12:05:45 +02:00
Mateusz Sikora
cf780c3ebd Update requirements.pip 2015-03-11 10:59:55 +01:00
Mateusz Sikora
8182a33c66 #23 - add test case to cover solcial auth for existing user 2015-02-27 13:56:29 +01:00
Mateusz Sikora
4e24b2a842 Update requirements.pip 2015-02-26 10:16:02 +01:00
Mateusz Sikora
e8ac019b4c Merge pull request #55 from vesterbaek/fix_drf_request
DRF 3 bug: get HttpRequest from DRF request object. Similar to issue #43
2015-02-25 17:38:43 +01:00
Mateusz Sikora
b2316f7de7 Merge pull request #54 from vesterbaek/readonly_email
Mark email as read only for user details
2015-02-25 17:38:07 +01:00
Jeppe Vesterbæk
c615cd94aa DRF 3 bug: get HttpRequest from DRF request object. Similar to issue #43 2015-02-25 16:52:51 +01:00
Jeppe Vesterbæk
846040259a Mark email as read only for user details 2015-02-25 16:06:46 +01:00
Mateusz Sikora
200dad30fb Merge branch 'master' of github.com:Tivix/django-rest-auth 2015-02-23 12:57:26 +01:00
Mateusz Sikora
03f5d398f9 update requirements in demo app 2015-02-23 12:57:16 +01:00
Mateusz Sikora
dc809a9a2c update FAQ section in docs 2015-02-23 12:56:42 +01:00
Mateusz Sikora
65770d2915 Update requirements.pip 2015-01-31 11:13:05 +01:00
Mateusz Sikora
3ad7029114 version 0.3.4 2015-01-27 17:13:47 +01:00
Mateusz Sikora
2158fffd2a fix #47 - PasswordResetConfirm doesn't check token 2015-01-27 16:52:54 +01:00
Mateusz Sikora
60c0f949f4 Merge pull request #46 from schemacs/dev
Adjust for djangorestframework 3
2015-01-18 19:36:09 +01:00
Mateusz Sikora
43eba15d2b Update requirements.pip 2015-01-18 19:34:58 +01:00
Lele Long
199a4989c5 Adjust for djangorestframework 3 2015-01-15 23:45:01 +08:00
Mateusz Sikora
ee263cd003 udpate DRA version in demo 2015-01-09 12:48:51 +01:00
Mateusz Sikora
ee8f620e83 version 0.3.3 2015-01-09 12:42:23 +01:00
Mateusz Sikora
7c1fe6fd94 upgrade demo requirements 2015-01-09 12:16:21 +01:00
Mateusz Sikora
01ffd4127b support django-rest-framework v3.0 2015-01-09 12:05:14 +01:00
Mateusz Sikora
e9fee3aa92 Support custom UserDetailsSerializer for registration 2015-01-09 11:20:46 +01:00
Mateusz Sikora
5d53a0e0a0 fix unit tests for social auth - required after changes in allauth v0.19.0 2015-01-09 11:12:00 +01:00
Mateusz Sikora
ed32f907d9 Merge pull request #39 from APSL/master
Support for custom UserDetailsSerializer on Register view
2015-01-09 10:53:01 +01:00
Mateusz Sikora
fbb0ba7732 Merge pull request #35 from jmb/demo-fixes
Demo fixes
2015-01-09 10:51:12 +01:00
Marc
b2d5c221c7 Support for custom UserDetailsSerializer on Register view 2014-12-18 16:52:28 +01:00
Mateusz Sikora
802de89ed7 Merge branch 'master' of github.com:Tivix/django-rest-auth 2014-12-15 00:09:58 +01:00
Mateusz Sikora
ec8b5af3e7 version 0.3.2 2014-12-15 00:09:49 +01:00
Jonathan Batchelor
b66563d703 Fixed authentication backend settings for demo 2014-12-04 17:35:01 +00:00
Jonathan Batchelor
72a3ee95ae Merge branch 'master' of github.com:Tivix/django-rest-auth into demo-fixes 2014-12-04 17:15:45 +00:00
Jonathan Batchelor
96002275e5 Some fixes for the demo app 2014-12-04 17:12:58 +00:00
Mateusz Sikora
3c5a705dd2 Merge pull request #34 from jmb/patch-1
Update requirements.pip
2014-12-03 17:18:54 +01:00
Jonathan Batchelor
6be73fd381 Update requirements.pip
As per issue #32
2014-12-03 16:08:50 +00:00
Mateusz Sikora
6ef9131060 update install_requires in setup.py 2014-12-02 15:04:07 +01:00
Mateusz Sikora
064d49fdef add missed requirement in setup.py 2014-11-30 23:37:40 +01:00
Mateusz Sikora
7b7e2d368b fix bug in PasswordChangeSerializer 2014-11-14 10:31:02 +01:00
Mateusz Sikora
986dcd12c0 update demo requirements 2014-11-12 12:27:55 +01:00
Mateusz Sikora
1b46c3625d version 0.3.1 2014-11-12 12:18:52 +01:00
Mateusz Sikora
86eb741b34 Merge pull request #30 from Tivix/develop
Develop
2014-11-12 12:00:22 +01:00
Mateusz Sikora
e8a7b0bdf4 add old_password field in PasswordChangeSerializer, disabled by default 2014-11-12 11:51:22 +01:00
Mateusz Sikora
0fc4d56dae add FAQ section in docs 2014-11-12 11:18:21 +01:00
Mateusz Sikora
059b0dcbab make all endpoints browsable 2014-11-12 10:33:29 +01:00
Mateusz Sikora
bbc9eeee3a make registration API browsable 2014-11-12 03:52:06 +01:00
Mateusz Sikora
1732b15041 Merge pull request #28 from APSL/master
modified utils.py in order to work properly with python2 and python3
2014-11-06 09:49:04 +01:00
Marc Tudurí marctc@gmail.com
be6b65dc55 modified utils.py in order to work properly with python2 and python3 2014-11-06 09:15:54 +01:00
Mateusz Sikora
644c4d28e1 Merge branch 'master' of github.com:Tivix/django-rest-auth 2014-10-24 15:52:17 +02:00
Mateusz Sikora
5825fae048 define permission classes inside views 2014-10-24 15:52:07 +02:00
Mateusz Sikora
ef3551fc47 Update requirements.pip 2014-10-24 14:30:43 +02:00
Mateusz Sikora
7332c9fb43 Merge pull request #18 from Madsn/patch-1
Fix app_settings import
2014-10-20 18:45:45 +02:00
Mikkel Madsen
81f435125b Fix app_settings import
The url paths
```
    url(r'^rest-auth/', include('rest_auth.urls')),
    url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
```
Weren't working for me, I kept getting an ImportError `No module named 'app_settings'`.
This fixes that issue for me on Win7 x64, Python 3.4, Django 1.7
2014-10-20 16:58:53 +02:00
Mateusz Sikora
87a8e5865a update demo doc 2014-10-16 15:57:30 +02:00
Mateusz Sikora
6e76b2e3ee Merge pull request #17 from Tivix/develop
Develop
2014-10-16 13:34:01 +02:00
Mateusz Sikora
28da4dc7c8 add requirements in demo, update doc 2014-10-16 13:31:25 +02:00
Mateusz Sikora
81a96cfe19 final versio of demo project 2014-10-16 13:13:55 +02:00
Mateusz Sikora
aac42af991 fix tests 2014-10-15 17:36:21 +02:00
Mateusz Sikora
5fd7ea5596 update demo project - still not finished 2014-10-15 17:32:19 +02:00
Mateusz Sikora
5bd8842c4b draft demo project 2014-10-14 16:44:57 +02:00
Mateusz Sikora
102fcedc82 move some test files outside app 2014-10-14 14:56:00 +02:00
83 changed files with 4460 additions and 739 deletions

73
.gitignore vendored
View File

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

View File

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

View File

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

View File

@ -1,18 +1,20 @@
===========
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.png .. image:: https://travis-ci.org/Tivix/django-rest-auth.svg
:target: https://travis-ci.org/Tivix/django-rest-auth :target: https://travis-ci.org/Tivix/django-rest-auth
.. image:: https://coveralls.io/repos/Tivix/django-rest-auth/badge.png .. image:: https://coveralls.io/repos/Tivix/django-rest-auth/badge.svg
:target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master :target: https://coveralls.io/r/Tivix/django-rest-auth?branch=master
.. image:: https://requires.io/github/Tivix/django-rest-auth/requirements.png?branch=master
:target: https://requires.io/github/Tivix/django-rest-auth/requirements/?branch=master
.. image:: https://readthedocs.org/projects/django-rest-auth/badge/?version=latest .. image:: https://readthedocs.org/projects/django-rest-auth/badge/?version=latest
:target: https://readthedocs.org/projects/django-rest-auth/?badge=latest :target: https://readthedocs.org/projects/django-rest-auth/?badge=latest
@ -29,3 +31,7 @@ Source code
----------- -----------
https://github.com/Tivix/django-rest-auth https://github.com/Tivix/django-rest-auth
Stack Overflow
-----------
http://stackoverflow.com/questions/tagged/django-rest-auth

0
demo/demo/__init__.py Normal file
View File

129
demo/demo/settings.py Normal file
View File

@ -0,0 +1,129 @@
"""
Django settings for demo project.
For more information on this file, see
https://docs.djangoproject.com/en/1.7/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.7/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
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!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
# 'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'allauth',
'allauth.account',
'rest_auth.registration',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'rest_framework_swagger',
)
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
# For backwards compatibility for Django 1.8
MIDDLEWARE_CLASSES = MIDDLEWARE
ROOT_URLCONF = 'demo.urls'
WSGI_APPLICATION = 'demo.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
STATIC_URL = '/static/'
# TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
TEMPLATES = [
{
'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'
SITE_ID = 1
ACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'username'
ACCOUNT_EMAIL_VERIFICATION = 'optional'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
SWAGGER_SETTINGS = {
'LOGIN_URL': 'login',
'LOGOUT_URL': 'logout',
}

44
demo/demo/urls.py Normal file
View File

@ -0,0 +1,44 @@
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView, RedirectView
from rest_framework_swagger.views import get_swagger_view
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name="home.html"), name='home'),
url(r'^signup/$', TemplateView.as_view(template_name="signup.html"),
name='signup'),
url(r'^email-verification/$',
TemplateView.as_view(template_name="email_verification.html"),
name='email-verification'),
url(r'^login/$', TemplateView.as_view(template_name="login.html"),
name='login'),
url(r'^logout/$', TemplateView.as_view(template_name="logout.html"),
name='logout'),
url(r'^password-reset/$',
TemplateView.as_view(template_name="password_reset.html"),
name='password-reset'),
url(r'^password-reset/confirm/$',
TemplateView.as_view(template_name="password_reset_confirm.html"),
name='password-reset-confirm'),
url(r'^user-details/$',
TemplateView.as_view(template_name="user_details.html"),
name='user-details'),
url(r'^password-change/$',
TemplateView.as_view(template_name="password_change.html"),
name='password-change'),
# this url is used to generate email content
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
TemplateView.as_view(template_name="password_reset_confirm.html"),
name='password_reset_confirm'),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
url(r'^account/', include('allauth.urls')),
url(r'^admin/', admin.site.urls),
url(r'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'),
url(r'^docs/$', get_swagger_view(title='API Docs'), name='api_docs')
]

14
demo/demo/wsgi.py Normal file
View File

@ -0,0 +1,14 @@
"""
WSGI config for demo project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

10
demo/manage.py Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

6
demo/requirements.pip Normal file
View File

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

98
demo/templates/base.html Normal file
View File

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Django-rest-auth demo">
<meta name="author" content="Tivix, Inc.">
<title>django-rest-auth demo</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body role="document">
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API endpoints <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<!-- these pages don't require user token -->
<li><a href="{% url 'signup' %}">Signup</a></li>
<li><a href="{% url 'email-verification' %}">E-mail verification</a></li>
<li><a href="{% url 'login' %}">Login</a></li>
<li><a href="{% url 'password-reset' %}">Password Reset</a></li>
<li><a href="{% url 'password-reset-confirm' %}">Password Reset Confirm</a></li>
<li class="divider"></li>
<!-- these pages require user token -->
<li><a href="{% url 'user-details' %}">User details</a></li>
<li><a href="{% url 'logout' %}">Logout</a></li>
<li><a href="{% url 'password-change' %}">Password change</a></li>
</ul>
</li>
</ul>
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">django-rest-auth demo</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<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="https://github.com/Tivix/django-rest-auth">Source code</a></li>
<li><a target="_blank" href="{% url 'api_docs' %}">API Docs</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container theme-showcase" role="main">
{% block content %}{% endblock %}
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
var error_response = function(data){
$('.api-response').html("API Response: " + data.status + ' ' + data.statusText + '<br/>Content: ' + data.responseText);
}
var susccess_response = function(data){
$('.api-response').html("API Response: OK<br/>Content: " + JSON.stringify(data));
}
$().ready(function(){
$('form.ajax-post button[type=submit]').click(function(){
var form = $('form.ajax-post');
$.post(form.attr('action'), form.serialize())
.fail(function(data){error_response(data);})
.done(function(data){susccess_response(data);});
return false;
});
});
</script>
{% block script %}{% endblock %}
</body>
</html>

View File

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

View File

@ -0,0 +1,18 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_verify_email' %}">{% csrf_token %}
<div class="form-group">
<label for="key" class="col-sm-2 control-label">Key</label>
<div class="col-sm-10">
<input name="key" type="text" class="form-control" id="key" placeholder="Key">
<p class="help-block">Put here a key which was sent in verification email</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Verify</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,24 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_login' %}">{% csrf_token %}
<div class="form-group">
<label for="username" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input name="username" type="text" class="form-control" id="username" placeholder="Username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input name="password" type="password" class="form-control" id="password" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Login</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,20 @@
{% block content %}
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_logout' %}">{% csrf_token %}
<div class="form-group">
<label for="token" class="col-sm-2 control-label">User Token</label>
<div class="col-sm-4">
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
<p class="help-block">Token received after login</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Logout</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>
{% endblock %}

View File

@ -0,0 +1,24 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_change' %}">{% csrf_token %}
<div class="form-group">
<label for="new_password1" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input name="new_password1" type="password" class="form-control" id="new_password1" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="new_password2" class="col-sm-2 control-label">Repeat password</label>
<div class="col-sm-10">
<input name="new_password2" type="password" class="form-control" id="new_password2" placeholder="Repeat password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Set new password</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,40 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset_confirm' %}">{% csrf_token %}
<div class="form-group">
<label for="uid" class="col-sm-2 control-label">Uid</label>
<div class="col-sm-10">
<input name="uid" type="text" class="form-control" id="uid" placeholder="Uid">
<p class="help-block">Uid value sent in email</p>
</div>
</div>
<div class="form-group">
<label for="token" class="col-sm-2 control-label">Token</label>
<div class="col-sm-10">
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
<p class="help-block">Token value sent in email</p>
</div>
</div>
<div class="form-group">
<label for="new_password1" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input name="new_password1" type="password" class="form-control" id="new_password1" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="new_password2" class="col-sm-2 control-label">Repeat password</label>
<div class="col-sm-10">
<input name="new_password2" type="password" class="form-control" id="new_password2" placeholder="Repeat password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Set new password</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,16 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" role="form" action="{% url 'rest_password_reset' %}">{% csrf_token %}
<div class="form-group">
<label for="email" class="col-sm-2 control-label">E-mail</label>
<div class="col-sm-10">
<input name="email" type="text" class="form-control" id="email" placeholder="Email">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Reset</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,38 @@
<!-- Signup form -->
<form class="form-horizontal ajax-post" id="signup" role="form" action="{% url 'rest_register' %}">{% csrf_token %}
<div class="form-group">
<label for="email" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="email" type="text" class="form-control" id="email" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="username" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input name="username" type="text" class="form-control" id="username" placeholder="Username">
</div>
</div>
<div class="form-group">
<label for="password1" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input name="password1" type="password" class="form-control" id="password1" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="password2" class="col-sm-2 control-label">Repeat password</label>
<div class="col-sm-10">
<input name="password2" type="password" class="form-control" id="password2" placeholder="Repeat password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign up</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

View File

@ -0,0 +1,39 @@
<!-- Signup form -->
<form class="form-horizontal" id="signup" role="form" action="{% url 'rest_user_details' %}">{% csrf_token %}
<div class="form-group">
<label for="email" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="email" type="text" class="form-control" id="email" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="username" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<input name="username" type="text" class="form-control" id="username" placeholder="Username">
</div>
</div>
<div class="form-group">
<label for="first_name" class="col-sm-2 control-label">First name</label>
<div class="col-sm-10">
<input name="first_name" type="text" class="form-control" id="first_name" placeholder="First name">
</div>
</div>
<div class="form-group">
<label for="last_name" class="col-sm-2 control-label">Last name</label>
<div class="col-sm-10">
<input name="last_name" type="text" class="form-control" id="last_name" placeholder="Last name">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Save</button>
</div>
</div>
<div class="form-group api-response"></div>
</form>

9
demo/templates/home.html Normal file
View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
<h1>django-rest-auth demo</h1>
<p>Welcome in django-rest-auth demo project!</p>
</div>
{% endblock %}

View File

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

View File

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

View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="form-group">
<label for="token" class="col-sm-2 control-label">User Token</label>
<div class="col-sm-4">
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
<p class="help-block">Token received after login</p>
</div>
</div>
</div>
<div class="row">
<h3>Update User Details</h3><hr/>
{% include "fragments/password_change_form.html" %}
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$().ready(function(){
$('form button[type=submit]').click(function(){
var token = $('input[name=token]').val();
var form = $('form');
$.ajax({
url: form.attr('action'),
data: $('form').serialize(),
type: "POST",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);}
}).fail(function(data){error_response(data);})
.done(function(data){susccess_response(data);});
return false;
});
});
</script>
{% endblock %}

View File

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

View File

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<h3>Password reset confirmation</h3><hr/>
{% include "fragments/password_reset_confirm_form.html" %}
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
var url_elements = window.location.pathname.split('/');
if (url_elements.length == 6){
var uid = url_elements[url_elements.length - 3];
if (uid !== undefined){
$('input[name=uid]').val(uid);
}
var token = url_elements[url_elements.length - 2];
if (token !== undefined){
$('input[name=token]').val(token);
}
}
</script>
{% endblock %}

View File

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

View File

@ -0,0 +1,58 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<h3>Retrieve User Details</h3><hr/>
<div class="form-group">
<label for="token" class="col-sm-2 control-label">User Token</label>
<div class="col-sm-4">
<input name="token" type="text" class="form-control" id="token" placeholder="Token">
<p class="help-block">Token received after login</p>
</div>
<button id="get-user-details" class="btn btn-primary">GET user details</button>
</div>
</div>
<div class="row">
<h3>Update User Details</h3><hr/>
{% include "fragments/user_details_form.html" %}
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$().ready(function(){
$('#get-user-details').click(function(){
var token = $('input[name=token]').val();
$.ajax({
url: "{% url 'rest_user_details' %}",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);},
type: "GET",
success: function(data) {
$('input[name=username]').val(data.username);
$('input[name=email]').val(data.email);
$('input[name=first_name]').val(data.first_name);
$('input[name=last_name]').val(data.last_name);
}
});
return false;
});
$('form button[type=submit]').click(function(){
var token = $('input[name=token]').val();
var form = $('form');
$.ajax({
url: form.attr('action'),
data: $('form').serialize(),
type: "PUT",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Token '+token);}
}).fail(function(data){error_response(data);})
.done(function(data){susccess_response(data);});
return false;
});
});
</script>
{% endblock %}

4
dev-requirements.txt Normal file
View File

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

View File

@ -6,17 +6,21 @@ Basic
- /rest-auth/login/ (POST) - /rest-auth/login/ (POST)
- username (string) - username
- password (string) - email
- password
Returns Token key
- /rest-auth/logout/ (POST) - /rest-auth/logout/ (POST)
.. note:: ``ACCOUNT_LOGOUT_ON_GET = True`` to allow logout using GET - this is the exact same configuration from allauth. NOT recommended, see: http://django-allauth.readthedocs.io/en/latest/views.html#logout
- /rest-auth/password/reset/ (POST) - /rest-auth/password/reset/ (POST)
- email - email
- /rest-auth/password/reset/confim/ (POST) - /rest-auth/password/reset/confirm/ (POST)
- uid - uid
- token - token
@ -29,15 +33,18 @@ Basic
- new_password1 - new_password1
- new_password2 - new_password2
- old_password
- /rest-auth/user/ (GET) .. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
- /rest-auth/user/ (PUT/PATCH) - /rest-auth/user/ (GET, PUT, PATCH)
- username - username
- first_name - first_name
- last_name - last_name
- email
Returns pk, username, email, first_name, last_name
Registration Registration
@ -50,17 +57,7 @@ Registration
- password2 - password2
- email - email
.. note:: This endpoint is based on ``allauth.account.views.SignupView`` and uses the same form as in this view. To override fields you have to create custom Signup Form and define it in django settings: - /rest-auth/registration/verify-email/ (POST)
.. code-block:: python
ACCOUNT_FORMS = {
'signup': 'path.to.custom.SignupForm'
}
See allauth documentation for more details.
- /rest-auth/registration/ (POST)
- key - key
@ -73,3 +70,11 @@ Basing on example from installation section :doc:`Installation </installation>`
- /rest-auth/facebook/ (POST) - /rest-auth/facebook/ (POST)
- access_token - access_token
- code
.. note:: ``access_token`` OR ``code`` can be used as standalone arguments, see https://github.com/Tivix/django-rest-auth/blob/master/rest_auth/registration/views.py
- /rest-auth/twitter/ (POST)
- access_token
- token_secret

View File

@ -1,6 +1,110 @@
Changelog Changelog
========= =========
0.9.5
-----
- fixed package distribution issue
0.9.4
-----
- Compatibility fixes (#437, #506)
- JWT auth cookie fix (#345)
- config & packaging fixes
- updated docs
- added new translations (Czech, Chinese, Turkish, Korean)
0.9.3
-----
- added social connect views
- added check for pre-existing accounts in social login
- prevent double-validation in LoginSerializer
- unit tests and demo project changes for Django 2.0
0.9.2
-----
- added permission classes configuration for registration
- added more info to JWT docs
- added Polish translations
0.9.1
-----
- fixed import error when extending rest_auth serializers
- added sensitive fields decorator
- added Spanish translations
0.9.0
-----
- allowed using custom UserDetailsSerializer with JWTSerializer
- fixed error with logout on GET
- updated api endpoints and configuration docs
- bugfixes
- minor text fixes
0.8.2
-----
- fixed allauth import error
- added swagger docs to demo project
0.8.1
-----
- added support for django-allauth hmac email confirmation pattern
0.8.0
-----
- added support for django-rest-framework-jwt
- bugfixes
0.7.0
-----
- Wrapped API returned strings in ugettext_lazy
- Fixed not using ``get_username`` which caused issues when using custom user model without username field
- Django 1.9 support
- Added ``TwitterLoginSerializer``
0.6.0
-----
- dropped support for Python 2.6
- dropped support for Django 1.6
- fixed demo code
- added better validation support for serializers
- added optional logout after password change
- compatibility fixes
- bugfixes
0.5.0
-----
- replaced request.DATA with request.data for compatibility with DRF 3.2
- authorization codes for social login
- view classes rename (appended "View" to all of them)
- bugfixes
0.4.0
-----
- Django 1.8 compatiblity fixes
0.3.4
-----
- fixed bug in PasswordResetConfirmation serializer (token field wasn't validated)
- fixed bug in Register view
0.3.3
-----
- support django-rest-framework v3.0
0.3.2
-----
- fixed few minor bugs
0.3.1
-----
- added old_password field in PasswordChangeSerializer
- make all endpoints browsable
- removed LoggedInRESTAPIView, LoggedOutRESTAPIView
- fixed minor bugs
0.3.0 0.3.0
----- -----

View File

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

View File

@ -6,17 +6,19 @@ Configuration
You can define your custom serializers for each endpoint without overriding urls and views by adding ``REST_AUTH_SERIALIZERS`` dictionary in your django settings. You can define your custom serializers for each endpoint without overriding urls and views by adding ``REST_AUTH_SERIALIZERS`` dictionary in your django settings.
Possible key values: Possible key values:
- LOGIN_SERIALIZER - serializer class in ``rest_auth.views.Login``, default value ``rest_auth.serializers.LoginSerializer`` - LOGIN_SERIALIZER - serializer class in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.LoginSerializer``
- TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.Login``, default value ``rest_auth.serializers.TokenSerializer`` - TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer``
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetails``, default value ``rest_auth.serializers.UserDetailsSerializer`` - JWT_SERIALIZER - (Using REST_USE_JWT=True) response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.JWTSerializer``
- PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordReset``, default value ``rest_auth.serializers.PasswordResetSerializer`` - USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer``
- PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirm``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer`` - PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer``
- PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChange``, default value ``rest_auth.serializers.PasswordChangeSerializer`` - PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirmView``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer``
- PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChangeView``, default value ``rest_auth.serializers.PasswordChangeSerializer``
Example configuration: Example configuration:
@ -29,5 +31,23 @@ Configuration
... ...
} }
- **REST_AUTH_REGISTER_SERIALIZERS**
You can define your custom serializers for registration endpoint.
Possible key values:
- REGISTER_SERIALIZER - serializer class in ``rest_auth.registration.views.RegisterView``, default value ``rest_auth.registration.serializers.RegisterSerializer``
.. note:: The custom REGISTER_SERIALIZER must define a ``def save(self, request)`` method that returns a user model instance
- **REST_AUTH_TOKEN_MODEL** - model class for tokens, default value ``rest_framework.authtoken.models``
- **REST_AUTH_TOKEN_CREATOR** - callable to create tokens, default value ``rest_auth.utils.default_create_token``.
- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True) - **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
- **REST_USE_JWT** - Enable JWT Authentication instead of Token/Session based. This is built on top of django-rest-framework-jwt http://getblimp.github.io/django-rest-framework-jwt/, which must also be installed. (default: False)
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
- **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change

17
docs/demo.rst Normal file
View File

@ -0,0 +1,17 @@
Demo project
============
The idea of creating demo project was to show how you can potentially use
django-rest-auth app with jQuery on frontend.
Do these steps to make it running (ideally in virtualenv).
.. code-block:: python
cd /tmp
git clone https://github.com/Tivix/django-rest-auth.git
cd django-rest-auth/demo/
pip install -r requirements.pip
python manage.py migrate --settings=demo.settings --noinput
python manage.py runserver --settings=demo.settings
Now, go to ``http://127.0.0.1:8000/`` in your browser.

72
docs/faq.rst Normal file
View File

@ -0,0 +1,72 @@
FAQ
===
1. Why account_confirm_email url is defined but it is not usable?
In /rest_auth/registration/urls.py we can find something like this:
.. code-block:: python
url(r'^account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
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.
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:
django-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py
2. I get an error: Reverse for 'password_reset_confirm' not found.
You need to add `password_reset_confirm` url into your ``urls.py`` (at the top of any other included urls). Please check the ``urls.py`` module inside demo app example for more details.
3. How can I update UserProfile assigned to User model?
Assuming you already have UserProfile model defined like this
.. code-block:: python
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
# custom fields for user
company_name = models.CharField(max_length=100)
To allow update user details within one request send to rest_auth.views.UserDetailsView view, create serializer like this:
.. code-block:: python
from rest_framework import serializers
from rest_auth.serializers import UserDetailsSerializer
class UserSerializer(UserDetailsSerializer):
company_name = serializers.CharField(source="userprofile.company_name")
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('company_name',)
def update(self, instance, validated_data):
profile_data = validated_data.pop('userprofile', {})
company_name = profile_data.get('company_name')
instance = super(UserSerializer, self).update(instance, validated_data)
# get and update user profile
profile = instance.userprofile
if profile_data and company_name:
profile.company_name = company_name
profile.save()
return instance
And setup USER_DETAILS_SERIALIZER in django settings:
.. code-block:: python
REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'demo.serializers.UserSerializer'
}

View File

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

View File

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

View File

@ -14,7 +14,21 @@ Features
* Password reset via e-mail * Password reset via e-mail
* Social Media authentication * Social Media authentication
Apps structure Apps structure
-------------- --------------
* ``rest_auth`` has basic auth functionality like login, logout, password reset and password change * ``rest_auth`` has basic auth functionality like login, logout, password reset and password change
* ``rest_auth.registration`` has logic related with registration and social media authentication * ``rest_auth.registration`` has logic related with registration and social media authentication
Angular app
-----------
- Tivix has also created angular module which uses API endpoints from this app - `angular-django-registration-auth <https://github.com/Tivix/angular-django-registration-auth>`_
Demo project
------------
- You can also check our :doc:`Demo Project </demo>` which is using jQuery on frontend.

View File

@ -2,19 +2,25 @@ from django.conf import settings
from rest_auth.serializers import ( from rest_auth.serializers import (
TokenSerializer as DefaultTokenSerializer, TokenSerializer as DefaultTokenSerializer,
JWTSerializer as DefaultJWTSerializer,
UserDetailsSerializer as DefaultUserDetailsSerializer, UserDetailsSerializer as DefaultUserDetailsSerializer,
LoginSerializer as DefaultLoginSerializer, LoginSerializer as DefaultLoginSerializer,
PasswordResetSerializer as DefaultPasswordResetSerializer, PasswordResetSerializer as DefaultPasswordResetSerializer,
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer, PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
PasswordChangeSerializer as DefaultPasswordChangeSerializer) PasswordChangeSerializer as DefaultPasswordChangeSerializer)
from .utils import import_callable from .utils import import_callable, default_create_token
create_token = import_callable(
getattr(settings, 'REST_AUTH_TOKEN_CREATOR', default_create_token))
serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {}) serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
TokenSerializer = import_callable( TokenSerializer = import_callable(
serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer)) serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer))
JWTSerializer = import_callable(
serializers.get('JWT_SERIALIZER', DefaultJWTSerializer))
UserDetailsSerializer = import_callable( UserDetailsSerializer = import_callable(
serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer) serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer)
) )
@ -24,18 +30,22 @@ LoginSerializer = import_callable(
) )
PasswordResetSerializer = import_callable( PasswordResetSerializer = import_callable(
serializers.get('PASSWORD_RESET_SERIALIZER', serializers.get(
DefaultPasswordResetSerializer) 'PASSWORD_RESET_SERIALIZER',
DefaultPasswordResetSerializer
)
) )
PasswordResetConfirmSerializer = import_callable( PasswordResetConfirmSerializer = import_callable(
serializers.get('PASSWORD_RESET_CONFIRM_SERIALIZER', serializers.get(
DefaultPasswordResetConfirmSerializer) 'PASSWORD_RESET_CONFIRM_SERIALIZER',
DefaultPasswordResetConfirmSerializer
)
) )
PasswordChangeSerializer = import_callable( PasswordChangeSerializer = import_callable(
serializers.get('PASSWORD_CHANGE_SERIALIZER', serializers.get(
DefaultPasswordChangeSerializer) 'PASSWORD_CHANGE_SERIALIZER',
DefaultPasswordChangeSerializer
)
) )

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,98 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: registration/serializers.py:53
msgid "View is not defined, pass it as a context variable"
msgstr "\"View\" ist nicht definiert, übergib es als Contextvariable"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "Definier \"adapter_class\" in view"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "Definier \"callback_url\" in view"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "Definier \"client_class\" in view"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "Falsche Eingabe. \"access_token\" oder \"code\" erforderlich."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "Falscher Wert."
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "Ein User mit dieser E-Mail Adresse ist schon registriert."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "Die beiden Passwörter sind nicht identisch."
#: registration/views.py:91
msgid "ok"
msgstr "Ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "Muss \"email\" und \"password\" enthalten."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "Muss \"username\" und \"password\" enthalten."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "Muss entweder \"username\" oder \"email\" und password \"password\""
#: serializers.py:95
msgid "User account is disabled."
msgstr "Der Useraccount ist deaktiviert."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "E-Mail Adresse ist nicht verifiziert."
#: views.py:126
msgid "Successfully logged out."
msgstr "Erfolgreich ausgeloggt."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "Die E-Mail zum Zurücksetzen des Passwortes wurde verschickt."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "Das Passwort wurde mit dem neuen Passwort ersetzt."
#: views.py:222
msgid "New password has been saved."
msgstr "Das neue Passwort wurde gespeichert."
#~ msgid "Error"
#~ msgstr "Fehler"

Binary file not shown.

View File

@ -0,0 +1,99 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 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."

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,99 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-05 21:56-0800\n"
"PO-Revision-Date: 2018-03-20 17:52+0900\n"
"Last-Translator: Jeonsgoo Park <toracle@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: registration/serializers.py:53
msgid "View is not defined, pass it as a context variable"
msgstr "View가 정의되지 않았습니다. 컨텍스트 변수에 포함해주세요"
#: registration/serializers.py:58
msgid "Define adapter_class in view"
msgstr "view에 adapter_class를 정의하세요"
#: registration/serializers.py:77
msgid "Define callback_url in view"
msgstr "view에 callback_url을 정의하세요"
#: registration/serializers.py:81
msgid "Define client_class in view"
msgstr "view에 client_class를 정의하세요"
#: registration/serializers.py:102
msgid "Incorrect input. access_token or code is required."
msgstr "올바르지 않은 입력입니다. access_token이나 code가 필요합니다."
#: registration/serializers.py:111
msgid "Incorrect value"
msgstr "올바르지 않은 값"
#: registration/serializers.py:140
msgid "A user is already registered with this e-mail address."
msgstr "이미 이 이메일 주소로 등록된 사용자가 있습니다."
#: registration/serializers.py:148
msgid "The two password fields didn't match."
msgstr "두 개의 패스워드 필드가 서로 맞지 않습니다."
#: registration/views.py:44
msgid "Verification e-mail sent."
msgstr "확인 이메일을 발송했습니다."
#: registration/views.py:91
msgid "ok"
msgstr "ok"
#: serializers.py:30
msgid "Must include \"email\" and \"password\"."
msgstr "\"email\"과 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:41
msgid "Must include \"username\" and \"password\"."
msgstr "\"username\"과 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:54
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "\"username\"이나 \"email\", 그리고 \"password\"를 반드시 포함해야 합니다."
#: serializers.py:95
msgid "User account is disabled."
msgstr "사용자 계정이 비활성화 되어있습니다."
#: serializers.py:98
msgid "Unable to log in with provided credentials."
msgstr "주어진 자격 증명으로 로그인이 불가능합니다."
#: serializers.py:107
msgid "E-mail is not verified."
msgstr "이메일 주소가 확인되지 않았습니다."
#: views.py:126
msgid "Successfully logged out."
msgstr "로그아웃되었습니다."
#: views.py:174
msgid "Password reset e-mail has been sent."
msgstr "패스워드 초기화 이메일이 발송되었습니다."
#: views.py:200
msgid "Password has been reset with the new password."
msgstr "새로운 패스워드로 패스워드가 초기화 되었습니다."
#: views.py:222
msgid "New password has been saved."
msgstr "새로운 패스워드가 저장되었습니다."

Binary file not shown.

View File

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

View File

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

Binary file not shown.

View File

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

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

Binary file not shown.

View File

@ -0,0 +1,104 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-28 11:41+0800\n"
"PO-Revision-Date: 2018-10-28 11:45+0806\n"
"Last-Translator: b' <admin@xx.com>'\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Translated-Using: django-rosetta 0.9.0\n"
#: registration/serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr "View未定义请通过context变量传入"
#: registration/serializers.py:72
msgid "Define adapter_class in view"
msgstr "请在View中定义adapter_class"
#: registration/serializers.py:91
msgid "Define callback_url in view"
msgstr "请在view中定义callback_url"
#: registration/serializers.py:95
msgid "Define client_class in view"
msgstr "请在view中定义client_class"
#: registration/serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr "输入错误。access_token或code是必填项。"
#: registration/serializers.py:125
msgid "Incorrect value"
msgstr "错误的值"
#: registration/serializers.py:139
msgid "User is already registered with this e-mail address."
msgstr "该邮箱地址已被注册。"
#: registration/serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr "该邮箱地址已被注册。"
#: registration/serializers.py:193
msgid "The two password fields didn't match."
msgstr "两次输入的密码不相同"
#: registration/views.py:51
msgid "Verification e-mail sent."
msgstr "验证邮件已发送。"
#: registration/views.py:98
msgid "ok"
msgstr "好的"
#: serializers.py:33
msgid "Must include \"email\" and \"password\"."
msgstr "比如包含\"email\"和\"password\"。"
#: serializers.py:44
msgid "Must include \"username\" and \"password\"."
msgstr "比如包含\"username\"和\"password\"。"
#: serializers.py:57
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr "比如包含\"username\"\"email\"\"password\"其中一个。"
#: serializers.py:98
msgid "User account is disabled."
msgstr "用户账号已被禁用。"
#: serializers.py:101
msgid "Unable to log in with provided credentials."
msgstr "无法使用提供的信息登录。"
#: serializers.py:110
msgid "E-mail is not verified."
msgstr "邮箱未验证。"
#: views.py:127
msgid "Successfully logged out."
msgstr "已成功登出。"
#: views.py:175
msgid "Password reset e-mail has been sent."
msgstr "密码重置邮件已发送。"
#: views.py:201
msgid "Password has been reset with the new password."
msgstr "密码重置成功。"
#: views.py:223
msgid "New password has been saved."
msgstr "新密码已设置成功。"

Binary file not shown.

View File

@ -0,0 +1,103 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-28 11:41+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: registration/serializers.py:67
msgid "View is not defined, pass it as a context variable"
msgstr ""
#: registration/serializers.py:72
msgid "Define adapter_class in view"
msgstr ""
#: registration/serializers.py:91
msgid "Define callback_url in view"
msgstr ""
#: registration/serializers.py:95
msgid "Define client_class in view"
msgstr ""
#: registration/serializers.py:116
msgid "Incorrect input. access_token or code is required."
msgstr ""
#: registration/serializers.py:125
msgid "Incorrect value"
msgstr ""
#: registration/serializers.py:139
msgid "User is already registered with this e-mail address."
msgstr ""
#: registration/serializers.py:185
msgid "A user is already registered with this e-mail address."
msgstr ""
#: registration/serializers.py:193
msgid "The two password fields didn't match."
msgstr ""
#: registration/views.py:51
msgid "Verification e-mail sent."
msgstr ""
#: registration/views.py:98
msgid "ok"
msgstr ""
#: serializers.py:33
msgid "Must include \"email\" and \"password\"."
msgstr ""
#: serializers.py:44
msgid "Must include \"username\" and \"password\"."
msgstr ""
#: serializers.py:57
msgid "Must include either \"username\" or \"email\" and \"password\"."
msgstr ""
#: serializers.py:98
msgid "User account is disabled."
msgstr ""
#: serializers.py:101
msgid "Unable to log in with provided credentials."
msgstr ""
#: serializers.py:110
msgid "E-mail is not verified."
msgstr ""
#: views.py:127
msgid "Successfully logged out."
msgstr ""
#: views.py:175
msgid "Password reset e-mail has been sent."
msgstr ""
#: views.py:201
msgid "Password has been reset with the new password."
msgstr ""
#: views.py:223
msgid "New password has been saved."
msgstr ""

View File

@ -1,3 +1,10 @@
# from django.db import models from django.conf import settings
from rest_framework.authtoken.models import Token as DefaultTokenModel
from .utils import import_callable
# Register your models here. # Register your models here.
TokenModel = import_callable(
getattr(settings, 'REST_AUTH_TOKEN_MODEL', DefaultTokenModel))

View File

@ -0,0 +1,19 @@
from django.conf import settings
from rest_framework.permissions import AllowAny
from rest_auth.registration.serializers import (
RegisterSerializer as DefaultRegisterSerializer)
from ..utils import import_callable
serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {})
RegisterSerializer = import_callable(
serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer))
def register_permission_classes():
permission_classes = [AllowAny, ]
for klass in getattr(settings, 'REST_AUTH_REGISTER_PERMISSION_CLASSES', tuple()):
permission_classes.append(import_callable(klass))
return tuple(permission_classes)

View File

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

View File

@ -1,16 +1,23 @@
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.conf.urls import patterns, url from django.conf.urls import url
from .views import Register, VerifyEmail from .views import RegisterView, VerifyEmailView
urlpatterns = patterns('', urlpatterns = [
url(r'^$', Register.as_view(), name='rest_register'), url(r'^$', RegisterView.as_view(), name='rest_register'),
url(r'^verify-email/$', VerifyEmail.as_view(), name='verify_email'), url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'),
url(r'^account-email-verification-sent/$', TemplateView.as_view(), # This url is used by django-allauth and empty TemplateView is
name='account_email_verification_sent'), # defined just to allow reverse() call inside app, for example when email
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(), # with verification link is being sent, then it's required to render email
# content.
# account_confirm_email - You should override this view 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:
# django-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py
url(r'^account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
name='account_confirm_email'), name='account_confirm_email'),
]
)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,382 +0,0 @@
import json
from datetime import datetime, date, time
from django.conf import settings
from django.core.urlresolvers import reverse
from django.test.client import Client, MULTIPART_CONTENT
from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.core import mail
from django.test.utils import override_settings
from django.contrib.sites.models import Site
from allauth.socialaccount.models import SocialApp
import responses
from rest_framework import status
class APIClient(Client):
def patch(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
return self.generic('PATCH', path, data, content_type, **extra)
def options(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
return self.generic('OPTIONS', path, data, content_type, **extra)
# class CustomJSONEncoder(json.JSONEncoder):
# """
# Convert datetime/date objects into isoformat
# """
# def default(self, obj):
# if isinstance(obj, (datetime, date, time)):
# return obj.isoformat()
# else:
# return super(CustomJSONEncoder, self).default(obj)
class BaseAPITestCase(object):
"""
base for API tests:
* easy request calls, f.e.: self.post(url, data), self.get(url)
* easy status check, f.e.: self.post(url, data, status_code=200)
"""
def send_request(self, request_method, *args, **kwargs):
request_func = getattr(self.client, request_method)
status_code = None
if not 'content_type' in kwargs and request_method != 'get':
kwargs['content_type'] = 'application/json'
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
data = kwargs.get('data', '')
kwargs['data'] = json.dumps(data) # , cls=CustomJSONEncoder
if 'status_code' in kwargs:
status_code = kwargs.pop('status_code')
# check_headers = kwargs.pop('check_headers', True)
if hasattr(self, 'token'):
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
self.response = request_func(*args, **kwargs)
is_json = bool(
filter(lambda x: 'json' in x, self.response._headers['content-type']))
if is_json and self.response.content:
self.response.json = json.loads(self.response.content)
else:
self.response.json = {}
if status_code:
self.assertEqual(self.response.status_code, status_code)
return self.response
def post(self, *args, **kwargs):
return self.send_request('post', *args, **kwargs)
def get(self, *args, **kwargs):
return self.send_request('get', *args, **kwargs)
def patch(self, *args, **kwargs):
return self.send_request('patch', *args, **kwargs)
# def put(self, *args, **kwargs):
# return self.send_request('put', *args, **kwargs)
# def delete(self, *args, **kwargs):
# return self.send_request('delete', *args, **kwargs)
# def options(self, *args, **kwargs):
# return self.send_request('options', *args, **kwargs)
# def post_file(self, *args, **kwargs):
# kwargs['content_type'] = MULTIPART_CONTENT
# return self.send_request('post', *args, **kwargs)
# def get_file(self, *args, **kwargs):
# content_type = None
# if 'content_type' in kwargs:
# content_type = kwargs.pop('content_type')
# response = self.send_request('get', *args, **kwargs)
# if content_type:
# self.assertEqual(
# bool(filter(lambda x: content_type in x, response._headers['content-type'])), True)
# return response
def init(self):
settings.DEBUG = True
self.client = APIClient()
# -----------------------
# T E S T H E R E
# -----------------------
class APITestCase1(TestCase, BaseAPITestCase):
"""
Case #1:
- user profile: defined
- custom registration: backend defined
"""
urls = 'rest_auth.test_urls'
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
NEW_PASS = 'new-test-pass'
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
# data without user profile
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS
}
REGISTRATION_DATA_WITH_EMAIL = REGISTRATION_DATA.copy()
REGISTRATION_DATA_WITH_EMAIL['email'] = EMAIL
BASIC_USER_DATA = {
'first_name': "John",
'last_name': 'Smith',
'email': EMAIL
}
USER_DATA = BASIC_USER_DATA.copy()
USER_DATA['newsletter_subscribe'] = True
def setUp(self):
self.init()
self.login_url = reverse('rest_login')
self.logout_url = reverse('rest_logout')
self.password_change_url = reverse('rest_password_change')
self.register_url = reverse('rest_register')
self.password_reset_url = reverse('rest_password_reset')
self.user_url = reverse('rest_user_details')
self.veirfy_email_url = reverse('verify_email')
def _login(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
def _logout(self):
self.post(self.logout_url, status=status.HTTP_200_OK)
def _generate_uid_and_token(self, user):
result = {}
from django.utils.encoding import force_bytes
from django.contrib.auth.tokens import default_token_generator
from django import VERSION
if VERSION[1] == 5:
from django.utils.http import int_to_base36
result['uid'] = int_to_base36(user.pk)
else:
from django.utils.http import urlsafe_base64_encode
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
result['token'] = default_token_generator.make_token(user)
return result
def test_login(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
# there is no users in db so it should throw error (400)
self.post(self.login_url, data=payload, status_code=400)
self.post(self.password_change_url, status_code=403)
# create user
user = User.objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=payload, status_code=200)
self.assertEqual('key' in self.response.json.keys(), True)
self.token = self.response.json['key']
self.post(self.password_change_url, status_code=400)
# test inactive user
user.is_active = False
user.save()
self.post(self.login_url, data=payload, status_code=400)
# test wrong username/password
payload = {
"username": self.USERNAME + '?',
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=400)
# test empty payload
self.post(self.login_url, data={}, status_code=400)
def test_password_change(self):
login_payload = {
"username": self.USERNAME,
"password": self.PASS
}
User.objects.create_user(self.USERNAME, '', self.PASS)
self.post(self.login_url, data=login_payload, status_code=200)
self.token = self.response.json['key']
new_password_payload = {
"new_password1": "new_person",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=200)
# user should not be able to login using old password
self.post(self.login_url, data=login_payload, status_code=400)
# new password should work
login_payload['password'] = new_password_payload['new_password1']
self.post(self.login_url, data=login_payload, status_code=200)
# pass1 and pass2 are not equal
new_password_payload = {
"new_password1": "new_person1",
"new_password2": "new_person"
}
self.post(self.password_change_url, data=new_password_payload,
status_code=400)
# send empty payload
self.post(self.password_change_url, data={}, status_code=400)
def test_password_reset(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
# call password reset
mail_count = len(mail.outbox)
payload = {'email': self.EMAIL}
self.post(self.password_reset_url, data=payload, status_code=200)
self.assertEqual(len(mail.outbox), mail_count + 1)
url_kwargs = self._generate_uid_and_token(user)
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS,
'uid': url_kwargs['uid'],
'token': url_kwargs['token']
}
url = reverse('rest_password_reset_confirm')
self.post(url, data=data, status_code=200)
payload = {
"username": self.USERNAME,
"password": self.NEW_PASS
}
self.post(self.login_url, data=payload, status_code=200)
def test_user_details(self):
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=200)
self.token = self.response.json['key']
self.get(self.user_url, status_code=200)
self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
user = User.objects.get(pk=user.pk)
self.assertEqual(user.first_name, self.response.json['first_name'])
self.assertEqual(user.last_name, self.response.json['last_name'])
self.assertEqual(user.email, self.response.json['email'])
def test_registration(self):
user_count = User.objects.all().count()
# test empty payload
self.post(self.register_url, data={}, status_code=400)
self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
self.assertEqual(User.objects.all().count(), user_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
self._login()
self._logout()
@override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True
)
def test_registration_with_email_verification(self):
user_count = User.objects.all().count()
mail_count = len(mail.outbox)
# test empty payload
self.post(self.register_url, data={},
status_code=status.HTTP_400_BAD_REQUEST)
self.post(self.register_url, data=self.REGISTRATION_DATA_WITH_EMAIL,
status_code=status.HTTP_201_CREATED)
self.assertEqual(User.objects.all().count(), user_count + 1)
self.assertEqual(len(mail.outbox), mail_count + 1)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
# email is not verified yet
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload,
status=status.HTTP_400_BAD_REQUEST)
# veirfy email
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
.emailconfirmation_set.order_by('-created')[0]
self.post(self.veirfy_email_url, data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK)
# try to login again
self._login()
self._logout()
class TestSocialAuth(TestCase, BaseAPITestCase):
urls = 'rest_auth.test_urls'
def setUp(self):
social_app = SocialApp.objects.create(
provider='facebook',
name='Facebook',
client_id='123123123',
secret='321321321',
)
site = Site.objects.get_current()
social_app.sites.add(site)
self.fb_login_url = reverse('fb_login')
@responses.activate
def test_failed_social_auth(self):
# fake response
responses.add(responses.GET, 'https://graph.facebook.com/me',
body='', status=400, content_type='application/json')
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=400)
@responses.activate
def test_social_auth(self):
# fake response for facebook call
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}'
responses.add(responses.GET, 'https://graph.facebook.com/me',
body=resp_body, status=200, content_type='application/json')
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())

View File

View File

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

105
rest_auth/tests/mixins.py Normal file
View File

@ -0,0 +1,105 @@
import json
from django.conf import settings
from django.test.client import Client, MULTIPART_CONTENT
from django.utils.encoding import force_text
from rest_framework import status
from rest_framework import permissions
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
class CustomPermissionClass(permissions.BasePermission):
message = 'You shall not pass!'
def has_permission(self, request, view):
return False
class APIClient(Client):
def patch(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
return self.generic('PATCH', path, data, content_type, **extra)
def options(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, **extra):
return self.generic('OPTIONS', path, data, content_type, **extra)
class TestsMixin(object):
"""
base for API tests:
* easy request calls, f.e.: self.post(url, data), self.get(url)
* easy status check, f.e.: self.post(url, data, status_code=200)
"""
def send_request(self, request_method, *args, **kwargs):
request_func = getattr(self.client, request_method)
status_code = None
if 'content_type' not in kwargs and request_method != 'get':
kwargs['content_type'] = 'application/json'
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
data = kwargs.get('data', '')
kwargs['data'] = json.dumps(data) # , cls=CustomJSONEncoder
if 'status_code' in kwargs:
status_code = kwargs.pop('status_code')
# check_headers = kwargs.pop('check_headers', True)
if hasattr(self, 'token'):
if getattr(settings, 'REST_USE_JWT', False):
kwargs['HTTP_AUTHORIZATION'] = 'JWT %s' % self.token
else:
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
self.response = request_func(*args, **kwargs)
is_json = bool(
[x for x in self.response._headers['content-type'] if 'json' in x])
self.response.json = {}
if is_json and self.response.content:
self.response.json = json.loads(force_text(self.response.content))
if status_code:
self.assertEqual(self.response.status_code, status_code)
return self.response
def post(self, *args, **kwargs):
return self.send_request('post', *args, **kwargs)
def get(self, *args, **kwargs):
return self.send_request('get', *args, **kwargs)
def patch(self, *args, **kwargs):
return self.send_request('patch', *args, **kwargs)
def init(self):
settings.DEBUG = True
self.client = APIClient()
self.login_url = reverse('rest_login')
self.logout_url = reverse('rest_logout')
self.password_change_url = reverse('rest_password_change')
self.register_url = reverse('rest_register')
self.password_reset_url = reverse('rest_password_reset')
self.user_url = reverse('rest_user_details')
self.verify_email_url = reverse('rest_verify_email')
self.fb_login_url = reverse('fb_login')
self.tw_login_url = reverse('tw_login')
self.tw_login_no_view_url = reverse('tw_login_no_view')
self.tw_login_no_adapter_url = reverse('tw_login_no_adapter')
self.fb_connect_url = reverse('fb_connect')
self.tw_connect_url = reverse('tw_connect')
self.social_account_list_url = reverse('social_account_list')
def _login(self):
payload = {
"username": self.USERNAME,
"password": self.PASS
}
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
def _logout(self):
self.post(self.logout_url, status=status.HTTP_200_OK)

View File

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

View File

@ -1,8 +1,8 @@
import django
import os import os
import sys import sys
PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0]) PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0])
ROOT_URLCONF = 'urls' ROOT_URLCONF = 'urls'
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATIC_ROOT = '%s/staticserve' % PROJECT_ROOT STATIC_ROOT = '%s/staticserve' % PROJECT_ROOT
@ -18,17 +18,14 @@ IS_STAGING = False
IS_PROD = False IS_PROD = False
IS_TEST = 'test' in sys.argv or 'test_coverage' in sys.argv IS_TEST = 'test' in sys.argv or 'test_coverage' in sys.argv
if django.VERSION[:2] >= (1, 3):
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:', 'NAME': ':memory:',
} }
} }
else:
DATABASE_ENGINE = 'sqlite3'
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
'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',
@ -36,6 +33,9 @@ MIDDLEWARE_CLASSES = [
'django.contrib.messages.middleware.MessageMiddleware' 'django.contrib.messages.middleware.MessageMiddleware'
] ]
# Adding for backwards compatibility for Django 1.8 tests
MIDDLEWARE_CLASSES = MIDDLEWARE
TEMPLATE_CONTEXT_PROCESSORS = [ TEMPLATE_CONTEXT_PROCESSORS = [
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug', 'django.core.context_processors.debug',
@ -48,6 +48,27 @@ TEMPLATE_CONTEXT_PROCESSORS = [
"allauth.socialaccount.context_processors.socialaccount", "allauth.socialaccount.context_processors.socialaccount",
] ]
# avoid deprecation warnings during tests
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
# insert your TEMPLATE_DIRS here
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': TEMPLATE_CONTEXT_PROCESSORS,
},
},
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
@ -62,18 +83,24 @@ INSTALLED_APPS = [
'allauth.account', 'allauth.account',
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.twitter',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'rest_auth', 'rest_auth',
'rest_auth.registration' 'rest_auth.registration',
'rest_framework_jwt'
] ]
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd" SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
ACCOUNT_ACTIVATION_DAYS = 1 ACCOUNT_ACTIVATION_DAYS = 1
SITE_ID = 1 SITE_ID = 1
MIGRATION_MODULES = { AUTHENTICATION_BACKENDS = (
'authtoken': 'authtoken.migrations', # Needed to login by username in Django admin, regardless of `allauth`
} 'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)

518
rest_auth/tests/test_api.py Normal file
View File

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

View File

@ -0,0 +1,447 @@
import json
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.test.utils import override_settings
from django.contrib.sites.models import Site
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
import responses
from rest_framework import status
from .mixins import TestsMixin
@override_settings(ROOT_URLCONF="tests.urls")
class TestSocialAuth(TestsMixin, TestCase):
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS,
"email": EMAIL
}
def setUp(self):
self.init()
social_app = SocialApp.objects.create(
provider='facebook',
name='Facebook',
client_id='123123123',
secret='321321321',
)
twitter_social_app = SocialApp.objects.create(
provider='twitter',
name='Twitter',
client_id='11223344',
secret='55667788',
)
site = Site.objects.get_current()
social_app.sites.add(site)
twitter_social_app.sites.add(site)
self.graph_api_url = GRAPH_API_URL + '/me'
self.twitter_url = 'http://twitter.com/foobarme'
@responses.activate
def test_failed_social_auth(self):
# fake response
responses.add(
responses.GET,
self.graph_api_url,
body='',
status=400,
content_type='application/json'
)
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=400)
@responses.activate
def test_social_auth(self):
# fake response for facebook call
resp_body = {
"id": "123123123123",
"first_name": "John",
"gender": "male",
"last_name": "Smith",
"link": "https://www.facebook.com/john.smith",
"locale": "en_US",
"name": "John Smith",
"timezone": 2,
"updated_time": "2014-08-13T10:14:38+0000",
"username": "john.smith",
"verified": True
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
# make sure that second request will not create a new user
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
def _twitter_social_auth(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_url, data=payload)
self.assertIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
# make sure that second request will not create a new user
self.post(self.tw_login_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
@responses.activate
@override_settings(SOCIALACCOUNT_AUTO_SIGNUP=True)
def test_twitter_social_auth(self):
self._twitter_social_auth()
@responses.activate
@override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False)
def test_twitter_social_auth_without_auto_singup(self):
self._twitter_social_auth()
@responses.activate
def test_twitter_social_auth_request_error(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_url, data=payload, status_code=400)
self.assertNotIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
def test_twitter_social_auth_no_view_in_context(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_no_view_url, data=payload, status_code=400)
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
def test_twitter_social_auth_no_adapter(self):
# fake response for twitter call
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
'https://api.twitter.com/1.1/account/verify_credentials.json',
body=json.dumps(resp_body),
status=400,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_login_no_adapter_url, data=payload, status_code=400)
self.assertEqual(get_user_model().objects.all().count(), users_count)
@responses.activate
@override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True,
REST_SESSION_LOGIN=False,
ACCOUNT_EMAIL_CONFIRMATION_HMAC=False
)
def test_email_clash_with_existing_account(self):
resp_body = {
"id": "123123123123",
"first_name": "John",
"gender": "male",
"last_name": "Smith",
"link": "https://www.facebook.com/john.smith",
"locale": "en_US",
"name": "John Smith",
"timezone": 2,
"updated_time": "2014-08-13T10:14:38+0000",
"username": "john.smith",
"verified": True,
"email": self.EMAIL
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
# test empty payload
self.post(self.register_url, data={}, status_code=400)
# register user and send email confirmation
self.post(
self.register_url,
data=self.REGISTRATION_DATA,
status_code=201
)
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
# verify email
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
.emailconfirmation_set.order_by('-created')[0]
self.post(
self.verify_email_url,
data={"key": email_confirmation.key},
status_code=status.HTTP_200_OK
)
self._login()
self._logout()
# fb log in with already existing email
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=400)
@responses.activate
@override_settings(
REST_USE_JWT=True
)
def test_jwt(self):
resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}' # noqa
responses.add(
responses.GET,
self.graph_api_url,
body=resp_body,
status=200,
content_type='application/json'
)
users_count = get_user_model().objects.all().count()
payload = {
'access_token': 'abc123'
}
self.post(self.fb_login_url, data=payload, status_code=200)
self.assertIn('token', self.response.json.keys())
self.assertIn('user', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1)
@override_settings(ROOT_URLCONF="tests.urls")
class TestSocialConnectAuth(TestsMixin, TestCase):
USERNAME = 'person'
PASS = 'person'
EMAIL = "person1@world.com"
REGISTRATION_DATA = {
"username": USERNAME,
"password1": PASS,
"password2": PASS,
"email": EMAIL
}
def setUp(self):
self.init()
facebook_social_app = SocialApp.objects.create(
provider='facebook',
name='Facebook',
client_id='123123123',
secret='321321321',
)
twitter_social_app = SocialApp.objects.create(
provider='twitter',
name='Twitter',
client_id='11223344',
secret='55667788',
)
site = Site.objects.get_current()
facebook_social_app.sites.add(site)
twitter_social_app.sites.add(site)
self.graph_api_url = GRAPH_API_URL + '/me'
self.twitter_url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
@responses.activate
def test_social_connect_no_auth(self):
responses.add(
responses.GET,
self.graph_api_url,
body='',
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123'
}
self.post(self.fb_connect_url, data=payload, status_code=403)
self.post(self.tw_connect_url, data=payload, status_code=403)
@responses.activate
def test_social_connect(self):
# register user
self.post(
self.register_url,
data=self.REGISTRATION_DATA,
status_code=201
)
# Test Facebook
resp_body = {
"id": "123123123123",
"first_name": "John",
"gender": "male",
"last_name": "Smith",
"link": "https://www.facebook.com/john.smith",
"locale": "en_US",
"name": "John Smith",
"timezone": 2,
"updated_time": "2014-08-13T10:14:38+0000",
"username": "john.smith",
"verified": True
}
responses.add(
responses.GET,
self.graph_api_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123'
}
self.post(self.fb_connect_url, data=payload, status_code=200)
self.assertIn('key', self.response.json.keys())
# Test Twitter
resp_body = {
"id": "123123123123",
}
responses.add(
responses.GET,
self.twitter_url,
body=json.dumps(resp_body),
status=200,
content_type='application/json'
)
payload = {
'access_token': 'abc123',
'token_secret': '1111222233334444'
}
self.post(self.tw_connect_url, data=payload)
self.assertIn('key', self.response.json.keys())
# Check current social accounts
self.get(self.social_account_list_url)
self.assertEqual(len(self.response.json), 2)
self.assertEqual(self.response.json[0]['provider'], 'facebook')
self.assertEqual(self.response.json[1]['provider'], 'twitter')
facebook_social_account_id = self.response.json[0]['id']
# Try disconnecting accounts
self.incorrect_disconnect_url = reverse(
'social_account_disconnect', args=[999999999]
)
self.post(self.incorrect_disconnect_url, status_code=404)
self.disconnect_url = reverse(
'social_account_disconnect', args=[facebook_social_account_id]
)
self.post(self.disconnect_url, status_code=200)
# Check social accounts after disconnecting
self.get(self.social_account_list_url)
self.assertEqual(len(self.response.json), 1)
self.assertEqual(self.response.json[0]['provider'], 'twitter')

72
rest_auth/tests/urls.py Normal file
View File

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

View File

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

View File

@ -1,11 +1,29 @@
from django.utils.importlib import import_module from six import string_types
from importlib import import_module
def import_callable(path_or_callable): def import_callable(path_or_callable):
if hasattr(path_or_callable, '__call__'): if hasattr(path_or_callable, '__call__'):
return path_or_callable return path_or_callable
else: else:
assert isinstance(path_or_callable, (str, unicode)) assert isinstance(path_or_callable, string_types)
package, attr = path_or_callable.rsplit('.', 1) package, attr = path_or_callable.rsplit('.', 1)
return getattr(import_module(package), attr) return getattr(import_module(package), attr)
def default_create_token(token_model, user, serializer):
token, _ = token_model.objects.get_or_create(user=user)
return token
def jwt_encode(user):
try:
from rest_framework_jwt.settings import api_settings
except ImportError:
raise ImportError("djangorestframework_jwt needs to be installed")
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
return jwt_encode_handler(payload)

View File

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

View File

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

39
setup.cfg Normal file
View File

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

View File

@ -1,14 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
import os import os
from setuptools import setup, find_packages
here = os.path.dirname(os.path.abspath(__file__)) here = os.path.dirname(os.path.abspath(__file__))
f = open(os.path.join(here, 'README.rst')) f = open(os.path.join(here, 'README.rst'))
@ -18,7 +12,7 @@ f.close()
setup( setup(
name='django-rest-auth', name='django-rest-auth',
version='0.3.0', version='0.9.5',
author='Sumit Chachra', author='Sumit Chachra',
author_email='chachra@tivix.com', author_email='chachra@tivix.com',
url='http://github.com/Tivix/django-rest-auth', url='http://github.com/Tivix/django-rest-auth',
@ -28,12 +22,20 @@ setup(
keywords='django rest auth registration rest-framework django-registration api', keywords='django rest auth registration rest-framework django-registration api',
zip_safe=False, zip_safe=False,
install_requires=[ install_requires=[
'Django>=1.5.0', 'Django>=1.8.0',
'djangorestframework>=2.3.13', 'djangorestframework>=3.1.3',
'six>=1.9.0',
], ],
test_suite='rest_auth.runtests.runtests', extras_require={
'with_social': ['django-allauth>=0.25.0'],
},
tests_require=[
'responses>=0.5.0',
'django-allauth>=0.25.0',
'djangorestframework-jwt>=1.9.0',
],
test_suite='runtests.runtests',
include_package_data=True, include_package_data=True,
# cmdclass={},
classifiers=[ classifiers=[
'Framework :: Django', 'Framework :: Django',
'Intended Audience :: Developers', 'Intended Audience :: Developers',

View File

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