-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: return security errors in the SIP-40 format #9796
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ | |
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
# pylint: disable=too-few-public-methods | ||
# pylint: disable=too-few-public-methods,invalid-name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than adding the blanket disable could we put this on the specific line? Note the long term goal would be to remedy all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is for the Enum names, and I want them to match the string they're equivalent to, I think this might be ok to disable for the whole file. Alternatively, we could change the max variable name length pylint config (not sure why we have it in the first place, I've rarely seen names that are too long, far more often names that are too short) |
||
from enum import Enum | ||
from typing import Any, Dict, Optional | ||
|
||
|
@@ -28,13 +28,23 @@ class SupersetErrorType(str, Enum): | |
Keep in sync with superset-frontend/src/components/ErrorMessage/types.ts | ||
""" | ||
|
||
# Frontend errors | ||
FRONTEND_CSRF_ERROR = "FRONTEND_CSRF_ERROR" | ||
FRONTEND_NETWORK_ERROR = "FRONTEND_NETWORK_ERROR" | ||
FRONTEND_TIMEOUT_ERROR = "FRONTEND_TIMEOUT_ERROR" | ||
|
||
# DB Engine errors | ||
GENERIC_DB_ENGINE_ERROR = "GENERIC_DB_ENGINE_ERROR" | ||
|
||
# Viz errors | ||
VIZ_GET_DF_ERROR = "VIZ_GET_DF_ERROR" | ||
UNKNOWN_DATASOURCE_TYPE_ERROR = "UNKNOWN_DATASOURCE_TYPE_ERROR" | ||
FAILED_FETCHING_DATASOURCE_INFO_ERROR = "FAILED_FETCHING_DATASOURCE_INFO_ERROR" | ||
|
||
# Security access errors | ||
TABLE_SECURITY_ACCESS_ERROR = "TABLE_SECURITY_ACCESS_ERROR" | ||
DATASOURCE_SECURITY_ACCESS_ERROR = "DATASOURCE_SECURITY_ACCESS_ERROR" | ||
MISSING_OWNERSHIP_ERROR = "MISSING_OWNERSHIP_ERROR" | ||
|
||
|
||
class ErrorLevel(str, Enum): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
from datetime import datetime | ||
from typing import Any, Dict, List, Optional | ||
|
||
import dataclasses | ||
import simplejson as json | ||
import yaml | ||
from flask import abort, flash, g, get_flashed_messages, redirect, Response, session | ||
|
@@ -44,6 +45,7 @@ | |
security_manager, | ||
) | ||
from superset.connectors.sqla import models | ||
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType | ||
from superset.exceptions import SupersetException, SupersetSecurityException | ||
from superset.translations.utils import get_language_pack | ||
from superset.utils import core as utils | ||
|
@@ -81,7 +83,7 @@ def get_error_msg() -> str: | |
def json_error_response( | ||
msg: Optional[str] = None, | ||
status: int = 500, | ||
payload: Optional[dict] = None, | ||
payload: Optional[Dict[str, Any]] = None, | ||
link: Optional[str] = None, | ||
) -> Response: | ||
if not payload: | ||
|
@@ -96,6 +98,22 @@ def json_error_response( | |
) | ||
|
||
|
||
def json_errors_response( | ||
errors: List[SupersetError], | ||
status: int = 500, | ||
payload: Optional[Dict[str, Any]] = None, | ||
) -> Response: | ||
if not payload: | ||
payload = {} | ||
|
||
payload["errors"] = [dataclasses.asdict(error) for error in errors] | ||
return Response( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. huh? this is inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦 |
||
json.dumps(payload, default=utils.json_iso_dttm_ser, ignore_nan=True), | ||
status=status, | ||
mimetype="application/json", | ||
) | ||
|
||
|
||
def json_success(json_msg: str, status: int = 200) -> Response: | ||
return Response(json_msg, status=status, mimetype="application/json") | ||
|
||
|
@@ -142,8 +160,8 @@ def wraps(self, *args, **kwargs): | |
return f(self, *args, **kwargs) | ||
except SupersetSecurityException as ex: | ||
logger.exception(ex) | ||
return json_error_response( | ||
utils.error_msg_from_exception(ex), status=ex.status, link=ex.link | ||
return json_errors_response( | ||
errors=[ex.error], status=ex.status, payload=ex.payload | ||
) | ||
except SupersetException as ex: | ||
logger.exception(ex) | ||
|
@@ -432,7 +450,11 @@ def check_ownership(obj: Any, raise_if_false: bool = True) -> bool: | |
return False | ||
|
||
security_exception = SupersetSecurityException( | ||
"You don't have the rights to alter [{}]".format(obj) | ||
SupersetError( | ||
error_type=SupersetErrorType.MISSING_OWNERSHIP_ERROR, | ||
message="You don't have the rights to alter [{}]".format(obj), | ||
level=ErrorLevel.ERROR, | ||
) | ||
) | ||
|
||
if g.user.is_anonymous: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@etr2460 What do you think of removing
_ERROR
suffix for the more specific errors and make it read like natural English? E.g.