Skip to content

Commit c5f32c3

Browse files
illia-vvimalloc
authored andcommitted
Allow more data structures in config options
Allow more data structures for JWT_TOKEN_LOCATION & JWT_BLACKLIST_TOKEN_CHECKS (#215)
1 parent 5dd816c commit c5f32c3

File tree

3 files changed

+89
-31
lines changed

3 files changed

+89
-31
lines changed

flask_jwt_extended/config.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import datetime
22
from warnings import warn
33

4+
# In Python 2.7 collections.abc is a part of the collections module.
5+
try:
6+
from collections.abc import Sequence, Set
7+
except ImportError: # pragma: no cover
8+
from collections import Sequence, Set
9+
410
from flask import current_app
511

612
# Older versions of pyjwt do not have the requires_cryptography set. Also,
@@ -42,9 +48,11 @@ def decode_key(self):
4248
@property
4349
def token_location(self):
4450
locations = current_app.config['JWT_TOKEN_LOCATION']
45-
if not isinstance(locations, list):
46-
locations = [locations]
47-
if not locations:
51+
if isinstance(locations, str):
52+
locations = (locations,)
53+
elif not isinstance(locations, (Sequence, Set)):
54+
raise RuntimeError('JWT_TOKEN_LOCATION must be a sequence or a set')
55+
elif not locations:
4856
raise RuntimeError('JWT_TOKEN_LOCATION must contain at least one '
4957
'of "headers", "cookies", "query_string", or "json"')
5058
for location in locations:
@@ -202,8 +210,10 @@ def blacklist_enabled(self):
202210
@property
203211
def blacklist_checks(self):
204212
check_type = current_app.config['JWT_BLACKLIST_TOKEN_CHECKS']
205-
if not isinstance(check_type, list):
206-
check_type = [check_type]
213+
if isinstance(check_type, str):
214+
check_type = (check_type,)
215+
elif not isinstance(check_type, (Sequence, Set)):
216+
raise RuntimeError('JWT_BLACKLIST_TOKEN_CHECKS must be a sequence or a set')
207217
for item in check_type:
208218
if item not in ('access', 'refresh'):
209219
err = 'JWT_BLACKLIST_TOKEN_CHECKS must be "access" or "refresh"'

flask_jwt_extended/jwt_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def _set_default_configuration_options(app):
138138
Sets the default configuration options used by this extension
139139
"""
140140
# Where to look for the JWT. Available options are cookies or headers
141-
app.config.setdefault('JWT_TOKEN_LOCATION', ['headers'])
141+
app.config.setdefault('JWT_TOKEN_LOCATION', ('headers',))
142142

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

193193
# Options for blacklisting/revoking tokens
194194
app.config.setdefault('JWT_BLACKLIST_ENABLED', False)
195-
app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ['access', 'refresh'])
195+
app.config.setdefault('JWT_BLACKLIST_TOKEN_CHECKS', ('access', 'refresh'))
196196

197197
app.config.setdefault('JWT_IDENTITY_CLAIM', 'identity')
198198
app.config.setdefault('JWT_USER_CLAIMS', 'user_claims')

tests/test_config.py

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def app():
1818

1919
def test_default_configs(app):
2020
with app.test_request_context():
21-
assert config.token_location == ['headers']
21+
assert config.token_location == ('headers',)
2222
assert config.jwt_in_query_string is False
2323
assert config.jwt_in_cookies is False
2424
assert config.jwt_in_json is False
@@ -56,7 +56,7 @@ def test_default_configs(app):
5656
assert config.algorithm == 'HS256'
5757
assert config.is_asymmetric is False
5858
assert config.blacklist_enabled is False
59-
assert config.blacklist_checks == ['access', 'refresh']
59+
assert config.blacklist_checks == ('access', 'refresh')
6060
assert config.blacklist_access_tokens is True
6161
assert config.blacklist_refresh_tokens is True
6262

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

107107
app.config['JWT_BLACKLIST_ENABLED'] = True
108-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'refresh'
108+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ('refresh',)
109109

110110
app.config['JWT_IDENTITY_CLAIM'] = 'foo'
111111
app.config['JWT_USER_CLAIMS'] = 'bar'
@@ -156,7 +156,7 @@ class CustomJSONEncoder(JSONEncoder):
156156
assert config.algorithm == 'HS512'
157157

158158
assert config.blacklist_enabled is True
159-
assert config.blacklist_checks == ['refresh']
159+
assert config.blacklist_checks == ('refresh',)
160160
assert config.blacklist_access_tokens is False
161161
assert config.blacklist_refresh_tokens is True
162162

@@ -246,6 +246,18 @@ def test_invalid_config_options(app):
246246
with pytest.raises(RuntimeError):
247247
config.token_location
248248

249+
app.config['JWT_TOKEN_LOCATION'] = 1
250+
with pytest.raises(RuntimeError):
251+
config.token_location
252+
253+
app.config['JWT_TOKEN_LOCATION'] = {'location': 'headers'}
254+
with pytest.raises(RuntimeError):
255+
config.token_location
256+
257+
app.config['JWT_TOKEN_LOCATION'] = range(99)
258+
with pytest.raises(RuntimeError):
259+
config.token_location
260+
249261
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies', 'banana']
250262
with pytest.raises(RuntimeError):
251263
config.token_location
@@ -275,37 +287,73 @@ def test_invalid_config_options(app):
275287
with pytest.raises(RuntimeError):
276288
config.blacklist_checks
277289

290+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 1
291+
with pytest.raises(RuntimeError):
292+
config.blacklist_checks
293+
294+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = {'token_type': 'access'}
295+
with pytest.raises(RuntimeError):
296+
config.blacklist_checks
297+
298+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = range(99)
299+
with pytest.raises(RuntimeError):
300+
config.blacklist_checks
301+
278302
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'banana']
279303
with pytest.raises(RuntimeError):
280304
config.blacklist_checks
281305

282306

283307
def test_jwt_token_locations_config(app):
284308
with app.test_request_context():
285-
app.config['JWT_TOKEN_LOCATION'] = 'headers'
286-
assert config.token_location == ['headers']
287-
app.config['JWT_TOKEN_LOCATION'] = ['headers']
288-
assert config.token_location == ['headers']
289-
app.config['JWT_TOKEN_LOCATION'] = 'cookies'
290-
assert config.token_location == ['cookies']
291-
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
292-
assert config.token_location == ['cookies']
293-
app.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers']
294-
assert config.token_location == ['cookies', 'headers']
309+
allowed_locations = ('headers', 'cookies', 'query_string', 'json')
310+
allowed_data_structures = (tuple, list, frozenset, set)
311+
312+
for location in allowed_locations:
313+
app.config['JWT_TOKEN_LOCATION'] = location
314+
assert config.token_location == (location,)
315+
316+
for locations in (
317+
data_structure((location,))
318+
for data_structure in allowed_data_structures
319+
for location in allowed_locations
320+
):
321+
app.config['JWT_TOKEN_LOCATION'] = locations
322+
assert config.token_location == locations
323+
324+
for locations in (
325+
data_structure(allowed_locations[:i])
326+
for data_structure in allowed_data_structures
327+
for i in range(1, len(allowed_locations))
328+
):
329+
app.config['JWT_TOKEN_LOCATION'] = locations
330+
assert config.token_location == locations
295331

296332

297333
def test_jwt_blacklist_token_checks_config(app):
298334
with app.test_request_context():
299-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'access'
300-
assert config.blacklist_checks == ['access']
301-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access']
302-
assert config.blacklist_checks == ['access']
303-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = 'refresh'
304-
assert config.blacklist_checks == ['refresh']
305-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['refresh']
306-
assert config.blacklist_checks == ['refresh']
307-
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
308-
assert config.blacklist_checks == ['access', 'refresh']
335+
allowed_token_types = ('access', 'refresh')
336+
allowed_data_structures = (tuple, list, frozenset, set)
337+
338+
for token_type in allowed_token_types:
339+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_type
340+
assert config.blacklist_checks == (token_type,)
341+
342+
for token_types in (
343+
data_structure((token_type,))
344+
for data_structure in allowed_data_structures
345+
for token_type in allowed_token_types
346+
):
347+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_types
348+
assert config.blacklist_checks == token_types
349+
350+
for token_types in (
351+
data_structure(allowed_token_types[:i])
352+
for data_structure in allowed_data_structures
353+
for i in range(1, len(allowed_token_types))
354+
):
355+
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = token_types
356+
assert config.blacklist_checks == token_types
309357

310358

311359
def test_csrf_protect_config(app):

0 commit comments

Comments
 (0)