Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions flask_jwt_extended/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import datetime
from warnings import warn

# In Python 2.7 collections.abc is a part of the collections module.
try:
from collections.abc import Sequence, Set
except ImportError: # pragma: no cover
from collections import Sequence, Set

from flask import current_app

# Older versions of pyjwt do not have the requires_cryptography set. Also,
Expand Down Expand Up @@ -42,9 +48,11 @@ def decode_key(self):
@property
def token_location(self):
locations = current_app.config['JWT_TOKEN_LOCATION']
if not isinstance(locations, list):
locations = [locations]
if not locations:
if isinstance(locations, str):
locations = (locations,)
elif not isinstance(locations, (Sequence, Set)):
raise RuntimeError('JWT_TOKEN_LOCATION must be a sequence or a set')
elif not locations:
raise RuntimeError('JWT_TOKEN_LOCATION must contain at least one '
'of "headers", "cookies", "query_string", or "json"')
for location in locations:
Expand Down Expand Up @@ -202,8 +210,10 @@ def blacklist_enabled(self):
@property
def blacklist_checks(self):
check_type = current_app.config['JWT_BLACKLIST_TOKEN_CHECKS']
if not isinstance(check_type, list):
check_type = [check_type]
if isinstance(check_type, str):
check_type = (check_type,)
elif not isinstance(check_type, (Sequence, Set)):
raise RuntimeError('JWT_BLACKLIST_TOKEN_CHECKS must be a sequence or a set')
for item in check_type:
if item not in ('access', 'refresh'):
err = 'JWT_BLACKLIST_TOKEN_CHECKS must be "access" or "refresh"'
Expand Down
4 changes: 2 additions & 2 deletions flask_jwt_extended/jwt_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def _set_default_configuration_options(app):
Sets the default configuration options used by this extension
"""
# Where to look for the JWT. Available options are cookies or headers
app.config.setdefault('JWT_TOKEN_LOCATION', ['headers'])
app.config.setdefault('JWT_TOKEN_LOCATION', ('headers',))

# Options for JWTs when the TOKEN_LOCATION is headers
app.config.setdefault('JWT_HEADER_NAME', 'Authorization')
Expand Down Expand Up @@ -192,7 +192,7 @@ def _set_default_configuration_options(app):

# Options for blacklisting/revoking tokens
app.config.setdefault('JWT_BLACKLIST_ENABLED', False)
app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ['access', 'refresh'])
app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ('access', 'refresh'))

app.config.setdefault('JWT_IDENTITY_CLAIM', 'identity')
app.config.setdefault('JWT_USER_CLAIMS', 'user_claims')
Expand Down
96 changes: 72 additions & 24 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def app():

def test_default_configs(app):
with app.test_request_context():
assert config.token_location == ['headers']
assert config.token_location == ('headers',)
assert config.jwt_in_query_string is False
assert config.jwt_in_cookies is False
assert config.jwt_in_json is False
Expand Down Expand Up @@ -56,7 +56,7 @@ def test_default_configs(app):
assert config.algorithm == 'HS256'
assert config.is_asymmetric is False
assert config.blacklist_enabled is False
assert config.blacklist_checks == ['access', 'refresh']
assert config.blacklist_checks == ('access', 'refresh')
assert config.blacklist_access_tokens is True
assert config.blacklist_refresh_tokens is True

Expand Down Expand Up @@ -105,7 +105,7 @@ def test_override_configs(app):
app.config['JWT_ALGORITHM'] = 'HS512'

app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'refresh'
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ('refresh',)

app.config['JWT_IDENTITY_CLAIM'] = 'foo'
app.config['JWT_USER_CLAIMS'] = 'bar'
Expand Down Expand Up @@ -156,7 +156,7 @@ class CustomJSONEncoder(JSONEncoder):
assert config.algorithm == 'HS512'

assert config.blacklist_enabled is True
assert config.blacklist_checks == ['refresh']
assert config.blacklist_checks == ('refresh',)
assert config.blacklist_access_tokens is False
assert config.blacklist_refresh_tokens is True

Expand Down Expand Up @@ -246,6 +246,18 @@ def test_invalid_config_options(app):
with pytest.raises(RuntimeError):
config.token_location

app.config['JWT_TOKEN_LOCATION'] = 1
with pytest.raises(RuntimeError):
config.token_location

app.config['JWT_TOKEN_LOCATION'] = {'location': 'headers'}
with pytest.raises(RuntimeError):
config.token_location

app.config['JWT_TOKEN_LOCATION'] = range(99)
with pytest.raises(RuntimeError):
config.token_location

app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies', 'banana']
with pytest.raises(RuntimeError):
config.token_location
Expand Down Expand Up @@ -275,37 +287,73 @@ def test_invalid_config_options(app):
with pytest.raises(RuntimeError):
config.blacklist_checks

app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 1
with pytest.raises(RuntimeError):
config.blacklist_checks

app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = {'token_type': 'access'}
with pytest.raises(RuntimeError):
config.blacklist_checks

app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = range(99)
with pytest.raises(RuntimeError):
config.blacklist_checks

app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'banana']
with pytest.raises(RuntimeError):
config.blacklist_checks


def test_jwt_token_locations_config(app):
with app.test_request_context():
app.config['JWT_TOKEN_LOCATION'] = 'headers'
assert config.token_location == ['headers']
app.config['JWT_TOKEN_LOCATION'] = ['headers']
assert config.token_location == ['headers']
app.config['JWT_TOKEN_LOCATION'] = 'cookies'
assert config.token_location == ['cookies']
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
assert config.token_location == ['cookies']
app.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers']
assert config.token_location == ['cookies', 'headers']
allowed_locations = ('headers', 'cookies', 'query_string', 'json')
allowed_data_structures = (tuple, list, frozenset, set)

for location in allowed_locations:
app.config['JWT_TOKEN_LOCATION'] = location
assert config.token_location == (location,)

for locations in (
data_structure((location,))
for data_structure in allowed_data_structures
for location in allowed_locations
):
app.config['JWT_TOKEN_LOCATION'] = locations
assert config.token_location == locations

for locations in (
data_structure(allowed_locations[:i])
for data_structure in allowed_data_structures
for i in range(1, len(allowed_locations))
):
app.config['JWT_TOKEN_LOCATION'] = locations
assert config.token_location == locations


def test_jwt_blacklist_token_checks_config(app):
with app.test_request_context():
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'access'
assert config.blacklist_checks == ['access']
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access']
assert config.blacklist_checks == ['access']
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'refresh'
assert config.blacklist_checks == ['refresh']
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['refresh']
assert config.blacklist_checks == ['refresh']
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
assert config.blacklist_checks == ['access', 'refresh']
allowed_token_types = ('access', 'refresh')
allowed_data_structures = (tuple, list, frozenset, set)

for token_type in allowed_token_types:
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_type
assert config.blacklist_checks == (token_type,)

for token_types in (
data_structure((token_type,))
for data_structure in allowed_data_structures
for token_type in allowed_token_types
):
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_types
assert config.blacklist_checks == token_types

for token_types in (
data_structure(allowed_token_types[:i])
for data_structure in allowed_data_structures
for i in range(1, len(allowed_token_types))
):
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_types
assert config.blacklist_checks == token_types


def test_csrf_protect_config(app):
Expand Down