From 423b937623b8d4b66840cf6ef72a6b956340f62c Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Tue, 1 Nov 2022 14:55:08 +0200 Subject: [PATCH 1/6] feat(rbac): add customizable RBAC roles filter --- superset/dashboards/filters.py | 14 +++++++++----- superset/views/base_api.py | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py index e09609ff511e0..68a61c065a845 100644 --- a/superset/dashboards/filters.py +++ b/superset/dashboards/filters.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +import re import uuid from typing import Any, Optional, Union @@ -33,6 +34,8 @@ from superset.views.base import BaseFilter from superset.views.base_api import BaseFavoriteFilter +DAR_REGEX = re.compile("DAR-.+") + class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-methods name = _("Title or Slug") @@ -201,11 +204,12 @@ class FilterRelatedRoles(BaseFilter): # pylint: disable=too-few-public-methods def apply(self, query: Query, value: Optional[Any]) -> Query: role_model = security_manager.role_model - if value: - return query.filter( - role_model.name.ilike(f"%{value}%"), - ) - return query + role_filters = [ + role_model.name.ilike(f"{role}%") + for role in security_manager.get_user_roles() + if DAR_REGEX.match(role.name) + ] + return query.filter(or_(*role_filters)) class DashboardCertifiedFilter(BaseFilter): # pylint: disable=too-few-public-methods diff --git a/superset/views/base_api.py b/superset/views/base_api.py index 47fc611ba21a0..9f00a8df41e72 100644 --- a/superset/views/base_api.py +++ b/superset/views/base_api.py @@ -308,7 +308,7 @@ def _get_related_filter( base_filters = self.base_related_field_filters.get(column_name) if base_filters: filters.add_filter_list(base_filters) - if value and filter_field: + if filter_field: filters.add_filter( filter_field.field_name, filter_field.filter_class, value ) From cbcdc160d4f2f50b24a41b3ad0110ec119b5e152 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Thu, 22 Dec 2022 14:56:44 +0200 Subject: [PATCH 2/6] implement customizable base filters --- superset/config.py | 27 +++++++++++++++++++ superset/dashboards/filters.py | 21 +++++++-------- superset/views/filters.py | 24 +++++++++++------ .../integration_tests/dashboards/api_tests.py | 23 ++++++++++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/superset/config.py b/superset/config.py index 948e234e4ca0c..fb72ad7caf385 100644 --- a/superset/config.py +++ b/superset/config.py @@ -44,6 +44,7 @@ Tuple, Type, TYPE_CHECKING, + TypedDict, Union, ) @@ -54,6 +55,7 @@ from flask import Blueprint from flask_appbuilder.security.manager import AUTH_DB from pandas._libs.parsers import STR_NA_VALUES # pylint: disable=no-name-in-module +from sqlalchemy.orm.query import Query from superset.advanced_data_type.plugins.internet_address import internet_address from superset.advanced_data_type.plugins.internet_port import internet_port @@ -77,6 +79,7 @@ from superset.models.core import Database from superset.models.dashboard import Dashboard from superset.models.slice import Slice + from superset.security import SupersetSecurityManager # Realtime stats logger, a StatsD implementation exists STATS_LOGGER = DummyStatsLogger() @@ -1480,6 +1483,30 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument }, } + +# RBAC base filters make it possible to limit which objects are shown in the UI. +# For examples, to only show "admin" or users starting with the letter "b" in the +# "Onwers" dropdowns, you could add the following in your config: +# def user_filter(query, security_manager): +# user_model = security_manager.user_model +# filters = [ +# user_model.username == "admin", +# user_model.username.ilike("b%"), +# ] +# return query.filter(or_(*filters)) +# +# RBAC_BASE_FILTERS = {"user": user_filter} +# +# Similarly, to restrict the roles in the "Roles" dropdown you can provide a custom +# filter callback for the "role" key. +class RbacBaseFilters(TypedDict, total=False): + role: Callable[[Query, SupersetSecurityManager], Query] + user: Callable[[Query, SupersetSecurityManager], Query] + + +RBAC_BASE_FILTERS: RbacBaseFilters = {} + + # ------------------------------------------------------------------- # * WARNING: STOP EDITING HERE * # ------------------------------------------------------------------- diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py index 68a61c065a845..9f6458a8d805d 100644 --- a/superset/dashboards/filters.py +++ b/superset/dashboards/filters.py @@ -18,7 +18,7 @@ import uuid from typing import Any, Optional, Union -from flask import g +from flask import current_app, g from flask_appbuilder.security.sqla.models import Role from flask_babel import lazy_gettext as _ from sqlalchemy import and_, or_ @@ -34,8 +34,6 @@ from superset.views.base import BaseFilter from superset.views.base_api import BaseFavoriteFilter -DAR_REGEX = re.compile("DAR-.+") - class DashboardTitleOrSlugFilter(BaseFilter): # pylint: disable=too-few-public-methods name = _("Title or Slug") @@ -202,14 +200,15 @@ class FilterRelatedRoles(BaseFilter): # pylint: disable=too-few-public-methods name = _("Role") arg_name = "roles" - def apply(self, query: Query, value: Optional[Any]) -> Query: - role_model = security_manager.role_model - role_filters = [ - role_model.name.ilike(f"{role}%") - for role in security_manager.get_user_roles() - if DAR_REGEX.match(role.name) - ] - return query.filter(or_(*role_filters)) + def apply(self, query: Query, value: Optional[str]) -> Query: + if base_filter := current_app.config["RBAC_BASE_FILTERS"].get("role"): + query = base_filter(query, security_manager) + + if value: + role_model = security_manager.role_model + query = query.filter(role_model.name.like(f"{value}%")) + + return query class DashboardCertifiedFilter(BaseFilter): # pylint: disable=too-few-public-methods diff --git a/superset/views/filters.py b/superset/views/filters.py index 9450a830332d8..93d331c23a2cd 100644 --- a/superset/views/filters.py +++ b/superset/views/filters.py @@ -43,15 +43,23 @@ class FilterRelatedOwners(BaseFilter): # pylint: disable=too-few-public-methods arg_name = "owners" def apply(self, query: Query, value: Optional[Any]) -> Query: - user_model = security_manager.user_model - like_value = "%" + cast(str, value) + "%" - return query.filter( - or_( - # could be made to handle spaces between names more gracefully - (user_model.first_name + " " + user_model.last_name).ilike(like_value), - user_model.username.ilike(like_value), + if base_filter := current_app.config["RBAC_BASE_FILTERS"].get("user"): + query = base_filter(query, security_manager) + + if value: + user_model = security_manager.user_model + like_value = "%" + cast(str, value) + "%" + return query.filter( + or_( + # could be made to handle spaces between names more gracefully + (user_model.first_name + " " + user_model.last_name).ilike( + like_value + ), + user_model.username.ilike(like_value), + ) ) - ) + + return query class BaseFilterRelatedUsers(BaseFilter): # pylint: disable=too-few-public-methods diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index 1ca80aae38dfe..e73a82d7a08ac 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -1828,6 +1828,29 @@ def test_get_filter_related_roles(self): response_roles = [result["text"] for result in response["result"]] assert "Alpha" in response_roles + def test_get_all_related_roles_with_base_filter(self): + """ + API: Test get filter related roles with custom base filter + """ + self.login(username="admin") + + def _base_filter(query, _security_manager): + role_model = _security_manager.role_model + return query.filter(role_model.name == "Alpha") + + with patch.dict( + "superset.dashboards.filters.current_app.config", + {"RBAC_BASE_FILTERS": {"role": _base_filter}}, + ): + uri = f"api/v1/dashboard/related/roles" + rv = self.client.get(uri) + assert rv.status_code == 200 + response = json.loads(rv.data.decode("utf-8")) + assert response["count"] == 1 + + response_roles = [result["text"] for result in response["result"]] + assert response_roles == ["Alpha"] + @pytest.mark.usefixtures("load_world_bank_dashboard_with_slices") def test_embedded_dashboards(self): self.login(username="admin") From 9eecdb46fb31a69cde96f4908ad2069398fb54f5 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Thu, 22 Dec 2022 16:12:40 +0200 Subject: [PATCH 3/6] more --- superset/config.py | 16 ++++++------ superset/dashboards/filters.py | 6 ++--- superset/views/filters.py | 8 +++--- tests/integration_tests/base_api_tests.py | 25 +++++++++++++++++++ .../integration_tests/dashboards/api_tests.py | 11 ++++---- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/superset/config.py b/superset/config.py index fb72ad7caf385..6a0686c1ad8ac 100644 --- a/superset/config.py +++ b/superset/config.py @@ -53,6 +53,7 @@ from celery.schedules import crontab from dateutil import tz from flask import Blueprint +from flask_appbuilder.models.sqla import Model from flask_appbuilder.security.manager import AUTH_DB from pandas._libs.parsers import STR_NA_VALUES # pylint: disable=no-name-in-module from sqlalchemy.orm.query import Query @@ -79,7 +80,6 @@ from superset.models.core import Database from superset.models.dashboard import Dashboard from superset.models.slice import Slice - from superset.security import SupersetSecurityManager # Realtime stats logger, a StatsD implementation exists STATS_LOGGER = DummyStatsLogger() @@ -1487,7 +1487,9 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument # RBAC base filters make it possible to limit which objects are shown in the UI. # For examples, to only show "admin" or users starting with the letter "b" in the # "Onwers" dropdowns, you could add the following in your config: -# def user_filter(query, security_manager): +# def user_filter(query: Query, *args, *kwargs): +# from superset import security_manager +# # user_model = security_manager.user_model # filters = [ # user_model.username == "admin", @@ -1495,16 +1497,16 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument # ] # return query.filter(or_(*filters)) # -# RBAC_BASE_FILTERS = {"user": user_filter} +# RELATED_QUERY_MUTATORS = {"user": user_filter} # # Similarly, to restrict the roles in the "Roles" dropdown you can provide a custom # filter callback for the "role" key. -class RbacBaseFilters(TypedDict, total=False): - role: Callable[[Query, SupersetSecurityManager], Query] - user: Callable[[Query, SupersetSecurityManager], Query] +class RelatedQueryMutators(TypedDict, total=False): + role: Callable[[Query], Query] + user: Callable[[Query], Query] -RBAC_BASE_FILTERS: RbacBaseFilters = {} +RELATED_QUERY_MUTATORS: RelatedQueryMutators = {} # ------------------------------------------------------------------- diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py index 9f6458a8d805d..910f890b2a0bb 100644 --- a/superset/dashboards/filters.py +++ b/superset/dashboards/filters.py @@ -201,12 +201,12 @@ class FilterRelatedRoles(BaseFilter): # pylint: disable=too-few-public-methods arg_name = "roles" def apply(self, query: Query, value: Optional[str]) -> Query: - if base_filter := current_app.config["RBAC_BASE_FILTERS"].get("role"): - query = base_filter(query, security_manager) + if base_filter := current_app.config["RELATED_QUERY_MUTATORS"].get("role"): + query = base_filter(query) if value: role_model = security_manager.role_model - query = query.filter(role_model.name.like(f"{value}%")) + return query.filter(role_model.name.like(f"{value}%")) return query diff --git a/superset/views/filters.py b/superset/views/filters.py index 93d331c23a2cd..37e85b1433c1c 100644 --- a/superset/views/filters.py +++ b/superset/views/filters.py @@ -42,13 +42,13 @@ class FilterRelatedOwners(BaseFilter): # pylint: disable=too-few-public-methods name = lazy_gettext("Owner") arg_name = "owners" - def apply(self, query: Query, value: Optional[Any]) -> Query: - if base_filter := current_app.config["RBAC_BASE_FILTERS"].get("user"): - query = base_filter(query, security_manager) + def apply(self, query: Query, value: Optional[str]) -> Query: + if base_filter := current_app.config["RELATED_QUERY_MUTATORS"].get("user"): + query = base_filter(query) if value: user_model = security_manager.user_model - like_value = "%" + cast(str, value) + "%" + like_value = f"%{value}%" return query.filter( or_( # could be made to handle spaces between names more gracefully diff --git a/tests/integration_tests/base_api_tests.py b/tests/integration_tests/base_api_tests.py index 66544f1447e05..73171a71fc3f3 100644 --- a/tests/integration_tests/base_api_tests.py +++ b/tests/integration_tests/base_api_tests.py @@ -219,6 +219,31 @@ def test_get_related_owners(self): for expected_user in expected_users: assert expected_user in response_users + def test_get_related_owners_with_query_mutator(self): + """ + API: Test get related owners with query mutator + """ + self.login(username="admin") + + def _base_filter(query): + return query.filter_by(username="alpha") + + with patch.dict( + "superset.dashboards.filters.current_app.config", + {"RELATED_QUERY_MUTATORS": {"user": _base_filter}}, + ): + uri = f"api/v1/{self.resource_name}/related/owners" + rv = self.client.get(uri) + assert rv.status_code == 200 + response = json.loads(rv.data.decode("utf-8")) + users = db.session.query(security_manager.user_model).all() + expected_users = [str(user) for user in users] + assert response["count"] == 1 + # This needs to be implemented like this, because ordering varies between + # postgres and mysql + response_users = [result["text"] for result in response["result"]] + assert response_users == ["alpha user"] + def test_get_related_owners_paginated(self): """ API: Test get related owners with pagination diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index e73a82d7a08ac..8ab0bc277c4e4 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -1828,19 +1828,18 @@ def test_get_filter_related_roles(self): response_roles = [result["text"] for result in response["result"]] assert "Alpha" in response_roles - def test_get_all_related_roles_with_base_filter(self): + def test_get_all_related_roles_with_query_mutator(self): """ - API: Test get filter related roles with custom base filter + API: Test get filter related roles with query mutator """ self.login(username="admin") - def _base_filter(query, _security_manager): - role_model = _security_manager.role_model - return query.filter(role_model.name == "Alpha") + def _base_filter(query): + return query.filter_by(name="Alpha") with patch.dict( "superset.dashboards.filters.current_app.config", - {"RBAC_BASE_FILTERS": {"role": _base_filter}}, + {"RELATED_QUERY_MUTATORS": {"role": _base_filter}}, ): uri = f"api/v1/dashboard/related/roles" rv = self.client.get(uri) From 2f10e2a8d2e781f4d06f7bd578e15e73dc6288fd Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Thu, 22 Dec 2022 17:32:24 +0200 Subject: [PATCH 4/6] refactor --- superset/config.py | 12 ++-- superset/dashboards/api.py | 8 ++- superset/dashboards/filters.py | 15 ++--- superset/views/base_api.py | 2 +- superset/views/filters.py | 56 ++++++++++++------- tests/integration_tests/base_api_tests.py | 2 +- .../integration_tests/dashboards/api_tests.py | 2 +- 7 files changed, 58 insertions(+), 39 deletions(-) diff --git a/superset/config.py b/superset/config.py index 6a0686c1ad8ac..2c6b2e17c7db1 100644 --- a/superset/config.py +++ b/superset/config.py @@ -1484,9 +1484,9 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument } -# RBAC base filters make it possible to limit which objects are shown in the UI. -# For examples, to only show "admin" or users starting with the letter "b" in the -# "Onwers" dropdowns, you could add the following in your config: +# Extra related query filters make it possible to limit which objects are shown +# in the UI. For examples, to only show "admin" or users starting with the letter "b" in +# the "Owners" dropdowns, you could add the following in your config: # def user_filter(query: Query, *args, *kwargs): # from superset import security_manager # @@ -1497,16 +1497,16 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument # ] # return query.filter(or_(*filters)) # -# RELATED_QUERY_MUTATORS = {"user": user_filter} +# EXTRA_RELATED_QUERY_FILTERS = {"user": user_filter} # # Similarly, to restrict the roles in the "Roles" dropdown you can provide a custom # filter callback for the "role" key. -class RelatedQueryMutators(TypedDict, total=False): +class ExtraRelatedQueryFilters(TypedDict, total=False): role: Callable[[Query], Query] user: Callable[[Query], Query] -RELATED_QUERY_MUTATORS: RelatedQueryMutators = {} +EXTRA_RELATED_QUERY_FILTERS: ExtraRelatedQueryFilters = {} # ------------------------------------------------------------------- diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index 615855123d263..64ea637c663d8 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -95,7 +95,11 @@ requires_json, statsd_metrics, ) -from superset.views.filters import BaseFilterRelatedUsers, FilterRelatedOwners +from superset.views.filters import ( + BaseFilterRelatedRoles, + BaseFilterRelatedUsers, + FilterRelatedOwners, +) logger = logging.getLogger(__name__) @@ -244,7 +248,9 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]: base_related_field_filters = { "owners": [["id", BaseFilterRelatedUsers, lambda: []]], "created_by": [["id", BaseFilterRelatedUsers, lambda: []]], + "roles": [["id", BaseFilterRelatedRoles, lambda: []]], } + related_field_filters = { "owners": RelatedFieldFilter("first_name", FilterRelatedOwners), "roles": RelatedFieldFilter("name", FilterRelatedRoles), diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py index 910f890b2a0bb..e09609ff511e0 100644 --- a/superset/dashboards/filters.py +++ b/superset/dashboards/filters.py @@ -14,11 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -import re import uuid from typing import Any, Optional, Union -from flask import current_app, g +from flask import g from flask_appbuilder.security.sqla.models import Role from flask_babel import lazy_gettext as _ from sqlalchemy import and_, or_ @@ -200,14 +199,12 @@ class FilterRelatedRoles(BaseFilter): # pylint: disable=too-few-public-methods name = _("Role") arg_name = "roles" - def apply(self, query: Query, value: Optional[str]) -> Query: - if base_filter := current_app.config["RELATED_QUERY_MUTATORS"].get("role"): - query = base_filter(query) - + def apply(self, query: Query, value: Optional[Any]) -> Query: + role_model = security_manager.role_model if value: - role_model = security_manager.role_model - return query.filter(role_model.name.like(f"{value}%")) - + return query.filter( + role_model.name.ilike(f"%{value}%"), + ) return query diff --git a/superset/views/base_api.py b/superset/views/base_api.py index 9f00a8df41e72..47fc611ba21a0 100644 --- a/superset/views/base_api.py +++ b/superset/views/base_api.py @@ -308,7 +308,7 @@ def _get_related_filter( base_filters = self.base_related_field_filters.get(column_name) if base_filters: filters.add_filter_list(base_filters) - if filter_field: + if value and filter_field: filters.add_filter( filter_field.field_name, filter_field.filter_class, value ) diff --git a/superset/views/filters.py b/superset/views/filters.py index 37e85b1433c1c..625566b98828a 100644 --- a/superset/views/filters.py +++ b/superset/views/filters.py @@ -42,24 +42,16 @@ class FilterRelatedOwners(BaseFilter): # pylint: disable=too-few-public-methods name = lazy_gettext("Owner") arg_name = "owners" - def apply(self, query: Query, value: Optional[str]) -> Query: - if base_filter := current_app.config["RELATED_QUERY_MUTATORS"].get("user"): - query = base_filter(query) - - if value: - user_model = security_manager.user_model - like_value = f"%{value}%" - return query.filter( - or_( - # could be made to handle spaces between names more gracefully - (user_model.first_name + " " + user_model.last_name).ilike( - like_value - ), - user_model.username.ilike(like_value), - ) + def apply(self, query: Query, value: Optional[Any]) -> Query: + user_model = security_manager.user_model + like_value = "%" + cast(str, value) + "%" + return query.filter( + or_( + # could be made to handle spaces between names more gracefully + (user_model.first_name + " " + user_model.last_name).ilike(like_value), + user_model.username.ilike(like_value), ) - - return query + ) class BaseFilterRelatedUsers(BaseFilter): # pylint: disable=too-few-public-methods @@ -80,11 +72,35 @@ class BaseFilterRelatedUsers(BaseFilter): # pylint: disable=too-few-public-meth arg_name = "username" def apply(self, query: Query, value: Optional[Any]) -> Query: - user_model = security_manager.user_model + if extra_filters := current_app.config["EXTRA_RELATED_QUERY_FILTERS"].get( + "user", + ): + query = extra_filters(query) + exclude_users = ( security_manager.get_exclude_users_from_lists() if current_app.config["EXCLUDE_USERS_FROM_LISTS"] is None else current_app.config["EXCLUDE_USERS_FROM_LISTS"] ) - query_ = query.filter(and_(user_model.username.not_in(exclude_users))) - return query_ + if exclude_users: + user_model = security_manager.user_model + return query.filter(and_(user_model.username.not_in(exclude_users))) + + return query + + +class BaseFilterRelatedRoles(BaseFilter): # pylint: disable=too-few-public-methods + """ + Filter to apply on related roles. + """ + + name = lazy_gettext("role") + arg_name = "role" + + def apply(self, query: Query, value: Optional[Any]) -> Query: + if extra_filters := current_app.config["EXTRA_RELATED_QUERY_FILTERS"].get( + "role", + ): + return extra_filters(query) + + return query diff --git a/tests/integration_tests/base_api_tests.py b/tests/integration_tests/base_api_tests.py index 73171a71fc3f3..afe1417bc42e3 100644 --- a/tests/integration_tests/base_api_tests.py +++ b/tests/integration_tests/base_api_tests.py @@ -230,7 +230,7 @@ def _base_filter(query): with patch.dict( "superset.dashboards.filters.current_app.config", - {"RELATED_QUERY_MUTATORS": {"user": _base_filter}}, + {"EXTRA_RELATED_QUERY_FILTERS": {"user": _base_filter}}, ): uri = f"api/v1/{self.resource_name}/related/owners" rv = self.client.get(uri) diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index 8ab0bc277c4e4..b6a53b4d58ce5 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -1839,7 +1839,7 @@ def _base_filter(query): with patch.dict( "superset.dashboards.filters.current_app.config", - {"RELATED_QUERY_MUTATORS": {"role": _base_filter}}, + {"EXTRA_RELATED_QUERY_FILTERS": {"role": _base_filter}}, ): uri = f"api/v1/dashboard/related/roles" rv = self.client.get(uri) From 0b604ddff60af52387288db10011ebe2e7a1427d Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Sat, 24 Dec 2022 11:49:27 +0200 Subject: [PATCH 5/6] fix tests --- tests/integration_tests/base_api_tests.py | 11 +++-------- tests/integration_tests/dashboards/api_tests.py | 8 +++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/integration_tests/base_api_tests.py b/tests/integration_tests/base_api_tests.py index afe1417bc42e3..478fee0a0dca4 100644 --- a/tests/integration_tests/base_api_tests.py +++ b/tests/integration_tests/base_api_tests.py @@ -219,9 +219,9 @@ def test_get_related_owners(self): for expected_user in expected_users: assert expected_user in response_users - def test_get_related_owners_with_query_mutator(self): + def test_get_related_owners_with_extra_filters(self): """ - API: Test get related owners with query mutator + API: Test get related owners with extra related query filters """ self.login(username="admin") @@ -229,18 +229,13 @@ def _base_filter(query): return query.filter_by(username="alpha") with patch.dict( - "superset.dashboards.filters.current_app.config", + "superset.views.filters.current_app.config", {"EXTRA_RELATED_QUERY_FILTERS": {"user": _base_filter}}, ): uri = f"api/v1/{self.resource_name}/related/owners" rv = self.client.get(uri) assert rv.status_code == 200 response = json.loads(rv.data.decode("utf-8")) - users = db.session.query(security_manager.user_model).all() - expected_users = [str(user) for user in users] - assert response["count"] == 1 - # This needs to be implemented like this, because ordering varies between - # postgres and mysql response_users = [result["text"] for result in response["result"]] assert response_users == ["alpha user"] diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index b6a53b4d58ce5..10ca16f4e7138 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -1828,9 +1828,9 @@ def test_get_filter_related_roles(self): response_roles = [result["text"] for result in response["result"]] assert "Alpha" in response_roles - def test_get_all_related_roles_with_query_mutator(self): + def test_get_all_related_roles_with_with_extra_filters(self): """ - API: Test get filter related roles with query mutator + API: Test get filter related roles with extra related query filters """ self.login(username="admin") @@ -1838,15 +1838,13 @@ def _base_filter(query): return query.filter_by(name="Alpha") with patch.dict( - "superset.dashboards.filters.current_app.config", + "superset.views.filters.current_app.config", {"EXTRA_RELATED_QUERY_FILTERS": {"role": _base_filter}}, ): uri = f"api/v1/dashboard/related/roles" rv = self.client.get(uri) assert rv.status_code == 200 response = json.loads(rv.data.decode("utf-8")) - assert response["count"] == 1 - response_roles = [result["text"] for result in response["result"]] assert response_roles == ["Alpha"] From 512cba6999a567f5edea231d40aa56e04f758cba Mon Sep 17 00:00:00 2001 From: Ville Brofeldt Date: Sun, 25 Dec 2022 10:53:14 +0000 Subject: [PATCH 6/6] lint --- superset/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/superset/config.py b/superset/config.py index 2c6b2e17c7db1..5660dfbb1208f 100644 --- a/superset/config.py +++ b/superset/config.py @@ -53,7 +53,6 @@ from celery.schedules import crontab from dateutil import tz from flask import Blueprint -from flask_appbuilder.models.sqla import Model from flask_appbuilder.security.manager import AUTH_DB from pandas._libs.parsers import STR_NA_VALUES # pylint: disable=no-name-in-module from sqlalchemy.orm.query import Query