1
1
import unittest
2
2
from calendar import timegm
3
3
from datetime import datetime , timedelta
4
+ import time
4
5
5
6
from django import get_version
6
7
from django .test import TestCase
7
8
from django .test .utils import override_settings
8
9
from django .conf .urls import patterns
9
- from freezegun import freeze_time
10
10
from rest_framework import status
11
11
from rest_framework .test import APIClient
12
12
13
13
from rest_framework_jwt import utils
14
14
from rest_framework_jwt .compat import get_user_model
15
15
from rest_framework_jwt .settings import api_settings , DEFAULTS
16
16
17
+ from cryptography .hazmat .backends import default_backend
18
+ from cryptography .hazmat .primitives .asymmetric import rsa
19
+
17
20
from . import utils as test_utils
18
21
19
22
User = get_user_model ()
@@ -255,6 +258,9 @@ class TokenTestCase(BaseTestCase):
255
258
Handlers for getting tokens from the API, or creating arbitrary ones.
256
259
"""
257
260
261
+ def setUp (self ):
262
+ super (TokenTestCase , self ).setUp ()
263
+
258
264
def get_token (self ):
259
265
client = APIClient (enforce_csrf_checks = True )
260
266
response = client .post ('/auth-token/' , self .data , format = 'json' )
@@ -272,22 +278,20 @@ def create_token(self, user, exp=None, orig_iat=None):
272
278
return token
273
279
274
280
275
- class VerifyJSONWebTokenTests (TokenTestCase ):
281
+ class VerifyJSONWebTokenTestsSymmetric (TokenTestCase ):
276
282
277
283
def test_verify_jwt (self ):
278
284
"""
279
285
Test that a valid, non-expired token will return a 200 response
280
286
and itself when passed to the validation endpoint.
281
287
"""
282
288
client = APIClient (enforce_csrf_checks = True )
289
+ orig_token = self .get_token ()
283
290
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' )
286
294
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' )
291
295
self .assertEqual (response .status_code , status .HTTP_200_OK )
292
296
293
297
self .assertEqual (response .data ['token' ], orig_token )
@@ -345,6 +349,102 @@ def test_verify_jwt_fails_with_missing_user(self):
345
349
"User doesn't exist" )
346
350
347
351
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
+
348
448
class RefreshJSONWebTokenTests (TokenTestCase ):
349
449
350
450
def setUp (self ):
@@ -354,28 +454,29 @@ def setUp(self):
354
454
def test_refresh_jwt (self ):
355
455
"""
356
456
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.
357
460
"""
358
461
client = APIClient (enforce_csrf_checks = True )
462
+ orig_token = self .get_token ()
463
+ orig_token_decoded = utils .jwt_decode_handler (orig_token )
359
464
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 ())
365
466
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 )
369
470
370
- with freeze_time ( '2015-01-01 00:00:03' ):
471
+ time . sleep ( 1 )
371
472
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 )
376
477
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 )
379
480
380
481
# Make sure 'orig_iat' on the new token is same as original
381
482
self .assertEquals (new_token_decoded ['orig_iat' ], orig_iat )
0 commit comments