chore(database): Creating helper make_url_safe to wrap potential errors (#19526)

* Creating helper make_url_safe to wrap potential errors

* Fixing imports

* Fixing imports again

* Adding comment

* Linting

* Fixing test

* Fixing test again...

* Fixing import
This commit is contained in:
Craig Rueda
2022-04-05 11:17:30 -07:00
committed by GitHub
parent a59718b094
commit f64d654de3
18 changed files with 90 additions and 71 deletions

View File

@@ -23,7 +23,6 @@ from flask import current_app as app
from flask_appbuilder.security.sqla.models import User
from flask_babel import gettext as _
from func_timeout import func_timeout, FunctionTimedOut
from sqlalchemy.engine.url import make_url
from sqlalchemy.exc import DBAPIError, NoSuchModuleError
from superset.commands.base import BaseCommand
@@ -34,6 +33,7 @@ from superset.databases.commands.exceptions import (
DatabaseTestConnectionUnexpectedError,
)
from superset.databases.dao import DatabaseDAO
from superset.databases.utils import make_url_safe
from superset.errors import ErrorLevel, SupersetErrorType
from superset.exceptions import SupersetSecurityException, SupersetTimeoutException
from superset.extensions import event_logger
@@ -55,7 +55,7 @@ class TestConnectionDatabaseCommand(BaseCommand):
uri = self._model.sqlalchemy_uri_decrypted
# context for error messages
url = make_url(uri)
url = make_url_safe(uri)
context = {
"hostname": url.host,
"password": url.password,

View File

@@ -20,7 +20,6 @@ from typing import Any, Dict, Optional
from flask_appbuilder.security.sqla.models import User
from flask_babel import gettext as __
from sqlalchemy.engine.url import make_url
from superset.commands.base import BaseCommand
from superset.databases.commands.exceptions import (
@@ -30,6 +29,7 @@ from superset.databases.commands.exceptions import (
InvalidParametersError,
)
from superset.databases.dao import DatabaseDAO
from superset.databases.utils import make_url_safe
from superset.db_engine_specs import get_engine_specs
from superset.db_engine_specs.base import BasicParametersMixin
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
@@ -121,7 +121,7 @@ class ValidateDatabaseParametersCommand(BaseCommand):
with closing(engine.raw_connection()) as conn:
alive = engine.dialect.do_ping(conn)
except Exception as ex:
url = make_url(sqlalchemy_uri)
url = make_url_safe(sqlalchemy_uri)
context = {
"hostname": url.host,
"password": url.password,

View File

@@ -24,10 +24,10 @@ from marshmallow import EXCLUDE, fields, pre_load, Schema, validates_schema
from marshmallow.validate import Length, ValidationError
from marshmallow_enum import EnumField
from sqlalchemy import MetaData
from sqlalchemy.engine.url import make_url
from sqlalchemy.exc import ArgumentError
from superset import db
from superset.databases.commands.exceptions import DatabaseInvalidError
from superset.databases.utils import make_url_safe
from superset.db_engine_specs import BaseEngineSpec, get_engine_specs
from superset.exceptions import CertificateException, SupersetSecurityException
from superset.models.core import ConfigurationMethod, Database, PASSWORD_MASK
@@ -144,8 +144,8 @@ def sqlalchemy_uri_validator(value: str) -> str:
Validate if it's a valid SQLAlchemy URI and refuse SQLLite by default
"""
try:
uri = make_url(value.strip())
except (ArgumentError, AttributeError, ValueError) as ex:
uri = make_url_safe(value.strip())
except DatabaseInvalidError as ex:
raise ValidationError(
[
_(
@@ -649,7 +649,7 @@ class ImportV1DatabaseSchema(Schema):
return
uri = data["sqlalchemy_uri"]
password = make_url(uri).password
password = make_url_safe(uri).password
if password == PASSWORD_MASK and data.get("password") is None:
raise ValidationError("Must provide a password for the database")

View File

@@ -16,14 +16,15 @@
# under the License.
from typing import Any, Dict, List, Optional
from superset import app
from superset.models.core import Database
from sqlalchemy.engine.url import make_url, URL
custom_password_store = app.config["SQLALCHEMY_CUSTOM_PASSWORD_STORE"]
from superset.databases.commands.exceptions import DatabaseInvalidError
def get_foreign_keys_metadata(
database: Database, table_name: str, schema_name: Optional[str]
database: Any,
table_name: str,
schema_name: Optional[str],
) -> List[Dict[str, Any]]:
foreign_keys = database.get_foreign_keys(table_name, schema_name)
for fk in foreign_keys:
@@ -33,7 +34,7 @@ def get_foreign_keys_metadata(
def get_indexes_metadata(
database: Database, table_name: str, schema_name: Optional[str]
database: Any, table_name: str, schema_name: Optional[str]
) -> List[Dict[str, Any]]:
indexes = database.get_indexes(table_name, schema_name)
for idx in indexes:
@@ -51,7 +52,7 @@ def get_col_type(col: Dict[Any, Any]) -> str:
def get_table_metadata(
database: Database, table_name: str, schema_name: Optional[str]
database: Any, table_name: str, schema_name: Optional[str]
) -> Dict[str, Any]:
"""
Get table metadata information, including type, pk, fks.
@@ -101,3 +102,17 @@ def get_table_metadata(
"indexes": keys,
"comment": table_comment,
}
def make_url_safe(raw_url: str) -> URL:
"""
Wrapper for SQLAlchemy's make_url(), which tends to raise too detailed of
errors, which inevitably find their way into server logs. ArgumentErrors
tend to contain usernames and passwords, which makes them non-log-friendly
:param raw_url:
:return:
"""
try:
return make_url(raw_url.strip())
except Exception:
raise DatabaseInvalidError() # pylint: disable=raise-missing-from