def test_get_required_empty_claims(self): """ Test getting the claims that are required and empty. Expected Result: Only the names of claims that are not optional, but have no value are returned. """ easyjwt = EasyJWT(self.key) # Assert there is an optional, empty claim. This claim is not included in the output. self.assertIsNone(easyjwt.not_before_date) self.assertTrue(easyjwt._is_optional_claim('nbf')) # Set an optional claim. This claim is not included in the output. easyjwt.expiration_date = self.expiration_date self.assertTrue(easyjwt._is_optional_claim('exp')) # Create a non-optional claim and set a value. This claim is not included in the output. easyjwt.required = True self.assertTrue(easyjwt._is_claim('required')) # Create a non-optional, empty claim. This claim is included in the output. required_empty_claim = 'required_empty' easyjwt.required_empty = None self.assertTrue(easyjwt._is_claim(required_empty_claim)) self.assertSetEqual({required_empty_claim}, easyjwt._get_required_empty_claims())
def test_algorithms_supported(self): """ Test that the algorithms in the enum are actually supported. Expected result: All algorithms specified in the enum can encode and decode the token. """ key = 'abcdefghijklmnopqrstuvwxyz' # noinspection PyTypeChecker for algorithm in list(Algorithm): # Set the algorithm on the class so it is used for decoding. EasyJWT.algorithm = algorithm easyjwt_creation = EasyJWT(key) # Encode the token with the current algorithm. token = easyjwt_creation.create() self.assertEqual(algorithm, easyjwt_creation.algorithm, msg=f'Algorithm {algorithm} not set on token') self.assertIsNotNone( token, msg=f'Failed to encode token with algorithm {algorithm}') # Decode the token with the current algorithm. easyjwt_verification = EasyJWT.verify(token, key) self.assertEqual(algorithm, easyjwt_verification.algorithm, msg=f'Algorithm {algorithm} not set on token') self.assertIsNotNone( easyjwt_verification, msg=f'Failed to decode token with algorithm {algorithm}')
def test_restore_claim_set_with_optional_claims(self): """ Test restoring a claim set if optional claims are given. Expected Result: The values in the claim set are correctly mapped to their respective instance variables. The date values are converted to `datetime` objects. """ # Prepare a claim set. The dates must be included as a timestamp (seconds since the epoch). exp_timestamp = int( self.expiration_date.replace(tzinfo=timezone.utc).timestamp()) iat_timestamp = int( self.issued_at_date.replace(tzinfo=timezone.utc).timestamp()) nbf_timestamp = int( self.not_before_date.replace(tzinfo=timezone.utc).timestamp()) claim_set = dict( _easyjwt_class='EasyJWT', aud=self.audience, exp=exp_timestamp, iat=iat_timestamp, iss=self.issuer, jti=self.JWT_ID, nbf=nbf_timestamp, sub=self.subject, ) easyjwt = EasyJWT(self.key) easyjwt._restore_claim_set(claim_set) self.assertEqual(self.audience, easyjwt.audience) self.assertEqual(self.expiration_date, easyjwt.expiration_date) self.assertEqual(self.issued_at_date, easyjwt.issued_at_date) self.assertEqual(self.issuer, easyjwt.issuer) self.assertEqual(self.JWT_ID, easyjwt.JWT_ID) self.assertEqual(self.not_before_date, easyjwt.not_before_date) self.assertEqual(self.subject, easyjwt.subject)
def test_get_claim_set_with_optional_claims(self): """ Test getting the claim set if optional claims are set. Expected Result: A dictionary with the entries for the class and the optional claims is returned. """ claim_set = dict( _easyjwt_class='EasyJWT', aud=self.audience, exp=self.expiration_date, iat=self.issued_at_date, iss=self.issuer, jti=self.JWT_ID, nbf=self.not_before_date, sub=self.subject, ) easyjwt = EasyJWT(self.key) easyjwt.audience = self.audience easyjwt.expiration_date = self.expiration_date easyjwt.issued_at_date = self.issued_at_date easyjwt.issuer = self.issuer easyjwt.JWT_ID = self.JWT_ID easyjwt.not_before_date = self.not_before_date easyjwt.subject = self.subject self.assertDictEqual(claim_set, easyjwt._get_claim_set())
def test_get_class_name(self): """ Test getting the name of the class. Expected Result: `EasyJWT` """ easyjwt = EasyJWT(self.key) self.assertEqual('EasyJWT', easyjwt._get_class_name())
def test_create_success_lenient_verification(self): """ Test creating a token with strict verification disabled. Expected Result: A token is created successfully. The create token can be decoded. """ EasyJWT.strict_verification = False easyjwt = EasyJWT(self.key) easyjwt.expiration_date = self.expiration_date easyjwt.issuer = self.issuer easyjwt.JWT_ID = self.JWT_ID easyjwt.not_before_date = self.not_before_date easyjwt.subject = self.subject token = easyjwt.create() self.assertIsNotNone(token) self.assertIsNotNone(easyjwt.issued_at_date) claim_set = decode(token, self.key, algorithms=easyjwt._get_decode_algorithms()) self.assertIsNotNone(claim_set)
def test_verify_claim_set_success_without_optional_claims(self): """ Test verifying a valid claim set not containing optional claims. Expected result: `True` """ easyjwt = EasyJWT(self.key) claim_set = easyjwt._get_claim_set() self.assertTrue(easyjwt._verify_claim_set(claim_set))
def test_get_claim_names_strict_verification(self): """ Test getting the set of claim names with strict verification enabled. Expected Result: A set with the claim names for the `EasyJWT` class and all optional claims returned. """ claim_names = { '_easyjwt_class', 'aud', 'exp', 'iat', 'iss', 'jti', 'nbf', 'sub' } easyjwt = EasyJWT(self.key) self.assertSetEqual(claim_names, easyjwt._get_claim_names())
def test_get_claim_set_lenient_verification(self): """ Test getting the claim set with strict verification disabled. Expected Result: The `_easyjwt_class` claim is not included. """ EasyJWT.strict_verification = False claim_set = dict() easyjwt = EasyJWT(self.key) self.assertDictEqual(claim_set, easyjwt._get_claim_set())
def test_str(self): """ Test converting the object to a string. Expected Result: The token is returned as if `create()` had been called. """ easyjwt = EasyJWT(self.key) # Note: If this assertion ever fails it might be because the issued-at date is set within `create()` and the # difference between the two calls is too large. self.assertEqual(easyjwt.create(), str(easyjwt))
def test_claim_names_and_claim_set_keys_equal(self): """ Assert that the set of claim names is exactly the same as the set of claim set keys (if empty claims are included). Expected Result: The set of claim names equals the set of claim set keys. """ easyjwt = EasyJWT(self.key) claim_names = easyjwt._get_claim_names() claim_set = easyjwt._get_claim_set(with_empty_claims=True) self.assertSetEqual(claim_names, set(claim_set.keys()))
def test_get_claim_set_without_optional_claims_and_without_empty_claims( self): """ Test getting the claim set without getting empty claims if optional claims are not set. Expected Result: A dictionary with the entry for the class is returned. Optional claims are not included. """ claim_set = dict(_easyjwt_class='EasyJWT', ) easyjwt = EasyJWT(self.key) self.assertDictEqual(claim_set, easyjwt._get_claim_set(with_empty_claims=False))
def test_verify_failure_invalid_issued_at(self): """ Test verifying a token with an invalid issued-at date. Expected Result: An `InvalidIssuedAtError` is raised. """ easyjwt_creation = EasyJWT(self.key) # noinspection PyTypeChecker token = easyjwt_creation.create(issued_at='NaN') with self.assertRaises(InvalidIssuedAtError): easyjwt_verification = EasyJWT.verify(token, self.key) self.assertIsNone(easyjwt_verification)
def test_verify_failure_invalid_audience_no_audience_expected(self): """ Test verifying a token with an audience claim, but without expecting one when verifying the token. Expected Result: An `InvalidAudienceError` is raised. """ easyjwt_creation = EasyJWT(self.key) easyjwt_creation.audience = self.audience token = easyjwt_creation.create() with self.assertRaises(InvalidAudienceError): easyjwt_verification = EasyJWT.verify(token, self.key) self.assertIsNone(easyjwt_verification)
def test_verify_claim_set_failure_class_missing(self): """ Test verifying a claim set with a missing class claim. Expected result: An `UnspecifiedClassError` error is raised. """ # Remove the class claim from the claim set. easyjwt = EasyJWT(self.key) claim_set = easyjwt._get_claim_set() del claim_set['_easyjwt_class'] with self.assertRaises(UnspecifiedClassError): easyjwt._verify_claim_set(claim_set)
def test_verify_failure_invalid_signature(self): """ Test verifying a token using an invalid key. Expected Result: An `InvalidSignatureError` error is raised. """ easyjwt_creation = EasyJWT(self.key) token = easyjwt_creation.create() key = 'invalid-' + self.key with self.assertRaises(InvalidSignatureError): easyjwt_verification = EasyJWT.verify(token, key) self.assertIsNone(easyjwt_verification)
def test_registered_claim_subject(self): """ Test the registered claim ``sub``. Expected Result: The field is an optional claim and correctly mapped to the claim name and vice versa. """ instance_var_name = 'subject' claim_name = 'sub' self.assertTrue(EasyJWT._is_claim(instance_var_name)) self.assertTrue(EasyJWT._is_optional_claim(claim_name)) self.assertEqual(instance_var_name, EasyJWT._map_claim_name_to_instance_var(claim_name)) self.assertEqual( claim_name, EasyJWT._map_instance_var_to_claim_name(instance_var_name))
def test_verify_failure_expired_token(self): """ Test verifying an expired token. Expected Result: An `ExpiredTokenError` error is raised. """ easyjwt_creation = EasyJWT(self.key) easyjwt_creation.expiration_date = self.expiration_date - timedelta( minutes=30) token = easyjwt_creation.create() with self.assertRaises(ExpiredTokenError): easyjwt_verification = EasyJWT.verify(token, self.key) self.assertIsNone(easyjwt_verification)
def test_verify_failure_not_yet_valid_token(self): """ Test verifying a token that is not yet valid. Expected Result: An `ImmatureTokenError` error is raised. """ easyjwt_creation = EasyJWT(self.key) easyjwt_creation.not_before_date = self.not_before_date + timedelta( minutes=15) token = easyjwt_creation.create() with self.assertRaises(ImmatureTokenError): easyjwt_verification = EasyJWT.verify(token, self.key) self.assertIsNone(easyjwt_verification)
def test_is_optional_claim_easyjwt_class(self): """ Test if the claim for the `EasyJWT` class is optional. Expected Result: `False` """ self.assertFalse(EasyJWT._is_optional_claim('_easyjwt_class'))
def test_verify_failure_invalid_audience_no_audience_in_token(self): """ Test verifying a token without an audience claim, but expecting one. Expected Result: An `InvalidClaimSetError` is raised. """ easyjwt_creation = EasyJWT(self.key) token = easyjwt_creation.create() with self.assertRaises(InvalidClaimSetError) as exception_cm: easyjwt_verification = EasyJWT.verify(token, self.key, audience=self.audience) self.assertIsNone(easyjwt_verification) self.assertIn('audience', exception_cm.exception.missing_claims)
def test_verify_failure_missing_issuer(self): """ Test verifying a token without giving an issuer. Expected Result: An `InvalidIssuerError` is raised. """ easyjwt_creation = EasyJWT(self.key) token = easyjwt_creation.create() with self.assertRaises(InvalidClaimSetError) as exception_cm: easyjwt_verification = EasyJWT.verify(token, self.key, issuer=self.issuer) self.assertIsNone(easyjwt_verification) self.assertSetEqual({'issuer'}, exception_cm.exception.missing_claims)
def test_verify_failure_invalid_issuer(self): """ Test verifying a token with an invalid issuer. Expected Result: An `InvalidIssuerError` is raised. """ easyjwt_creation = EasyJWT(self.key) easyjwt_creation.issuer = self.issuer token = easyjwt_creation.create() invalid_issuer = 'Impersonating ' + self.issuer with self.assertRaises(InvalidIssuerError): easyjwt_verification = EasyJWT.verify(token, self.key, issuer=invalid_issuer) self.assertIsNone(easyjwt_verification)
def test_verify_failure_invalid_audience_wrong_type(self): """ Test verifying a token with an audience claim, but giving an audience of a wrong type. Expected Result: An `InvalidAudienceError` is raised. """ easyjwt_creation = EasyJWT(self.key) easyjwt_creation.audience = self.audience token = easyjwt_creation.create() with self.assertRaises(InvalidAudienceError): # noinspection PyTypeChecker easyjwt_verification = EasyJWT.verify(token, self.key, audience=42.1337) self.assertIsNone(easyjwt_verification)
def test_verify_claim_set_failure_class_wrong(self): """ Test verifying a claim set with a faulty value in the class claim. Expected result: An `InvalidClassError` error with an explaining message is raised. """ # Manipulate the class claim in the claim set. easyjwt = EasyJWT(self.key) claim_set = easyjwt._get_claim_set() claim_set['_easyjwt_class'] = 'InheritedEasyJWT' with self.assertRaises(InvalidClassError) as exception_cm: easyjwt._verify_claim_set(claim_set) self.assertEqual('Expected class EasyJWT. Got class InheritedEasyJWT', str(exception_cm.exception))
def test_verify_claim_set_failure_claims_unexpected(self): """ Test verifying a claim set with unexpected claims. Expected result: An `InvalidClaimSetError` error with an explaining message is raised. """ easyjwt = EasyJWT(self.key) # Add a claim to the claim set. claim_set = easyjwt._get_claim_set() claim_set['user_id'] = 42 with self.assertRaises(InvalidClaimSetError) as exception_cm: easyjwt._verify_claim_set(claim_set) self.assertEqual('Missing claims: {}. Unexpected claims: {user_id}', str(exception_cm.exception))
def test_create_failure_missing_required_claims(self): """ Test creating a token if required claims are empty. Expected Result: An `MissingRequiredClaimsError` error is raised. """ # Unset the claim for the EasyJWT class which is always required. easyjwt = EasyJWT(self.key) easyjwt._easyjwt_class = None self.assertTrue(easyjwt._is_claim('_easyjwt_class')) with self.assertRaises(MissingRequiredClaimsError) as exception_cm: token = easyjwt.create() self.assertIsNone(token) self.assertEqual('Required empty claims: {_easyjwt_class}', str(exception_cm.exception))
def test_restore_claim_set_without_optional_claims(self): """ Test restoring a claim set if optional claims are not given. Expected Result: The values in the claim set are mapped to their respective instance variables. Optional claims are empty, without causing an error. """ claim_set = dict(_easyjwt_class='EasyJWT', ) easyjwt = EasyJWT(self.key) easyjwt._restore_claim_set(claim_set) self.assertIsNone(easyjwt.audience) self.assertIsNone(easyjwt.expiration_date) self.assertIsNone(easyjwt.issued_at_date) self.assertIsNone(easyjwt.issuer) self.assertIsNone(easyjwt.JWT_ID) self.assertIsNone(easyjwt.not_before_date) self.assertIsNone(easyjwt.subject)
def test_get_restore_method_for_claim_none(self): """ Test getting the restore method for a claim that has no such method. Expected Result: `None`. """ restore_method = EasyJWT._get_restore_method_for_claim( 'claim_with_no_restore_method') self.assertIsNone(restore_method)
def test_get_restore_method_for_claim_not_before_date(self): """ Test getting the restore method for the not-before date. Expected Result: The method `restoration.restore_timestamp_to_datetime()` is returned. """ restore_method = EasyJWT._get_restore_method_for_claim( 'not_before_date') self.assertEqual(restore_timestamp_to_datetime, restore_method)