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))
Example #10
0
 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)
Example #11
0
 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))
Example #14
0
 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()
Example #16
0
    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
Example #17
0
    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"
Example #18
0
    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"
Example #19
0
    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)