You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by su...@apache.org on 2021/12/15 04:39:57 UTC
[superset] branch guest-token-authz updated (bb76825 -> 7353826)
This is an automated email from the ASF dual-hosted git repository.
suddjian pushed a change to branch guest-token-authz
in repository https://gitbox.apache.org/repos/asf/superset.git.
discard bb76825 Merge branch 'embed-token' into guest-token-authz
omit 10ccc67 fix tests
omit 883dff3 Revert "403 works now!"
discard 52c453b eums don't work that way
discard 07a78b2 adjust csrf exempt list
discard 048d4b2 guest token dashboard authz
discard d687340 helper methods and dashboard access
omit bcba0fa get guest token from header instead of cookie
omit df2f49a 403 works now!
omit d76bbf2 Merge branch 'embedded' into embed-token
omit 45d016e lint again
omit 271913e lint
omit 814d314 customizable algo
omit 1920623 just one log entry
omit 2f3df2c fake being a non-anonymous user
omit 29358f7 Make guest role name configurable
omit f67c8a8 apparently typing is a problem again
omit cdb7909 quiet pylint
omit 2e1c22f Merge branch 'embedded' into embed-token
omit 1ee824d add feature flag
omit 19c6538 prettier
omit 5694212 Merge branch 'embedded' into embed-token
omit 0d704e6 type annotation
omit 9ca729d linting
omit 44af49c tests, and safer token decoding
omit 97f623a lint
omit 4b34e68 black
omit b1f0e0f Merge branch 'embed-token' of github.com:preset-io/superset into embed-token
omit 3e67e94 docs, standard jwt claims, tweaks
omit be4efd5 fix the stuff for compatibility with external request loaders
omit 7add90a add some auth setup, and rename token
omit d284ab3 improve existing tests
omit 26a84b1 generate an embed token
add bdc4e7a fix(explore): deck.gl Multiple chart broken (#17703)
add dad8c20 fix(Dashboard): Copy dashboard with duplicating charts 500 error (#17707)
add 8e69b2d chore: Update TS tasklist script (#17691)
add 8de9387 Fix flacky header (#17690)
add b5b6ceb fix(explore): don't apply time range filter to Samples table (#17711)
add 4b96971 Rename CertifiedIcon to CertifiedBadge (#17715)
add 6a7e649 fix(postgres): remove redundant tz factory (#17713)
add 7192016 Add missing translations (#17693)
add 485852d ci: temp fix for mysqlclient on an OS regression bug (#17724)
add 0d2299c fix: migration out-of-scope bind (#17728)
add e6db62c fix: Change datatype of column type in BaseColumn to allow larger datatype names for complexed columns (#17360)
add 67fdeff fixed misspelling of apprear to appear (#17735)
add 12d3079 chore: fixed spelling error on line 1342 of CONTRIBUTING.md (#17737)
add fceabf6 fix: import dash with missing immune ID (#17732)
add ee71eb8 Merge branch 'master' into embedded
add d705236 feat: Guest token (for embedded dashboard auth) (#17517)
new a21a417 helper methods and dashboard access
new ad3572e guest token dashboard authz
new 4fd8715 adjust csrf exempt list
new 7353826 eums don't work that way
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 (bb76825)
\
N -- N -- N refs/heads/guest-token-authz (7353826)
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 4 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:
CONTRIBUTING.md | 4 +-
UPDATING.md | 1 +
scripts/generate_frontend_ts_tasklist.js | 2 +-
scripts/python_tests.sh | 5 +
.../cypress/integration/dashboard/save.test.js | 8 +-
.../CertifiedBadge.stories.tsx} | 8 +-
.../CertifiedBadge.test.tsx} | 10 +-
.../{CertifiedIcon => CertifiedBadge}/index.tsx | 8 +-
.../src/components/Datasource/DatasourceEditor.jsx | 8 +-
.../src/components/EditableTitle/index.tsx | 19 +-
.../src/components/ListViewCard/index.tsx | 4 +-
.../src/components/TableSelector/index.tsx | 4 +-
.../src/dashboard/actions/dashboardState.js | 2 +-
.../src/dashboard/components/Header/index.jsx | 11 +-
.../explore/components/DataTablesPane/index.tsx | 10 +-
.../components/ExploreChartHeader/index.jsx | 4 +-
.../src/views/CRUD/chart/ChartList.tsx | 4 +-
.../src/views/CRUD/dashboard/DashboardList.tsx | 4 +-
.../src/views/CRUD/data/dataset/DatasetList.tsx | 4 +-
superset/common/query_actions.py | 2 +
superset/connectors/base/models.py | 2 +-
superset/dashboards/commands/importers/v1/utils.py | 4 +-
superset/datasets/schemas.py | 2 +-
superset/db_engine_specs/postgres.py | 8 -
...baac5_change_datatype_of_type_in_basecolumn.py} | 23 +-
...e27eaf93db_add_extra_config_column_to_alerts.py | 6 +-
superset/security/manager.py | 4 +-
superset/translations/de/LC_MESSAGES/messages.json | 978 +++++++++++-
superset/translations/de/LC_MESSAGES/messages.po | 1631 ++++++++++----------
superset/viz.py | 2 +
tests/integration_tests/celery_tests.py | 12 +-
tests/integration_tests/security/api_tests.py | 2 +-
tests/integration_tests/security_tests.py | 4 +-
.../unit_tests/{common => dashboards}/__init__.py | 0
.../{common => dashboards/commands}/__init__.py | 0
.../commands/importers}/__init__.py | 0
.../commands/importers/v1}/__init__.py | 0
.../dashboards/commands/importers/v1/utils_test.py | 71 +
38 files changed, 1964 insertions(+), 907 deletions(-)
rename superset-frontend/src/components/{CertifiedIcon/CertifiedIcon.stories.tsx => CertifiedBadge/CertifiedBadge.stories.tsx} (84%)
rename superset-frontend/src/components/{CertifiedIcon/CertifiedIcon.test.tsx => CertifiedBadge/CertifiedBadge.test.tsx} (88%)
rename superset-frontend/src/components/{CertifiedIcon => CertifiedBadge}/index.tsx (92%)
copy superset/migrations/versions/{181091c0ef16_add_extra_column_to_columns_model.py => 3ba29ecbaac5_change_datatype_of_type_in_basecolumn.py} (72%)
copy tests/unit_tests/{common => dashboards}/__init__.py (100%)
copy tests/unit_tests/{common => dashboards/commands}/__init__.py (100%)
copy tests/unit_tests/{common => dashboards/commands/importers}/__init__.py (100%)
copy tests/unit_tests/{common => dashboards/commands/importers/v1}/__init__.py (100%)
create mode 100644 tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
[superset] 02/04: guest token dashboard authz
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
suddjian pushed a commit to branch guest-token-authz
in repository https://gitbox.apache.org/repos/asf/superset.git
commit ad3572e54304323dac86023fc8907e54acb15686
Author: David Aaron Suddjian <aa...@gmail.com>
AuthorDate: Sat Dec 11 02:20:07 2021 -0800
guest token dashboard authz
---
superset/security/manager.py | 51 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 45 insertions(+), 6 deletions(-)
diff --git a/superset/security/manager.py b/superset/security/manager.py
index 6bd31f2..9a77528 100644
--- a/superset/security/manager.py
+++ b/superset/security/manager.py
@@ -69,6 +69,7 @@ from superset.security.guest_token import (
GuestToken,
GuestTokenResource,
GuestTokenResources,
+ GuestTokenResourceType,
GuestTokenUser,
GuestUser,
)
@@ -1068,11 +1069,17 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
assert datasource
+ is_dashboard_access_check_applicable = feature_flag_manager.is_feature_enabled(
+ "DASHBOARD_RBAC"
+ ) or feature_flag_manager.is_feature_enabled(
+ "EMBEDDED_SUPERSET"
+ )
+
if not (
self.can_access_schema(datasource)
or self.can_access("datasource_access", datasource.perm or "")
or (
- feature_flag_manager.is_feature_enabled("DASHBOARD_RBAC")
+ is_dashboard_access_check_applicable
and self.can_access_based_on_dashboard(datasource)
)
):
@@ -1215,19 +1222,25 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
from superset.views.base import is_user_admin
from superset.views.utils import is_owner
- has_rbac_access = True
-
- if is_feature_enabled("DASHBOARD_RBAC"):
- has_rbac_access = any(
+ def has_rbac_access() -> bool:
+ return is_feature_enabled("DASHBOARD_RBAC") and any(
dashboard_role.id
in [user_role.id for user_role in self.get_user_roles()]
for dashboard_role in dashboard.roles
)
+ has_published_access = (
+ not is_feature_enabled("DASHBOARD_RBAC")
+ and not self.is_guest_user(g.user)
+ and dashboard.published
+ )
+
can_access = (
is_user_admin()
or is_owner(dashboard, g.user)
- or (dashboard.published and has_rbac_access)
+ or has_published_access
+ or has_rbac_access()
+ or self.has_guest_access(GuestTokenResourceType.DASHBOARD, dashboard.id)
or (not dashboard.published and not dashboard.roles)
)
@@ -1329,4 +1342,30 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
@staticmethod
def is_guest_user(user: Any) -> bool:
+ # pylint: disable=import-outside-toplevel
+ from superset import is_feature_enabled
+
+ if not is_feature_enabled("EMBEDDED_SUPERSET"):
+ return False
return hasattr(user, "is_guest_user") and user.is_guest_user
+
+ def get_current_guest_user_if_guest(self) -> Optional[GuestUser]:
+ # pylint: disable=import-outside-toplevel
+ from superset.extensions import feature_flag_manager
+
+ if self.is_guest_user(g.user):
+ return g.user
+ return None
+
+ def has_guest_access(
+ self, resource_type: GuestTokenResourceType, id: Union[str, int]
+ ) -> bool:
+ user = self.get_current_guest_user_if_guest()
+ if not user:
+ return False
+
+ strid = str(id)
+ for resource in user.resources:
+ if resource["type"] == resource_type.value and str(resource["id"]) == strid:
+ return True
+ return False
[superset] 01/04: helper methods and dashboard access
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
suddjian pushed a commit to branch guest-token-authz
in repository https://gitbox.apache.org/repos/asf/superset.git
commit a21a4175c3fc1ff5c4189bd782be747e0de5a108
Author: David Aaron Suddjian <aa...@gmail.com>
AuthorDate: Tue Dec 7 14:13:44 2021 -0800
helper methods and dashboard access
---
superset/common/request_contexed_based.py | 16 +---------------
superset/config.py | 2 +-
superset/dashboards/filters.py | 24 +++++++++++++++++++-----
superset/security/api.py | 2 +-
superset/security/guest_token.py | 14 +++++++++++---
superset/security/manager.py | 23 +++++++++++++++++------
superset/views/base.py | 9 +--------
superset/views/core.py | 5 +++--
8 files changed, 54 insertions(+), 41 deletions(-)
diff --git a/superset/common/request_contexed_based.py b/superset/common/request_contexed_based.py
index 0b06a0c..5d8405e 100644
--- a/superset/common/request_contexed_based.py
+++ b/superset/common/request_contexed_based.py
@@ -16,24 +16,10 @@
# under the License.
from __future__ import annotations
-from typing import List, TYPE_CHECKING
-
-from flask import g
-
from superset import conf, security_manager
-if TYPE_CHECKING:
- from flask_appbuilder.security.sqla.models import Role
-
-
-def get_user_roles() -> List[Role]:
- if g.user.is_anonymous:
- public_role = conf.get("AUTH_ROLE_PUBLIC")
- return [security_manager.get_public_role()] if public_role else []
- return g.user.roles
-
def is_user_admin() -> bool:
- user_roles = [role.name.lower() for role in get_user_roles()]
+ user_roles = [role.name.lower() for role in security_manager.get_user_roles()]
admin_role = conf.get("AUTH_ROLE_ADMIN").lower()
return admin_role in user_roles
diff --git a/superset/config.py b/superset/config.py
index f9b7343..2ff2f1a 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -389,7 +389,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = {
# a custom security config could potentially give access to setting filters on
# tables that users do not have access to.
"ROW_LEVEL_SECURITY": True,
- "EMBEDDED_SUPERSET": False,
+ "EMBEDDED_SUPERSET": False, # This requires that the public role be available
# Enables Alerts and reports new implementation
"ALERT_REPORTS": False,
# Enable experimental feature to search for other dashboards
diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py
index 9658478..3d49978 100644
--- a/superset/dashboards/filters.py
+++ b/superset/dashboards/filters.py
@@ -16,6 +16,7 @@
# under the License.
from typing import Any, Optional
+from flask import g
from flask_appbuilder.security.sqla.models import Role
from flask_babel import lazy_gettext as _
from sqlalchemy import and_, or_
@@ -25,7 +26,8 @@ from superset import db, is_feature_enabled, security_manager
from superset.models.core import FavStar
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
-from superset.views.base import BaseFilter, get_user_roles, is_user_admin
+from superset.security.guest_token import GuestTokenResourceType, GuestUser
+from superset.views.base import BaseFilter, is_user_admin
from superset.views.base_api import BaseFavoriteFilter
@@ -112,7 +114,7 @@ class DashboardAccessFilter(BaseFilter): # pylint: disable=too-few-public-metho
)
)
- dashboard_rbac_or_filters = []
+ feature_flagged_filters = []
if is_feature_enabled("DASHBOARD_RBAC"):
roles_based_query = (
db.session.query(Dashboard.id)
@@ -121,19 +123,31 @@ class DashboardAccessFilter(BaseFilter): # pylint: disable=too-few-public-metho
and_(
Dashboard.published.is_(True),
dashboard_has_roles,
- Role.id.in_([x.id for x in get_user_roles()]),
+ Role.id.in_([x.id for x in security_manager.get_user_roles()]),
),
)
)
- dashboard_rbac_or_filters.append(Dashboard.id.in_(roles_based_query))
+ feature_flagged_filters.append(Dashboard.id.in_(roles_based_query))
+
+ if is_feature_enabled("EMBEDDED_SUPERSET") and security_manager.is_guest_user(
+ g.user
+ ):
+ guest_user: GuestUser = g.user
+ embedded_dashboard_ids = [
+ r["id"]
+ for r in guest_user.resources
+ if r["type"] == GuestTokenResourceType.DASHBOARD
+ ]
+ if len(embedded_dashboard_ids) != 0:
+ feature_flagged_filters.append(Dashboard.id.in_(embedded_dashboard_ids))
query = query.filter(
or_(
Dashboard.id.in_(owner_ids_query),
Dashboard.id.in_(datasource_perm_query),
Dashboard.id.in_(users_favorite_dash_query),
- *dashboard_rbac_or_filters,
+ *feature_flagged_filters,
)
)
diff --git a/superset/security/api.py b/superset/security/api.py
index c0a4a77..54efcd0 100644
--- a/superset/security/api.py
+++ b/superset/security/api.py
@@ -35,7 +35,7 @@ class UserSchema(Schema):
class ResourceSchema(Schema):
- type = fields.String(required=True)
+ type = fields.String(required=True) # todo figure out how to make this an enum
id = fields.String(required=True)
rls = fields.String()
diff --git a/superset/security/guest_token.py b/superset/security/guest_token.py
index cbef52f..60add81 100644
--- a/superset/security/guest_token.py
+++ b/superset/security/guest_token.py
@@ -14,6 +14,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
+from enum import Enum
from typing import List, Optional, TypedDict, Union
from flask_appbuilder.security.sqla.models import Role
@@ -26,17 +27,24 @@ class GuestTokenUser(TypedDict, total=False):
last_name: str
+class GuestTokenResourceType(Enum):
+ DASHBOARD = "dashboard"
+
+
class GuestTokenResource(TypedDict):
- type: str
+ type: GuestTokenResourceType
id: Union[str, int]
rls: Optional[str]
+GuestTokenResources = List[GuestTokenResource]
+
+
class GuestToken(TypedDict):
iat: float
exp: float
user: GuestTokenUser
- resources: List[GuestTokenResource]
+ resources: GuestTokenResources
class GuestUser(AnonymousUserMixin):
@@ -50,7 +58,7 @@ class GuestUser(AnonymousUserMixin):
def is_authenticated(self) -> bool:
"""
This is set to true because guest users should be considered authenticated,
- at least in most places. The treatment of this flag is pretty inconsistent.
+ at least in most places. The treatment of this flag is kind of inconsistent.
"""
return True
diff --git a/superset/security/manager.py b/superset/security/manager.py
index 1a0a453..6bd31f2 100644
--- a/superset/security/manager.py
+++ b/superset/security/manager.py
@@ -68,6 +68,7 @@ from superset.exceptions import SupersetSecurityException
from superset.security.guest_token import (
GuestToken,
GuestTokenResource,
+ GuestTokenResources,
GuestTokenUser,
GuestUser,
)
@@ -278,7 +279,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
"""
user = g.user
- if user.is_anonymous:
+ if user.is_anonymous and not self.is_guest_user(user):
return self.is_item_public(permission_name, view_name)
return self._has_view_access(user, permission_name, view_name)
@@ -1097,6 +1098,12 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
def get_anonymous_user(self) -> User: # pylint: disable=no-self-use
return AnonymousUserMixin()
+ def get_user_roles(self) -> List[Role]:
+ if g.user.is_anonymous:
+ public_role = current_app.config.get("AUTH_ROLE_PUBLIC")
+ return [self.get_public_role()] if public_role else []
+ return g.user.roles
+
def get_rls_filters(self, table: "BaseDatasource") -> List[SqlaQuery]:
"""
Retrieves the appropriate row level security filters for the current user and
@@ -1195,8 +1202,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
)
)
- @staticmethod
- def raise_for_dashboard_access(dashboard: "Dashboard") -> None:
+ def raise_for_dashboard_access(self, dashboard: "Dashboard") -> None:
"""
Raise an exception if the user cannot access the dashboard.
@@ -1206,14 +1212,15 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
# pylint: disable=import-outside-toplevel
from superset import is_feature_enabled
from superset.dashboards.commands.exceptions import DashboardAccessDeniedError
- from superset.views.base import get_user_roles, is_user_admin
+ from superset.views.base import is_user_admin
from superset.views.utils import is_owner
has_rbac_access = True
if is_feature_enabled("DASHBOARD_RBAC"):
has_rbac_access = any(
- dashboard_role.id in [user_role.id for user_role in get_user_roles()]
+ dashboard_role.id
+ in [user_role.id for user_role in self.get_user_roles()]
for dashboard_role in dashboard.roles
)
@@ -1255,7 +1262,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
return time.time()
def create_guest_access_token(
- self, user: GuestTokenUser, resources: List[GuestTokenResource]
+ self, user: GuestTokenUser, resources: GuestTokenResources
) -> bytes:
secret = current_app.config["GUEST_TOKEN_JWT_SECRET"]
algo = current_app.config["GUEST_TOKEN_JWT_ALGO"]
@@ -1319,3 +1326,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
if token.get("resources") is None:
raise ValueError("Guest token does not contain a resources claim")
return cast(GuestToken, token)
+
+ @staticmethod
+ def is_guest_user(user: Any) -> bool:
+ return hasattr(user, "is_guest_user") and user.is_guest_user
diff --git a/superset/views/base.py b/superset/views/base.py
index 72dc805..e5fc082 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -263,15 +263,8 @@ def create_table_permissions(table: models.SqlaTable) -> None:
security_manager.add_permission_view_menu("schema_access", table.schema_perm)
-def get_user_roles() -> List[Role]:
- if g.user.is_anonymous:
- public_role = conf.get("AUTH_ROLE_PUBLIC")
- return [security_manager.find_role(public_role)] if public_role else []
- return g.user.roles
-
-
def is_user_admin() -> bool:
- user_roles = [role.name.lower() for role in list(get_user_roles())]
+ user_roles = [role.name.lower() for role in list(security_manager.get_user_roles())]
return "admin" in user_roles
diff --git a/superset/views/core.py b/superset/views/core.py
index 9557c30..68f6e78 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -135,7 +135,6 @@ from superset.views.base import (
data_payload_response,
generate_download_headers,
get_error_msg,
- get_user_roles,
handle_api_exception,
json_error_response,
json_errors_response,
@@ -1886,7 +1885,9 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
f"ERROR: cannot find dashboard {dashboard_id}", status=404
)
- edit_perm = is_owner(dash, g.user) or admin_role in get_user_roles()
+ edit_perm = (
+ is_owner(dash, g.user) or admin_role in security_manager.get_user_roles()
+ )
if not edit_perm:
username = g.user.username if hasattr(g.user, "username") else "user"
return json_error_response(
[superset] 03/04: adjust csrf exempt list
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
suddjian pushed a commit to branch guest-token-authz
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 4fd8715b42185477706bdbd1493635636abe3a22
Author: David Aaron Suddjian <aa...@gmail.com>
AuthorDate: Sat Dec 11 02:35:24 2021 -0800
adjust csrf exempt list
---
superset/config.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/superset/config.py b/superset/config.py
index 2ff2f1a..f7a5bc5 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -191,7 +191,11 @@ QUERY_SEARCH_LIMIT = 1000
WTF_CSRF_ENABLED = True
# Add endpoints that need to be exempt from CSRF protection
-WTF_CSRF_EXEMPT_LIST = ["superset.views.core.log", "superset.charts.data.api.data"]
+WTF_CSRF_EXEMPT_LIST = [
+ "superset.views.core.log",
+ "superset.views.core.explore_json",
+ "superset.charts.data.api.data",
+]
# Whether to run the web server in debug mode or not
DEBUG = os.environ.get("FLASK_ENV") == "development"
[superset] 04/04: eums don't work that way
Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
suddjian pushed a commit to branch guest-token-authz
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 7353826f4fc73c752df0d5193b1e888ebcebcc6b
Author: David Aaron Suddjian <aa...@gmail.com>
AuthorDate: Sat Dec 11 02:35:35 2021 -0800
eums don't work that way
---
superset/dashboards/filters.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py
index 3d49978..e398af9 100644
--- a/superset/dashboards/filters.py
+++ b/superset/dashboards/filters.py
@@ -137,7 +137,7 @@ class DashboardAccessFilter(BaseFilter): # pylint: disable=too-few-public-metho
embedded_dashboard_ids = [
r["id"]
for r in guest_user.resources
- if r["type"] == GuestTokenResourceType.DASHBOARD
+ if r["type"] == GuestTokenResourceType.DASHBOARD.value
]
if len(embedded_dashboard_ids) != 0:
feature_flagged_filters.append(Dashboard.id.in_(embedded_dashboard_ids))