def test_auth_required(self, client, default_guard, use_cookie): """ This test verifies that the @auth_required decorator can be used to ensure that any access to a protected endpoint must have a properly structured auth header or cookie including a valid jwt token. Otherwise, a 401 error occurs with an informative error message. """ # Token is not in header or cookie response = client.get( "/protected", headers={}, ) exc_msg = textwrap.dedent(f""" Could not find token in any of the given locations: {default_guard.jwt_places} """).replace("\n", "") assert exc_msg in response.json["message"] assert response.status_code == 401 # Token has invalid structure response = client.get( "/protected", headers={"Authorization": "bad_structure iamatoken"}, ) assert "JWT header structure is invalid" in response.json["message"] assert response.status_code == 401 # Token is expired moment = pendulum.parse('2017-05-24 10:18:45') with plummet.frozen_time(moment): headers = default_guard.pack_header_for_user(self.the_dude) moment = (moment + default_guard.access_lifespan + pendulum.Duration(seconds=1)) with plummet.frozen_time(moment): response = client.get( "/protected", headers=headers, ) assert response.status_code == 401 assert "access permission has expired" in response.json["message"] # Token is present and valid in header or cookie with plummet.frozen_time('2017-05-24 10:38:45'): response = client.get( "/protected", headers=default_guard.pack_header_for_user(self.the_dude), ) assert response.status_code == 200 token = default_guard.encode_jwt_token(self.the_dude) with use_cookie(token): response = client.get("/protected") assert response.status_code == 200
def test_encode_eternal_jwt_token(self, app, user_class): """ This test verifies that the encode_eternal_jwt_token correctly encodes jwt data based on a user instance. Also verifies that the lifespan is set to the constant VITAM_AETERNUM """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_eternal_jwt_token(the_dude) token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert token_data["exp"] == (moment + VITAM_AETERNUM).int_timestamp assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + VITAM_AETERNUM).int_timestamp) assert token_data["id"] == the_dude.id
def test_read_token_from_header(self, app, db, user_class, client): """ This test verifies that a token may be properly read from a flask request's header using the configuration settings for header name and type """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) db.session.add(the_dude) db.session.commit() with plummet.frozen_time('2017-05-21 18:39:55'): token = guard.encode_jwt_token(the_dude) client.get( "/unprotected", headers={ "Content-Type": "application/json", DEFAULT_JWT_HEADER_NAME: DEFAULT_JWT_HEADER_TYPE + " " + token, }, ) assert guard.read_token_from_header() == token assert guard.read_token() == token
def test_auth_accepted(self, client, default_guard): """ This test verifies that the @auth_accepted decorator can be used to optionally use a properly structured auth header including a valid jwt token, setting the `current_user()`. """ # Token is not in header or cookie response = client.get( "/kinda_protected", headers={}, ) assert response.status_code == 200 assert "success" in response.json["message"] assert response.json["user"] is None # Token is present and valid with plummet.frozen_time('2017-05-24 10:38:45'): response = client.get( "/kinda_protected", headers=default_guard.pack_header_for_user(self.the_dude), ) assert response.status_code == 200 assert "success" in response.json["message"] assert response.json["user"] == self.the_dude.username
def test_init_persona__success(make_token, tmp_path, dummy_context, mocker): """ Validate that the ``init_persona()`` function will load tokens from the cache, validate them, extract identity data, and return a new ``Persona`` instance with the tokens and identity data contained within it. """ access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-16 22:30:00", ) access_token_path = tmp_path / "access.jwt" access_token_path.write_text(access_token) refresh_token = "dummy-refresh-token" refresh_token_path = tmp_path / "refresh.jwt" refresh_token_path.write_text(refresh_token) mocker.patch.object(settings, "JOBBERGATE_API_ACCESS_TOKEN_PATH", new=access_token_path) mocker.patch.object(settings, "JOBBERGATE_API_REFRESH_TOKEN_PATH", new=refresh_token_path) with plummet.frozen_time("2022-02-16 21:30:00"): persona = init_persona(dummy_context) assert persona.token_set.access_token == access_token assert persona.token_set.refresh_token == refresh_token assert persona.identity_data.user_email == "*****@*****.**" assert persona.identity_data.org_name == "good_org"
def test_get_user_from_registration_token( self, app, user_class, db, default_guard, ): """ This test verifies that a user can be extracted from an email based registration token. Also verifies that a token that has expired cannot be used to fetch a user. Also verifies that a registration token may not be refreshed """ # create our default test user the_dude = user_class( username="******", email="*****@*****.**", password=default_guard.hash_password("abides"), ) db.session.add(the_dude) db.session.commit() reg_token = default_guard.encode_jwt_token( the_dude, bypass_user_check=True, is_registration_token=True, ) extracted_user = default_guard.get_user_from_registration_token( reg_token) assert extracted_user == the_dude """ test to ensure a registration token that is expired sets off an 'ExpiredAccessError' exception """ with plummet.frozen_time('2019-01-30 16:30:00'): expired_reg_token = default_guard.encode_jwt_token( the_dude, bypass_user_check=True, override_access_lifespan=pendulum.Duration(minutes=1), is_registration_token=True, ) with plummet.frozen_time('2019-01-30 16:40:00'): with pytest.raises(ExpiredAccessError): default_guard.get_user_from_registration_token( expired_reg_token)
def test_validate_token_and_extract_identity__raises_abort_if_token_has_no_identity_data( make_token): """ Validate that the ``validate_token_and_extract_identity()`` function will raise an Abort if the access_token doesn't carry identity_data in it. """ access_token = make_token(expires="2022-02-16 22:30:00") with plummet.frozen_time("2022-02-16 21:30:00"): with pytest.raises(Abort, match="No identity data found"): validate_token_and_extract_identity( TokenSet(access_token=access_token))
def test_init_persona__refreshes_access_token_if_it_is_expired( make_token, tmp_path, respx_mock, dummy_context, mocker): """ Validate that the ``init_persona()`` function will refresh the access token if it is expired and, after validating it, save it to the cache. """ access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-16 22:30:00", ) access_token_path = tmp_path / "access.jwt" access_token_path.write_text(access_token) refresh_token = "dummy-refresh-token" refresh_token_path = tmp_path / "refresh.jwt" refresh_token_path.write_text(refresh_token) refreshed_access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-17 22:30:00", ) respx_mock.post(f"{LOGIN_DOMAIN}/oauth/token").mock( return_value=httpx.Response( httpx.codes.OK, json=dict(access_token=refreshed_access_token), ), ) mocker.patch.object(settings, "JOBBERGATE_API_ACCESS_TOKEN_PATH", new=access_token_path) mocker.patch.object(settings, "JOBBERGATE_API_REFRESH_TOKEN_PATH", new=refresh_token_path) with plummet.frozen_time("2022-02-16 23:30:00"): persona = init_persona(dummy_context) assert persona.token_set.access_token == refreshed_access_token assert persona.token_set.refresh_token == refresh_token assert persona.identity_data.user_email == "*****@*****.**" assert persona.identity_data.org_name == "good_org" assert access_token_path.exists() assert access_token_path.read_text() == refreshed_access_token assert refresh_token_path.exists() assert refresh_token_path.read_text() == refresh_token
def test_validate_token_and_extract_identity__raises_abort_if_identity_data_is_malformed( make_token): """ Validate that the ``validate_token_and_extract_identity()`` function will raise an Abort if the access token's identity data does not match the ``IdentityData`` class spec. """ access_token = make_token( expires="2022-02-16 22:30:00", identity_data=dict(bad="data", ), ) with plummet.frozen_time("2022-02-16 21:30:00"): with pytest.raises(Abort, match="malformed or incomplete"): validate_token_and_extract_identity( TokenSet(access_token=access_token))
def test__validate_jwt_data__succeeds_when_registering( self, app, user_class, ): guard = Praetorian(app, user_class) data = { "jti": "jti", "id": 1, "exp": pendulum.parse("2017-05-21 19:54:30").int_timestamp, REFRESH_EXPIRATION_CLAIM: pendulum.parse("2017-05-21 20:54:30").int_timestamp, IS_REGISTRATION_TOKEN_CLAIM: True, } with plummet.frozen_time('2017-05-21 19:54:28'): guard._validate_jwt_data(data, AccessType.register)
def test__validate_jwt_data__fails_on_access_with_reset_claim( self, app, user_class, ): guard = Praetorian(app, user_class) data = { "jti": "jti", "id": 1, "exp": pendulum.parse("2017-05-21 19:54:30").int_timestamp, REFRESH_EXPIRATION_CLAIM: pendulum.parse("2017-05-21 20:54:30").int_timestamp, IS_RESET_TOKEN_CLAIM: True, } with plummet.frozen_time('2017-05-21 19:54:28'): with pytest.raises(MisusedResetToken): guard._validate_jwt_data(data, AccessType.access)
def test_validate_token_and_extract_identity__success(make_token): """ Validate that the ``validate_token_and_extract_identity()`` function can successfully validate a good access token and extract identity_data from it. """ access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-16 22:30:00", ) with plummet.frozen_time("2022-02-16 21:30:00"): identity_data = validate_token_and_extract_identity( TokenSet(access_token=access_token)) assert identity_data.user_email == "*****@*****.**" assert identity_data.org_name == "good_org"
def test_validate_token_and_extract_identity__re_raises_ExpiredSignatureError( make_token): """ Validate that the ``validate_token_and_extract_identity()`` function will catch and then re-raise a ``ExpiredSignatureError`` thrown by the ``jwt.decode()`` function. """ access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-16 20:30:00", ) with plummet.frozen_time("2022-02-16 21:30:00"): with pytest.raises(ExpiredSignatureError): validate_token_and_extract_identity( TokenSet(access_token=access_token))
def test__validate_jwt_data__fails_when_refresh_has_expired( self, app, user_class, ): guard = Praetorian(app, user_class) data = { "jti": "jti", "id": 1, "exp": pendulum.parse("2017-05-21 19:54:30").int_timestamp, REFRESH_EXPIRATION_CLAIM: pendulum.parse("2017-05-21 20:54:30").int_timestamp, } with plummet.frozen_time('2017-05-21 20:54:32'): with pytest.raises(ExpiredRefreshError): guard._validate_jwt_data(data, AccessType.refresh)
def test_init_persona__uses_passed_token_set(make_token, tmp_path, dummy_context, mocker): """ Validate that the ``init_persona()`` function will used the passed ``TokenSet`` instance instead of loading it from the cache and will write the tokens to the cache after validating them. """ access_token = make_token( identity_data=dict( user_email="*****@*****.**", org_name="good_org", ), expires="2022-02-16 22:30:00", ) access_token_path = tmp_path / "access.jwt" refresh_token = "dummy-refresh-token" refresh_token_path = tmp_path / "refresh.jwt" token_set = TokenSet( access_token=access_token, refresh_token=refresh_token, ) assert not access_token_path.exists() assert not refresh_token_path.exists() mocker.patch.object(settings, "JOBBERGATE_API_ACCESS_TOKEN_PATH", new=access_token_path) mocker.patch.object(settings, "JOBBERGATE_API_REFRESH_TOKEN_PATH", new=refresh_token_path) with plummet.frozen_time("2022-02-16 21:30:00"): persona = init_persona(dummy_context, token_set) assert persona.token_set.access_token == access_token assert persona.token_set.refresh_token == refresh_token assert persona.identity_data.user_email == "*****@*****.**" assert persona.identity_data.org_name == "good_org" assert access_token_path.exists() assert access_token_path.read_text() == access_token assert refresh_token_path.exists()
def test_read_token_from_cookie(self, app, db, user_class, client, use_cookie): """ This test verifies that a token may be properly read from a flask request's cookies using the configuration settings for cookie """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) db.session.add(the_dude) db.session.commit() with plummet.frozen_time('2017-05-21 18:39:55'): token = guard.encode_jwt_token(the_dude) with use_cookie(token): client.get("/unprotected", ) assert guard.read_token_from_cookie() == token assert guard.read_token() == token
def test_pack_header_for_user(self, app, user_class): """ This test:: * verifies that the pack_header_for_user method can be used to package a token into a header dict for a specified user * verifies that custom claims may be packaged as well """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): header_dict = guard.pack_header_for_user(the_dude) token_header = header_dict.get(DEFAULT_JWT_HEADER_NAME) assert token_header is not None token = token_header.replace(DEFAULT_JWT_HEADER_TYPE, "") token = token.strip() token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert (token_data["exp"] == ( moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" moment = plummet.momentize('2017-05-21 18:39:55') override_access_lifespan = pendulum.Duration(minutes=1) override_refresh_lifespan = pendulum.Duration(hours=1) with plummet.frozen_time(moment): header_dict = guard.pack_header_for_user( the_dude, override_access_lifespan=override_access_lifespan, override_refresh_lifespan=override_refresh_lifespan, ) token_header = header_dict.get(DEFAULT_JWT_HEADER_NAME) assert token_header is not None token = token_header.replace(DEFAULT_JWT_HEADER_TYPE, "") token = token.strip() token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert (token_data["exp"] == ( moment + override_access_lifespan).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + override_refresh_lifespan).int_timestamp) assert token_data["id"] == the_dude.id moment = plummet.momentize('2018-08-14 09:08:39') with plummet.frozen_time(moment): header_dict = guard.pack_header_for_user( the_dude, duder="brief", el_duderino="not brief", ) token_header = header_dict.get(DEFAULT_JWT_HEADER_NAME) assert token_header is not None token = token_header.replace(DEFAULT_JWT_HEADER_TYPE, "") token = token.strip() token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert (token_data["exp"] == ( moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" assert token_data["duder"] == "brief" assert token_data["el_duderino"] == "not brief"
def test_refresh_jwt_token( self, app, db, user_class, validating_user_class, ): """ This test:: * verifies that the refresh_jwt_token properly generates a refreshed jwt token. * ensures that a token who's access permission has not expired may not be refreshed. * ensures that a token who's access permission has expired must not have an expired refresh permission for a new token to be issued. * ensures that if an override_access_lifespan argument is supplied that it is used instead of the instance's access_lifespan. * ensures that the access_lifespan may not exceed the refresh lifespan. * ensures that if the user_class has the instance method validate(), it is called an any exceptions it raises are wrapped in an InvalidUserError. * verifies that if a user is no longer identifiable that a MissingUserError is raised * verifies that any custom claims in the original token's payload are also packaged in the new token's payload """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) db.session.add(the_dude) db.session.commit() moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token(the_dude) new_moment = (pendulum.parse("2017-05-21 18:39:55") + DEFAULT_JWT_ACCESS_LIFESPAN + pendulum.Duration(minutes=1)) with plummet.frozen_time(new_moment): new_token = guard.refresh_jwt_token(token) new_token_data = jwt.decode( new_token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert new_token_data["iat"] == new_moment.int_timestamp assert (new_token_data["exp"] == ( new_moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (new_token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert new_token_data["id"] == the_dude.id assert new_token_data["rls"] == "admin,operator" moment = plummet.momentize("2017-05-21 18:39:55") with plummet.frozen_time('2017-05-21 18:39:55'): token = guard.encode_jwt_token(the_dude) new_moment = (pendulum.parse("2017-05-21 18:39:55") + DEFAULT_JWT_ACCESS_LIFESPAN + pendulum.Duration(minutes=1)) with plummet.frozen_time(new_moment): new_token = guard.refresh_jwt_token( token, override_access_lifespan=pendulum.Duration(hours=2), ) new_token_data = jwt.decode( new_token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert (new_token_data["exp"] == ( new_moment + pendulum.Duration(hours=2)).int_timestamp) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token( the_dude, override_refresh_lifespan=pendulum.Duration(hours=2), override_access_lifespan=pendulum.Duration(minutes=30), ) new_moment = moment + pendulum.Duration(minutes=31) with plummet.frozen_time(new_moment): new_token = guard.refresh_jwt_token( token, override_access_lifespan=pendulum.Duration(hours=2), ) new_token_data = jwt.decode( new_token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert (new_token_data["exp"] == new_token_data[REFRESH_EXPIRATION_CLAIM]) expiring_interval = DEFAULT_JWT_ACCESS_LIFESPAN + pendulum.Duration( minutes=1) validating_guard = Praetorian(app, validating_user_class) brandt = validating_user_class( username="******", password=guard.hash_password("can't watch"), is_active=True, ) db.session.add(brandt) db.session.commit() moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token(brandt) new_moment = moment + expiring_interval with plummet.frozen_time(new_moment): validating_guard.refresh_jwt_token(token) brandt.is_active = False db.session.merge(brandt) db.session.commit() new_moment = new_moment + expiring_interval with plummet.frozen_time(new_moment): with pytest.raises(InvalidUserError) as err_info: validating_guard.refresh_jwt_token(token) expected_message = "The user is not valid or has had access revoked" assert expected_message in str(err_info.value) expiring_interval = DEFAULT_JWT_ACCESS_LIFESPAN + pendulum.Duration( minutes=1) guard = Praetorian(app, user_class) bunny = user_class( username="******", password=guard.hash_password("can't blow that far"), ) db.session.add(bunny) db.session.commit() moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token(bunny) db.session.delete(bunny) db.session.commit() new_moment = moment + expiring_interval with plummet.frozen_time(new_moment): with pytest.raises(MissingUserError) as err_info: validating_guard.refresh_jwt_token(token) expected_message = "Could not find the requested user" assert expected_message in str(err_info.value) moment = plummet.momentize('2018-08-14 09:05:24') with plummet.frozen_time(moment): token = guard.encode_jwt_token( the_dude, duder="brief", el_duderino="not brief", ) new_moment = (pendulum.parse("2018-08-14 09:05:24") + DEFAULT_JWT_ACCESS_LIFESPAN + pendulum.Duration(minutes=1)) with plummet.frozen_time(new_moment): new_token = guard.refresh_jwt_token(token) new_token_data = jwt.decode( new_token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert new_token_data["iat"] == new_moment.int_timestamp assert (new_token_data["exp"] == ( new_moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (new_token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert new_token_data["id"] == the_dude.id assert new_token_data["rls"] == "admin,operator" assert new_token_data["duder"] == "brief" assert new_token_data["el_duderino"] == "not brief"
def test_encode_jwt_token(self, app, user_class, validating_user_class): """ This test:: * verifies that the encode_jwt_token correctly encodes jwt data based on a user instance. * verifies that if a user specifies an override for the access lifespan it is used in lieu of the instance's access_lifespan. * verifies that the access_lifespan cannot exceed the refresh lifespan. * ensures that if the user_class has the instance method validate(), it is called an any exceptions it raises are wrapped in an InvalidUserError * verifies that custom claims may be encoded in the token and validates that the custom claims do not collide with reserved claims """ guard = Praetorian(app, user_class) the_dude = user_class( username="******", password=guard.hash_password("abides"), roles="admin,operator", ) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token(the_dude) token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert (token_data["exp"] == ( moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" override_access_lifespan = pendulum.Duration(minutes=1) override_refresh_lifespan = pendulum.Duration(hours=1) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token( the_dude, override_access_lifespan=override_access_lifespan, override_refresh_lifespan=override_refresh_lifespan, ) token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert (token_data["exp"] == ( moment + override_access_lifespan).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + override_refresh_lifespan).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" override_access_lifespan = pendulum.Duration(hours=1) override_refresh_lifespan = pendulum.Duration(minutes=1) moment = plummet.momentize('2017-05-21 18:39:55') with plummet.frozen_time(moment): token = guard.encode_jwt_token( the_dude, override_access_lifespan=override_access_lifespan, override_refresh_lifespan=override_refresh_lifespan, ) token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert token_data["exp"] == token_data[REFRESH_EXPIRATION_CLAIM] assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + override_refresh_lifespan).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" validating_guard = Praetorian(app, validating_user_class) brandt = validating_user_class( username="******", password=guard.hash_password("can't watch"), is_active=True, ) validating_guard.encode_jwt_token(brandt) brandt.is_active = False with pytest.raises(InvalidUserError) as err_info: validating_guard.encode_jwt_token(brandt) expected_message = "The user is not valid or has had access revoked" assert expected_message in str(err_info.value) moment = plummet.momentize('2018-08-18 08:55:12') with plummet.frozen_time(moment): token = guard.encode_jwt_token( the_dude, duder="brief", el_duderino="not brief", ) token_data = jwt.decode( token, guard.encode_key, algorithms=guard.allowed_algorithms, ) assert token_data["iat"] == moment.int_timestamp assert (token_data["exp"] == ( moment + DEFAULT_JWT_ACCESS_LIFESPAN).int_timestamp) assert (token_data[REFRESH_EXPIRATION_CLAIM] == ( moment + DEFAULT_JWT_REFRESH_LIFESPAN).int_timestamp) assert token_data["id"] == the_dude.id assert token_data["rls"] == "admin,operator" assert token_data["duder"] == "brief" assert token_data["el_duderino"] == "not brief" with pytest.raises(ClaimCollisionError) as err_info: guard.encode_jwt_token(the_dude, exp="nice marmot") expected_message = "custom claims collide" assert expected_message in str(err_info.value)