This commit is contained in:
Daniel Stanton 2017-01-26 19:11:08 +00:00 committed by GitHub
commit c5bc4255d7
15 changed files with 354 additions and 59 deletions

5
.gitignore vendored
View File

@ -55,3 +55,8 @@ db.sqlite3
# IntelliJ IDE files # IntelliJ IDE files
.idea .idea
# Virtual Environments
.vagrant/
cookbooks/
tmp/

View File

@ -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)
*************************
- uid **Request (standalone):**
- token
- new_password1
- new_password2
.. note:: uid and token are sent in email after calling /rest-auth/password/reset/ - No values expected
- /rest-auth/password/change/ (POST) **Request (using django-rest-knox):**
- new_password1 - ``Authorization: Token TOKEN`` *(Header)*
- new_password2
- old_password
.. note:: ``OLD_PASSWORD_FIELD_ENABLED = True`` to use old_password. **Response:**
.. note:: ``LOGOUT_ON_PASSWORD_CHANGE = False`` to keep the user logged in after password change
- /rest-auth/user/ (GET, PUT, PATCH) - No values
- username .. 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
- first_name
- last_name
Returns pk, username, email, first_name, last_name /rest-auth/logoutall/ (POST)
****************************
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``

View File

@ -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)

View File

@ -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

View File

@ -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)
) )

View File

@ -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)
@ -66,8 +73,10 @@ class RegisterView(CreateAPIView):
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,

View File

@ -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):

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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'),
] ]

View File

@ -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')
)

View File

@ -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

View File

@ -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,6 +120,10 @@ class LogoutView(APIView):
Accepts/Returns nothing. Accepts/Returns nothing.
""" """
if getattr(settings, 'REST_USE_KNOX', False):
authentication_classes = (KnoxTokenAuthentication,)
permission_classes = (IsAuthenticated,)
else:
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -117,6 +139,9 @@ class LogoutView(APIView):
def logout(self, request): def logout(self, request):
try: try:
if getattr(settings, 'REST_USE_KNOX', False):
request._auth.delete()
else:
request.user.auth_token.delete() request.user.auth_token.delete()
except (AttributeError, ObjectDoesNotExist): except (AttributeError, ObjectDoesNotExist):
pass pass
@ -127,6 +152,40 @@ class LogoutView(APIView):
status=status.HTTP_200_OK) 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):
pass
django_logout(request)
return Response({"detail": _("Successfully logged out.")},
status=status.HTTP_200_OK)
class UserDetailsView(RetrieveUpdateAPIView): class UserDetailsView(RetrieveUpdateAPIView):
""" """
Reads and updates UserModel fields Reads and updates UserModel fields

View File

@ -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,