11import unittest
22from calendar import timegm
33from datetime import datetime , timedelta
4+ import time
45
56from django import get_version
67from django .test import TestCase
78from django .test .utils import override_settings
89from django .conf .urls import patterns
9- from freezegun import freeze_time
1010from rest_framework import status
1111from rest_framework .test import APIClient
1212
1313from rest_framework_jwt import utils
1414from rest_framework_jwt .compat import get_user_model
1515from rest_framework_jwt .settings import api_settings , DEFAULTS
1616
17+ from cryptography .hazmat .backends import default_backend
18+ from cryptography .hazmat .primitives .asymmetric import rsa
19+
1720from . import utils as test_utils
1821
1922User = get_user_model ()
@@ -255,6 +258,9 @@ class TokenTestCase(BaseTestCase):
255258 Handlers for getting tokens from the API, or creating arbitrary ones.
256259 """
257260
261+ def setUp (self ):
262+ super (TokenTestCase , self ).setUp ()
263+
258264 def get_token (self ):
259265 client = APIClient (enforce_csrf_checks = True )
260266 response = client .post ('/auth-token/' , self .data , format = 'json' )
@@ -272,22 +278,20 @@ def create_token(self, user, exp=None, orig_iat=None):
272278 return token
273279
274280
275- class VerifyJSONWebTokenTests (TokenTestCase ):
281+ class VerifyJSONWebTokenTestsSymmetric (TokenTestCase ):
276282
277283 def test_verify_jwt (self ):
278284 """
279285 Test that a valid, non-expired token will return a 200 response
280286 and itself when passed to the validation endpoint.
281287 """
282288 client = APIClient (enforce_csrf_checks = True )
289+ orig_token = self .get_token ()
283290
284- with freeze_time ('2015-01-01 00:00:01' ):
285- orig_token = self .get_token ()
291+ # Now try to get a refreshed token
292+ response = client .post ('/auth-token-verify/' , {'token' : orig_token },
293+ format = 'json' )
286294
287- with freeze_time ('2015-01-01 00:00:10' ):
288- # Now try to get a refreshed token
289- response = client .post ('/auth-token-verify/' , {'token' : orig_token },
290- format = 'json' )
291295 self .assertEqual (response .status_code , status .HTTP_200_OK )
292296
293297 self .assertEqual (response .data ['token' ], orig_token )
@@ -345,6 +349,102 @@ def test_verify_jwt_fails_with_missing_user(self):
345349 "User doesn't exist" )
346350
347351
352+ class VerifyJSONWebTokenTestsAsymmetric (TokenTestCase ):
353+
354+ def setUp (self ):
355+
356+ super (VerifyJSONWebTokenTestsAsymmetric , self ).setUp ()
357+
358+ private_key = rsa .generate_private_key (public_exponent = 65537 ,
359+ key_size = 2048 ,
360+ backend = default_backend ())
361+ public_key = private_key .public_key ()
362+
363+ api_settings .JWT_PRIVATE_KEY = private_key
364+ api_settings .JWT_PUBLIC_KEY = public_key
365+ api_settings .JWT_ALGORITHM = 'RS512'
366+
367+ def test_verify_jwt_with_pub_pvt_key (self ):
368+ """
369+ Test that a token can be signed with asymmetrics keys
370+ """
371+ client = APIClient (enforce_csrf_checks = True )
372+
373+ orig_token = self .get_token ()
374+
375+ # Now try to get a refreshed token
376+ response = client .post ('/auth-token-verify/' , {'token' : orig_token },
377+ format = 'json' )
378+
379+ self .assertEqual (response .status_code , status .HTTP_200_OK )
380+ self .assertEqual (response .data ['token' ], orig_token )
381+
382+ def test_verify_jwt_fails_with_expired_token (self ):
383+ """
384+ Test that an expired token will fail with the correct error.
385+ """
386+ client = APIClient (enforce_csrf_checks = True )
387+
388+ # Make an expired token..
389+ token = self .create_token (
390+ self .user ,
391+ exp = datetime .utcnow () - timedelta (seconds = 5 ),
392+ orig_iat = datetime .utcnow () - timedelta (hours = 1 )
393+ )
394+
395+ response = client .post ('/auth-token-verify/' , {'token' : token },
396+ format = 'json' )
397+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
398+ self .assertRegexpMatches (response .data ['non_field_errors' ][0 ],
399+ 'Signature has expired' )
400+
401+ def test_verify_jwt_fails_with_bad_token (self ):
402+ """
403+ Test that an invalid token will fail with the correct error.
404+ """
405+
406+ client = APIClient (enforce_csrf_checks = True )
407+
408+ token = "i am not a correctly formed token"
409+
410+ response = client .post ('/auth-token-verify/' , {'token' : token },
411+ format = 'json' )
412+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
413+ self .assertRegexpMatches (response .data ['non_field_errors' ][0 ],
414+ 'Error decoding signature' )
415+
416+ def test_verify_jwt_fails_with_bad_pvt_key (self ):
417+ """
418+ Test that an mismatched private key token will fail with
419+ the correct error.
420+ """
421+
422+ # Generate a new private key
423+ private_key = rsa .generate_private_key (public_exponent = 65537 ,
424+ key_size = 2048 ,
425+ backend = default_backend ())
426+
427+ # Don't set the private key
428+ api_settings .JWT_PRIVATE_KEY = private_key
429+
430+ client = APIClient (enforce_csrf_checks = True )
431+ orig_token = self .get_token ()
432+
433+ # Now try to get a refreshed token
434+ response = client .post ('/auth-token-verify/' , {'token' : orig_token },
435+ format = 'json' )
436+
437+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
438+ self .assertRegexpMatches (response .data ['non_field_errors' ][0 ],
439+ 'Error decoding signature' )
440+
441+ def tearDown (self ):
442+ # Restore original settings
443+ api_settings .JWT_ALGORITHM = DEFAULTS ['JWT_ALGORITHM' ]
444+ api_settings .JWT_PRIVATE_KEY = DEFAULTS ['JWT_PRIVATE_KEY' ]
445+ api_settings .JWT_PUBLIC_KEY = DEFAULTS ['JWT_PUBLIC_KEY' ]
446+
447+
348448class RefreshJSONWebTokenTests (TokenTestCase ):
349449
350450 def setUp (self ):
@@ -354,28 +454,29 @@ def setUp(self):
354454 def test_refresh_jwt (self ):
355455 """
356456 Test getting a refreshed token from original token works
457+
458+ No date/time modifications are neccessary because it is assumed
459+ that this operation will take less than 300 seconds.
357460 """
358461 client = APIClient (enforce_csrf_checks = True )
462+ orig_token = self .get_token ()
463+ orig_token_decoded = utils .jwt_decode_handler (orig_token )
359464
360- with freeze_time ('2015-01-01 00:00:01' ):
361- orig_token = self .get_token ()
362- orig_token_decoded = utils .jwt_decode_handler (orig_token )
363-
364- expected_orig_iat = timegm (datetime .utcnow ().utctimetuple ())
465+ expected_orig_iat = timegm (datetime .utcnow ().utctimetuple ())
365466
366- # Make sure 'orig_iat' exists and is the current time (give some slack)
367- orig_iat = orig_token_decoded ['orig_iat' ]
368- self .assertLessEqual (orig_iat - expected_orig_iat , 1 )
467+ # Make sure 'orig_iat' exists and is the current time (give some slack)
468+ orig_iat = orig_token_decoded ['orig_iat' ]
469+ self .assertLessEqual (orig_iat - expected_orig_iat , 1 )
369470
370- with freeze_time ( '2015-01-01 00:00:03' ):
471+ time . sleep ( 1 )
371472
372- # Now try to get a refreshed token
373- response = client .post ('/auth-token-refresh/' , {'token' : orig_token },
374- format = 'json' )
375- self .assertEqual (response .status_code , status .HTTP_200_OK )
473+ # Now try to get a refreshed token
474+ response = client .post ('/auth-token-refresh/' , {'token' : orig_token },
475+ format = 'json' )
476+ self .assertEqual (response .status_code , status .HTTP_200_OK )
376477
377- new_token = response .data ['token' ]
378- new_token_decoded = utils .jwt_decode_handler (new_token )
478+ new_token = response .data ['token' ]
479+ new_token_decoded = utils .jwt_decode_handler (new_token )
379480
380481 # Make sure 'orig_iat' on the new token is same as original
381482 self .assertEquals (new_token_decoded ['orig_iat' ], orig_iat )
0 commit comments