fix: guest queries (#27566)

This commit is contained in:
Beto Dealmeida
2024-03-19 11:20:52 -04:00
committed by GitHub
parent 8341afbcf0
commit 36290ce72f
2 changed files with 132 additions and 11 deletions

View File

@@ -26,7 +26,7 @@ from superset.connectors.sqla.models import Database, SqlaTable
from superset.exceptions import SupersetSecurityException
from superset.extensions import appbuilder
from superset.models.slice import Slice
from superset.security.manager import SupersetSecurityManager
from superset.security.manager import query_context_modified, SupersetSecurityManager
from superset.sql_parse import Table
from superset.superset_typing import AdhocMetric
from superset.utils.core import override_user
@@ -414,3 +414,120 @@ def test_raise_for_access_chart_owner(
sm.raise_for_access(
chart=slice,
)
def test_query_context_modified(
mocker: MockFixture,
stored_metrics: list[AdhocMetric],
) -> None:
"""
Test the `query_context_modified` function.
The function is used to ensure guest users are not modifying the request payload on
embedded dashboard, preventing users from modifying it to access metrics different
from the ones stored in dashboard charts.
"""
query_context = mocker.MagicMock()
query_context.slice_.id = 42
query_context.slice_.query_context = None
query_context.slice_.params_dict = {
"metrics": stored_metrics,
}
query_context.form_data = {
"slice_id": 42,
"metrics": stored_metrics,
}
query_context.queries = [QueryObject(metrics=stored_metrics)] # type: ignore
assert not query_context_modified(query_context)
def test_query_context_modified_tampered(
mocker: MockFixture,
stored_metrics: list[AdhocMetric],
) -> None:
"""
Test the `query_context_modified` function when the request is tampered with.
The function is used to ensure guest users are not modifying the request payload on
embedded dashboard, preventing users from modifying it to access metrics different
from the ones stored in dashboard charts.
"""
query_context = mocker.MagicMock()
query_context.slice_.id = 42
query_context.slice_.query_context = None
query_context.slice_.params_dict = {
"metrics": stored_metrics,
}
tampered_metrics = [
{
"column": None,
"expressionType": "SQL",
"hasCustomLabel": False,
"label": "COUNT(*) + 2",
"sqlExpression": "COUNT(*) + 2",
}
]
query_context.form_data = {
"slice_id": 42,
"metrics": tampered_metrics,
}
query_context.queries = [QueryObject(metrics=tampered_metrics)] # type: ignore
assert query_context_modified(query_context)
def test_query_context_modified_native_filter(mocker: MockFixture) -> None:
"""
Test the `query_context_modified` function with a native filter request.
A native filter request has no chart (slice) associated with it.
"""
query_context = mocker.MagicMock()
query_context.slice_ = None
assert not query_context_modified(query_context)
def test_query_context_modified_mixed_chart(mocker: MockFixture) -> None:
"""
Test the `query_context_modified` function for a mixed chart request.
The metrics in the mixed chart are a nested dictionary (due to `columns`), and need
to be serialized to JSON with the keys sorted in order to compare the request
metrics with the chart metrics.
"""
stored_metrics = [
{
"optionName": "metric_vgops097wej_g8uff99zhk7",
"label": "AVG(num)",
"expressionType": "SIMPLE",
"column": {"column_name": "num", "type": "BIGINT(20)"},
"aggregate": "AVG",
}
]
# different order (remember, dicts have order!)
requested_metrics = [
{
"aggregate": "AVG",
"column": {"column_name": "num", "type": "BIGINT(20)"},
"expressionType": "SIMPLE",
"label": "AVG(num)",
"optionName": "metric_vgops097wej_g8uff99zhk7",
}
]
query_context = mocker.MagicMock()
query_context.slice_.id = 42
query_context.slice_.query_context = None
query_context.slice_.params_dict = {
"metrics": stored_metrics,
}
query_context.form_data = {
"slice_id": 42,
"metrics": requested_metrics,
}
query_context.queries = [QueryObject(metrics=requested_metrics)] # type: ignore
assert not query_context_modified(query_context)