diff --git a/flask_jwt_extended/config.py b/flask_jwt_extended/config.py index 34a51d12..0fcd2f12 100644 --- a/flask_jwt_extended/config.py +++ b/flask_jwt_extended/config.py @@ -179,14 +179,16 @@ def refresh_csrf_header_name(self): def access_expires(self): delta = current_app.config['JWT_ACCESS_TOKEN_EXPIRES'] if not isinstance(delta, datetime.timedelta) and delta is not False: - raise RuntimeError('JWT_ACCESS_TOKEN_EXPIRES must be a datetime.timedelta or False') + err = 'JWT_ACCESS_TOKEN_EXPIRES must be a datetime.timedelta or False' + raise RuntimeError(err) return delta @property def refresh_expires(self): delta = current_app.config['JWT_REFRESH_TOKEN_EXPIRES'] if not isinstance(delta, datetime.timedelta) and delta is not False: - raise RuntimeError('JWT_REFRESH_TOKEN_EXPIRES must be a datetime.timedelta or False') + err = 'JWT_REFRESH_TOKEN_EXPIRES must be a datetime.timedelta or False' + raise RuntimeError(err) return delta @property @@ -204,7 +206,8 @@ def blacklist_checks(self): check_type = [check_type] for item in check_type: if item not in ('access', 'refresh'): - raise RuntimeError('JWT_BLACKLIST_TOKEN_CHECKS must be "access" or "refresh"') + err = 'JWT_BLACKLIST_TOKEN_CHECKS must be "access" or "refresh"' + raise RuntimeError(err) return check_type @property @@ -275,4 +278,5 @@ def error_msg_key(self): def json_encoder(self): return current_app.json_encoder + config = _Config() diff --git a/flask_jwt_extended/default_callbacks.py b/flask_jwt_extended/default_callbacks.py index 972a834f..afb951f7 100644 --- a/flask_jwt_extended/default_callbacks.py +++ b/flask_jwt_extended/default_callbacks.py @@ -84,7 +84,8 @@ def default_user_loader_error_callback(identity): function returns None, we return a general error message with a 401 status code """ - return jsonify({config.error_msg_key: "Error loading the user {}".format(identity)}), 401 + result = {config.error_msg_key: "Error loading the user {}".format(identity)} + return jsonify(result), 401 def default_claims_verification_callback(user_claims): @@ -94,7 +95,7 @@ def default_claims_verification_callback(user_claims): return True -def default_claims_verification_failed_callback(): +def default_verify_claims_failed_callback(): """ By default, if the user claims verification failed, we return a generic error message with a 400 status code diff --git a/flask_jwt_extended/jwt_manager.py b/flask_jwt_extended/jwt_manager.py index db03ae8f..b7407dbe 100644 --- a/flask_jwt_extended/jwt_manager.py +++ b/flask_jwt_extended/jwt_manager.py @@ -13,8 +13,7 @@ default_user_identity_callback, default_invalid_token_callback, default_unauthorized_callback, default_needs_fresh_token_callback, default_revoked_token_callback, default_user_loader_error_callback, - default_claims_verification_callback, - default_claims_verification_failed_callback + default_claims_verification_callback, default_verify_claims_failed_callback ) from flask_jwt_extended.tokens import ( encode_refresh_token, encode_access_token @@ -53,7 +52,7 @@ def __init__(self, app=None): self._user_loader_error_callback = default_user_loader_error_callback self._token_in_blacklist_callback = None self._claims_verification_callback = default_claims_verification_callback - self._claims_verification_failed_callback = default_claims_verification_failed_callback + self._verify_claims_failed_callback = default_verify_claims_failed_callback # Register this extension with the flask app now (if it is provided) if app is not None: @@ -83,7 +82,7 @@ def handle_auth_error(e): return self._unauthorized_callback(str(e)) @app.errorhandler(CSRFError) - def handle_auth_error(e): + def handle_csrf_error(e): return self._unauthorized_callback(str(e)) @app.errorhandler(ExpiredSignatureError) @@ -124,7 +123,7 @@ def handler_user_load_error(e): @app.errorhandler(UserClaimsVerificationError) def handle_failed_user_claims_verification(e): - return self._claims_verification_failed_callback() + return self._verify_claims_failed_callback() @staticmethod def _set_default_configuration_options(app): @@ -376,7 +375,7 @@ def claims_verification_failed_loader(self, callback): This callback must be a function that takes no arguments, and returns a Flask response. """ - self._claims_verification_failed_callback = callback + self._verify_claims_failed_callback = callback return callback def _create_refresh_token(self, identity, expires_delta=None): @@ -418,4 +417,3 @@ def _create_access_token(self, identity, fresh=False, expires_delta=None): json_encoder=config.json_encoder ) return access_token - diff --git a/flask_jwt_extended/utils.py b/flask_jwt_extended/utils.py index 1b87cc1f..1394ba4a 100644 --- a/flask_jwt_extended/utils.py +++ b/flask_jwt_extended/utils.py @@ -205,8 +205,9 @@ def set_access_cookies(response, encoded_access_token, max_age=None): :param encoded_access_token: The encoded access token to set in the cookies. :param max_age: The max age of the cookie. If this is None, it will use the `JWT_SESSION_COOKIE` option (see :ref:`Configuration Options`). - Otherwise, it will use this as the cookies `max-age` and the JWT_SESSION_COOKIE option will be ignored. - Values should be the number of seconds (as an integer). + Otherwise, it will use this as the cookies `max-age` and the + JWT_SESSION_COOKIE option will be ignored. Values should be + the number of seconds (as an integer). """ if not config.jwt_in_cookies: raise RuntimeWarning("set_access_cookies() called without " @@ -245,8 +246,9 @@ def set_refresh_cookies(response, encoded_refresh_token, max_age=None): :param encoded_refresh_token: The encoded refresh token to set in the cookies. :param max_age: The max age of the cookie. If this is None, it will use the `JWT_SESSION_COOKIE` option (see :ref:`Configuration Options`). - Otherwise, it will use this as the cookies `max-age` and the JWT_SESSION_COOKIE option will be ignored. - Values should be the number of seconds (as an integer). + Otherwise, it will use this as the cookies `max-age` and the + JWT_SESSION_COOKIE option will be ignored. Values should be + the number of seconds (as an integer). """ if not config.jwt_in_cookies: raise RuntimeWarning("set_refresh_cookies() called without " diff --git a/flask_jwt_extended/view_decorators.py b/flask_jwt_extended/view_decorators.py index 9bd2535d..e7d19998 100644 --- a/flask_jwt_extended/view_decorators.py +++ b/flask_jwt_extended/view_decorators.py @@ -184,7 +184,10 @@ def _decode_jwt_from_headers(): encoded_token = parts[0] else: if parts[0] != header_type or len(parts) != 2: - msg = "Bad {} header. Expected value '{} '".format(header_name, header_type) + msg = "Bad {} header. Expected value '{} '".format( + header_name, + header_type + ) raise InvalidHeaderError(msg) encoded_token = parts[1] @@ -274,7 +277,7 @@ def _decode_jwt_from_request(request_type): err_msg = "Missing JWT in {start_locs} or {end_locs} ({details})".format( start_locs=", ".join(token_locations[:-1]), end_locs=token_locations[-1], - details= "; ".join(errors) + details="; ".join(errors) ) raise NoAuthorizationError(err_msg) else: diff --git a/tests/test_cookies.py b/tests/test_cookies.py index a54ffca5..62aa7bdc 100644 --- a/tests/test_cookies.py +++ b/tests/test_cookies.py @@ -7,6 +7,7 @@ unset_jwt_cookies, unset_access_cookies, unset_refresh_cookies, jwt_optional ) + def _get_cookie_from_response(response, cookie_name): cookie_headers = response.headers.getlist('Set-Cookie') for header in cookie_headers: @@ -19,6 +20,7 @@ def _get_cookie_from_response(response, cookie_name): return cookie return None + @pytest.fixture(scope='function') def app(): app = Flask(__name__) @@ -87,7 +89,7 @@ def optional_post_protected(): @pytest.mark.parametrize("options", [ - ('/refresh_token', 'refresh_token_cookie', '/refresh_protected', '/delete_refresh_tokens'), + ('/refresh_token', 'refresh_token_cookie', '/refresh_protected', '/delete_refresh_tokens'), # nopep8 ('/access_token', 'access_token_cookie', '/protected', '/delete_access_tokens') ]) def test_jwt_refresh_required_with_cookies(app, options): @@ -200,7 +202,7 @@ def test_csrf_with_custom_header_names(app, options): @pytest.mark.parametrize("options", [ - ('/refresh_token', 'csrf_refresh_token', '/refresh_protected', '/post_refresh_protected'), + ('/refresh_token', 'csrf_refresh_token', '/refresh_protected', '/post_refresh_protected'), # nopep8 ('/access_token', 'csrf_access_token', '/protected', '/post_protected') ]) def test_custom_csrf_methods(app, options): @@ -412,6 +414,7 @@ def test_cookies_without_csrf(app): refresh_cookie = _get_cookie_from_response(response, 'refresh_token_cookie') assert refresh_cookie is not None + def test_jwt_optional_with_csrf_enabled(app): test_client = app.test_client() @@ -423,7 +426,8 @@ def test_jwt_optional_with_csrf_enabled(app): # User with a token should still get a CSRF error if csrf not present response = test_client.get('/access_token') - csrf_token = _get_cookie_from_response(response, 'csrf_access_token')['csrf_access_token'] + csrf_cookie = _get_cookie_from_response(response, 'csrf_access_token') + csrf_token = csrf_cookie['csrf_access_token'] response = test_client.post('/optional_post_protected') assert response.status_code == 401 assert response.get_json() == {'msg': 'Missing CSRF token in headers'} diff --git a/tests/test_headers.py b/tests/test_headers.py index cf931962..db3ffe46 100644 --- a/tests/test_headers.py +++ b/tests/test_headers.py @@ -49,8 +49,9 @@ def test_custom_header_type(app): # Insure 'default' headers no longer work access_headers = {'Authorization': 'Bearer {}'.format(access_token)} response = test_client.get('/protected', headers=access_headers) + expected_json = {'msg': "Bad Authorization header. Expected value 'JWT '"} assert response.status_code == 422 - assert response.get_json() == {'msg': "Bad Authorization header. Expected value 'JWT '"} + assert response.get_json() == expected_json # Insure new headers do work access_headers = {'Authorization': 'JWT {}'.format(access_token)} @@ -69,7 +70,8 @@ def test_custom_header_type(app): app.config['JWT_HEADER_TYPE'] = '' access_headers = {'Authorization': 'Bearer {}'.format(access_token)} response = test_client.get('/protected', headers=access_headers) - assert response.get_json() == {'msg': "Bad Authorization header. Expected value ''"} + expected_json = {'msg': "Bad Authorization header. Expected value ''"} + assert response.get_json() == expected_json assert response.status_code == 422 diff --git a/tests/test_json.py b/tests/test_json.py index 9ecc5693..737020bd 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -1,7 +1,10 @@ import pytest from flask import Flask, jsonify -from flask_jwt_extended import JWTManager, jwt_required, jwt_refresh_token_required, create_access_token, create_refresh_token +from flask_jwt_extended import ( + JWTManager, jwt_required, jwt_refresh_token_required, create_access_token, + create_refresh_token +) from tests.utils import get_jwt_manager @@ -34,15 +37,15 @@ def test_content_type(app): data = {'access_token': access_token} response = test_client.post('/protected', data=data) - + expected_json = {'msg': 'Invalid content-type. Must be application/json.'} assert response.status_code == 401 - assert response.get_json() == {'msg': 'Invalid content-type. Must be application/json.'} + assert response.get_json() == expected_json data = {'refresh_token': refresh_token} response = test_client.post('/refresh', data=data) - + expected_json = {'msg': 'Invalid content-type. Must be application/json.'} assert response.status_code == 401 - assert response.get_json() == {'msg': 'Invalid content-type. Must be application/json.'} + assert response.get_json() == expected_json def test_custom_body_key(app): @@ -60,7 +63,6 @@ def test_custom_body_key(app): assert response.status_code == 401 assert response.get_json() == {'msg': 'Missing "Foo" key in json data.'} - data = {'refresh_token': refresh_token} response = test_client.post('/refresh', json=data) assert response.status_code == 401 @@ -97,6 +99,7 @@ def custom_response(err_str): assert response.status_code == 201 assert response.get_json() == {'foo': "bar"} + def test_defaults(app): test_client = app.test_client() diff --git a/tests/test_multiple_token_locations.py b/tests/test_multiple_token_locations.py index f55ac785..a21ecf90 100644 --- a/tests/test_multiple_token_locations.py +++ b/tests/test_multiple_token_locations.py @@ -73,13 +73,9 @@ def test_json_access(app): @pytest.mark.parametrize("options", [ (['cookies', 'headers'], ('Missing JWT in cookies or headers (Missing cookie ' '"access_token_cookie"; Missing Authorization Header)')), - (['cookies', 'query_string'], ('Missing JWT in cookies or query_string (Missing cookie ' - '"access_token_cookie"; Missing "jwt" query paramater)')), - (['headers', 'query_string'], ('Missing JWT in headers or query_string (Missing "jwt" ' - 'query paramater; Missing Authorization Header)')), - (['cookies', 'headers', 'query_string'], ('Missing JWT in cookies, headers or ' - 'query_string (Missing cookie "access_token_cookie"; ' - 'Missing "jwt" query paramater; Missing Authorization Header)')) + (['json', 'query_string'], ('Missing JWT in json or query_string (Missing "jwt" ' + 'query paramater; Invalid content-type. Must be ' + 'application/json.)')), ]) def test_no_jwt_in_request(app, options): token_locations, expected_err = options diff --git a/tests/test_options_method.py b/tests/test_options_method.py index f135a09c..563fd5cd 100644 --- a/tests/test_options_method.py +++ b/tests/test_options_method.py @@ -4,6 +4,7 @@ ) import pytest + @pytest.fixture(scope='function') def app(): app = Flask(__name__) @@ -27,16 +28,19 @@ def jwt_refresh_token_required_endpoint(): return app + def test_access_jwt_required_enpoint(app): res = app.test_client().options('/jwt_required') assert res.status_code == 200 assert res.data == b'ok' + def test_access_jwt_refresh_token_required_enpoint(app): res = app.test_client().options('/jwt_refresh_token_required') assert res.status_code == 200 assert res.data == b'ok' + def test_access_fresh_jwt_required_enpoint(app): res = app.test_client().options('/fresh_jwt_required') assert res.status_code == 200 diff --git a/tests/test_view_decorators.py b/tests/test_view_decorators.py index fe65810f..946922aa 100644 --- a/tests/test_view_decorators.py +++ b/tests/test_view_decorators.py @@ -75,9 +75,15 @@ def test_fresh_jwt_required(app): with app.test_request_context(): access_token = create_access_token('username') fresh_access_token = create_access_token('username', fresh=True) - fresh_timed_access_token = create_access_token('username', fresh=timedelta(minutes=5)) - stale_timed_access_token = create_access_token('username', fresh=timedelta(minutes=-1)) refresh_token = create_refresh_token('username') + fresh_timed_access_token = create_access_token( + identity='username', + fresh=timedelta(minutes=5) + ) + stale_timed_access_token = create_access_token( + identity='username', + fresh=timedelta(minutes=-1) + ) response = test_client.get(url, headers=make_headers(fresh_access_token)) assert response.status_code == 200 @@ -146,8 +152,11 @@ def test_jwt_optional(app): with app.test_request_context(): access_token = create_access_token('username') fresh_access_token = create_access_token('username', fresh=True) - expired_token = create_access_token('username', expires_delta=timedelta(minutes=-1)) refresh_token = create_refresh_token('username') + expired_token = create_access_token( + identity='username', + expires_delta=timedelta(minutes=-1) + ) response = test_client.get(url, headers=make_headers(fresh_access_token)) assert response.status_code == 200