diff --git a/superset-frontend/src/explore/components/PropertiesModal/PropertiesModal.test.tsx b/superset-frontend/src/explore/components/PropertiesModal/PropertiesModal.test.tsx index 49c5d3b0480a..40cbcc4bc8a2 100644 --- a/superset-frontend/src/explore/components/PropertiesModal/PropertiesModal.test.tsx +++ b/superset-frontend/src/explore/components/PropertiesModal/PropertiesModal.test.tsx @@ -58,16 +58,16 @@ fetchMock.get('glob:*/api/v1/chart/318', { viz_type: 'Viz Type', }, result: { - cache_timeout: null, - certified_by: 'John Doe', - certification_details: 'Sample certification', + cache_timeout: '1000', + certified_by: 'Test certified by', + certification_details: 'Test certification details', dashboards: [ { dashboard_title: 'FCC New Coder Survey 2018', id: 23, }, ], - description: null, + description: 'Test description', owners: [ { first_name: 'Superset', @@ -78,8 +78,9 @@ fetchMock.get('glob:*/api/v1/chart/318', { ], params: '{"adhoc_filters": [], "all_columns_x": ["age"], "color_scheme": "supersetColors", "datasource": "42__table", "granularity_sqla": "time_start", "groupby": null, "label_colors": {}, "link_length": "25", "queryFields": {"groupby": "groupby"}, "row_limit": 10000, "slice_id": 1380, "time_range": "No filter", "url_params": {}, "viz_type": "histogram", "x_axis_label": "age", "y_axis_label": "count"}', - slice_name: 'Age distribution of respondents', + slice_name: 'Test chart new name', viz_type: VizType.Histogram, + show_title: 'Show Slice', }, show_columns: [ 'cache_timeout', diff --git a/superset-frontend/src/explore/components/PropertiesModal/index.tsx b/superset-frontend/src/explore/components/PropertiesModal/index.tsx index 9a6831fc595c..57dbe91edd34 100644 --- a/superset-frontend/src/explore/components/PropertiesModal/index.tsx +++ b/superset-frontend/src/explore/components/PropertiesModal/index.tsx @@ -174,20 +174,19 @@ function PropertiesModal({ } try { - const res = await SupersetClient.put({ - endpoint: `/api/v1/chart/${slice.slice_id}`, + // Retrieving the code from the following issue: + // https://github.com/apache/superset/pull/33392 + const chartEndpoint = `/api/v1/chart/${slice.slice_id}`; + await SupersetClient.put({ + endpoint: chartEndpoint, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); - // update the redux state - const updatedChart = { - ...payload, - ...res.json.result, - tags, - id: slice.slice_id, - owners: selectedOwners, - }; - onSave(updatedChart); + + const res = await SupersetClient.get({ + endpoint: chartEndpoint, + }); + onSave(res.json.result); addSuccessToast(t('Chart properties updated')); onHide(); } catch (res) { diff --git a/superset/charts/filters.py b/superset/charts/filters.py index 1d13ec177f3d..88d433b1edd5 100644 --- a/superset/charts/filters.py +++ b/superset/charts/filters.py @@ -27,8 +27,11 @@ from superset.models.core import FavStar from superset.models.slice import Slice from superset.tags.filters import BaseTagIdFilter, BaseTagNameFilter +from superset.utils.charts import get_charts_authorized_for_owners from superset.utils.core import get_user_id -from superset.utils.datasets import get_datasets_authorized_for_user_roles +from superset.utils.datasets import ( + get_datasets_authorized_for_user_roles, +) from superset.utils.filters import get_dataset_access_filters from superset.views.base import BaseFilter from superset.views.base_api import BaseFavoriteFilter @@ -111,12 +114,18 @@ def apply(self, query: Query, value: Any) -> Query: models.Database, table_alias.database_id == models.Database.id ) - # Select datasets authorized for this user's roles + # Display the charts for which the current user is the owner + # or authorized by dataset rights for one of the user's groups. feature_flagged_filters = [] if is_feature_enabled("DATASET_RBAC"): + # Roles access roles_based_query = get_datasets_authorized_for_user_roles() feature_flagged_filters.append(SqlaTable.id.in_(roles_based_query)) + # Owners access + owners_based_query = get_charts_authorized_for_owners() + feature_flagged_filters.append(Slice.id.in_(owners_based_query)) + return query.filter( or_(get_dataset_access_filters(self.model), *feature_flagged_filters) ) diff --git a/superset/utils/charts.py b/superset/utils/charts.py new file mode 100644 index 000000000000..f5bd5b7d2ca3 --- /dev/null +++ b/superset/utils/charts.py @@ -0,0 +1,33 @@ +# 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. +from superset import db +from superset.models.slice import Slice, slice_user +from superset.utils.core import get_user_id, QueryObjectFilterClause + + +def get_charts_authorized_for_owners() -> list[QueryObjectFilterClause]: + """ + Function that returns the list of charts where current user is owner + """ + return ( + db.session.query(slice_user.c.slice_id) + .join( + Slice, + Slice.id == slice_user.c.slice_id, + ) + .filter(slice_user.c.user_id == get_user_id()) + ) diff --git a/superset/utils/datasets.py b/superset/utils/datasets.py index 5d8f00c682c8..fe82847dd921 100644 --- a/superset/utils/datasets.py +++ b/superset/utils/datasets.py @@ -15,8 +15,8 @@ # specific language governing permissions and limitations # under the License. from superset import db, security_manager -from superset.connectors.sqla.models import SqlaTable, sqlatable_roles -from superset.utils.core import QueryObjectFilterClause +from superset.connectors.sqla.models import SqlaTable, sqlatable_roles, sqlatable_user +from superset.utils.core import get_user_id, QueryObjectFilterClause def get_datasets_authorized_for_user_roles() -> list[QueryObjectFilterClause]: @@ -35,3 +35,17 @@ def get_datasets_authorized_for_user_roles() -> list[QueryObjectFilterClause]: ), ) ) + + +def get_datasets_authorized_for_owners() -> list[QueryObjectFilterClause]: + """ + Function that returns the list of datasets where user is owner + """ + return ( + db.session.query(sqlatable_user.c.table_id) + .join( + SqlaTable, + SqlaTable.id == sqlatable_user.c.table_id, + ) + .filter(sqlatable_user.c.user_id == get_user_id()) + ) diff --git a/superset/views/base.py b/superset/views/base.py index 95aeb4411a34..aca69fa8eba5 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -67,7 +67,10 @@ from superset.utils.filters import get_dataset_access_filters from superset.views.error_handling import json_error_response -from ..utils.datasets import get_datasets_authorized_for_user_roles +from ..utils.datasets import ( + get_datasets_authorized_for_owners, + get_datasets_authorized_for_user_roles, +) from .utils import bootstrap_user_data FRONTEND_CONF_KEYS = ( @@ -452,12 +455,18 @@ def apply(self, query: Query, value: Any) -> Query: models.Database.id == self.model.database_id, ) - # Select datasets authorized for this user's roles + # Display the datasets for which the current user is the owner + # or authorized for one of the user's groups. feature_flagged_filters = [] if is_feature_enabled("DATASET_RBAC"): + # Role access roles_based_query = get_datasets_authorized_for_user_roles() feature_flagged_filters.append(SqlaTable.id.in_(roles_based_query)) + # Owner + owners_based_query = get_datasets_authorized_for_owners() + feature_flagged_filters.append(SqlaTable.id.in_(owners_based_query)) + return query.filter( or_(get_dataset_access_filters(self.model), *feature_flagged_filters) )