This is an automated email from the ASF dual-hosted git repository.
rusackas 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 a6ad0bf1692 fix(re-encrypt-secrets): use db.Model.metadata to discover
encrypted … (#39390)
a6ad0bf1692 is described below
commit a6ad0bf1692574405f80903bcc51fd1ff7804a6e
Author: Andy <[email protected]>
AuthorDate: Tue May 12 00:16:41 2026 -0400
fix(re-encrypt-secrets): use db.Model.metadata to discover encrypted …
(#39390)
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
superset/utils/encrypt.py | 21 ++++++++++++++++++---
tests/integration_tests/utils/encrypt_tests.py | 15 +++++++++++++++
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/superset/utils/encrypt.py b/superset/utils/encrypt.py
index b833fc79de8..bf1c5b159c4 100644
--- a/superset/utils/encrypt.py
+++ b/superset/utils/encrypt.py
@@ -99,14 +99,29 @@ class SecretsMigrator:
def discover_encrypted_fields(self) -> dict[str, dict[str, EncryptedType]]:
"""
- Iterates over SqlAlchemy's metadata, looking for EncryptedType
- columns along the way. Builds up a dict of
+ Iterates over ORM-mapped tables, looking for EncryptedType columns
+ along the way. Builds up a dict of
table_name -> dict of col_name: enc type instance
- :return:
+
+ Superset's ORM models inherit from Flask-AppBuilder's declarative base
+ (`flask_appbuilder.Model`), whose MetaData is distinct from
+ `db.metadata`. We combine both sources so encrypted columns are found
+ regardless of which base a model uses. FAB's metadata takes precedence
+ when a table name appears in both registries.
+
+ :return: mapping of table name to a dict of {column name:
EncryptedType}
"""
+ from flask_appbuilder import ( # pylint:
disable=import-outside-toplevel
+ Model as FABModel,
+ )
+
meta_info: dict[str, Any] = {}
+ tables: dict[str, Any] = dict(FABModel.metadata.tables)
for table_name, table in self._db.metadata.tables.items():
+ tables.setdefault(table_name, table)
+
+ for table_name, table in tables.items():
for col_name, col in table.columns.items():
if isinstance(col.type, EncryptedType):
cols = meta_info.get(table_name, {})
diff --git a/tests/integration_tests/utils/encrypt_tests.py
b/tests/integration_tests/utils/encrypt_tests.py
index 95279ee607e..e791411ca4e 100644
--- a/tests/integration_tests/utils/encrypt_tests.py
+++ b/tests/integration_tests/utils/encrypt_tests.py
@@ -89,6 +89,21 @@ class EncryptedFieldTest(SupersetTestCase):
" encrypted_field_factory"
)
+ def test_discover_encrypted_fields_finds_dbs_table(self):
+ """
+ Ensure discover_encrypted_fields finds the encrypted columns on the
+ dbs table (password, encrypted_extra, server_cert). This guards
+ against db.metadata diverging from db.Model.metadata.
+ """
+ migrator = SecretsMigrator("")
+ encrypted_fields = migrator.discover_encrypted_fields()
+ assert "dbs" in encrypted_fields, (
+ "dbs table not found in encrypted fields — "
+ "discover_encrypted_fields may be using the wrong MetaData
instance"
+ )
+ dbs_cols = set(encrypted_fields["dbs"].keys())
+ assert {"password", "encrypted_extra",
"server_cert"}.issubset(dbs_cols)
+
def test_lazy_key_resolution(self):
"""
Verify that the encryption key is resolved lazily at runtime,