You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2026/09/22 22:02:48 UTC
(superset) branch master updated: fix(views): return 401 for NoAuthorizationError in handle_api_exception (SC-121647) (#44533)
This is an automated email from the ASF dual-hosted git repository.
eschutho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 269b9f99fef fix(views): return 401 for NoAuthorizationError in handle_api_exception (SC-121647) (#44533)
269b9f99fef is described below
commit 269b9f99fef803ff154f71bd744f80998e36ac75
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Tue Sep 22 15:02:41 2026 -0700
fix(views): return 401 for NoAuthorizationError in handle_api_exception (SC-121647) (#44533)
Co-authored-by: Claude Opus 4.6 <no...@anthropic.com>
---
superset/views/error_handling.py | 6 +++++-
tests/unit_tests/views/test_error_handling.py | 22 ++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/superset/views/error_handling.py b/superset/views/error_handling.py
index bf8456db12a..71c7bb8d12e 100644
--- a/superset/views/error_handling.py
+++ b/superset/views/error_handling.py
@@ -32,6 +32,7 @@ from flask import (
)
from flask_babel import gettext as _
from flask_babel.speaklater import LazyString
+from flask_jwt_extended.exceptions import NoAuthorizationError
from flask_wtf.csrf import CSRFError
from sqlalchemy import exc
from werkzeug.exceptions import HTTPException
@@ -138,7 +139,7 @@ def handle_api_exception( # noqa: C901
exceptions.
"""
- def wraps(self: BaseSupersetView, *args: Any, **kwargs: Any) -> FlaskResponse:
+ def wraps(self: BaseSupersetView, *args: Any, **kwargs: Any) -> FlaskResponse: # noqa: C901
try:
return f(self, *args, **kwargs)
except SupersetSecurityException as ex:
@@ -175,6 +176,9 @@ def handle_api_exception( # noqa: C901
return json_error_response(utils.error_msg_from_exception(ex), status=422)
except sshtunnel.BaseSSHTunnelForwarderError as ex:
return handle_ssh_tunnel_error(ex)
+ except NoAuthorizationError as ex:
+ logger.warning("Api failed- no authorization", exc_info=True)
+ return json_error_response(str(ex), status=401)
except Exception as ex: # pylint: disable=broad-except
logger.exception(ex)
return json_error_response(utils.error_msg_from_exception(ex))
diff --git a/tests/unit_tests/views/test_error_handling.py b/tests/unit_tests/views/test_error_handling.py
index f9ce636a00d..7f7535ec44a 100644
--- a/tests/unit_tests/views/test_error_handling.py
+++ b/tests/unit_tests/views/test_error_handling.py
@@ -24,6 +24,7 @@ import pytest
import sshtunnel
from flask import Flask, Response, session
from flask_babel import Babel
+from flask_jwt_extended.exceptions import NoAuthorizationError
from flask_wtf.csrf import CSRFProtect, generate_csrf
from freezegun import freeze_time
from sqlalchemy.exc import IntegrityError, OperationalError, ProgrammingError
@@ -72,6 +73,27 @@ class TestHandleApiExceptionSSHTunnelError:
)
+class TestHandleApiExceptionNoAuthorizationError:
+ def test_returns_401_and_logs_at_warning_not_error(
+ self, app, caplog: pytest.LogCaptureFixture
+ ):
+ @handle_api_exception
+ def view(self: object) -> FlaskResponse:
+ raise NoAuthorizationError("Missing JWT in cookies or headers")
+
+ with app.test_request_context():
+ with caplog.at_level(logging.WARNING):
+ response = cast(Response, view(self=object()))
+
+ assert response.status_code == 401
+ assert not any(record.levelno >= logging.ERROR for record in caplog.records)
+ assert any(
+ record.levelno == logging.WARNING
+ and "Api failed- no authorization" in record.message
+ for record in caplog.records
+ )
+
+
class TestHandleApiExceptionDatabaseErrors:
"""
`OperationalError` is a `DatabaseError` subclass, so it must be matched by