diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py index dafd1ba7fc719..ef22374ef8a89 100644 --- a/superset/databases/schemas.py +++ b/superset/databases/schemas.py @@ -14,6 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + +# pylint: disable=no-self-use, unused-argument + import inspect import json from typing import Any, Dict @@ -240,7 +243,6 @@ class DatabaseParametersSchemaMixin: # pylint: disable=too-few-public-methods missing=ConfigurationMethod.SQLALCHEMY_FORM, ) - # pylint: disable=no-self-use, unused-argument @pre_load def build_sqlalchemy_uri( self, data: Dict[str, Any], **kwargs: Any @@ -303,10 +305,28 @@ def build_sqlalchemy_uri( return data +def rename_encrypted_extra( + self: Schema, + data: Dict[str, Any], + **kwargs: Any, +) -> Dict[str, Any]: + """ + Rename ``encrypted_extra`` to ``masked_encrypted_extra``. + + PR #21248 changed the database schema for security reasons. This pre-loader keeps + Superset backwards compatible with older clients. + """ + if "encrypted_extra" in data: + data["masked_encrypted_extra"] = data.pop("encrypted_extra") + return data + + class DatabaseValidateParametersSchema(Schema): class Meta: # pylint: disable=too-few-public-methods unknown = EXCLUDE + rename_encrypted_extra = pre_load(rename_encrypted_extra) + id = fields.Integer(allow_none=True, description="Database ID (for updates)") engine = fields.String(required=True, description="SQLAlchemy engine to use") driver = fields.String(allow_none=True, description="SQLAlchemy driver to use") @@ -349,6 +369,8 @@ class DatabasePostSchema(Schema, DatabaseParametersSchemaMixin): class Meta: # pylint: disable=too-few-public-methods unknown = EXCLUDE + rename_encrypted_extra = pre_load(rename_encrypted_extra) + database_name = fields.String( description=database_name_description, required=True, @@ -393,6 +415,8 @@ class DatabasePutSchema(Schema, DatabaseParametersSchemaMixin): class Meta: # pylint: disable=too-few-public-methods unknown = EXCLUDE + rename_encrypted_extra = pre_load(rename_encrypted_extra) + database_name = fields.String( description=database_name_description, allow_none=True, @@ -433,6 +457,9 @@ class Meta: # pylint: disable=too-few-public-methods class DatabaseTestConnectionSchema(Schema, DatabaseParametersSchemaMixin): + + rename_encrypted_extra = pre_load(rename_encrypted_extra) + database_name = fields.String( description=database_name_description, allow_none=True, @@ -578,7 +605,6 @@ class DatabaseFunctionNamesResponse(Schema): class ImportV1DatabaseExtraSchema(Schema): - # pylint: disable=no-self-use, unused-argument @pre_load def fix_schemas_allowed_for_csv_upload( # pylint: disable=invalid-name self, data: Dict[str, Any], **kwargs: Any @@ -616,7 +642,6 @@ def fix_schemas_allowed_for_csv_upload( # pylint: disable=invalid-name class ImportV1DatabaseSchema(Schema): - # pylint: disable=no-self-use, unused-argument @pre_load def fix_allow_csv_upload( self, data: Dict[str, Any], **kwargs: Any @@ -647,7 +672,6 @@ def fix_allow_csv_upload( is_managed_externally = fields.Boolean(allow_none=True, default=False) external_url = fields.String(allow_none=True) - # pylint: disable=no-self-use, unused-argument @validates_schema def validate_password(self, data: Dict[str, Any], **kwargs: Any) -> None: """If sqlalchemy_uri has a masked password, password is required""" diff --git a/tests/unit_tests/databases/schema_tests.py b/tests/unit_tests/databases/schema_tests.py index a8e96c614416c..c9fca22d1b28d 100644 --- a/tests/unit_tests/databases/schema_tests.py +++ b/tests/unit_tests/databases/schema_tests.py @@ -190,3 +190,38 @@ def test_database_parameters_schema_mixin_invalid_type( dummy_schema.load(payload) except ValidationError as err: assert err.messages == {"port": ["Not a valid integer."]} + + +def test_rename_encrypted_extra() -> None: + """ + Test that ``encrypted_extra`` gets renamed to ``masked_encrypted_extra``. + """ + from superset.databases.schemas import ConfigurationMethod, DatabasePostSchema + + schema = DatabasePostSchema() + + # current schema + payload = schema.load( + { + "database_name": "My database", + "masked_encrypted_extra": "{}", + } + ) + assert payload == { + "database_name": "My database", + "configuration_method": ConfigurationMethod.SQLALCHEMY_FORM, + "masked_encrypted_extra": "{}", + } + + # previous schema + payload = schema.load( + { + "database_name": "My database", + "encrypted_extra": "{}", + } + ) + assert payload == { + "database_name": "My database", + "configuration_method": ConfigurationMethod.SQLALCHEMY_FORM, + "masked_encrypted_extra": "{}", + }