sadpandajoe commented on code in PR #45538:
URL: https://github.com/apache/superset/pull/45538#discussion_r4340075316


##########
tests/integration_tests/dashboards/api_tests.py:
##########
@@ -5441,6 +5441,140 @@ def test_copy_dashboard_layout_without_current_charts(
                 db.session.delete(chart)
             db.session.commit()
 
+    @contextmanager
+    def _layout_access_fixture(self) -> Iterator[dict[str, int]]:
+        """A dashboard Gamma edits, whose member chart Gamma cannot read,
+        plus a chart Gamma owns, an unrelated chart Gamma cannot read, and
+        an archived member."""
+        from superset.connectors.sqla.models import SqlaTable
+        from superset.models.helpers import skip_visibility_filter
+
+        admin: User = self.get_user(ADMIN_USERNAME)
+        gamma: User = self.get_user(GAMMA_USERNAME)
+        dataset_id: int = (
+            db.session.query(SqlaTable.id).order_by(SqlaTable.id).first()[0]
+        )
+        member: Slice = self.insert_chart(
+            "layout-access-member", [admin.id], dataset_id
+        )
+        archived: Slice = self.insert_chart(
+            "layout-access-archived", [admin.id], dataset_id
+        )
+        own: Slice = self.insert_chart("layout-access-own", [gamma.id], 
dataset_id)
+        foreign: Slice = self.insert_chart(
+            "layout-access-foreign", [admin.id], dataset_id
+        )
+        dashboard: Dashboard = self.insert_dashboard(
+            "layout-access", None, [gamma.id], slices=[member, archived]
+        )
+        ids: dict[str, int] = {
+            "dashboard": dashboard.id,
+            "member": member.id,
+            "archived": archived.id,
+            "own": own.id,
+            "foreign": foreign.id,
+        }
+        archived.deleted_at = datetime.now()
+        db.session.commit()
+        try:
+            yield ids
+        finally:
+            db.session.rollback()
+            with skip_visibility_filter(db.session, Slice):
+                for dash in db.session.query(Dashboard).filter(
+                    Dashboard.dashboard_title.like("layout-access%")
+                ):
+                    db.session.delete(dash)
+                for chart in db.session.query(Slice).filter(
+                    Slice.slice_name.like("layout-access-%")
+                ):
+                    db.session.delete(chart)
+            db.session.commit()
+
+    @staticmethod
+    def _chart_layout(*chart_ids: int) -> dict[str, Any]:
+        return {
+            f"CHART-{chart_id}": {
+                "id": f"CHART-{chart_id}",
+                "type": "CHART",
+                "children": [],
+                "meta": {"chartId": chart_id, "width": 4, "height": 50},
+            }
+            for chart_id in chart_ids
+        }
+
+    @parameterized.expand(
+        [
+            ("existing_and_readable", ("member", "archived", "own"), 200),

Review Comment:
   Both successful new-chart cases (`own` here and in the copy case below) are 
charts Gamma edits, so `can_access_chart` short-circuits on editorship. An 
implementation that only accepted owned charts would pass every case while 
rejecting charts Gamma can read through a granted dataset. Should there be a 
case with a chart Gamma doesn't edit but whose dataset Gamma has been granted, 
asserting 200 and that the chart is persisted as a member?



##########
superset/daos/dashboard.py:
##########
@@ -660,6 +713,10 @@ def set_dash_metadata(  # noqa: C901
                 current_slices = (
                     
db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()
                 )
+                _raise_for_unreadable_new_charts(

Review Comment:
   This rejection now fires after `UpdateDashboardCommand.run()` has already 
run `process_tab_diff()` / `process_native_filter_diff()`, which deactivate the 
affected reports and synchronously send the "deactivated" emails. If a save 
removes a native filter or tab used by an active report and also adds an 
unreadable chart, the caller gets a 422 and the DB rolls back (report stays 
active), but report owners have already been emailed that it was deactivated. 
Should the chart check run before those side effects, or should the emails be 
deferred until the save commits?



##########
superset/daos/dashboard.py:
##########
@@ -660,6 +713,10 @@ def set_dash_metadata(  # noqa: C901
                 current_slices = (
                     
db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()
                 )
+                _raise_for_unreadable_new_charts(
+                    current_slices,
+                    _member_chart_ids(dashboard.id) | (allowed_chart_ids or 
set()),
+                )

Review Comment:
   Agreed—`can_access_chart` reads `Slice.editors` / `Slice.viewers` lazily and 
the `Slice` query here doesn't preload them, so a non-admin save adding N 
readable charts the caller doesn't own issues roughly 2N extra SELECTs 
(existing members short-circuit, so unchanged layouts are unaffected). 
`prefetch_chart_access()` already solves this for dashboard reads; should the 
new-chart candidates use it?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to