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,

Reply via email to