mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 20:40:14 +03:00
Merge 232b3ed4d4
into b519018125
This commit is contained in:
commit
984f8b86e9
|
@ -7,6 +7,7 @@ import base64
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework import exceptions, HTTP_HEADER_ENCODING
|
from rest_framework import exceptions, HTTP_HEADER_ENCODING
|
||||||
from rest_framework.compat import CsrfViewMiddleware
|
from rest_framework.compat import CsrfViewMiddleware
|
||||||
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
|
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
|
||||||
|
@ -174,6 +175,10 @@ class TokenAuthentication(BaseAuthentication):
|
||||||
if not token.user.is_active:
|
if not token.user.is_active:
|
||||||
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
raise exceptions.AuthenticationFailed('User inactive or deleted')
|
||||||
|
|
||||||
|
token_settings = api_settings.DEFAULT_TOKEN_EXPIRE
|
||||||
|
if token_settings['is_expired'] and token.check_for_expiration():
|
||||||
|
raise exceptions.AuthenticationFailed('Token has expired')
|
||||||
|
|
||||||
return (token.user, token)
|
return (token.user, token)
|
||||||
|
|
||||||
def authenticate_header(self, request):
|
def authenticate_header(self, request):
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import binascii
|
import binascii
|
||||||
import os
|
import os
|
||||||
|
import datetime
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
# Prior to Django 1.5, the AUTH_USER_MODEL setting does not exist.
|
# Prior to Django 1.5, the AUTH_USER_MODEL setting does not exist.
|
||||||
|
@ -36,5 +38,13 @@ class Token(models.Model):
|
||||||
def generate_key(self):
|
def generate_key(self):
|
||||||
return binascii.hexlify(os.urandom(20)).decode()
|
return binascii.hexlify(os.urandom(20)).decode()
|
||||||
|
|
||||||
|
def check_for_expiration(self):
|
||||||
|
token_settings = api_settings.DEFAULT_TOKEN_EXPIRE
|
||||||
|
if token_settings['is_expired']:
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
difference = datetime.timedelta(days=token_settings['expiration_time'])
|
||||||
|
return self.created < (now - difference)
|
||||||
|
return False
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.key
|
return self.key
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import datetime
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework import parsers
|
from rest_framework import parsers
|
||||||
|
@ -5,12 +6,14 @@ from rest_framework import renderers
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
class ObtainAuthToken(APIView):
|
class ObtainAuthToken(APIView):
|
||||||
throttle_classes = ()
|
throttle_classes = ()
|
||||||
permission_classes = ()
|
permission_classes = ()
|
||||||
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
|
parser_classes = (parsers.FormParser, parsers.MultiPartParser,
|
||||||
|
parsers.JSONParser,)
|
||||||
renderer_classes = (renderers.JSONRenderer,)
|
renderer_classes = (renderers.JSONRenderer,)
|
||||||
serializer_class = AuthTokenSerializer
|
serializer_class = AuthTokenSerializer
|
||||||
model = Token
|
model = Token
|
||||||
|
@ -18,8 +21,17 @@ class ObtainAuthToken(APIView):
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
serializer = self.serializer_class(data=request.DATA)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
token, created = Token.objects.get_or_create(user=serializer.object['user'])
|
token, created = Token.objects.get_or_create(
|
||||||
return Response({'token': token.key})
|
user=serializer.object['user'])
|
||||||
|
|
||||||
|
token_settings = api_settings.DEFAULT_TOKEN_EXPIRE
|
||||||
|
key = token.key
|
||||||
|
if not created and token_settings['is_expired']:
|
||||||
|
# update the created time of the token to keep it valid
|
||||||
|
key = token.generate_key() if token.check_for_expiration() else key
|
||||||
|
Token.objects.filter(key=token.key).update(
|
||||||
|
key=key, created=datetime.datetime.now())
|
||||||
|
return Response({'token': key})
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,11 @@ DEFAULTS = {
|
||||||
),
|
),
|
||||||
'DEFAULT_CONTENT_NEGOTIATION_CLASS':
|
'DEFAULT_CONTENT_NEGOTIATION_CLASS':
|
||||||
'rest_framework.negotiation.DefaultContentNegotiation',
|
'rest_framework.negotiation.DefaultContentNegotiation',
|
||||||
|
'DEFAULT_TOKEN_EXPIRE': {
|
||||||
|
'is_expired': False,
|
||||||
|
# in days
|
||||||
|
'expiration_time': 30
|
||||||
|
},
|
||||||
|
|
||||||
# Genric view behavior
|
# Genric view behavior
|
||||||
'DEFAULT_MODEL_SERIALIZER_CLASS':
|
'DEFAULT_MODEL_SERIALIZER_CLASS':
|
||||||
|
@ -139,7 +144,8 @@ IMPORT_STRINGS = (
|
||||||
'UNAUTHENTICATED_USER',
|
'UNAUTHENTICATED_USER',
|
||||||
'UNAUTHENTICATED_TOKEN',
|
'UNAUTHENTICATED_TOKEN',
|
||||||
'VIEW_NAME_FUNCTION',
|
'VIEW_NAME_FUNCTION',
|
||||||
'VIEW_DESCRIPTION_FUNCTION'
|
'VIEW_DESCRIPTION_FUNCTION',
|
||||||
|
'DEFAULT_TOKEN_EXPIRE'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ from rest_framework.compat import oauth2_provider, oauth2_provider_scope
|
||||||
from rest_framework.compat import oauth, oauth_provider
|
from rest_framework.compat import oauth, oauth_provider
|
||||||
from rest_framework.test import APIRequestFactory, APIClient
|
from rest_framework.test import APIRequestFactory, APIClient
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
import base64
|
import base64
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -231,6 +232,28 @@ class TokenAuthTests(TestCase):
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data['token'], self.key)
|
self.assertEqual(response.data['token'], self.key)
|
||||||
|
|
||||||
|
def test_token_expired(self):
|
||||||
|
""" Ensure token login view using expired token """
|
||||||
|
api_settings.DEFAULT_TOKEN_EXPIRE['is_expired'] = True
|
||||||
|
client = APIClient(enforce_csrf_checks=True)
|
||||||
|
self.token.created = self.token.created - datetime.timedelta(days=40)
|
||||||
|
self.token.save()
|
||||||
|
response = client.post('/token/', {'example': 'example'},
|
||||||
|
HTTP_AUTHORIZATION='Token %s' % self.token.key,
|
||||||
|
format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
def test_token_expire_after_renewal(self):
|
||||||
|
""" Ensure token renewes on next login after expiration """
|
||||||
|
api_settings.DEFAULT_TOKEN_EXPIRE['is_expired'] = True
|
||||||
|
self.token.created = self.token.created - datetime.timedelta(days=40)
|
||||||
|
self.token.save()
|
||||||
|
client = APIClient(enforce_csrf_checks=True)
|
||||||
|
response = client.post('/auth-token/', {'username': self.username,
|
||||||
|
'password': self.password})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertNotEqual(response.data['token'], self.key)
|
||||||
|
|
||||||
|
|
||||||
class IncorrectCredentialsTests(TestCase):
|
class IncorrectCredentialsTests(TestCase):
|
||||||
def test_incorrect_credentials(self):
|
def test_incorrect_credentials(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user