Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

fix: reorder JWT decoders #3941

Merged
merged 1 commit into from
Apr 11, 2023
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
35 changes: 18 additions & 17 deletions ecommerce/extensions/api/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
custom jwt_decode_handler.

"""


import logging

import jwt
Expand All @@ -18,8 +16,6 @@

logger = logging.getLogger(__name__)

JWT_DECODE_HANDLER_METRIC_KEY = 'ecom_jwt_decode_handler'


def _ecommerce_jwt_decode_handler_multiple_issuers(token):
"""
Expand Down Expand Up @@ -94,26 +90,31 @@ def jwt_decode_handler(token):

"""

# First, try ecommerce decoder that handles multiple issuers.
# See ARCH-276 for details of removing additional issuers and retiring this
# custom jwt_decode_handler.
try:
jwt_payload = _ecommerce_jwt_decode_handler_multiple_issuers(token)
monitoring_utils.set_custom_metric(JWT_DECODE_HANDLER_METRIC_KEY, 'ecommerce-multiple-issuers')
return jwt_payload
except Exception: # pylint: disable=broad-except
if waffle.switch_is_active('jwt_decode_handler.log_exception.ecommerce-multiple-issuers'):
logger.info('Failed to use ecommerce multiple issuer jwt_decode_handler.', exc_info=True)

# Next, try jwt_decode_handler from edx_drf_extensions
# First, try jwt_decode_handler from edx_drf_extensions
# Note: this jwt_decode_handler can handle asymmetric keys, but only a
# single issuer. Therefore, the LMS must be the first configured issuer.
try:
jwt_payload = edx_drf_extensions_jwt_decode_handler(token)
monitoring_utils.set_custom_metric(JWT_DECODE_HANDLER_METRIC_KEY, 'edx-drf-extensions')
# Note: use increment in case there are multiple calls for different JWTs in the same transaction.
monitoring_utils.increment("ecom_jwt_decode_standard")
return jwt_payload
except Exception: # pylint: disable=broad-except
# continue and try again
if waffle.switch_is_active('jwt_decode_handler.log_exception.edx-drf-extensions'):
logger.info('Failed to use edx-drf-extensions jwt_decode_handler.', exc_info=True)

# Next, try ecommerce decoder that handles multiple issuers.
# See ARCH-276 for details of removing additional issuers and retiring this
# custom jwt_decode_handler.
try:
jwt_payload = _ecommerce_jwt_decode_handler_multiple_issuers(token)
# Ultimately we want there to be no JWTs that aren't handled by the first case.
# This enables monitoring to see if there are still cases where the custom JWT
# decoder is in use.
monitoring_utils.increment("ecom_jwt_decode_custom")
return jwt_payload
except Exception: # pylint: disable=broad-except
if waffle.switch_is_active('jwt_decode_handler.log_exception.ecommerce-multiple-issuers'):
logger.info('Failed to use ecommerce multiple issuer jwt_decode_handler.', exc_info=True)
monitoring_utils.increment("ecom_jwt_decode_failed")
raise
14 changes: 7 additions & 7 deletions ecommerce/extensions/api/tests/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ def setUp(self):
'JWT_VERIFY_AUDIENCE': False,
}
)
@mock.patch('edx_django_utils.monitoring.set_custom_metric')
@mock.patch('edx_django_utils.monitoring.increment')
@mock.patch('ecommerce.extensions.api.handlers._ecommerce_jwt_decode_handler_multiple_issuers')
def test_decode_success_edx_drf_extensions(self, mock_multiple_issuer_decoder, mock_set_custom_metric):
def test_decode_success_edx_drf_extensions(self, mock_multiple_issuer_decoder, mock_increment):
"""
Should pass using the edx-drf-extensions jwt_decode_handler.

Expand All @@ -63,7 +63,7 @@ def test_decode_success_edx_drf_extensions(self, mock_multiple_issuer_decoder, m
payload = generate_jwt_payload(self.user, issuer_name=first_issuer['ISSUER'])
token = generate_jwt_token(payload, first_issuer['SECRET_KEY'])
self.assertDictContainsSubset(payload, jwt_decode_handler(token))
mock_set_custom_metric.assert_called_with('ecom_jwt_decode_handler', 'edx-drf-extensions')
mock_increment.assert_called_with('ecom_jwt_decode_standard')

@override_settings(
JWT_AUTH={
Expand All @@ -87,9 +87,9 @@ def test_decode_success_edx_drf_extensions(self, mock_multiple_issuer_decoder, m
'JWT_VERIFY_AUDIENCE': False,
}
)
@mock.patch('edx_django_utils.monitoring.set_custom_metric')
@mock.patch('edx_django_utils.monitoring.increment')
@mock.patch('ecommerce.extensions.api.handlers.logger')
def test_decode_success_multiple_issuers(self, mock_logger, mock_set_custom_metric):
def test_decode_success_multiple_issuers(self, mock_logger, mock_increment):
"""
Should pass using ``_ecommerce_jwt_decode_handler_multiple_issuers``.

Expand All @@ -101,7 +101,7 @@ def test_decode_success_multiple_issuers(self, mock_logger, mock_set_custom_metr
payload = generate_jwt_payload(self.user, issuer_name=non_first_issuer['ISSUER'])
token = generate_jwt_token(payload, non_first_issuer['SECRET_KEY'])
self.assertDictContainsSubset(payload, jwt_decode_handler(token))
mock_set_custom_metric.assert_called_with('ecom_jwt_decode_handler', 'ecommerce-multiple-issuers')
mock_increment.assert_called_with('ecom_jwt_decode_custom')
mock_logger.exception.assert_not_called()
mock_logger.warning.assert_not_called()
mock_logger.error.assert_not_called()
Expand Down Expand Up @@ -141,6 +141,6 @@ def test_decode_error_invalid_token(self, mock_logger):

mock_logger.exception.assert_called_with('Custom config JWT decode failed!')
mock_logger.info.assert_has_calls(calls=[
mock.call('Failed to use ecommerce multiple issuer jwt_decode_handler.', exc_info=True),
mock.call('Failed to use edx-drf-extensions jwt_decode_handler.', exc_info=True),
mock.call('Failed to use ecommerce multiple issuer jwt_decode_handler.', exc_info=True),
])