You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2023/06/13 23:39:11 UTC

[superset] branch elizabeth/test-2.1.1 updated (2502bf0986 -> 2f3471a87e)

This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a change to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git


    omit 2502bf0986 update changelog
    omit a0e6f35f47 fix: update order of build for testing a release (#24317)
    omit 3d4ab2963d fix: load examples as anon user (#23600)
    omit 7e23738038 update package version
    omit d22fb4ef0d update changelog
    omit ca94a69c63 merge in fix with migration (#24314)
    omit 54cb51a3bf fix: handle temporal columns in presto partitions (#24054)
    omit addfce3c54 lint
    omit 57fd6d4c22 fix: handle comments in `has_table_query` (#23882)
    omit e7e1bdc345 fix: enable strong session protection by default (#24256)
    omit ca4ae003bc fix: disable SHOW_STACKTRACE by default (#24137)
    omit 2a74a3a456 fix: db validate parameters permission (#24185)
    omit 9adadc63c8 chore: update UPDATING for 2.1.0 (#24294)
    omit 8265cc5533 chore: Remove unnecessary information from response (#24056)
    omit 9bb8615f55 remove tests that don't apply
    omit 15084144f2 add changelog
    omit 0bf1898229 lint
    omit 43cbf8ac99 remove blocking test from release
    omit 2b3aa098ae fix: allow db driver distinction on enforced URI params (#23769)
    omit dc5bed4ec4 lint
    omit 78d043309c add license to package and plugin readme files
    omit 48ea6c0866 feat: add enforce URI query params with a specific for MySQL (#23723)
    omit 70bdf408a6 fix: permission checks on import (#23200)
    omit 7213aa994f fix: check sqlalchemy_uri (#23901)
     new 502b8b81e0 test #1
     new 831978f0f7 fix: check sqlalchemy_uri (#23901)
     new cfc2ca672e fix: permission checks on import (#23200)
     new 0a9f47e4ac fix: load examples as anon user (#23600)
     new 8821174921 feat: add enforce URI query params with a specific for MySQL (#23723)
     new 4345a14841 add license to package and plugin readme files
     new 2f3471a87e lint

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (2502bf0986)
            \
             N -- N -- N   refs/heads/elizabeth/test-2.1.1 (2f3471a87e)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/docker.yml                       |  18 ++
 CHANGELOG.md                                       |  34 +---
 RELEASING/from_tarball_entrypoint.sh               |   6 +-
 UPDATING.md                                        |  10 +-
 docs/docs/contributing/testing-locally.mdx         |   2 +-
 docs/docs/security.mdx                             |  31 +---
 superset-frontend/package-lock.json                |   4 +-
 superset-frontend/package.json                     |   2 +-
 superset/charts/api.py                             |   2 +
 superset/config.py                                 |   9 +-
 superset/connectors/sqla/models.py                 |   7 +-
 superset/constants.py                              |   4 +-
 superset/dashboards/api.py                         |   3 +
 superset/dashboards/permalink/commands/base.py     |   3 +-
 superset/dashboards/permalink/commands/create.py   |   1 -
 superset/dashboards/permalink/commands/get.py      |   6 +-
 superset/dashboards/schemas.py                     |   4 +-
 superset/datasets/api.py                           |   4 +-
 superset/db_engine_specs/base.py                   |  21 +--
 superset/db_engine_specs/drill.py                  |  11 +-
 superset/db_engine_specs/hive.py                   |  12 +-
 superset/db_engine_specs/mysql.py                  |  17 +-
 superset/db_engine_specs/presto.py                 |  27 ++-
 superset/db_engine_specs/snowflake.py              |  10 +-
 superset/examples/utils.py                         |   2 +-
 superset/explore/permalink/commands/base.py        |   3 +-
 superset/explore/permalink/commands/create.py      |   3 +-
 superset/explore/permalink/commands/get.py         |   1 -
 superset/extensions/metastore_cache.py             |  11 +-
 superset/key_value/commands/create.py              |  23 +--
 superset/key_value/commands/get.py                 |  15 +-
 superset/key_value/commands/update.py              |  11 +-
 superset/key_value/commands/upsert.py              |  13 +-
 superset/key_value/shared_entries.py               |  12 +-
 superset/key_value/types.py                        |  33 +---
 ...2a5681ddfd_convert_key_value_entries_to_json.py |  96 ----------
 superset/models/core.py                            |  15 +-
 superset/models/dashboard.py                       |   6 +-
 superset/models/filter_set.py                      |   6 +-
 superset/models/slice.py                           |   8 +-
 superset/queries/api.py                            |   1 +
 superset/queries/schemas.py                        |   2 +-
 superset/sql_parse.py                              |   4 +-
 superset/tags/schemas.py                           |  59 ------
 superset/temporary_cache/api.py                    |  13 +-
 superset/temporary_cache/commands/parameters.py    |   3 -
 tests/integration_tests/charts/api_tests.py        | 109 +----------
 tests/integration_tests/csv_upload_tests.py        | 146 +++++++--------
 tests/integration_tests/dashboards/api_tests.py    | 106 +----------
 .../integration_tests/databases/commands_tests.py  | 200 +++++++++++++++++++++
 tests/integration_tests/datasets/api_tests.py      | 102 -----------
 .../explore/permalink/api_tests.py                 |   5 +-
 .../key_value/commands/create_test.py              |  55 +-----
 .../key_value/commands/delete_test.py              |  13 +-
 .../key_value/commands/fixtures.py                 |  15 +-
 .../key_value/commands/get_test.py                 |  25 ++-
 .../key_value/commands/update_test.py              |  11 +-
 .../key_value/commands/upsert_test.py              |  11 +-
 tests/integration_tests/model_tests.py             |  12 +-
 tests/integration_tests/queries/api_tests.py       |   1 +
 tests/integration_tests/sqllab_tests.py            |  10 +-
 tests/unit_tests/db_engine_specs/test_mysql.py     |  32 +---
 tests/unit_tests/db_engine_specs/test_presto.py    |  43 +----
 tests/unit_tests/sql_parse_tests.py                |   8 -
 64 files changed, 448 insertions(+), 1044 deletions(-)
 delete mode 100644 superset/migrations/versions/2023-05-01_12-03_9c2a5681ddfd_convert_key_value_entries_to_json.py
 delete mode 100644 superset/tags/schemas.py


[superset] 02/07: fix: check sqlalchemy_uri (#23901)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 831978f0f749d04cb786ec963678792dd6870d25
Author: Daniel Vaz Gaspar <da...@gmail.com>
AuthorDate: Wed May 3 11:14:03 2023 +0100

    fix: check sqlalchemy_uri (#23901)
    
    (cherry picked from commit e5f512e348bb335816e2ceff4680167f477158de)
---
 superset/security/analytics_db_safety.py           | 14 ++--
 .../security/analytics_db_safety_tests.py          | 84 +++++++++++++++++-----
 2 files changed, 75 insertions(+), 23 deletions(-)

diff --git a/superset/security/analytics_db_safety.py b/superset/security/analytics_db_safety.py
index bfa93613e1..29583b5255 100644
--- a/superset/security/analytics_db_safety.py
+++ b/superset/security/analytics_db_safety.py
@@ -14,6 +14,8 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+import re
+
 from flask_babel import lazy_gettext as _
 from sqlalchemy.engine.url import URL
 from sqlalchemy.exc import NoSuchModuleError
@@ -24,18 +26,20 @@ from superset.exceptions import SupersetSecurityException
 # list of unsafe SQLAlchemy dialects
 BLOCKLIST = {
     # sqlite creates a local DB, which allows mapping server's filesystem
-    "sqlite",
+    re.compile(r"sqlite(?:\+[^\s]*)?$"),
     # shillelagh allows opening local files (eg, 'SELECT * FROM "csv:///etc/passwd"')
-    "shillelagh",
-    "shillelagh+apsw",
+    re.compile(r"shillelagh$"),
+    re.compile(r"shillelagh\+apsw$"),
 }
 
 
 def check_sqlalchemy_uri(uri: URL) -> None:
-    if uri.drivername in BLOCKLIST:
+    for blocklist_regex in BLOCKLIST:
+        if not re.match(blocklist_regex, uri.drivername):
+            continue
         try:
             dialect = uri.get_dialect().__name__
-        except NoSuchModuleError:
+        except (NoSuchModuleError, ValueError):
             dialect = uri.drivername
 
         raise SupersetSecurityException(
diff --git a/tests/integration_tests/security/analytics_db_safety_tests.py b/tests/integration_tests/security/analytics_db_safety_tests.py
index f6518fe935..7e36268e30 100644
--- a/tests/integration_tests/security/analytics_db_safety_tests.py
+++ b/tests/integration_tests/security/analytics_db_safety_tests.py
@@ -14,30 +14,78 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from typing import Optional
 
 import pytest
 from sqlalchemy.engine.url import make_url
 
 from superset.exceptions import SupersetSecurityException
 from superset.security.analytics_db_safety import check_sqlalchemy_uri
-from tests.integration_tests.base_tests import SupersetTestCase
 
 
-class TestDBConnections(SupersetTestCase):
-    def test_check_sqlalchemy_uri_ok(self):
-        check_sqlalchemy_uri(make_url("postgres://user:password@test.com"))
-
-    def test_check_sqlalchemy_url_sqlite(self):
-        with pytest.raises(SupersetSecurityException) as excinfo:
-            check_sqlalchemy_uri(make_url("sqlite:///home/superset/bad.db"))
-        assert (
-            str(excinfo.value)
-            == "SQLiteDialect_pysqlite cannot be used as a data source for security reasons."
-        )
-
+@pytest.mark.parametrize(
+    "sqlalchemy_uri, error, error_message",
+    [
+        ("postgres://user:password@test.com", False, None),
+        (
+            "sqlite:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+pysqlite:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+aiosqlite:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+pysqlcipher:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+new+driver:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "sqlite+new+:///home/superset/bad.db",
+            True,
+            "SQLiteDialect_pysqlite cannot be used as a data source for security reasons.",
+        ),
+        (
+            "shillelagh:///home/superset/bad.db",
+            True,
+            "shillelagh cannot be used as a data source for security reasons.",
+        ),
+        (
+            "shillelagh+apsw:///home/superset/bad.db",
+            True,
+            "shillelagh cannot be used as a data source for security reasons.",
+        ),
+        ("shillelagh+:///home/superset/bad.db", False, None),
+        (
+            "shillelagh+something:///home/superset/bad.db",
+            False,
+            None,
+        ),
+    ],
+)
+def test_check_sqlalchemy_uri(
+    sqlalchemy_uri: str, error: bool, error_message: Optional[str]
+):
+    if error:
         with pytest.raises(SupersetSecurityException) as excinfo:
-            check_sqlalchemy_uri(make_url("shillelagh:///home/superset/bad.db"))
-        assert (
-            str(excinfo.value)
-            == "shillelagh cannot be used as a data source for security reasons."
-        )
+            check_sqlalchemy_uri(make_url(sqlalchemy_uri))
+            assert str(excinfo.value) == error_message
+    else:
+        check_sqlalchemy_uri(make_url(sqlalchemy_uri))


[superset] 05/07: feat: add enforce URI query params with a specific for MySQL (#23723)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 882117492117378bce0c002c7e250322ed560931
Author: Daniel Vaz Gaspar <da...@gmail.com>
AuthorDate: Tue Apr 18 17:07:37 2023 +0100

    feat: add enforce URI query params with a specific for MySQL (#23723)
---
 superset/db_engine_specs/base.py               |  9 +++++++-
 superset/db_engine_specs/mysql.py              |  6 ++++-
 tests/integration_tests/model_tests.py         | 15 ++++++++++++
 tests/unit_tests/db_engine_specs/test_mysql.py | 32 ++++++++++++++++++++++++--
 4 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py
index 5243b4660d..21aa171323 100644
--- a/superset/db_engine_specs/base.py
+++ b/superset/db_engine_specs/base.py
@@ -356,6 +356,8 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
     top_keywords: Set[str] = {"TOP"}
     # A set of disallowed connection query parameters
     disallow_uri_query_params: Set[str] = set()
+    # A Dict of query parameters that will always be used on every connection
+    enforce_uri_query_params: Dict[str, Any] = {}
 
     force_column_alias_quotes = False
     arraysize = 0
@@ -1016,8 +1018,13 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
 
         Some database drivers like Presto accept '{catalog}/{schema}' in
         the database component of the URL, that can be handled here.
+
+        Currently, changing the catalog is not supported. The method accepts a catalog so
+        that when catalog support is added to Superset the interface remains the same.
+        This is important because DB engine specs can be installed from 3rd party
+        packages.
         """
-        return uri
+        return uri, {**cls.enforce_uri_query_params}
 
     @classmethod
     def patch(cls) -> None:
diff --git a/superset/db_engine_specs/mysql.py b/superset/db_engine_specs/mysql.py
index 348b3287e3..28ef442319 100644
--- a/superset/db_engine_specs/mysql.py
+++ b/superset/db_engine_specs/mysql.py
@@ -174,6 +174,7 @@ class MySQLEngineSpec(BaseEngineSpec, BasicParametersMixin):
         ),
     }
     disallow_uri_query_params = {"local_infile"}
+    enforce_uri_query_params = {"local_infile": 0}
 
     @classmethod
     def convert_dttm(
@@ -192,10 +193,13 @@ class MySQLEngineSpec(BaseEngineSpec, BasicParametersMixin):
     def adjust_database_uri(
         cls, uri: URL, selected_schema: Optional[str] = None
     ) -> URL:
+        uri, new_connect_args = super(
+            MySQLEngineSpec, MySQLEngineSpec
+        ).adjust_database_uri(uri)
         if selected_schema:
             uri = uri.set(database=parse.quote(selected_schema, safe=""))
 
-        return uri
+        return uri, new_connect_args
 
     @classmethod
     def get_datatype(cls, type_code: Any) -> Optional[str]:
diff --git a/tests/integration_tests/model_tests.py b/tests/integration_tests/model_tests.py
index da6c5e6a3c..35dbcc0a6b 100644
--- a/tests/integration_tests/model_tests.py
+++ b/tests/integration_tests/model_tests.py
@@ -188,6 +188,21 @@ class TestDatabaseModel(SupersetTestCase):
                 "password": "original_user_password",
             }
 
+    @unittest.skipUnless(
+        SupersetTestCase.is_module_installed("MySQLdb"), "mysqlclient not installed"
+    )
+    @mock.patch("superset.models.core.create_engine")
+    def test_adjust_engine_params_mysql(self, mocked_create_engine):
+        model = Database(
+            database_name="test_database",
+            sqlalchemy_uri="mysql://user:password@localhost",
+        )
+        model._get_sqla_engine()
+        call_args = mocked_create_engine.call_args
+
+        assert str(call_args[0][0]) == "mysql://user:password@localhost"
+        assert call_args[1]["connect_args"]["local_infile"] == 0
+
     @mock.patch("superset.models.core.create_engine")
     def test_impersonate_user_trino(self, mocked_create_engine):
         principal_user = security_manager.find_user(username="gamma")
diff --git a/tests/unit_tests/db_engine_specs/test_mysql.py b/tests/unit_tests/db_engine_specs/test_mysql.py
index a512e71a97..3a24e1c2dc 100644
--- a/tests/unit_tests/db_engine_specs/test_mysql.py
+++ b/tests/unit_tests/db_engine_specs/test_mysql.py
@@ -16,7 +16,7 @@
 # under the License.
 
 from datetime import datetime
-from typing import Any, Dict, Optional, Type
+from typing import Any, Dict, Optional, Tuple, Type
 from unittest.mock import Mock, patch
 
 import pytest
@@ -33,7 +33,7 @@ from sqlalchemy.dialects.mysql import (
     TINYINT,
     TINYTEXT,
 )
-from sqlalchemy.engine.url import make_url
+from sqlalchemy.engine.url import make_url, URL
 
 from superset.utils.core import GenericDataType
 from tests.unit_tests.db_engine_specs.utils import (
@@ -119,6 +119,34 @@ def test_validate_database_uri(sqlalchemy_uri: str, error: bool) -> None:
     MySQLEngineSpec.validate_database_uri(url)
 
 
+@pytest.mark.parametrize(
+    "sqlalchemy_uri,connect_args,returns",
+    [
+        ("mysql://user:password@host/db1", {"local_infile": 1}, {"local_infile": 0}),
+        ("mysql://user:password@host/db1", {"local_infile": -1}, {"local_infile": 0}),
+        ("mysql://user:password@host/db1", {"local_infile": 0}, {"local_infile": 0}),
+        (
+            "mysql://user:password@host/db1",
+            {"param1": "some_value"},
+            {"local_infile": 0, "param1": "some_value"},
+        ),
+        (
+            "mysql://user:password@host/db1",
+            {"local_infile": 1, "param1": "some_value"},
+            {"local_infile": 0, "param1": "some_value"},
+        ),
+    ],
+)
+def test_adjust_database_uri(
+    sqlalchemy_uri: str, connect_args: Dict[str, Any], returns: Dict[str, Any]
+) -> None:
+    from superset.db_engine_specs.mysql import MySQLEngineSpec
+
+    url = make_url(sqlalchemy_uri)
+    returned_url, returned_connect_args = MySQLEngineSpec.adjust_database_uri(url)
+    assert returned_connect_args == returns
+
+
 @patch("sqlalchemy.engine.Engine.connect")
 def test_get_cancel_query_id(engine_mock: Mock) -> None:
     from superset.db_engine_specs.mysql import MySQLEngineSpec


[superset] 04/07: fix: load examples as anon user (#23600)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 0a9f47e4ace1a9ece43461973b49162fd74a535f
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Thu Apr 6 08:23:00 2023 -0700

    fix: load examples as anon user (#23600)
---
 superset/charts/commands/importers/v1/utils.py     |  7 ++--
 superset/commands/importers/v1/examples.py         | 41 +++++++++++++++-------
 superset/dashboards/commands/importers/v1/utils.py | 10 ++++--
 superset/databases/commands/importers/v1/utils.py  |  6 +++-
 superset/datasets/commands/importers/v1/utils.py   |  6 +++-
 5 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/superset/charts/commands/importers/v1/utils.py b/superset/charts/commands/importers/v1/utils.py
index cea8af7b4c..d4aeb17a1e 100644
--- a/superset/charts/commands/importers/v1/utils.py
+++ b/superset/charts/commands/importers/v1/utils.py
@@ -27,9 +27,12 @@ from superset.models.slice import Slice
 
 
 def import_chart(
-    session: Session, config: Dict[str, Any], overwrite: bool = False
+    session: Session,
+    config: Dict[str, Any],
+    overwrite: bool = False,
+    ignore_permissions: bool = False,
 ) -> Slice:
-    can_write = security_manager.can_access("can_write", "Chart")
+    can_write = ignore_permissions or security_manager.can_access("can_write", "Chart")
     existing = session.query(Slice).filter_by(uuid=config["uuid"]).first()
     if existing:
         if not overwrite or not can_write:
diff --git a/superset/commands/importers/v1/examples.py b/superset/commands/importers/v1/examples.py
index 8d14c8298a..35efdb1393 100644
--- a/superset/commands/importers/v1/examples.py
+++ b/superset/commands/importers/v1/examples.py
@@ -21,7 +21,7 @@ from sqlalchemy.orm import Session
 from sqlalchemy.orm.exc import MultipleResultsFound
 from sqlalchemy.sql import select
 
-from superset import db, security_manager
+from superset import db
 from superset.charts.commands.importers.v1 import ImportChartsCommand
 from superset.charts.commands.importers.v1.utils import import_chart
 from superset.charts.schemas import ImportV1ChartSchema
@@ -42,7 +42,7 @@ from superset.datasets.commands.importers.v1 import ImportDatasetsCommand
 from superset.datasets.commands.importers.v1.utils import import_dataset
 from superset.datasets.schemas import ImportV1DatasetSchema
 from superset.models.dashboard import dashboard_slices
-from superset.utils.core import get_example_default_schema, override_user
+from superset.utils.core import get_example_default_schema
 from superset.utils.database import get_example_database
 
 
@@ -69,13 +69,12 @@ class ImportExamplesCommand(ImportModelsCommand):
 
         # rollback to prevent partial imports
         try:
-            with override_user(security_manager.find_user(username="admin")):
-                self._import(
-                    db.session,
-                    self._configs,
-                    self.overwrite,
-                    self.force_data,
-                )
+            self._import(
+                db.session,
+                self._configs,
+                self.overwrite,
+                self.force_data,
+            )
             db.session.commit()
         except Exception as ex:
             db.session.rollback()
@@ -102,7 +101,12 @@ class ImportExamplesCommand(ImportModelsCommand):
         database_ids: Dict[str, int] = {}
         for file_name, config in configs.items():
             if file_name.startswith("databases/"):
-                database = import_database(session, config, overwrite=overwrite)
+                database = import_database(
+                    session,
+                    config,
+                    overwrite=overwrite,
+                    ignore_permissions=True,
+                )
                 database_ids[str(database.uuid)] = database.id
 
         # import datasets
@@ -131,9 +135,10 @@ class ImportExamplesCommand(ImportModelsCommand):
                         config,
                         overwrite=overwrite,
                         force_data=force_data,
+                        ignore_permissions=True,
                     )
                 except MultipleResultsFound:
-                    # Multiple result can be found for datasets. There was a bug in
+                    # Multiple results can be found for datasets. There was a bug in
                     # load-examples that resulted in datasets being loaded with a NULL
                     # schema. Users could then add a new dataset with the same name in
                     # the correct schema, resulting in duplicates, since the uniqueness
@@ -156,7 +161,12 @@ class ImportExamplesCommand(ImportModelsCommand):
             ):
                 # update datasource id, type, and name
                 config.update(dataset_info[config["dataset_uuid"]])
-                chart = import_chart(session, config, overwrite=overwrite)
+                chart = import_chart(
+                    session,
+                    config,
+                    overwrite=overwrite,
+                    ignore_permissions=True,
+                )
                 chart_ids[str(chart.uuid)] = chart.id
 
         # store the existing relationship between dashboards and charts
@@ -173,7 +183,12 @@ class ImportExamplesCommand(ImportModelsCommand):
                 except KeyError:
                     continue
 
-                dashboard = import_dashboard(session, config, overwrite=overwrite)
+                dashboard = import_dashboard(
+                    session,
+                    config,
+                    overwrite=overwrite,
+                    ignore_permissions=True,
+                )
                 dashboard.published = True
 
                 for uuid in find_chart_uuids(config["position"]):
diff --git a/superset/dashboards/commands/importers/v1/utils.py b/superset/dashboards/commands/importers/v1/utils.py
index 30bad57cb1..4d2ff96969 100644
--- a/superset/dashboards/commands/importers/v1/utils.py
+++ b/superset/dashboards/commands/importers/v1/utils.py
@@ -146,9 +146,15 @@ def update_id_refs(  # pylint: disable=too-many-locals
 
 
 def import_dashboard(
-    session: Session, config: Dict[str, Any], overwrite: bool = False
+    session: Session,
+    config: Dict[str, Any],
+    overwrite: bool = False,
+    ignore_permissions: bool = False,
 ) -> Dashboard:
-    can_write = security_manager.can_access("can_write", "Dashboard")
+    can_write = ignore_permissions or security_manager.can_access(
+        "can_write",
+        "Dashboard",
+    )
     existing = session.query(Dashboard).filter_by(uuid=config["uuid"]).first()
     if existing:
         if not overwrite or not can_write:
diff --git a/superset/databases/commands/importers/v1/utils.py b/superset/databases/commands/importers/v1/utils.py
index 673e4f86df..5f7af41e6e 100644
--- a/superset/databases/commands/importers/v1/utils.py
+++ b/superset/databases/commands/importers/v1/utils.py
@@ -29,8 +29,12 @@ def import_database(
     session: Session,
     config: Dict[str, Any],
     overwrite: bool = False,
+    ignore_permissions: bool = False,
 ) -> Database:
-    can_write = security_manager.can_access("can_write", "Database")
+    can_write = ignore_permissions or security_manager.can_access(
+        "can_write",
+        "Database",
+    )
     existing = session.query(Database).filter_by(uuid=config["uuid"]).first()
     if existing:
         if not overwrite or not can_write:
diff --git a/superset/datasets/commands/importers/v1/utils.py b/superset/datasets/commands/importers/v1/utils.py
index 2363cf7497..2df85cdfa2 100644
--- a/superset/datasets/commands/importers/v1/utils.py
+++ b/superset/datasets/commands/importers/v1/utils.py
@@ -105,8 +105,12 @@ def import_dataset(
     config: Dict[str, Any],
     overwrite: bool = False,
     force_data: bool = False,
+    ignore_permissions: bool = False,
 ) -> SqlaTable:
-    can_write = security_manager.can_access("can_write", "Dataset")
+    can_write = ignore_permissions or security_manager.can_access(
+        "can_write",
+        "Dataset",
+    )
     existing = session.query(SqlaTable).filter_by(uuid=config["uuid"]).first()
     if existing:
         if not overwrite or not can_write:


[superset] 01/07: test #1

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 502b8b81e03adc8f22695cf987d71edc233a2b88
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Mon May 1 14:18:58 2023 -0600

    test #1
---
 .github/workflows/docker.yml | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index cbbb9a8379..bd3fa3730c 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -8,6 +8,24 @@ on:
     types: [synchronize, opened, reopened, ready_for_review]
 
 jobs:
+  config:
+    runs-on: "ubuntu-latest"
+    if: github.event.pull_request.draft == false
+    outputs:
+      has-secrets: ${{ steps.check.outputs.has-secrets }}
+    steps:
+      - name: "Check for secrets"
+        id: check
+        shell: bash
+        run: |
+          if [ -n "${{ (secrets.DOCKERHUB_USER != '' && secrets.DOCKERHUB_TOKEN != '') || '' }}" ]; then
+            echo "has-secrets=1" >> "$GITHUB_OUTPUT"
+            echo "has secrets!"
+          else
+            echo "has-secrets=0" >> "$GITHUB_OUTPUT"
+            echo "no secrets!"
+          fi
+
   docker-build:
     if: github.event.pull_request.draft == false
     name: docker-build


[superset] 03/07: fix: permission checks on import (#23200)

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit cfc2ca672e7cea0e461b0b37be5174f88e6468ca
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Wed Mar 15 08:31:09 2023 -0700

    fix: permission checks on import (#23200)
---
 superset/charts/commands/importers/v1/utils.py     |   9 +-
 superset/commands/importers/v1/examples.py         |  21 +-
 superset/dashboards/commands/importers/v1/utils.py |  10 +-
 superset/databases/commands/importers/v1/utils.py  |  13 +-
 superset/datasets/commands/importers/v1/utils.py   |   9 +-
 superset/examples/utils.py                         |   4 +-
 tests/integration_tests/charts/commands_tests.py   |  11 +-
 .../integration_tests/dashboards/commands_tests.py |  12 +-
 .../integration_tests/databases/commands_tests.py  | 310 +++++++++++++++++----
 tests/integration_tests/datasets/commands_tests.py |  17 +-
 tests/integration_tests/fixtures/importexport.py   | 105 +++++++
 .../queries/saved_queries/commands_tests.py        |  10 +-
 .../charts/commands/importers/v1/import_test.py    |  45 ++-
 .../commands/importers/v1/assets_test.py           |  16 +-
 .../commands/importers/v1/import_test.py           |  46 ++-
 .../databases/commands/importers/v1/import_test.py |  44 ++-
 .../datasets/commands/importers/v1/import_test.py  |  38 ++-
 17 files changed, 621 insertions(+), 99 deletions(-)

diff --git a/superset/charts/commands/importers/v1/utils.py b/superset/charts/commands/importers/v1/utils.py
index d4c69d3ada..cea8af7b4c 100644
--- a/superset/charts/commands/importers/v1/utils.py
+++ b/superset/charts/commands/importers/v1/utils.py
@@ -21,17 +21,24 @@ from typing import Any, Dict
 from flask import g
 from sqlalchemy.orm import Session
 
+from superset import security_manager
+from superset.commands.exceptions import ImportFailedError
 from superset.models.slice import Slice
 
 
 def import_chart(
     session: Session, config: Dict[str, Any], overwrite: bool = False
 ) -> Slice:
+    can_write = security_manager.can_access("can_write", "Chart")
     existing = session.query(Slice).filter_by(uuid=config["uuid"]).first()
     if existing:
-        if not overwrite:
+        if not overwrite or not can_write:
             return existing
         config["id"] = existing.id
+    elif not can_write:
+        raise ImportFailedError(
+            "Chart doesn't exist and user doesn't have permission to create charts"
+        )
 
     # TODO (betodealmeida): move this logic to import_from_dict
     config["params"] = json.dumps(config["params"])
diff --git a/superset/commands/importers/v1/examples.py b/superset/commands/importers/v1/examples.py
index 99aa831faa..8d14c8298a 100644
--- a/superset/commands/importers/v1/examples.py
+++ b/superset/commands/importers/v1/examples.py
@@ -21,7 +21,7 @@ from sqlalchemy.orm import Session
 from sqlalchemy.orm.exc import MultipleResultsFound
 from sqlalchemy.sql import select
 
-from superset import db
+from superset import db, security_manager
 from superset.charts.commands.importers.v1 import ImportChartsCommand
 from superset.charts.commands.importers.v1.utils import import_chart
 from superset.charts.schemas import ImportV1ChartSchema
@@ -42,7 +42,7 @@ from superset.datasets.commands.importers.v1 import ImportDatasetsCommand
 from superset.datasets.commands.importers.v1.utils import import_dataset
 from superset.datasets.schemas import ImportV1DatasetSchema
 from superset.models.dashboard import dashboard_slices
-from superset.utils.core import get_example_default_schema
+from superset.utils.core import get_example_default_schema, override_user
 from superset.utils.database import get_example_database
 
 
@@ -69,7 +69,13 @@ class ImportExamplesCommand(ImportModelsCommand):
 
         # rollback to prevent partial imports
         try:
-            self._import(db.session, self._configs, self.overwrite, self.force_data)
+            with override_user(security_manager.find_user(username="admin")):
+                self._import(
+                    db.session,
+                    self._configs,
+                    self.overwrite,
+                    self.force_data,
+                )
             db.session.commit()
         except Exception as ex:
             db.session.rollback()
@@ -119,13 +125,12 @@ class ImportExamplesCommand(ImportModelsCommand):
                 if config["schema"] is None:
                     config["schema"] = get_example_default_schema()
 
-                dataset = import_dataset(
-                    session, config, overwrite=overwrite, force_data=force_data
-                )
-
                 try:
                     dataset = import_dataset(
-                        session, config, overwrite=overwrite, force_data=force_data
+                        session,
+                        config,
+                        overwrite=overwrite,
+                        force_data=force_data,
                     )
                 except MultipleResultsFound:
                     # Multiple result can be found for datasets. There was a bug in
diff --git a/superset/dashboards/commands/importers/v1/utils.py b/superset/dashboards/commands/importers/v1/utils.py
index 513d1efcdb..30bad57cb1 100644
--- a/superset/dashboards/commands/importers/v1/utils.py
+++ b/superset/dashboards/commands/importers/v1/utils.py
@@ -22,6 +22,8 @@ from typing import Any, Dict, Set
 from flask import g
 from sqlalchemy.orm import Session
 
+from superset import security_manager
+from superset.commands.exceptions import ImportFailedError
 from superset.models.dashboard import Dashboard
 
 logger = logging.getLogger(__name__)
@@ -146,11 +148,17 @@ def update_id_refs(  # pylint: disable=too-many-locals
 def import_dashboard(
     session: Session, config: Dict[str, Any], overwrite: bool = False
 ) -> Dashboard:
+    can_write = security_manager.can_access("can_write", "Dashboard")
     existing = session.query(Dashboard).filter_by(uuid=config["uuid"]).first()
     if existing:
-        if not overwrite:
+        if not overwrite or not can_write:
             return existing
         config["id"] = existing.id
+    elif not can_write:
+        raise ImportFailedError(
+            "Dashboard doesn't exist and user doesn't "
+            "have permission to create dashboards"
+        )
 
     # TODO (betodealmeida): move this logic to import_from_dict
     config = config.copy()
diff --git a/superset/databases/commands/importers/v1/utils.py b/superset/databases/commands/importers/v1/utils.py
index 6704ccd465..673e4f86df 100644
--- a/superset/databases/commands/importers/v1/utils.py
+++ b/superset/databases/commands/importers/v1/utils.py
@@ -20,17 +20,26 @@ from typing import Any, Dict
 
 from sqlalchemy.orm import Session
 
+from superset import security_manager
+from superset.commands.exceptions import ImportFailedError
 from superset.models.core import Database
 
 
 def import_database(
-    session: Session, config: Dict[str, Any], overwrite: bool = False
+    session: Session,
+    config: Dict[str, Any],
+    overwrite: bool = False,
 ) -> Database:
+    can_write = security_manager.can_access("can_write", "Database")
     existing = session.query(Database).filter_by(uuid=config["uuid"]).first()
     if existing:
-        if not overwrite:
+        if not overwrite or not can_write:
             return existing
         config["id"] = existing.id
+    elif not can_write:
+        raise ImportFailedError(
+            "Database doesn't exist and user doesn't have permission to create databases"
+        )
 
     # https://github.com/apache/superset/pull/16756 renamed ``csv`` to ``file``.
     config["allow_file_upload"] = config.pop("allow_csv_upload")
diff --git a/superset/datasets/commands/importers/v1/utils.py b/superset/datasets/commands/importers/v1/utils.py
index b3fb2a8041..2363cf7497 100644
--- a/superset/datasets/commands/importers/v1/utils.py
+++ b/superset/datasets/commands/importers/v1/utils.py
@@ -28,6 +28,8 @@ from sqlalchemy.orm import Session
 from sqlalchemy.orm.exc import MultipleResultsFound
 from sqlalchemy.sql.visitors import VisitableType
 
+from superset import security_manager
+from superset.commands.exceptions import ImportFailedError
 from superset.connectors.sqla.models import SqlaTable
 from superset.datasets.commands.exceptions import DatasetForbiddenDataURI
 from superset.models.core import Database
@@ -104,11 +106,16 @@ def import_dataset(
     overwrite: bool = False,
     force_data: bool = False,
 ) -> SqlaTable:
+    can_write = security_manager.can_access("can_write", "Dataset")
     existing = session.query(SqlaTable).filter_by(uuid=config["uuid"]).first()
     if existing:
-        if not overwrite:
+        if not overwrite or not can_write:
             return existing
         config["id"] = existing.id
+    elif not can_write:
+        raise ImportFailedError(
+            "Dataset doesn't exist and user doesn't have permission to create datasets"
+        )
 
     # TODO (betodealmeida): move this logic to import_from_dict
     config = config.copy()
diff --git a/superset/examples/utils.py b/superset/examples/utils.py
index 4568e4f196..aea1f0f93d 100644
--- a/superset/examples/utils.py
+++ b/superset/examples/utils.py
@@ -92,7 +92,9 @@ def load_configs_from_directory(
     contents[METADATA_FILE_NAME] = yaml.dump(metadata)
 
     command = ImportExamplesCommand(
-        contents, overwrite=overwrite, force_data=force_data
+        contents,
+        overwrite=overwrite,
+        force_data=force_data,
     )
     try:
         command.run()
diff --git a/tests/integration_tests/charts/commands_tests.py b/tests/integration_tests/charts/commands_tests.py
index da9a7550ac..fb2073499b 100644
--- a/tests/integration_tests/charts/commands_tests.py
+++ b/tests/integration_tests/charts/commands_tests.py
@@ -164,9 +164,10 @@ class TestExportChartsCommand(SupersetTestCase):
 
 class TestImportChartsCommand(SupersetTestCase):
     @patch("superset.charts.commands.importers.v1.utils.g")
-    def test_import_v1_chart(self, mock_g):
+    @patch("superset.security.manager.g")
+    def test_import_v1_chart(self, sm_g, utils_g):
         """Test that we can import a chart"""
-        mock_g.user = security_manager.find_user("admin")
+        admin = sm_g.user = utils_g.user = security_manager.find_user("admin")
         contents = {
             "metadata.yaml": yaml.safe_dump(chart_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -227,7 +228,7 @@ class TestImportChartsCommand(SupersetTestCase):
         assert database.database_name == "imported_database"
         assert chart.table.database == database
 
-        assert chart.owners == [mock_g.user]
+        assert chart.owners == [admin]
 
         chart.owners = []
         dataset.owners = []
@@ -237,8 +238,10 @@ class TestImportChartsCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_chart_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_chart_multiple(self, sm_g):
         """Test that a chart can be imported multiple times"""
+        sm_g.user = security_manager.find_user("admin")
         contents = {
             "metadata.yaml": yaml.safe_dump(chart_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
diff --git a/tests/integration_tests/dashboards/commands_tests.py b/tests/integration_tests/dashboards/commands_tests.py
index 927865e4a0..ad9152585e 100644
--- a/tests/integration_tests/dashboards/commands_tests.py
+++ b/tests/integration_tests/dashboards/commands_tests.py
@@ -485,9 +485,10 @@ class TestImportDashboardsCommand(SupersetTestCase):
         db.session.commit()
 
     @patch("superset.dashboards.commands.importers.v1.utils.g")
-    def test_import_v1_dashboard(self, mock_g):
+    @patch("superset.security.manager.g")
+    def test_import_v1_dashboard(self, sm_g, utils_g):
         """Test that we can import a dashboard"""
-        mock_g.user = security_manager.find_user("admin")
+        admin = sm_g.user = utils_g.user = security_manager.find_user("admin")
         contents = {
             "metadata.yaml": yaml.safe_dump(dashboard_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -570,7 +571,7 @@ class TestImportDashboardsCommand(SupersetTestCase):
         database = dataset.database
         assert str(database.uuid) == database_config["uuid"]
 
-        assert dashboard.owners == [mock_g.user]
+        assert dashboard.owners == [admin]
 
         dashboard.owners = []
         chart.owners = []
@@ -582,8 +583,11 @@ class TestImportDashboardsCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_dashboard_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_dashboard_multiple(self, mock_g):
         """Test that a dashboard can be imported multiple times"""
+        mock_g.user = security_manager.find_user("admin")
+
         num_dashboards = db.session.query(Dashboard).count()
 
         contents = {
diff --git a/tests/integration_tests/databases/commands_tests.py b/tests/integration_tests/databases/commands_tests.py
index 7e4fcaad78..7b2d4fdd27 100644
--- a/tests/integration_tests/databases/commands_tests.py
+++ b/tests/integration_tests/databases/commands_tests.py
@@ -40,7 +40,7 @@ from superset.databases.commands.importers.v1 import ImportDatabasesCommand
 from superset.databases.commands.tables import TablesDatabaseCommand
 from superset.databases.commands.test_connection import TestConnectionDatabaseCommand
 from superset.databases.commands.validate import ValidateDatabaseParametersCommand
-from superset.databases.schemas import DatabaseTestConnectionSchema
+from superset.databases.ssh_tunnel.models import SSHTunnel
 from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
 from superset.exceptions import (
     SupersetErrorsException,
@@ -63,16 +63,19 @@ from tests.integration_tests.fixtures.energy_dashboard import (
 from tests.integration_tests.fixtures.importexport import (
     database_config,
     database_metadata_config,
+    database_with_ssh_tunnel_config_mix_credentials,
+    database_with_ssh_tunnel_config_no_credentials,
+    database_with_ssh_tunnel_config_password,
+    database_with_ssh_tunnel_config_private_key,
+    database_with_ssh_tunnel_config_private_pass_only,
     dataset_config,
     dataset_metadata_config,
 )
 
 
 class TestCreateDatabaseCommand(SupersetTestCase):
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_create_duplicate_error(self, mock_g, mock_logger):
         example_db = get_example_database()
         mock_g.user = security_manager.find_user("admin")
@@ -90,10 +93,8 @@ class TestCreateDatabaseCommand(SupersetTestCase):
             "DatabaseRequiredFieldValidationError"
         )
 
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_multiple_error_logging(self, mock_g, mock_logger):
         mock_g.user = security_manager.find_user("admin")
         command = CreateDatabaseCommand({})
@@ -395,8 +396,11 @@ class TestExportDatabasesCommand(SupersetTestCase):
 
 
 class TestImportDatabasesCommand(SupersetTestCase):
-    def test_import_v1_database(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_database(self, mock_g):
         """Test that a database can be imported"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "metadata.yaml": yaml.safe_dump(database_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -421,7 +425,8 @@ class TestImportDatabasesCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_database_broken_csv_fields(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_broken_csv_fields(self, mock_g):
         """
         Test that a database can be imported with broken schema.
 
@@ -429,6 +434,8 @@ class TestImportDatabasesCommand(SupersetTestCase):
         the V1 schema. This test ensures that we can import databases that were
         exported with the broken schema.
         """
+        mock_g.user = security_manager.find_user("admin")
+
         broken_config = database_config.copy()
         broken_config["allow_file_upload"] = broken_config.pop("allow_csv_upload")
         broken_config["extra"] = {"schemas_allowed_for_file_upload": ["upload"]}
@@ -457,8 +464,11 @@ class TestImportDatabasesCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_database_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_multiple(self, mock_g):
         """Test that a database can be imported multiple times"""
+        mock_g.user = security_manager.find_user("admin")
+
         num_databases = db.session.query(Database).count()
 
         contents = {
@@ -498,8 +508,11 @@ class TestImportDatabasesCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_database_with_dataset(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_with_dataset(self, mock_g):
         """Test that a database can be imported with datasets"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
             "datasets/imported_dataset.yaml": yaml.safe_dump(dataset_config),
@@ -518,8 +531,11 @@ class TestImportDatabasesCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_database_with_dataset_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_with_dataset_multiple(self, mock_g):
         """Test that a database can be imported multiple times w/o changing datasets"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
             "datasets/imported_dataset.yaml": yaml.safe_dump(dataset_config),
@@ -623,6 +639,200 @@ class TestImportDatabasesCommand(SupersetTestCase):
             }
         }
 
+    @patch("superset.databases.schemas.is_feature_enabled")
+    def test_import_v1_database_masked_ssh_tunnel_password(
+        self, mock_schema_is_feature_enabled
+    ):
+        """Test that database imports with masked ssh_tunnel passwords are rejected"""
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_password.copy()
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        with pytest.raises(CommandInvalidError) as excinfo:
+            command.run()
+        assert str(excinfo.value) == "Error importing database"
+        assert excinfo.value.normalized_messages() == {
+            "databases/imported_database.yaml": {
+                "_schema": ["Must provide a password for the ssh tunnel"]
+            }
+        }
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    def test_import_v1_database_masked_ssh_tunnel_private_key_and_password(
+        self, mock_schema_is_feature_enabled
+    ):
+        """Test that database imports with masked ssh_tunnel private_key and private_key_password are rejected"""
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_private_key.copy()
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        with pytest.raises(CommandInvalidError) as excinfo:
+            command.run()
+        assert str(excinfo.value) == "Error importing database"
+        assert excinfo.value.normalized_messages() == {
+            "databases/imported_database.yaml": {
+                "_schema": [
+                    "Must provide a private key for the ssh tunnel",
+                    "Must provide a private key password for the ssh tunnel",
+                ]
+            }
+        }
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_with_ssh_tunnel_password(
+        self,
+        mock_g,
+        mock_schema_is_feature_enabled,
+    ):
+        """Test that a database with ssh_tunnel password can be imported"""
+        mock_g.user = security_manager.find_user("admin")
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_password.copy()
+        masked_database_config["ssh_tunnel"]["password"] = "TEST"
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        command.run()
+
+        database = (
+            db.session.query(Database).filter_by(uuid=database_config["uuid"]).one()
+        )
+        assert database.allow_file_upload
+        assert database.allow_ctas
+        assert database.allow_cvas
+        assert database.allow_dml
+        assert not database.allow_run_async
+        assert database.cache_timeout is None
+        assert database.database_name == "imported_database"
+        assert database.expose_in_sqllab
+        assert database.extra == "{}"
+        assert database.sqlalchemy_uri == "sqlite:///test.db"
+
+        model_ssh_tunnel = (
+            db.session.query(SSHTunnel)
+            .filter(SSHTunnel.database_id == database.id)
+            .one()
+        )
+        self.assertEqual(model_ssh_tunnel.password, "TEST")
+
+        db.session.delete(database)
+        db.session.commit()
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    @patch("superset.security.manager.g")
+    def test_import_v1_database_with_ssh_tunnel_private_key_and_password(
+        self,
+        mock_g,
+        mock_schema_is_feature_enabled,
+    ):
+        """Test that a database with ssh_tunnel private_key and private_key_password can be imported"""
+        mock_g.user = security_manager.find_user("admin")
+
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_private_key.copy()
+        masked_database_config["ssh_tunnel"]["private_key"] = "TestPrivateKey"
+        masked_database_config["ssh_tunnel"]["private_key_password"] = "TEST"
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        command.run()
+
+        database = (
+            db.session.query(Database).filter_by(uuid=database_config["uuid"]).one()
+        )
+        assert database.allow_file_upload
+        assert database.allow_ctas
+        assert database.allow_cvas
+        assert database.allow_dml
+        assert not database.allow_run_async
+        assert database.cache_timeout is None
+        assert database.database_name == "imported_database"
+        assert database.expose_in_sqllab
+        assert database.extra == "{}"
+        assert database.sqlalchemy_uri == "sqlite:///test.db"
+
+        model_ssh_tunnel = (
+            db.session.query(SSHTunnel)
+            .filter(SSHTunnel.database_id == database.id)
+            .one()
+        )
+        self.assertEqual(model_ssh_tunnel.private_key, "TestPrivateKey")
+        self.assertEqual(model_ssh_tunnel.private_key_password, "TEST")
+
+        db.session.delete(database)
+        db.session.commit()
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    def test_import_v1_database_masked_ssh_tunnel_no_credentials(
+        self, mock_schema_is_feature_enabled
+    ):
+        """Test that databases with ssh_tunnels that have no credentials are rejected"""
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_no_credentials.copy()
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        with pytest.raises(CommandInvalidError) as excinfo:
+            command.run()
+        assert str(excinfo.value) == "Must provide credentials for the SSH Tunnel"
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    def test_import_v1_database_masked_ssh_tunnel_multiple_credentials(
+        self, mock_schema_is_feature_enabled
+    ):
+        """Test that databases with ssh_tunnels that have multiple credentials are rejected"""
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = database_with_ssh_tunnel_config_mix_credentials.copy()
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        with pytest.raises(CommandInvalidError) as excinfo:
+            command.run()
+        assert (
+            str(excinfo.value) == "Cannot have multiple credentials for the SSH Tunnel"
+        )
+
+    @patch("superset.databases.schemas.is_feature_enabled")
+    def test_import_v1_database_masked_ssh_tunnel_only_priv_key_psswd(
+        self, mock_schema_is_feature_enabled
+    ):
+        """Test that databases with ssh_tunnels that have multiple credentials are rejected"""
+        mock_schema_is_feature_enabled.return_value = True
+        masked_database_config = (
+            database_with_ssh_tunnel_config_private_pass_only.copy()
+        )
+        contents = {
+            "metadata.yaml": yaml.safe_dump(database_metadata_config),
+            "databases/imported_database.yaml": yaml.safe_dump(masked_database_config),
+        }
+        command = ImportDatabasesCommand(contents)
+        with pytest.raises(CommandInvalidError) as excinfo:
+            command.run()
+        assert str(excinfo.value) == "Error importing database"
+        assert excinfo.value.normalized_messages() == {
+            "databases/imported_database.yaml": {
+                "_schema": [
+                    "Must provide a private key for the ssh tunnel",
+                    "Must provide a private key password for the ssh tunnel",
+                ]
+            }
+        }
+
     @patch("superset.databases.commands.importers.v1.import_dataset")
     def test_import_v1_rollback(self, mock_import_dataset):
         """Test than on an exception everything is rolled back"""
@@ -648,11 +858,9 @@ class TestImportDatabasesCommand(SupersetTestCase):
 
 
 class TestTestConnectionDatabaseCommand(SupersetTestCase):
-    @mock.patch("superset.databases.dao.Database._get_sqla_engine")
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.Database._get_sqla_engine")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_connection_db_exception(
         self, mock_g, mock_event_logger, mock_get_sqla_engine
     ):
@@ -671,11 +879,9 @@ class TestTestConnectionDatabaseCommand(SupersetTestCase):
             )
         mock_event_logger.assert_called()
 
-    @mock.patch("superset.databases.dao.Database._get_sqla_engine")
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.Database._get_sqla_engine")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_connection_do_ping_exception(
         self, mock_g, mock_event_logger, mock_get_sqla_engine
     ):
@@ -696,11 +902,9 @@ class TestTestConnectionDatabaseCommand(SupersetTestCase):
             == SupersetErrorType.GENERIC_DB_ENGINE_ERROR
         )
 
-    @mock.patch("superset.databases.commands.test_connection.func_timeout")
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.commands.test_connection.func_timeout")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_connection_do_ping_timeout(
         self, mock_g, mock_event_logger, mock_func_timeout
     ):
@@ -720,11 +924,9 @@ class TestTestConnectionDatabaseCommand(SupersetTestCase):
             == SupersetErrorType.CONNECTION_DATABASE_TIMEOUT
         )
 
-    @mock.patch("superset.databases.dao.Database._get_sqla_engine")
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.Database._get_sqla_engine")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_connection_superset_security_connection(
         self, mock_g, mock_event_logger, mock_get_sqla_engine
     ):
@@ -745,11 +947,9 @@ class TestTestConnectionDatabaseCommand(SupersetTestCase):
 
         mock_event_logger.assert_called()
 
-    @mock.patch("superset.databases.dao.Database._get_sqla_engine")
-    @mock.patch(
-        "superset.databases.commands.test_connection.event_logger.log_with_context"
-    )
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.Database._get_sqla_engine")
+    @patch("superset.databases.commands.test_connection.event_logger.log_with_context")
+    @patch("superset.utils.core.g")
     def test_connection_db_api_exc(
         self, mock_g, mock_event_logger, mock_get_sqla_engine
     ):
@@ -772,9 +972,9 @@ class TestTestConnectionDatabaseCommand(SupersetTestCase):
         mock_event_logger.assert_called()
 
 
-@mock.patch("superset.db_engine_specs.base.is_hostname_valid")
-@mock.patch("superset.db_engine_specs.base.is_port_open")
-@mock.patch("superset.databases.commands.validate.DatabaseDAO")
+@patch("superset.db_engine_specs.base.is_hostname_valid")
+@patch("superset.db_engine_specs.base.is_port_open")
+@patch("superset.databases.commands.validate.DatabaseDAO")
 def test_validate(DatabaseDAO, is_port_open, is_hostname_valid, app_context):
     """
     Test parameter validation.
@@ -797,8 +997,8 @@ def test_validate(DatabaseDAO, is_port_open, is_hostname_valid, app_context):
     command.run()
 
 
-@mock.patch("superset.db_engine_specs.base.is_hostname_valid")
-@mock.patch("superset.db_engine_specs.base.is_port_open")
+@patch("superset.db_engine_specs.base.is_hostname_valid")
+@patch("superset.db_engine_specs.base.is_port_open")
 def test_validate_partial(is_port_open, is_hostname_valid, app_context):
     """
     Test parameter validation when only some parameters are present.
@@ -838,7 +1038,7 @@ def test_validate_partial(is_port_open, is_hostname_valid, app_context):
     ]
 
 
-@mock.patch("superset.db_engine_specs.base.is_hostname_valid")
+@patch("superset.db_engine_specs.base.is_hostname_valid")
 def test_validate_partial_invalid_hostname(is_hostname_valid, app_context):
     """
     Test parameter validation when only some parameters are present.
@@ -892,7 +1092,7 @@ def test_validate_partial_invalid_hostname(is_hostname_valid, app_context):
 
 
 class TestTablesDatabaseCommand(SupersetTestCase):
-    @mock.patch("superset.databases.dao.DatabaseDAO.find_by_id")
+    @patch("superset.databases.dao.DatabaseDAO.find_by_id")
     def test_database_tables_list_with_unknown_database(self, mock_find_by_id):
         mock_find_by_id.return_value = None
         command = TablesDatabaseCommand(1, "test", False)
@@ -901,9 +1101,9 @@ class TestTablesDatabaseCommand(SupersetTestCase):
             command.run()
             assert str(excinfo.value) == ("Database not found.")
 
-    @mock.patch("superset.databases.dao.DatabaseDAO.find_by_id")
-    @mock.patch("superset.security.manager.SupersetSecurityManager.can_access_database")
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.DatabaseDAO.find_by_id")
+    @patch("superset.security.manager.SupersetSecurityManager.can_access_database")
+    @patch("superset.utils.core.g")
     def test_database_tables_superset_exception(
         self, mock_g, mock_can_access_database, mock_find_by_id
     ):
@@ -920,9 +1120,9 @@ class TestTablesDatabaseCommand(SupersetTestCase):
             command.run()
             assert str(excinfo.value) == "Test Error"
 
-    @mock.patch("superset.databases.dao.DatabaseDAO.find_by_id")
-    @mock.patch("superset.security.manager.SupersetSecurityManager.can_access_database")
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.DatabaseDAO.find_by_id")
+    @patch("superset.security.manager.SupersetSecurityManager.can_access_database")
+    @patch("superset.utils.core.g")
     def test_database_tables_exception(
         self, mock_g, mock_can_access_database, mock_find_by_id
     ):
@@ -939,9 +1139,9 @@ class TestTablesDatabaseCommand(SupersetTestCase):
                 == "Unexpected error occurred, please check your logs for details"
             )
 
-    @mock.patch("superset.databases.dao.DatabaseDAO.find_by_id")
-    @mock.patch("superset.security.manager.SupersetSecurityManager.can_access_database")
-    @mock.patch("superset.utils.core.g")
+    @patch("superset.databases.dao.DatabaseDAO.find_by_id")
+    @patch("superset.security.manager.SupersetSecurityManager.can_access_database")
+    @patch("superset.utils.core.g")
     def test_database_tables_list_tables(
         self, mock_g, mock_can_access_database, mock_find_by_id
     ):
diff --git a/tests/integration_tests/datasets/commands_tests.py b/tests/integration_tests/datasets/commands_tests.py
index 0ce98477a0..6f5b796c6a 100644
--- a/tests/integration_tests/datasets/commands_tests.py
+++ b/tests/integration_tests/datasets/commands_tests.py
@@ -327,10 +327,11 @@ class TestImportDatasetsCommand(SupersetTestCase):
         db.session.commit()
 
     @patch("superset.datasets.commands.importers.v1.utils.g")
+    @patch("superset.security.manager.g")
     @pytest.mark.usefixtures("load_energy_table_with_slice")
-    def test_import_v1_dataset(self, mock_g):
+    def test_import_v1_dataset(self, sm_g, utils_g):
         """Test that we can import a dataset"""
-        mock_g.user = security_manager.find_user("admin")
+        admin = sm_g.user = utils_g.user = security_manager.find_user("admin")
         contents = {
             "metadata.yaml": yaml.safe_dump(dataset_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -360,7 +361,7 @@ class TestImportDatasetsCommand(SupersetTestCase):
         )
 
         # user should be included as one of the owners
-        assert dataset.owners == [mock_g.user]
+        assert dataset.owners == [admin]
 
         # database is also imported
         assert str(dataset.database.uuid) == "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89"
@@ -395,8 +396,11 @@ class TestImportDatasetsCommand(SupersetTestCase):
         db.session.delete(dataset.database)
         db.session.commit()
 
-    def test_import_v1_dataset_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_dataset_multiple(self, mock_g):
         """Test that a dataset can be imported multiple times"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "metadata.yaml": yaml.safe_dump(dataset_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -483,8 +487,11 @@ class TestImportDatasetsCommand(SupersetTestCase):
             }
         }
 
-    def test_import_v1_dataset_existing_database(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_dataset_existing_database(self, mock_g):
         """Test that a dataset can be imported when the database already exists"""
+        mock_g.user = security_manager.find_user("admin")
+
         # first import database...
         contents = {
             "metadata.yaml": yaml.safe_dump(database_metadata_config),
diff --git a/tests/integration_tests/fixtures/importexport.py b/tests/integration_tests/fixtures/importexport.py
index b624f3e63c..0f695f044e 100644
--- a/tests/integration_tests/fixtures/importexport.py
+++ b/tests/integration_tests/fixtures/importexport.py
@@ -360,6 +360,111 @@ database_config: Dict[str, Any] = {
     "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
     "version": "1.0.0",
 }
+database_with_ssh_tunnel_config_private_key: Dict[str, Any] = {
+    "allow_csv_upload": True,
+    "allow_ctas": True,
+    "allow_cvas": True,
+    "allow_dml": True,
+    "allow_run_async": False,
+    "cache_timeout": None,
+    "database_name": "imported_database",
+    "expose_in_sqllab": True,
+    "extra": {},
+    "sqlalchemy_uri": "sqlite:///test.db",
+    "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
+    "ssh_tunnel": {
+        "server_address": "localhost",
+        "server_port": 22,
+        "username": "Test",
+        "private_key": "XXXXXXXXXX",
+        "private_key_password": "XXXXXXXXXX",
+    },
+    "version": "1.0.0",
+}
+
+database_with_ssh_tunnel_config_password: Dict[str, Any] = {
+    "allow_csv_upload": True,
+    "allow_ctas": True,
+    "allow_cvas": True,
+    "allow_dml": True,
+    "allow_run_async": False,
+    "cache_timeout": None,
+    "database_name": "imported_database",
+    "expose_in_sqllab": True,
+    "extra": {},
+    "sqlalchemy_uri": "sqlite:///test.db",
+    "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
+    "ssh_tunnel": {
+        "server_address": "localhost",
+        "server_port": 22,
+        "username": "Test",
+        "password": "XXXXXXXXXX",
+    },
+    "version": "1.0.0",
+}
+
+database_with_ssh_tunnel_config_no_credentials: Dict[str, Any] = {
+    "allow_csv_upload": True,
+    "allow_ctas": True,
+    "allow_cvas": True,
+    "allow_dml": True,
+    "allow_run_async": False,
+    "cache_timeout": None,
+    "database_name": "imported_database",
+    "expose_in_sqllab": True,
+    "extra": {},
+    "sqlalchemy_uri": "sqlite:///test.db",
+    "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
+    "ssh_tunnel": {
+        "server_address": "localhost",
+        "server_port": 22,
+        "username": "Test",
+    },
+    "version": "1.0.0",
+}
+
+database_with_ssh_tunnel_config_mix_credentials: Dict[str, Any] = {
+    "allow_csv_upload": True,
+    "allow_ctas": True,
+    "allow_cvas": True,
+    "allow_dml": True,
+    "allow_run_async": False,
+    "cache_timeout": None,
+    "database_name": "imported_database",
+    "expose_in_sqllab": True,
+    "extra": {},
+    "sqlalchemy_uri": "sqlite:///test.db",
+    "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
+    "ssh_tunnel": {
+        "server_address": "localhost",
+        "server_port": 22,
+        "username": "Test",
+        "password": "XXXXXXXXXX",
+        "private_key": "XXXXXXXXXX",
+    },
+    "version": "1.0.0",
+}
+
+database_with_ssh_tunnel_config_private_pass_only: Dict[str, Any] = {
+    "allow_csv_upload": True,
+    "allow_ctas": True,
+    "allow_cvas": True,
+    "allow_dml": True,
+    "allow_run_async": False,
+    "cache_timeout": None,
+    "database_name": "imported_database",
+    "expose_in_sqllab": True,
+    "extra": {},
+    "sqlalchemy_uri": "sqlite:///test.db",
+    "uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7fe89",
+    "ssh_tunnel": {
+        "server_address": "localhost",
+        "server_port": 22,
+        "username": "Test",
+        "private_key_password": "XXXXXXXXXX",
+    },
+    "version": "1.0.0",
+}
 
 dataset_config: Dict[str, Any] = {
     "table_name": "imported_dataset",
diff --git a/tests/integration_tests/queries/saved_queries/commands_tests.py b/tests/integration_tests/queries/saved_queries/commands_tests.py
index bd90419155..5c7b862209 100644
--- a/tests/integration_tests/queries/saved_queries/commands_tests.py
+++ b/tests/integration_tests/queries/saved_queries/commands_tests.py
@@ -142,8 +142,11 @@ class TestExportSavedQueriesCommand(SupersetTestCase):
 
 
 class TestImportSavedQueriesCommand(SupersetTestCase):
-    def test_import_v1_saved_queries(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_saved_queries(self, mock_g):
         """Test that we can import a saved query"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "metadata.yaml": yaml.safe_dump(saved_queries_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
@@ -169,8 +172,11 @@ class TestImportSavedQueriesCommand(SupersetTestCase):
         db.session.delete(database)
         db.session.commit()
 
-    def test_import_v1_saved_queries_multiple(self):
+    @patch("superset.security.manager.g")
+    def test_import_v1_saved_queries_multiple(self, mock_g):
         """Test that a saved query can be imported multiple times"""
+        mock_g.user = security_manager.find_user("admin")
+
         contents = {
             "metadata.yaml": yaml.safe_dump(saved_queries_metadata_config),
             "databases/imported_database.yaml": yaml.safe_dump(database_config),
diff --git a/tests/unit_tests/charts/commands/importers/v1/import_test.py b/tests/unit_tests/charts/commands/importers/v1/import_test.py
index e29fd70fb8..06e0063fe9 100644
--- a/tests/unit_tests/charts/commands/importers/v1/import_test.py
+++ b/tests/unit_tests/charts/commands/importers/v1/import_test.py
@@ -18,19 +18,26 @@
 
 import copy
 
+import pytest
+from pytest_mock import MockFixture
 from sqlalchemy.orm.session import Session
 
+from superset.commands.exceptions import ImportFailedError
 
-def test_import_chart(session: Session) -> None:
+
+def test_import_chart(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a chart.
     """
+    from superset import security_manager
     from superset.charts.commands.importers.v1.utils import import_chart
     from superset.connectors.sqla.models import SqlaTable
     from superset.models.core import Database
     from superset.models.slice import Slice
     from tests.integration_tests.fixtures.importexport import chart_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -45,16 +52,19 @@ def test_import_chart(session: Session) -> None:
     assert chart.external_url is None
 
 
-def test_import_chart_managed_externally(session: Session) -> None:
+def test_import_chart_managed_externally(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a chart that is managed externally.
     """
+    from superset import security_manager
     from superset.charts.commands.importers.v1.utils import import_chart
     from superset.connectors.sqla.models import SqlaTable
     from superset.models.core import Database
     from superset.models.slice import Slice
     from tests.integration_tests.fixtures.importexport import chart_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -67,3 +77,34 @@ def test_import_chart_managed_externally(session: Session) -> None:
     chart = import_chart(session, config)
     assert chart.is_managed_externally is True
     assert chart.external_url == "https://example.org/my_chart"
+
+
+def test_import_chart_without_permission(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
+    """
+    Test importing a chart when a user doesn't have permissions to create.
+    """
+    from superset import security_manager
+    from superset.charts.commands.importers.v1.utils import import_chart
+    from superset.connectors.sqla.models import SqlaTable
+    from superset.models.core import Database
+    from superset.models.slice import Slice
+    from tests.integration_tests.fixtures.importexport import chart_config
+
+    mocker.patch.object(security_manager, "can_access", return_value=False)
+
+    engine = session.get_bind()
+    Slice.metadata.create_all(engine)  # pylint: disable=no-member
+
+    config = copy.deepcopy(chart_config)
+    config["datasource_id"] = 1
+    config["datasource_type"] = "table"
+
+    with pytest.raises(ImportFailedError) as excinfo:
+        import_chart(session, config)
+    assert (
+        str(excinfo.value)
+        == "Chart doesn't exist and user doesn't have permission to create charts"
+    )
diff --git a/tests/unit_tests/commands/importers/v1/assets_test.py b/tests/unit_tests/commands/importers/v1/assets_test.py
index 1a345ff2b9..d48eed1be7 100644
--- a/tests/unit_tests/commands/importers/v1/assets_test.py
+++ b/tests/unit_tests/commands/importers/v1/assets_test.py
@@ -17,6 +17,7 @@
 
 import copy
 
+from pytest_mock import MockFixture
 from sqlalchemy.orm.session import Session
 from sqlalchemy.sql import select
 
@@ -30,14 +31,17 @@ from tests.unit_tests.fixtures.assets_configs import (
 )
 
 
-def test_import_new_assets(session: Session) -> None:
+def test_import_new_assets(mocker: MockFixture, session: Session) -> None:
     """
     Test that all new assets are imported correctly.
     """
+    from superset import security_manager
     from superset.commands.importers.v1.assets import ImportAssetsCommand
     from superset.models.dashboard import dashboard_slices
     from superset.models.slice import Slice
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
     configs = {
@@ -59,14 +63,17 @@ def test_import_new_assets(session: Session) -> None:
     assert len(dashboard_ids) == expected_number_of_dashboards
 
 
-def test_import_adds_dashboard_charts(session: Session) -> None:
+def test_import_adds_dashboard_charts(mocker: MockFixture, session: Session) -> None:
     """
     Test that existing dashboards are updated with new charts.
     """
+    from superset import security_manager
     from superset.commands.importers.v1.assets import ImportAssetsCommand
     from superset.models.dashboard import dashboard_slices
     from superset.models.slice import Slice
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
     base_configs = {
@@ -95,14 +102,17 @@ def test_import_adds_dashboard_charts(session: Session) -> None:
     assert len(dashboard_ids) == expected_number_of_dashboards
 
 
-def test_import_removes_dashboard_charts(session: Session) -> None:
+def test_import_removes_dashboard_charts(mocker: MockFixture, session: Session) -> None:
     """
     Test that existing dashboards are updated without old charts.
     """
+    from superset import security_manager
     from superset.commands.importers.v1.assets import ImportAssetsCommand
     from superset.models.dashboard import dashboard_slices
     from superset.models.slice import Slice
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
     base_configs = {
diff --git a/tests/unit_tests/dashboards/commands/importers/v1/import_test.py b/tests/unit_tests/dashboards/commands/importers/v1/import_test.py
index 08f681d916..e07a23f6bf 100644
--- a/tests/unit_tests/dashboards/commands/importers/v1/import_test.py
+++ b/tests/unit_tests/dashboards/commands/importers/v1/import_test.py
@@ -18,19 +18,26 @@
 
 import copy
 
+import pytest
+from pytest_mock import MockFixture
 from sqlalchemy.orm.session import Session
 
+from superset.commands.exceptions import ImportFailedError
 
-def test_import_dashboard(session: Session) -> None:
+
+def test_import_dashboard(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a dashboard.
     """
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable
     from superset.dashboards.commands.importers.v1.utils import import_dashboard
     from superset.models.core import Database
     from superset.models.slice import Slice
     from tests.integration_tests.fixtures.importexport import dashboard_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -43,16 +50,22 @@ def test_import_dashboard(session: Session) -> None:
     assert dashboard.external_url is None
 
 
-def test_import_dashboard_managed_externally(session: Session) -> None:
+def test_import_dashboard_managed_externally(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
     """
     Test importing a dashboard that is managed externally.
     """
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable
     from superset.dashboards.commands.importers.v1.utils import import_dashboard
     from superset.models.core import Database
     from superset.models.slice import Slice
     from tests.integration_tests.fixtures.importexport import dashboard_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Slice.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -63,3 +76,32 @@ def test_import_dashboard_managed_externally(session: Session) -> None:
     dashboard = import_dashboard(session, config)
     assert dashboard.is_managed_externally is True
     assert dashboard.external_url == "https://example.org/my_dashboard"
+
+
+def test_import_dashboard_without_permission(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
+    """
+    Test importing a dashboard when a user doesn't have permissions to create.
+    """
+    from superset import security_manager
+    from superset.connectors.sqla.models import SqlaTable
+    from superset.dashboards.commands.importers.v1.utils import import_dashboard
+    from superset.models.core import Database
+    from superset.models.slice import Slice
+    from tests.integration_tests.fixtures.importexport import dashboard_config
+
+    mocker.patch.object(security_manager, "can_access", return_value=False)
+
+    engine = session.get_bind()
+    Slice.metadata.create_all(engine)  # pylint: disable=no-member
+
+    config = copy.deepcopy(dashboard_config)
+
+    with pytest.raises(ImportFailedError) as excinfo:
+        import_dashboard(session, config)
+    assert (
+        str(excinfo.value)
+        == "Dashboard doesn't exist and user doesn't have permission to create dashboards"
+    )
diff --git a/tests/unit_tests/databases/commands/importers/v1/import_test.py b/tests/unit_tests/databases/commands/importers/v1/import_test.py
index e665bcb505..f9d2695f26 100644
--- a/tests/unit_tests/databases/commands/importers/v1/import_test.py
+++ b/tests/unit_tests/databases/commands/importers/v1/import_test.py
@@ -18,17 +18,24 @@
 
 import copy
 
+import pytest
+from pytest_mock import MockFixture
 from sqlalchemy.orm.session import Session
 
+from superset.commands.exceptions import ImportFailedError
 
-def test_import_database(session: Session) -> None:
+
+def test_import_database(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a database.
     """
+    from superset import security_manager
     from superset.databases.commands.importers.v1.utils import import_database
     from superset.models.core import Database
     from tests.integration_tests.fixtures.importexport import database_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Database.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -58,14 +65,20 @@ def test_import_database(session: Session) -> None:
     assert database.allow_dml is False
 
 
-def test_import_database_managed_externally(session: Session) -> None:
+def test_import_database_managed_externally(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
     """
     Test importing a database that is managed externally.
     """
+    from superset import security_manager
     from superset.databases.commands.importers.v1.utils import import_database
     from superset.models.core import Database
     from tests.integration_tests.fixtures.importexport import database_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     Database.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -76,3 +89,30 @@ def test_import_database_managed_externally(session: Session) -> None:
     database = import_database(session, config)
     assert database.is_managed_externally is True
     assert database.external_url == "https://example.org/my_database"
+
+
+def test_import_database_without_permission(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
+    """
+    Test importing a database when a user doesn't have permissions to create.
+    """
+    from superset import security_manager
+    from superset.databases.commands.importers.v1.utils import import_database
+    from superset.models.core import Database
+    from tests.integration_tests.fixtures.importexport import database_config
+
+    mocker.patch.object(security_manager, "can_access", return_value=False)
+
+    engine = session.get_bind()
+    Database.metadata.create_all(engine)  # pylint: disable=no-member
+
+    config = copy.deepcopy(database_config)
+
+    with pytest.raises(ImportFailedError) as excinfo:
+        import_database(session, config)
+    assert (
+        str(excinfo.value)
+        == "Database doesn't exist and user doesn't have permission to create databases"
+    )
diff --git a/tests/unit_tests/datasets/commands/importers/v1/import_test.py b/tests/unit_tests/datasets/commands/importers/v1/import_test.py
index 5b52ac7f1d..839374425b 100644
--- a/tests/unit_tests/datasets/commands/importers/v1/import_test.py
+++ b/tests/unit_tests/datasets/commands/importers/v1/import_test.py
@@ -25,20 +25,27 @@ from unittest.mock import Mock, patch
 
 import pytest
 from flask import current_app
+from pytest_mock import MockFixture
 from sqlalchemy.orm.session import Session
 
-from superset.datasets.commands.exceptions import DatasetForbiddenDataURI
+from superset.datasets.commands.exceptions import (
+    DatasetForbiddenDataURI,
+    ImportFailedError,
+)
 from superset.datasets.commands.importers.v1.utils import validate_data_uri
 
 
-def test_import_dataset(session: Session) -> None:
+def test_import_dataset(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a dataset.
     """
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable
     from superset.datasets.commands.importers.v1.utils import import_dataset
     from superset.models.core import Database
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -143,15 +150,18 @@ def test_import_dataset(session: Session) -> None:
     assert sqla_table.database.id == database.id
 
 
-def test_import_dataset_duplicate_column(session: Session) -> None:
+def test_import_dataset_duplicate_column(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a dataset with a column that already exists.
     """
+    from superset import security_manager
     from superset.columns.models import Column as NewColumn
     from superset.connectors.sqla.models import SqlaTable, TableColumn
     from superset.datasets.commands.importers.v1.utils import import_dataset
     from superset.models.core import Database
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -266,15 +276,18 @@ def test_import_dataset_duplicate_column(session: Session) -> None:
     assert sqla_table.database.id == database.id
 
 
-def test_import_column_extra_is_string(session: Session) -> None:
+def test_import_column_extra_is_string(mocker: MockFixture, session: Session) -> None:
     """
     Test importing a dataset when the column extra is a string.
     """
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable, SqlMetric, TableColumn
     from superset.datasets.commands.importers.v1.utils import import_dataset
     from superset.datasets.schemas import ImportV1DatasetSchema
     from superset.models.core import Database
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -347,12 +360,17 @@ def test_import_column_extra_is_string(session: Session) -> None:
 
 
 @patch("superset.datasets.commands.importers.v1.utils.request")
-def test_import_column_allowed_data_url(request: Mock, session: Session) -> None:
+def test_import_column_allowed_data_url(
+    request: Mock,
+    mocker: MockFixture,
+    session: Session,
+) -> None:
     """
     Test importing a dataset when using data key to fetch data from a URL.
     """
     import io
 
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable
     from superset.datasets.commands.importers.v1.utils import import_dataset
     from superset.datasets.schemas import ImportV1DatasetSchema
@@ -360,6 +378,8 @@ def test_import_column_allowed_data_url(request: Mock, session: Session) -> None
 
     request.urlopen.return_value = io.StringIO("col1\nvalue1\nvalue2\n")
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
 
@@ -419,15 +439,21 @@ def test_import_column_allowed_data_url(request: Mock, session: Session) -> None
     ).fetchall()
 
 
-def test_import_dataset_managed_externally(session: Session) -> None:
+def test_import_dataset_managed_externally(
+    mocker: MockFixture,
+    session: Session,
+) -> None:
     """
     Test importing a dataset that is managed externally.
     """
+    from superset import security_manager
     from superset.connectors.sqla.models import SqlaTable
     from superset.datasets.commands.importers.v1.utils import import_dataset
     from superset.models.core import Database
     from tests.integration_tests.fixtures.importexport import dataset_config
 
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+
     engine = session.get_bind()
     SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
 


[superset] 06/07: add license to package and plugin readme files

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 4345a148416c9eb80214a69761e369d8dca9bd16
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Wed May 31 16:50:33 2023 -0700

    add license to package and plugin readme files
---
 superset-frontend/CHANGELOG.md                        | 18 +++++++++++++++++-
 .../packages/generator-superset/CHANGELOG.md          | 19 ++++++++++++++++++-
 .../packages/superset-ui-chart-controls/CHANGELOG.md  | 18 +++++++++++++++++-
 .../packages/superset-ui-core/CHANGELOG.md            | 19 ++++++++++++++++++-
 .../packages/superset-ui-switchboard/CHANGELOG.md     | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-calendar/CHANGELOG.md | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-chord/CHANGELOG.md    | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-country-map/CHANGELOG.md      | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-event-flow/CHANGELOG.md       | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-heatmap/CHANGELOG.md  | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-histogram/CHANGELOG.md        | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-horizon/CHANGELOG.md  | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-map-box/CHANGELOG.md  | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-paired-t-test/CHANGELOG.md    | 19 ++++++++++++++++++-
 .../CHANGELOG.md                                      | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-partition/CHANGELOG.md        | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-pivot-table/CHANGELOG.md      | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-rose/CHANGELOG.md     | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-sankey-loop/CHANGELOG.md      | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-sankey/CHANGELOG.md   | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-sunburst/CHANGELOG.md | 19 ++++++++++++++++++-
 .../plugins/legacy-plugin-chart-treemap/CHANGELOG.md  | 19 ++++++++++++++++++-
 .../legacy-plugin-chart-world-map/CHANGELOG.md        | 19 ++++++++++++++++++-
 .../plugins/legacy-preset-chart-deckgl/CHANGELOG.md   | 19 ++++++++++++++++++-
 .../plugins/legacy-preset-chart-nvd3/CHANGELOG.md     | 19 ++++++++++++++++++-
 .../plugins/plugin-chart-echarts/CHANGELOG.md         | 19 ++++++++++++++++++-
 .../plugins/plugin-chart-handlebars/CHANGELOG.md      | 19 ++++++++++++++++++-
 .../plugins/plugin-chart-pivot-table/CHANGELOG.md     | 19 ++++++++++++++++++-
 .../plugins/plugin-chart-table/CHANGELOG.md           | 19 ++++++++++++++++++-
 .../plugins/plugin-chart-word-cloud/CHANGELOG.md      | 19 ++++++++++++++++++-
 .../plugins/preset-chart-xy/CHANGELOG.md              | 19 ++++++++++++++++++-
 31 files changed, 556 insertions(+), 31 deletions(-)

diff --git a/superset-frontend/CHANGELOG.md b/superset-frontend/CHANGELOG.md
index dcc8959813..2a2a07c250 100644
--- a/superset-frontend/CHANGELOG.md
+++ b/superset-frontend/CHANGELOG.md
@@ -1,4 +1,20 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/packages/generator-superset/CHANGELOG.md b/superset-frontend/packages/generator-superset/CHANGELOG.md
index 24fd28122c..b62b507b60 100644
--- a/superset-frontend/packages/generator-superset/CHANGELOG.md
+++ b/superset-frontend/packages/generator-superset/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/packages/superset-ui-chart-controls/CHANGELOG.md b/superset-frontend/packages/superset-ui-chart-controls/CHANGELOG.md
index 4d91702798..f179205629 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/CHANGELOG.md
+++ b/superset-frontend/packages/superset-ui-chart-controls/CHANGELOG.md
@@ -1,4 +1,20 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/packages/superset-ui-core/CHANGELOG.md b/superset-frontend/packages/superset-ui-core/CHANGELOG.md
index 8c988454a3..a72967cbf6 100644
--- a/superset-frontend/packages/superset-ui-core/CHANGELOG.md
+++ b/superset-frontend/packages/superset-ui-core/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/packages/superset-ui-switchboard/CHANGELOG.md b/superset-frontend/packages/superset-ui-switchboard/CHANGELOG.md
index 87a1707afd..e1008e20c2 100644
--- a/superset-frontend/packages/superset-ui-switchboard/CHANGELOG.md
+++ b/superset-frontend/packages/superset-ui-switchboard/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-calendar/CHANGELOG.md
index 7929fddb21..5900463500 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-chord/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-chord/CHANGELOG.md
index 15afd684f6..1ce90346d5 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-chord/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-chord/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-country-map/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-country-map/CHANGELOG.md
index 4538d952cd..c6ff32c11e 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-country-map/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-country-map/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-event-flow/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-event-flow/CHANGELOG.md
index 4e619913f4..e52e4bf1ad 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-event-flow/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-event-flow/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-heatmap/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-heatmap/CHANGELOG.md
index 16acc873fb..40bc9f6a78 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-heatmap/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-heatmap/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-histogram/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-histogram/CHANGELOG.md
index 58bf904871..47f6d82715 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-histogram/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-histogram/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-horizon/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-horizon/CHANGELOG.md
index 4977399434..f9b46a6651 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-horizon/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-horizon/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md
index ca05e28088..7e48f054a3 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/CHANGELOG.md
index 7d53c44e48..99f137ea08 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/CHANGELOG.md
index a577319271..0e0290c63f 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-partition/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-partition/CHANGELOG.md
index 736d256f15..3b1121dc71 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-partition/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-partition/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-pivot-table/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-pivot-table/CHANGELOG.md
index cc0b6be2ff..b7aa7c6b58 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-pivot-table/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-pivot-table/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-rose/CHANGELOG.md
index 458b02a73b..752803f987 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-rose/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-rose/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-sankey-loop/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-sankey-loop/CHANGELOG.md
index d966108f15..1fe75014b3 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-sankey-loop/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-sankey-loop/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-sankey/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-sankey/CHANGELOG.md
index 41cecbe0f5..76ea22dafd 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-sankey/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-sankey/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-sunburst/CHANGELOG.md
index aeff058ee5..d93fc4b7d9 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-sunburst/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-treemap/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-treemap/CHANGELOG.md
index dafa3ded83..cb7d9eb745 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-treemap/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-treemap/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-world-map/CHANGELOG.md
index 7ae7791827..cbfd92b54b 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-world-map/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md b/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md
index 6fcd18f5f8..9a3acc262b 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/CHANGELOG.md b/superset-frontend/plugins/legacy-preset-chart-nvd3/CHANGELOG.md
index a4df7dd5fd..1c7b44cc15 100644
--- a/superset-frontend/plugins/legacy-preset-chart-nvd3/CHANGELOG.md
+++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/plugin-chart-echarts/CHANGELOG.md b/superset-frontend/plugins/plugin-chart-echarts/CHANGELOG.md
index a79d77cfb0..963f420e5e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/CHANGELOG.md
+++ b/superset-frontend/plugins/plugin-chart-echarts/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/plugin-chart-handlebars/CHANGELOG.md b/superset-frontend/plugins/plugin-chart-handlebars/CHANGELOG.md
index ad44047778..fcf6471725 100644
--- a/superset-frontend/plugins/plugin-chart-handlebars/CHANGELOG.md
+++ b/superset-frontend/plugins/plugin-chart-handlebars/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/CHANGELOG.md b/superset-frontend/plugins/plugin-chart-pivot-table/CHANGELOG.md
index 83762f4f5a..ce5fd2e095 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/CHANGELOG.md
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/plugin-chart-table/CHANGELOG.md b/superset-frontend/plugins/plugin-chart-table/CHANGELOG.md
index 399354086e..7a1a067059 100644
--- a/superset-frontend/plugins/plugin-chart-table/CHANGELOG.md
+++ b/superset-frontend/plugins/plugin-chart-table/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/plugin-chart-word-cloud/CHANGELOG.md b/superset-frontend/plugins/plugin-chart-word-cloud/CHANGELOG.md
index 9a97af0b2c..12d3200dfa 100644
--- a/superset-frontend/plugins/plugin-chart-word-cloud/CHANGELOG.md
+++ b/superset-frontend/plugins/plugin-chart-word-cloud/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
diff --git a/superset-frontend/plugins/preset-chart-xy/CHANGELOG.md b/superset-frontend/plugins/preset-chart-xy/CHANGELOG.md
index 7b5b8ea01e..4631c68d1a 100644
--- a/superset-frontend/plugins/preset-chart-xy/CHANGELOG.md
+++ b/superset-frontend/plugins/preset-chart-xy/CHANGELOG.md
@@ -1,4 +1,21 @@
-# Change Log
+ <!-- * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License. -->
+
+ # Change Log
 
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.


[superset] 07/07: lint

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch elizabeth/test-2.1.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 2f3471a87e66b7a0115b523ea29298295178a6bd
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Wed May 31 17:19:58 2023 -0700

    lint
---
 superset/db_engine_specs/base.py  | 4 ++--
 superset/db_engine_specs/mysql.py | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py
index 21aa171323..af2699a6dd 100644
--- a/superset/db_engine_specs/base.py
+++ b/superset/db_engine_specs/base.py
@@ -999,8 +999,8 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
     def adjust_database_uri(  # pylint: disable=unused-argument
         cls,
         uri: URL,
-        selected_schema: Optional[str],
-    ) -> URL:
+        selected_schema: Optional[str] = None,
+    ) -> Tuple[URL, Dict[str, Any]]:
         """
         Return a modified URL with a new database component.
 
diff --git a/superset/db_engine_specs/mysql.py b/superset/db_engine_specs/mysql.py
index 28ef442319..457509f7a7 100644
--- a/superset/db_engine_specs/mysql.py
+++ b/superset/db_engine_specs/mysql.py
@@ -192,7 +192,7 @@ class MySQLEngineSpec(BaseEngineSpec, BasicParametersMixin):
     @classmethod
     def adjust_database_uri(
         cls, uri: URL, selected_schema: Optional[str] = None
-    ) -> URL:
+    ) -> Tuple[URL, Dict[str, Any]]:
         uri, new_connect_args = super(
             MySQLEngineSpec, MySQLEngineSpec
         ).adjust_database_uri(uri)