mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-07-07 20:53:04 +03:00
Merge ad61169dfa
into beb073f35f
This commit is contained in:
commit
c5bc4255d7
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -55,3 +55,8 @@ db.sqlite3
|
||||||
|
|
||||||
# IntelliJ IDE files
|
# IntelliJ IDE files
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Virtual Environments
|
||||||
|
.vagrant/
|
||||||
|
cookbooks/
|
||||||
|
tmp/
|
||||||
|
|
|
@ -4,77 +4,139 @@ API endpoints
|
||||||
Basic
|
Basic
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- /rest-auth/login/ (POST)
|
Typically, auth data is sent in the body, with a header ``Content-Type: application/x-www-form-urlencoded``
|
||||||
|
|
||||||
- username
|
/rest-auth/login/ (POST)
|
||||||
- email
|
************************
|
||||||
- password
|
**Request (standalone):**
|
||||||
|
|
||||||
Returns Token key
|
- ``username`` or ``email`` (email will be used to lookup username)
|
||||||
|
- ``password`` (required)
|
||||||
|
|
||||||
- /rest-auth/logout/ (POST)
|
**Request (using django-allauth):**
|
||||||
|
|
||||||
.. 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
|
- ``username`` (required when ``ACCOUNT_AUTHENTICATION_METHOD = 'username'`` or ``'username_email'``)
|
||||||
|
- ``email`` (required when ``ACCOUNT_AUTHENTICATION_METHOD = 'email'`` or ``'username_email'``)
|
||||||
|
- ``password`` (required)
|
||||||
|
|
||||||
- /rest-auth/password/reset/ (POST)
|
**Response:**
|
||||||
|
|
||||||
- email
|
- ``token``
|
||||||
|
- ``user`` (when using django-rest-framework-jwt or django-rest-knox)
|
||||||
|
|
||||||
- /rest-auth/password/reset/confirm/ (POST)
|
/rest-auth/logout/ (POST)
|
||||||
|
*************************
|
||||||
|
|
||||||
|
**Request (standalone):**
|
||||||
|
|
||||||
- uid
|
- No values expected
|
||||||
- token
|
|
||||||
- new_password1
|
|
||||||
- new_password2
|
|
||||||
|
|
||||||
.. note:: uid and token are sent in email after calling /rest-auth/password/reset/
|
**Request (using django-rest-knox):**
|
||||||
|
|
||||||
- /rest-auth/password/change/ (POST)
|
- ``Authorization: Token TOKEN`` *(Header)*
|
||||||
|
|
||||||
- new_password1
|
**Response:**
|
||||||
- new_password2
|
|
||||||
- old_password
|
|
||||||
|
|
||||||
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
|
- No values
|
||||||
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
|
|
||||||
|
|
||||||
- /rest-auth/user/ (GET, PUT, PATCH)
|
.. 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
|
||||||
|
|
||||||
- username
|
/rest-auth/logoutall/ (POST)
|
||||||
- first_name
|
****************************
|
||||||
- last_name
|
|
||||||
|
|
||||||
Returns pk, username, email, first_name, last_name
|
This endpoint deletes all Knox tokens, and will only be loaded when `REST_USE_KNOX = True`.
|
||||||
|
|
||||||
|
| **Request (using django-rest-knox):**
|
||||||
|
|
||||||
|
- `Authorization`: `Token TOKEN` (Header)
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
- No values
|
||||||
|
|
||||||
|
.. 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)
|
||||||
|
*********************************
|
||||||
|
|
||||||
|
- email
|
||||||
|
|
||||||
|
/rest-auth/password/reset/confirm/ (POST)
|
||||||
|
*****************************************
|
||||||
|
|
||||||
|
- uid
|
||||||
|
- token
|
||||||
|
- new_password1
|
||||||
|
- new_password2
|
||||||
|
|
||||||
|
.. note:: uid and token are sent in email after calling /rest-auth/password/reset/
|
||||||
|
|
||||||
|
/rest-auth/password/change/ (POST)
|
||||||
|
**********************************
|
||||||
|
- new_password1
|
||||||
|
- new_password2
|
||||||
|
- old_password
|
||||||
|
|
||||||
|
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password.
|
||||||
|
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
|
||||||
|
|
||||||
|
/rest-auth/user/ (GET, PUT, PATCH)
|
||||||
|
**********************************
|
||||||
|
- username
|
||||||
|
- first_name
|
||||||
|
- last_name
|
||||||
|
|
||||||
|
Returns pk, username, email, first_name, last_name
|
||||||
|
|
||||||
|
|
||||||
Registration
|
Registration
|
||||||
------------
|
------------
|
||||||
|
|
||||||
- /rest-auth/registration/ (POST)
|
/rest-auth/registration/ (POST)
|
||||||
|
*******************************
|
||||||
|
|
||||||
- username
|
**Request (using django-allauth):**
|
||||||
- password1
|
|
||||||
- password2
|
|
||||||
- email
|
|
||||||
|
|
||||||
- /rest-auth/registration/verify-email/ (POST)
|
- ``username`` (required when ``ACCOUNT_AUTHENTICATION_METHOD = 'username'`` or ``'username_email'``, or when ``ACCOUNT_USERNAME_REQUIRED = True``)
|
||||||
|
- ``email`` (required when ``ACCOUNT_AUTHENTICATION_METHOD = 'email'`` or ``'username_email'``, or when ``ACCOUNT_EMAIL_REQUIRED = True``)
|
||||||
|
- ``password1`` (required)
|
||||||
|
- ``password2`` (required)
|
||||||
|
|
||||||
- key
|
**Response (using django-allauth):**
|
||||||
|
|
||||||
|
- No values
|
||||||
|
|
||||||
|
**Response (using django-allauth and django-rest-knox)**
|
||||||
|
|
||||||
|
- ``token``
|
||||||
|
- ``user``
|
||||||
|
|
||||||
|
/rest-auth/registration/verify-email/ (POST)
|
||||||
|
********************************************
|
||||||
|
|
||||||
|
**Request (using django-allauth):**
|
||||||
|
|
||||||
|
- ``key``
|
||||||
|
|
||||||
|
**Response (using django-allauth):**
|
||||||
|
|
||||||
|
- No values
|
||||||
|
|
||||||
Social Media Authentication
|
Social Media Authentication
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Basing on example from installation section :doc:`Installation </installation>`
|
Based on the example from the installation section :doc:`Installation </installation>`
|
||||||
|
|
||||||
- /rest-auth/facebook/ (POST)
|
/rest-auth/facebook/ (POST)
|
||||||
|
***************************
|
||||||
|
|
||||||
- access_token
|
- ``access_token``
|
||||||
- code
|
- ``code``
|
||||||
|
|
||||||
.. note:: ``access_token`` OR ``code`` can be used as standalone arguments, see https://github.com/Tivix/django-rest-auth/blob/master/rest_auth/registration/views.py
|
.. note:: ``access_token`` OR ``code`` can be used as standalone arguments, see https://github.com/Tivix/django-rest-auth/blob/master/rest_auth/registration/views.py
|
||||||
|
|
||||||
- /rest-auth/twitter/ (POST)
|
/rest-auth/twitter/ (POST)
|
||||||
|
**************************
|
||||||
|
|
||||||
- access_token
|
- ``access_token``
|
||||||
- token_secret
|
- ``token_secret``
|
||||||
|
|
|
@ -12,6 +12,8 @@ Configuration
|
||||||
|
|
||||||
- JWT_SERIALIZER - (Using REST_USE_JWT=True) response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.JWTSerializer``
|
- JWT_SERIALIZER - (Using REST_USE_JWT=True) response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.JWTSerializer``
|
||||||
|
|
||||||
|
- KNOX_SERIALIZER - (Using REST_USE_KNOX=True) response for successful authentication in ``rest_auth.views.LoginView`` and successful registration in ``rest_auth.registration.views.RegisterView`` (using ``django-allauth`` and ``ACCOUNT_EMAIL_VERIFICATION = 'optional' or 'none'``), default value ``rest_auth.serializers.KnoxSerializer``
|
||||||
|
|
||||||
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer``
|
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer``
|
||||||
|
|
||||||
- PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer``
|
- PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer``
|
||||||
|
@ -48,6 +50,8 @@ Configuration
|
||||||
|
|
||||||
- **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)
|
- **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)
|
||||||
|
|
||||||
|
- **REST_USE_KNOX** - Use Knox token authentication instead of the built-in Django-Rest-Framework token authentication. Knox makes some significant security improvements, and supports multiple tokens per user. https://github.com/James1345/django-rest-knox/ must be installed. Not compatible with ``REST_USE_JWT`` (default: False)
|
||||||
|
|
||||||
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
||||||
|
|
||||||
- **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change
|
- **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change (default: False)
|
||||||
|
|
|
@ -153,7 +153,7 @@ JWT Support (optional)
|
||||||
|
|
||||||
By default, ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, you need to install the following:
|
By default, ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, you need to install the following:
|
||||||
|
|
||||||
1. Install ``django-rest-framework-jwt`` http://getblimp.github.io/django-rest-framework-jwt/ . Right now this is the only supported JWT library.
|
1. Install ``django-rest-framework-jwt`` http://getblimp.github.io/django-rest-framework-jwt/ .
|
||||||
|
|
||||||
2. Add the following to your settings
|
2. Add the following to your settings
|
||||||
|
|
||||||
|
@ -161,3 +161,19 @@ By default, ``django-rest-auth`` uses Django's Token-based authentication. If yo
|
||||||
|
|
||||||
REST_USE_JWT = True
|
REST_USE_JWT = True
|
||||||
|
|
||||||
|
|
||||||
|
Knox (optional)
|
||||||
|
---------------
|
||||||
|
By default, ``django-rest-auth`` uses Django's Token-based authentication. ``django-rest-knox`` provides more secure token authentication with additional features, including multiple tokens per user.
|
||||||
|
|
||||||
|
Knox and JWT cannot currently be used simultaneously.
|
||||||
|
|
||||||
|
1. Install ``django-rest-knox`` https://james1345.github.io/django-rest-knox/installation/ .
|
||||||
|
|
||||||
|
2. Configure ``django-rest-knox`` https://james1345.github.io/django-rest-knox/settings/ . ``REST_KNOX['USER_SERIALIZER']`` will not be used.
|
||||||
|
|
||||||
|
3. Add the following to your settings
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
REST_USE_KNOX = True
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.conf import settings
|
||||||
|
|
||||||
from rest_auth.serializers import (
|
from rest_auth.serializers import (
|
||||||
TokenSerializer as DefaultTokenSerializer,
|
TokenSerializer as DefaultTokenSerializer,
|
||||||
|
KnoxSerializer as DefaultKnoxSerializer,
|
||||||
JWTSerializer as DefaultJWTSerializer,
|
JWTSerializer as DefaultJWTSerializer,
|
||||||
UserDetailsSerializer as DefaultUserDetailsSerializer,
|
UserDetailsSerializer as DefaultUserDetailsSerializer,
|
||||||
LoginSerializer as DefaultLoginSerializer,
|
LoginSerializer as DefaultLoginSerializer,
|
||||||
|
@ -21,6 +22,9 @@ TokenSerializer = import_callable(
|
||||||
JWTSerializer = import_callable(
|
JWTSerializer = import_callable(
|
||||||
serializers.get('JWT_SERIALIZER', DefaultJWTSerializer))
|
serializers.get('JWT_SERIALIZER', DefaultJWTSerializer))
|
||||||
|
|
||||||
|
KnoxSerializer = import_callable(
|
||||||
|
serializers.get('KNOX_TOKEN_SERIALIZER', DefaultKnoxSerializer))
|
||||||
|
|
||||||
UserDetailsSerializer = import_callable(
|
UserDetailsSerializer = import_callable(
|
||||||
serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer)
|
serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer)
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,11 +16,12 @@ from allauth.account import app_settings as allauth_settings
|
||||||
|
|
||||||
from rest_auth.app_settings import (TokenSerializer,
|
from rest_auth.app_settings import (TokenSerializer,
|
||||||
JWTSerializer,
|
JWTSerializer,
|
||||||
|
KnoxSerializer,
|
||||||
create_token)
|
create_token)
|
||||||
from rest_auth.models import TokenModel
|
from rest_auth.models import TokenModel
|
||||||
from rest_auth.registration.serializers import (SocialLoginSerializer,
|
from rest_auth.registration.serializers import (SocialLoginSerializer,
|
||||||
VerifyEmailSerializer)
|
VerifyEmailSerializer)
|
||||||
from rest_auth.utils import jwt_encode
|
from rest_auth.utils import create_knox_token, jwt_encode
|
||||||
from rest_auth.views import LoginView
|
from rest_auth.views import LoginView
|
||||||
from .app_settings import RegisterSerializer
|
from .app_settings import RegisterSerializer
|
||||||
|
|
||||||
|
@ -49,8 +50,14 @@ class RegisterView(CreateAPIView):
|
||||||
'token': self.token
|
'token': self.token
|
||||||
}
|
}
|
||||||
return JWTSerializer(data).data
|
return JWTSerializer(data).data
|
||||||
|
elif getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
data = {
|
||||||
|
'user': user,
|
||||||
|
'token': self.token
|
||||||
|
}
|
||||||
|
return KnoxSerializer(data).data
|
||||||
else:
|
else:
|
||||||
return TokenSerializer(user.auth_token).data
|
return TokenSerializer(self.token).data
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
@ -65,9 +72,11 @@ class RegisterView(CreateAPIView):
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
user = serializer.save(self.request)
|
user = serializer.save(self.request)
|
||||||
if getattr(settings, 'REST_USE_JWT', False):
|
if getattr(settings, 'REST_USE_JWT', False):
|
||||||
self.token = jwt_encode(user)
|
self.token = jwt_encode(user)
|
||||||
|
elif getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
self.token = create_knox_token(user)
|
||||||
else:
|
else:
|
||||||
create_token(self.token_model, user, serializer)
|
self.token = create_token(self.token_model, user, serializer)
|
||||||
|
|
||||||
complete_signup(self.request._request, user,
|
complete_signup(self.request._request, user,
|
||||||
allauth_settings.EMAIL_VERIFICATION,
|
allauth_settings.EMAIL_VERIFICATION,
|
||||||
|
|
|
@ -114,7 +114,6 @@ class TokenSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for Token model.
|
Serializer for Token model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TokenModel
|
model = TokenModel
|
||||||
fields = ('key',)
|
fields = ('key',)
|
||||||
|
@ -133,7 +132,7 @@ class UserDetailsSerializer(serializers.ModelSerializer):
|
||||||
# Required to allow using custom USER_DETAILS_SERIALIZER in
|
# Required to allow using custom USER_DETAILS_SERIALIZER in
|
||||||
# JWTSerializer. Defining it here to avoid circular imports
|
# JWTSerializer. Defining it here to avoid circular imports
|
||||||
rest_auth_serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
|
rest_auth_serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
|
||||||
JWTUserDetailsSerializer = import_callable(
|
CustomUserDetailsSerializer = import_callable(
|
||||||
rest_auth_serializers.get('USER_DETAILS_SERIALIZER', UserDetailsSerializer)
|
rest_auth_serializers.get('USER_DETAILS_SERIALIZER', UserDetailsSerializer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -143,7 +142,15 @@ class JWTSerializer(serializers.Serializer):
|
||||||
Serializer for JWT authentication.
|
Serializer for JWT authentication.
|
||||||
"""
|
"""
|
||||||
token = serializers.CharField()
|
token = serializers.CharField()
|
||||||
user = JWTUserDetailsSerializer()
|
user = CustomUserDetailsSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
class KnoxSerializer(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
Serializer for Knox authentication.
|
||||||
|
"""
|
||||||
|
token = serializers.CharField()
|
||||||
|
user = CustomUserDetailsSerializer()
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetSerializer(serializers.Serializer):
|
class PasswordResetSerializer(serializers.Serializer):
|
||||||
|
|
|
@ -2,3 +2,4 @@ django-allauth>=0.19.1
|
||||||
responses>=0.3.0
|
responses>=0.3.0
|
||||||
flake8==2.4.0
|
flake8==2.4.0
|
||||||
djangorestframework-jwt>=1.7.2
|
djangorestframework-jwt>=1.7.2
|
||||||
|
django-rest-knox>=2.3.0
|
||||||
|
|
|
@ -66,6 +66,15 @@ REST_FRAMEWORK = {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REST_FRAMEWORK_KNOX = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
'knox.auth.TokenAuthentication',
|
||||||
|
),
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
@ -88,7 +97,9 @@ INSTALLED_APPS = [
|
||||||
'rest_auth',
|
'rest_auth',
|
||||||
'rest_auth.registration',
|
'rest_auth.registration',
|
||||||
|
|
||||||
'rest_framework_jwt'
|
'rest_framework_jwt',
|
||||||
|
|
||||||
|
'knox'
|
||||||
]
|
]
|
||||||
|
|
||||||
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.six.moves import reload_module
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import views as drf_views
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from allauth.account import app_settings as account_app_settings
|
from allauth.account import app_settings as account_app_settings
|
||||||
|
from knox.models import AuthToken
|
||||||
|
|
||||||
from .test_base import BaseAPITestCase
|
from .test_base import BaseAPITestCase
|
||||||
|
from .settings import REST_FRAMEWORK_KNOX
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF="tests.urls")
|
@override_settings(ROOT_URLCONF="tests.urls")
|
||||||
|
@ -148,6 +158,24 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
self.assertEqual('token' in self.response.json.keys(), True)
|
self.assertEqual('token' in self.response.json.keys(), True)
|
||||||
self.token = self.response.json['token']
|
self.token = self.response.json['token']
|
||||||
|
|
||||||
|
@override_settings(REST_USE_KNOX=True, REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication')},)
|
||||||
|
def test_login_knox(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']
|
||||||
|
self.assertEqual('user' in self.response.json.keys(), True)
|
||||||
|
self.user = self.response.json['user']
|
||||||
|
|
||||||
|
self.post(self.login_url, data=payload, status_code=200)
|
||||||
|
self.assertNotEqual(self.token, self.response.json['token'])
|
||||||
|
|
||||||
def test_login_by_email(self):
|
def test_login_by_email(self):
|
||||||
# starting test without allauth app
|
# starting test without allauth app
|
||||||
settings.INSTALLED_APPS.remove('allauth')
|
settings.INSTALLED_APPS.remove('allauth')
|
||||||
|
@ -382,6 +410,21 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
user = get_user_model().objects.get(pk=user.pk)
|
user = get_user_model().objects.get(pk=user.pk)
|
||||||
self.assertEqual(user.email, self.response.json['email'])
|
self.assertEqual(user.email, self.response.json['email'])
|
||||||
|
|
||||||
|
@override_settings(REST_USE_KNOX=True, REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication')},)
|
||||||
|
def test_user_details_using_knox(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):
|
def test_registration(self):
|
||||||
user_count = get_user_model().objects.all().count()
|
user_count = get_user_model().objects.all().count()
|
||||||
|
|
||||||
|
@ -411,6 +454,21 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
self._login()
|
self._login()
|
||||||
self._logout()
|
self._logout()
|
||||||
|
|
||||||
|
@override_settings(REST_USE_KNOX=True, REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication')},)
|
||||||
|
def test_registration_with_knox(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.assertIn('user', result.data)
|
||||||
|
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
|
||||||
|
|
||||||
|
self._login()
|
||||||
|
self._logout()
|
||||||
|
|
||||||
def test_registration_with_invalid_password(self):
|
def test_registration_with_invalid_password(self):
|
||||||
data = self.REGISTRATION_DATA.copy()
|
data = self.REGISTRATION_DATA.copy()
|
||||||
data['password2'] = 'foobar'
|
data['password2'] = 'foobar'
|
||||||
|
@ -493,3 +551,43 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
|
|
||||||
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
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)
|
self.get(self.logout_url, status_code=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||||
|
|
||||||
|
# @override_settings(REST_USE_KNOX=True, REST_FRAMEWORK = {
|
||||||
|
# 'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication')},)
|
||||||
|
def test_logout_knox(self):
|
||||||
|
with override_settings(REST_USE_KNOX=True, REST_FRAMEWORK=REST_FRAMEWORK_KNOX):
|
||||||
|
reload_module(drf_views)
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
|
||||||
|
self.client = APIClient()
|
||||||
|
|
||||||
|
response = self.client.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % response.data['token']))
|
||||||
|
self.client.post(self.logout_url, status_code=status.HTTP_200_OK)
|
||||||
|
self.assertEqual(AuthToken.objects.count(), 0)
|
||||||
|
reload_module(drf_views)
|
||||||
|
|
||||||
|
@override_settings(REST_USE_KNOX=True, REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication')},)
|
||||||
|
def test_logout_all_knox(self):
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
|
||||||
|
self.logout_all_url = reverse('rest_logout_all')
|
||||||
|
|
||||||
|
self.client = APIClient()
|
||||||
|
|
||||||
|
self.client.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
response = self.client.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % response.data['token']))
|
||||||
|
self.client.post(self.logout_all_url, status_code=status.HTTP_200_OK)
|
||||||
|
self.assertEqual(AuthToken.objects.count(), 0)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
|
|
||||||
from rest_auth.urls import urlpatterns
|
from rest_auth.urls import urlpatterns
|
||||||
|
from rest_auth.views import LogoutAllView
|
||||||
from rest_auth.registration.views import SocialLoginView
|
from rest_auth.registration.views import SocialLoginView
|
||||||
from rest_auth.social_serializers import TwitterLoginSerializer
|
from rest_auth.social_serializers import TwitterLoginSerializer
|
||||||
|
|
||||||
|
@ -49,5 +50,6 @@ urlpatterns += [
|
||||||
url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_login'),
|
url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_login'),
|
||||||
url(r'^social-login/twitter-no-view/$', twitter_login_view, name='tw_login_no_view'),
|
url(r'^social-login/twitter-no-view/$', twitter_login_view, name='tw_login_no_view'),
|
||||||
url(r'^social-login/twitter-no-adapter/$', TwitterLoginNoAdapter.as_view(), name='tw_login_no_adapter'),
|
url(r'^social-login/twitter-no-adapter/$', TwitterLoginNoAdapter.as_view(), name='tw_login_no_adapter'),
|
||||||
url(r'^accounts/', include('allauth.socialaccount.urls'))
|
url(r'^accounts/', include('allauth.socialaccount.urls')),
|
||||||
|
url(r'^logoutall/$', LogoutAllView.as_view(), name='rest_logout_all'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from rest_auth.views import (
|
from rest_auth.views import (
|
||||||
LoginView, LogoutView, UserDetailsView, PasswordChangeView,
|
LoginView, LogoutView, LogoutAllView, UserDetailsView,
|
||||||
PasswordResetView, PasswordResetConfirmView
|
PasswordChangeView, PasswordResetView, PasswordResetConfirmView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -18,3 +19,8 @@ urlpatterns = [
|
||||||
url(r'^password/change/$', PasswordChangeView.as_view(),
|
url(r'^password/change/$', PasswordChangeView.as_view(),
|
||||||
name='rest_password_change'),
|
name='rest_password_change'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
urlpatterns.append(
|
||||||
|
url(r'^logoutall/$', LogoutAllView.as_view(), name='rest_logout_all')
|
||||||
|
)
|
||||||
|
|
|
@ -16,6 +16,15 @@ def default_create_token(token_model, user, serializer):
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
def create_knox_token(user):
|
||||||
|
try:
|
||||||
|
from knox.models import AuthToken
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError("django-rest-knox needs to be installed")
|
||||||
|
token = AuthToken.objects.create(user=user)
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
def jwt_encode(user):
|
def jwt_encode(user):
|
||||||
try:
|
try:
|
||||||
from rest_framework_jwt.settings import api_settings
|
from rest_framework_jwt.settings import api_settings
|
||||||
|
|
|
@ -16,12 +16,19 @@ from rest_framework.generics import GenericAPIView, RetrieveUpdateAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
|
|
||||||
from .app_settings import (
|
from .app_settings import (
|
||||||
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
TokenSerializer, KnoxSerializer, UserDetailsSerializer,
|
||||||
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
LoginSerializer, PasswordResetSerializer,
|
||||||
PasswordChangeSerializer, JWTSerializer, create_token
|
PasswordResetConfirmSerializer, PasswordChangeSerializer,
|
||||||
|
JWTSerializer, create_token
|
||||||
)
|
)
|
||||||
from .models import TokenModel
|
from .models import TokenModel
|
||||||
from .utils import jwt_encode
|
from .utils import create_knox_token, jwt_encode
|
||||||
|
|
||||||
|
if getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
try:
|
||||||
|
from knox.auth import TokenAuthentication as KnoxTokenAuthentication
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError("Install django-rest-knox to use REST_USE_KNOX = True")
|
||||||
|
|
||||||
sensitive_post_parameters_m = method_decorator(
|
sensitive_post_parameters_m = method_decorator(
|
||||||
sensitive_post_parameters(
|
sensitive_post_parameters(
|
||||||
|
@ -54,6 +61,8 @@ class LoginView(GenericAPIView):
|
||||||
def get_response_serializer(self):
|
def get_response_serializer(self):
|
||||||
if getattr(settings, 'REST_USE_JWT', False):
|
if getattr(settings, 'REST_USE_JWT', False):
|
||||||
response_serializer = JWTSerializer
|
response_serializer = JWTSerializer
|
||||||
|
elif getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
response_serializer = KnoxSerializer
|
||||||
else:
|
else:
|
||||||
response_serializer = TokenSerializer
|
response_serializer = TokenSerializer
|
||||||
return response_serializer
|
return response_serializer
|
||||||
|
@ -63,6 +72,8 @@ class LoginView(GenericAPIView):
|
||||||
|
|
||||||
if getattr(settings, 'REST_USE_JWT', False):
|
if getattr(settings, 'REST_USE_JWT', False):
|
||||||
self.token = jwt_encode(self.user)
|
self.token = jwt_encode(self.user)
|
||||||
|
elif getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
self.token = create_knox_token(self.user)
|
||||||
else:
|
else:
|
||||||
self.token = create_token(self.token_model, self.user,
|
self.token = create_token(self.token_model, self.user,
|
||||||
self.serializer)
|
self.serializer)
|
||||||
|
@ -80,6 +91,13 @@ class LoginView(GenericAPIView):
|
||||||
}
|
}
|
||||||
serializer = serializer_class(instance=data,
|
serializer = serializer_class(instance=data,
|
||||||
context={'request': self.request})
|
context={'request': self.request})
|
||||||
|
elif getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
data = {
|
||||||
|
'user': self.user,
|
||||||
|
'token': self.token
|
||||||
|
}
|
||||||
|
serializer = serializer_class(instance=data,
|
||||||
|
context={'request': self.request})
|
||||||
else:
|
else:
|
||||||
serializer = serializer_class(instance=self.token,
|
serializer = serializer_class(instance=self.token,
|
||||||
context={'request': self.request})
|
context={'request': self.request})
|
||||||
|
@ -102,7 +120,11 @@ class LogoutView(APIView):
|
||||||
|
|
||||||
Accepts/Returns nothing.
|
Accepts/Returns nothing.
|
||||||
"""
|
"""
|
||||||
permission_classes = (AllowAny,)
|
if getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
authentication_classes = (KnoxTokenAuthentication,)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
else:
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if getattr(settings, 'ACCOUNT_LOGOUT_ON_GET', False):
|
if getattr(settings, 'ACCOUNT_LOGOUT_ON_GET', False):
|
||||||
|
@ -117,7 +139,44 @@ class LogoutView(APIView):
|
||||||
|
|
||||||
def logout(self, request):
|
def logout(self, request):
|
||||||
try:
|
try:
|
||||||
request.user.auth_token.delete()
|
if getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
request._auth.delete()
|
||||||
|
else:
|
||||||
|
request.user.auth_token.delete()
|
||||||
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
django_logout(request)
|
||||||
|
|
||||||
|
return Response({"detail": _("Successfully logged out.")},
|
||||||
|
status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoutAllView(APIView):
|
||||||
|
"""
|
||||||
|
Calls Django logout method and deletes all the Knox tokens
|
||||||
|
assigned to the current User object.
|
||||||
|
|
||||||
|
Accepts/Returns nothing.
|
||||||
|
"""
|
||||||
|
if getattr(settings, 'REST_USE_KNOX', False):
|
||||||
|
authentication_classes = (KnoxTokenAuthentication,)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if getattr(settings, 'ACCOUNT_LOGOUT_ON_GET', False):
|
||||||
|
response = self.logout_all(request)
|
||||||
|
else:
|
||||||
|
response = self.http_method_not_allowed(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return self.finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
return self.logout_all(request)
|
||||||
|
|
||||||
|
def logout_all(self, request):
|
||||||
|
try:
|
||||||
|
request.user.auth_token_set.all().delete()
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -38,6 +38,8 @@ setup(
|
||||||
tests_require=[
|
tests_require=[
|
||||||
'responses>=0.5.0',
|
'responses>=0.5.0',
|
||||||
'django-allauth>=0.25.0',
|
'django-allauth>=0.25.0',
|
||||||
|
'djangorestframework-jwt>=1.9.0',
|
||||||
|
'django-rest-knox>=2.3.0'
|
||||||
],
|
],
|
||||||
test_suite='runtests.runtests',
|
test_suite='runtests.runtests',
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user