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


##########
superset/commands/dataset/create.py:
##########
@@ -225,6 +234,12 @@ def validate(self) -> None:  # noqa: C901
                     sql=sql,
                     catalog=catalog,
                     schema=schema,
+                    # Jinja-templated SQL only reveals the tables it reads
+                    # once rendered with the params the dataset is saved with.
+                    template_params=template_params_for_access_check(

Review Comment:
   The access check here renders the SQL with only the stored 
`template_params`, but chart execution adds its own variables on top 
(`get_sqla_query` passes `columns`, `groupby`, `metrics`, `filter`, 
`time_grain`, etc. to the template processor). With template processing on, a 
user with dataset write and no grants on `public.customer_pii` can save `{% if 
columns is defined %}SELECT secret FROM public.customer_pii{% else %}SELECT 1 
AS secret{% endif %}` with no params. Creation (and column sync) renders only 
the `SELECT 1` branch and passes; chart-data then renders the restricted branch 
and is authorized by `is_editor(datasource)`. Update, import and 
`/datasource/save/` forward the same params, so they look affected too. Could 
dataset-backed chart execution re-check the tables of the SQL it actually 
renders instead of trusting editorship, or is there another way to close this 
branch-dependent case?



##########
superset/commands/dataset/importers/v1/utils.py:
##########
@@ -585,6 +610,22 @@ def import_dataset(  # noqa: C901
     if not ignore_permissions:
         try:
             security_manager.raise_for_access(datasource=dataset)
+            # The datasource check accepts editorship of the dataset itself,
+            # and an overwrite or restore only requires editorship of the row
+            # being replaced. Without this check an editor could re-import a
+            # ``SELECT 1`` decoy as a physical dataset over an arbitrary
+            # table. A physical dataset that is new or bound to a different
+            # table must be authorized by data access on that table.
+            if not dataset.sql and _table_binding(dataset) != previous_binding:
+                security_manager.raise_for_access(

Review Comment:
   This check runs after `import_from_dict` has already changed the existing 
row, so on an overwrite that changes only `schema` the table lookup finds that 
same dataset under the new schema. Its `datasource_access` still matches 
because the dataset permission name is `[db].[table](id:N)` and omits the 
schema, so `allow_dataset_editorship=False` doesn't reject it. A role with 
write/import and `datasource_access` on `public.t` (but nothing on 
`restricted.t`) could re-import that UUID with `schema: restricted` and then 
read `restricted.t` through chart-data. Can the target binding be authorized 
before the row is mutated, or the dataset being rebound be excluded from the 
rows that can authorize its new binding? The rejection test mocks 
`raise_for_access`, so it wouldn't catch this.



##########
superset/commands/dataset/create.py:
##########
@@ -120,7 +125,11 @@ def validate(self) -> None:  # noqa: C901
                     template_params_raw: str | None = self._properties.get(
                         "template_params"
                     )
-                    template_params = json_to_dict(template_params_raw or "")
+                    # Render with the params chart execution exposes, so the
+                    # derived schema comes from the SQL the access check sees.
+                    template_params = strip_reserved_template_params(

Review Comment:
   Nothing seems to cover this stripping: the new create tests only assert what 
is forwarded to the mocked `raise_for_access`, and the helper test never goes 
through this derivation. If `strip_reserved_template_params` were dropped here, 
would any test fail? A case next to 
`test_create_dataset_schema_uses_template_params` with 
`template_params='{"removed_filters": []}'` and `{% if removed_filters is 
defined %}SELECT * FROM decoy.t{% else %}SELECT * FROM actual.t{% endif %}`, 
asserting the derived schema is `actual`, would pin 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