gabotorresruiz commented on code in PR #45816:
URL: https://github.com/apache/superset/pull/45816#discussion_r4406936669


##########
tests/unit_tests/security/test_guest_query_datasource_scope.py:
##########
@@ -0,0 +1,478 @@
+# 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.
+"""Embedded guests and SQL Lab ``query`` datasources.
+
+An embedded guest token only grants the data sources reachable through the
+embedded dashboards it authorizes. The SQL Lab query branch of
+``raise_for_access`` must therefore not let a guest read a stored query (its
+own or another user's) merely because the guest role happens to hold access to
+the tables that query touches.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Optional, TYPE_CHECKING
+from unittest.mock import MagicMock
+
+import pytest
+from pytest_mock import MockerFixture
+
+from superset.common.db_query_status import QueryStatus
+from superset.errors import SupersetErrorType
+from superset.exceptions import SupersetSecurityException
+from superset.extensions import appbuilder
+from superset.security.guest_token import (
+    GuestToken,
+    GuestTokenResourceType,
+    GuestUser,
+)
+from superset.security.manager import SupersetSecurityManager
+from superset.utils import json
+from superset.utils.core import DatasourceType
+
+if TYPE_CHECKING:
+    from superset.app import SupersetApp
+
+QUERY_ID = 1
+DASHBOARD_ID = 10
+SLICE_ID = 20
+GUEST_DENIED = SupersetErrorType.QUERY_SECURITY_ACCESS_ERROR
+
+
+def _guest_user(datasets: Optional[list[int]] = None) -> GuestUser:
+    token: GuestToken = {
+        "user": {},
+        "resources": [
+            {"type": GuestTokenResourceType.DASHBOARD, "id": str(DASHBOARD_ID)}
+        ],
+        "rls_rules": [],
+        "iat": 0,
+        "exp": 9999999999,
+    }
+    if datasets is not None:
+        token["datasets"] = datasets
+    return GuestUser(token=token, roles=[])
+
+
+def _query(user_id: Optional[int]) -> MagicMock:
+    """A persisted SQL Lab query over ``public.sales``."""
+    database = MagicMock()
+    database.get_default_catalog.return_value = None
+    database.get_default_schema_for_query.return_value = "public"
+    database.db_engine_spec.supports_catalog = True
+    query = MagicMock()
+    query.id = QUERY_ID
+    query.type = DatasourceType.QUERY
+    query.catalog = None
+    query.schema = "public"
+    query.database = database
+    query.sql = "SELECT * FROM sales"
+    query.executed_sql = None
+    query.user_id = user_id
+    query.status = QueryStatus.SUCCESS
+    return query
+
+
+def _security_manager(
+    mocker: MockerFixture, guest: Optional[GuestUser]
+) -> SupersetSecurityManager:
+    """A security manager whose role grants access to the ``sales`` table."""
+    sm = SupersetSecurityManager(appbuilder)
+    mocker.patch.object(sm, "can_access_database", return_value=False)
+    mocker.patch.object(sm, "get_schema_perm", return_value="[db].[public]")
+    mocker.patch.object(sm, "can_access", return_value=True)
+    mocker.patch.object(sm, "is_guest_user", return_value=guest is not None)
+    mocker.patch.object(sm, "get_current_guest_user_if_guest", 
return_value=guest)
+    mocker.patch("superset.security.manager.get_user_id", return_value=None)
+    return sm
+
+
+def _bound_query_context(
+    mocker: MockerFixture,
+    sm: SupersetSecurityManager,
+    query: MagicMock,
+    *,
+    modified: bool = False,
+    guest_has_dashboard: bool = True,
+    stored_chart: bool = True,
+) -> MagicMock:
+    """A query request naming a dataset-backed chart on an embedded 
dashboard."""
+    slc = MagicMock()
+    slc.id = SLICE_ID
+    slc.datasource_id = QUERY_ID
+    slc.datasource_type = DatasourceType.TABLE
+    dashboard = MagicMock()
+    dashboard.id = DASHBOARD_ID
+    dashboard.slices = [slc]
+
+    session = MagicMock()
+    session.query.return_value.filter.return_value.one_or_none.return_value = 
dashboard
+    mocker.patch.object(
+        SupersetSecurityManager, "session", new_callable=mocker.PropertyMock
+    ).return_value = session
+    mocker.patch.object(sm, "has_guest_access", 
return_value=guest_has_dashboard)
+    mocker.patch(
+        "superset.security.manager.query_context_modified", 
return_value=modified
+    )
+
+    query_context = MagicMock()
+    query_context.datasource = query
+    query_context.slice_ = slc if stored_chart else None
+    query_context.form_data = {"dashboardId": DASHBOARD_ID, "slice_id": 
SLICE_ID}
+    return query_context
+
+
[email protected](
+    "datasets",
+    [pytest.param([3], id="scoped-token"), pytest.param(None, 
id="unscoped-token")],
+)
[email protected](
+    "owner_id",
+    [pytest.param(None, id="guest-own-query"), pytest.param(99, 
id="other-user")],
+)
+def test_guest_cannot_read_query_datasource_outside_token_scope(
+    mocker: MockerFixture,
+    app_context: None,
+    datasets: Optional[list[int]],
+    owner_id: Optional[int],
+) -> None:
+    """A guest is refused a stored query even when the guest role can read
+    the tables it touches, whatever the token scope and query author."""
+    sm = _security_manager(mocker, _guest_user(datasets))
+    with pytest.raises(SupersetSecurityException) as excinfo:
+        sm.raise_for_access(query=_query(owner_id), allow_editorship=False)

Review Comment:
   Thanks @aminghadersohi, `c0eb8d8` closes it. I ran the file at `e8e6a35` and 
then exempted `allow_query_authorship_bypass` from the guest refusal: 
`test_guest_refused_query_even_with_authorship_bypass` fails with `DID NOT 
RAISE`, along with the new form data and permalink cases, and all 37 are green 
again with the guard back. Resolving.



-- 
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