Run black formatter

This commit is contained in:
Simon Willison 2025-10-25 08:45:10 -07:00
commit 60a38cee85
14 changed files with 222 additions and 80 deletions

View file

@ -1041,7 +1041,6 @@ class Datasette:
for hook in pm.hook.track_event(datasette=self, event=event): for hook in pm.hook.track_event(datasette=self, event=event):
await await_me_maybe(hook) await await_me_maybe(hook)
async def check_visibility( async def check_visibility(
self, self,
actor: dict, actor: dict,
@ -1282,7 +1281,9 @@ class Datasette:
old_style_resource = None old_style_resource = None
# If restrictions don't allow this action, deny it # If restrictions don't allow this action, deny it
if not restrictions_allow_action(self, actor["_r"], action, old_style_resource): if not restrictions_allow_action(
self, actor["_r"], action, old_style_resource
):
result = False result = False
# Log the permission check for debugging # Log the permission check for debugging

View file

@ -450,7 +450,10 @@ async def build_permission_rules_sql(
# Build the UNION query # Build the UNION query
if not rule_sqls: if not rule_sqls:
# Return empty result set # Return empty result set
return "SELECT NULL AS parent, NULL AS child, 0 AS allow, NULL AS reason, NULL AS source_plugin WHERE 0", {} return (
"SELECT NULL AS parent, NULL AS child, 0 AS allow, NULL AS reason, NULL AS source_plugin WHERE 0",
{},
)
rules_union = " UNION ALL ".join(rule_sqls) rules_union = " UNION ALL ".join(rule_sqls)
return rules_union, all_params return rules_union, all_params

View file

@ -116,7 +116,9 @@ class DatabaseView(View):
attached_databases = [d.name for d in await db.attached_databases()] attached_databases = [d.name for d in await db.attached_databases()]
allow_execute_sql = await datasette.allowed( allow_execute_sql = await datasette.allowed(
action="execute-sql", resource=DatabaseResource(database=database), actor=request.actor action="execute-sql",
resource=DatabaseResource(database=database),
actor=request.actor,
) )
json_data = { json_data = {
"database": database, "database": database,
@ -726,7 +728,9 @@ class QueryView(View):
) )
allow_execute_sql = await datasette.allowed( allow_execute_sql = await datasette.allowed(
action="execute-sql", resource=DatabaseResource(database=database), actor=request.actor action="execute-sql",
resource=DatabaseResource(database=database),
actor=request.actor,
) )
show_hide_hidden = "" show_hide_hidden = ""
@ -937,7 +941,9 @@ class TableCreateView(BaseView):
# Must have create-table permission # Must have create-table permission
if not await self.ds.allowed( if not await self.ds.allowed(
action="create-table", resource=DatabaseResource(database=database_name), actor=request.actor action="create-table",
resource=DatabaseResource(database=database_name),
actor=request.actor,
): ):
return _error(["Permission denied"], 403) return _error(["Permission denied"], 403)
@ -974,7 +980,9 @@ class TableCreateView(BaseView):
if replace: if replace:
# Must have update-row permission # Must have update-row permission
if not await self.ds.allowed( if not await self.ds.allowed(
action="update-row", resource=DatabaseResource(database=database_name), actor=request.actor action="update-row",
resource=DatabaseResource(database=database_name),
actor=request.actor,
): ):
return _error(["Permission denied: need update-row"], 403) return _error(["Permission denied: need update-row"], 403)
@ -998,7 +1006,9 @@ class TableCreateView(BaseView):
if rows or row: if rows or row:
# Must have insert-row permission # Must have insert-row permission
if not await self.ds.allowed( if not await self.ds.allowed(
action="insert-row", resource=DatabaseResource(database=database_name), actor=request.actor action="insert-row",
resource=DatabaseResource(database=database_name),
actor=request.actor,
): ):
return _error(["Permission denied: need insert-row"], 403) return _error(["Permission denied: need insert-row"], 403)
@ -1011,7 +1021,9 @@ class TableCreateView(BaseView):
# alter=True only if they request it AND they have permission # alter=True only if they request it AND they have permission
if data.get("alter"): if data.get("alter"):
if not await self.ds.allowed( if not await self.ds.allowed(
action="alter-table", resource=DatabaseResource(database=database_name), actor=request.actor action="alter-table",
resource=DatabaseResource(database=database_name),
actor=request.actor,
): ):
return _error(["Permission denied: need alter-table"], 403) return _error(["Permission denied: need alter-table"], 403)
alter = True alter = True

View file

@ -183,7 +183,9 @@ async def _resolve_row_and_check_permission(datasette, request, permission):
# Ensure user has permission to delete this row # Ensure user has permission to delete this row
if not await datasette.allowed( if not await datasette.allowed(
action=permission, resource=TableResource(database=resolved.db.name, table=resolved.table), actor=request.actor action=permission,
resource=TableResource(database=resolved.db.name, table=resolved.table),
actor=request.actor,
): ):
return False, _error(["Permission denied"], 403) return False, _error(["Permission denied"], 403)
@ -256,7 +258,9 @@ class RowUpdateView(BaseView):
alter = data.get("alter") alter = data.get("alter")
if alter and not await self.ds.allowed( if alter and not await self.ds.allowed(
action="alter-table", resource=TableResource(database=resolved.db.name, table=resolved.table), actor=request.actor action="alter-table",
resource=TableResource(database=resolved.db.name, table=resolved.table),
actor=request.actor,
): ):
return _error(["Permission denied for alter-table"], 403) return _error(["Permission denied for alter-table"], 403)

View file

@ -535,7 +535,9 @@ class PermissionCheckView(BaseView):
resource = None resource = None
before_checks = len(self.ds._permission_checks) before_checks = len(self.ds._permission_checks)
allowed = await self.ds.allowed(action=action, resource=resource_obj, actor=request.actor) allowed = await self.ds.allowed(
action=action, resource=resource_obj, actor=request.actor
)
info = None info = None
if len(self.ds._permission_checks) > before_checks: if len(self.ds._permission_checks) > before_checks:
@ -659,7 +661,9 @@ class CreateTokenView(BaseView):
if database.name == "_memory": if database.name == "_memory":
continue continue
if not await self.ds.allowed( if not await self.ds.allowed(
action="view-database", resource=DatabaseResource(database=database.name), actor=request.actor action="view-database",
resource=DatabaseResource(database=database.name),
actor=request.actor,
): ):
continue continue
hidden_tables = await database.hidden_table_names() hidden_tables = await database.hidden_table_names()
@ -670,7 +674,7 @@ class CreateTokenView(BaseView):
if not await self.ds.allowed( if not await self.ds.allowed(
action="view-table", action="view-table",
resource=TableResource(database=database.name, table=table), resource=TableResource(database=database.name, table=table),
actor=request.actor actor=request.actor,
): ):
continue continue
tables.append({"name": table, "encoded": tilde_encode(table)}) tables.append({"name": table, "encoded": tilde_encode(table)})
@ -809,7 +813,9 @@ class ApiExplorerView(BaseView):
continue continue
if await self.ds.allowed( if await self.ds.allowed(
action="insert-row", resource=TableResource(database=name, table=table), actor=request.actor action="insert-row",
resource=TableResource(database=name, table=table),
actor=request.actor,
): ):
pks = await db.primary_keys(table) pks = await db.primary_keys(table)
table_links.extend( table_links.extend(
@ -845,7 +851,9 @@ class ApiExplorerView(BaseView):
] ]
) )
if await self.ds.allowed( if await self.ds.allowed(
action="drop-table", resource=TableResource(database=name, table=table), actor=request.actor action="drop-table",
resource=TableResource(database=name, table=table),
actor=request.actor,
): ):
table_links.append( table_links.append(
{ {
@ -857,7 +865,11 @@ class ApiExplorerView(BaseView):
) )
database_links = [] database_links = []
if ( if (
await self.ds.allowed(action="create-table", resource=DatabaseResource(database=name), actor=request.actor) await self.ds.allowed(
action="create-table",
resource=DatabaseResource(database=name),
actor=request.actor,
)
and db.is_mutable and db.is_mutable
): ):
database_links.append( database_links.append(

View file

@ -451,10 +451,14 @@ class TableInsertView(BaseView):
# Must have insert-row AND upsert-row permissions # Must have insert-row AND upsert-row permissions
if not ( if not (
await self.ds.allowed( await self.ds.allowed(
action="insert-row", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="insert-row",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
) )
and await self.ds.allowed( and await self.ds.allowed(
action="update-row", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="update-row",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
) )
): ):
return _error( return _error(
@ -463,7 +467,9 @@ class TableInsertView(BaseView):
else: else:
# Must have insert-row permission # Must have insert-row permission
if not await self.ds.allowed( if not await self.ds.allowed(
action="insert-row", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="insert-row",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
): ):
return _error(["Permission denied"], 403) return _error(["Permission denied"], 403)
@ -493,7 +499,9 @@ class TableInsertView(BaseView):
return _error(["Upsert does not support ignore or replace"], 400) return _error(["Upsert does not support ignore or replace"], 400)
if replace and not await self.ds.allowed( if replace and not await self.ds.allowed(
action="update-row", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="update-row",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
): ):
return _error(['Permission denied: need update-row to use "replace"'], 403) return _error(['Permission denied: need update-row to use "replace"'], 403)
@ -501,7 +509,9 @@ class TableInsertView(BaseView):
if alter: if alter:
# Must have alter-table permission # Must have alter-table permission
if not await self.ds.allowed( if not await self.ds.allowed(
action="alter-table", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="alter-table",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
): ):
return _error(["Permission denied for alter-table"], 403) return _error(["Permission denied for alter-table"], 403)
# Track initial schema to check if it changed later # Track initial schema to check if it changed later
@ -629,7 +639,9 @@ class TableDropView(BaseView):
if not await db.table_exists(table_name): if not await db.table_exists(table_name):
return _error(["Table not found: {}".format(table_name)], 404) return _error(["Table not found: {}".format(table_name)], 404)
if not await self.ds.allowed( if not await self.ds.allowed(
action="drop-table", resource=TableResource(database=database_name, table=table_name), actor=request.actor action="drop-table",
resource=TableResource(database=database_name, table=table_name),
actor=request.actor,
): ):
return _error(["Permission denied"], 403) return _error(["Permission denied"], 403)
if not db.is_mutable: if not db.is_mutable:
@ -916,7 +928,9 @@ async def table_view_traced(datasette, request):
), ),
is_sortable=any(c["sortable"] for c in data["display_columns"]), is_sortable=any(c["sortable"] for c in data["display_columns"]),
allow_execute_sql=await datasette.allowed( allow_execute_sql=await datasette.allowed(
action="execute-sql", resource=DatabaseResource(database=resolved.db.name), actor=request.actor action="execute-sql",
resource=DatabaseResource(database=resolved.db.name),
actor=request.actor,
), ),
query_ms=1.2, query_ms=1.2,
select_templates=[ select_templates=[

View file

@ -495,7 +495,7 @@ def register_actions(datasette):
takes_parent=True, takes_parent=True,
takes_child=False, takes_child=False,
resource_class=DatabaseResource, resource_class=DatabaseResource,
) ),
] ]
# Support old-style config for backwards compatibility # Support old-style config for backwards compatibility
@ -509,7 +509,11 @@ def register_actions(datasette):
description=p["description"], description=p["description"],
takes_parent=p.get("takes_database", False), takes_parent=p.get("takes_database", False),
takes_child=p.get("takes_resource", False), takes_child=p.get("takes_resource", False),
resource_class=DatabaseResource if p.get("takes_database") else InstanceResource, resource_class=(
DatabaseResource
if p.get("takes_database")
else InstanceResource
),
) )
) )
@ -521,7 +525,9 @@ def register_actions(datasette):
"InstanceResource": InstanceResource, "InstanceResource": InstanceResource,
"DatabaseResource": DatabaseResource, "DatabaseResource": DatabaseResource,
} }
resource_class = resource_class_map.get(a.get("resource_class", "InstanceResource"), InstanceResource) resource_class = resource_class_map.get(
a.get("resource_class", "InstanceResource"), InstanceResource
)
actions.append( actions.append(
Action( Action(
@ -561,7 +567,13 @@ def permission_resources_sql(datasette, actor, action):
else: else:
return None # No opinion return None # No opinion
return PermissionSQL(source="my_plugin", sql=sql, params={}) return PermissionSQL(source="my_plugin", sql=sql, params={})
elif action in ("insert-row", "create-table", "drop-table", "delete-row", "update-row"): elif action in (
"insert-row",
"create-table",
"drop-table",
"delete-row",
"update-row",
):
# Special permissions for latest.datasette.io demos # Special permissions for latest.datasette.io demos
actor_id = actor.get("id") if actor else None actor_id = actor.get("id") if actor else None
if actor_id == "todomvc": if actor_id == "todomvc":

View file

@ -358,18 +358,12 @@ async def test_root_with_root_enabled_gets_all_permissions(ds_client):
# Test instance-level permissions (no resource) # Test instance-level permissions (no resource)
assert ( assert (
await ds_client.ds.allowed(action="permissions-debug", actor=root_actor) await ds_client.ds.allowed(action="permissions-debug", actor=root_actor) is True
is True
) )
assert await ds_client.ds.allowed(action="debug-menu", actor=root_actor) is True assert await ds_client.ds.allowed(action="debug-menu", actor=root_actor) is True
# Test view permissions using the new ds.allowed() method # Test view permissions using the new ds.allowed() method
assert ( assert await ds_client.ds.allowed(action="view-instance", actor=root_actor) is True
await ds_client.ds.allowed(
action="view-instance", actor=root_actor
)
is True
)
assert ( assert (
await ds_client.ds.allowed( await ds_client.ds.allowed(
@ -462,10 +456,7 @@ async def test_root_without_root_enabled_no_special_permissions(ds_client):
# View permissions should still work (default=True) # View permissions should still work (default=True)
assert ( assert (
await ds_client.ds.allowed( await ds_client.ds.allowed(action="view-instance", actor=root_actor) is True
action="view-instance", actor=root_actor
)
is True
) # Default permission ) # Default permission
assert ( assert (
@ -479,9 +470,7 @@ async def test_root_without_root_enabled_no_special_permissions(ds_client):
# But restricted permissions should NOT automatically be granted # But restricted permissions should NOT automatically be granted
# Test with instance-level permission (no resource class) # Test with instance-level permission (no resource class)
result = await ds_client.ds.allowed( result = await ds_client.ds.allowed(action="permissions-debug", actor=root_actor)
action="permissions-debug", actor=root_actor
)
assert ( assert (
result is not True result is not True
), "Root without root_enabled should not automatically get permissions-debug" ), "Root without root_enabled should not automatically get permissions-debug"

View file

@ -5,7 +5,9 @@ import re
from .fixtures import make_app_client, app_client from .fixtures import make_app_client, app_client
# Mark entire module as xfail since view-query permission not yet migrated, refs #2534 # Mark entire module as xfail since view-query permission not yet migrated, refs #2534
pytestmark = pytest.mark.xfail(reason="view-query permission not yet migrated to new permission system, refs #2534") pytestmark = pytest.mark.xfail(
reason="view-query permission not yet migrated to new permission system, refs #2534"
)
@pytest.fixture @pytest.fixture

View file

@ -19,8 +19,16 @@ async def test_root_permissions_allow():
config = {"permissions": {"execute-sql": {"id": "alice"}}} config = {"permissions": {"execute-sql": {"id": "alice"}}}
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "alice"}) assert await ds.allowed(
assert not await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "bob"}) action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "alice"},
)
assert not await ds.allowed(
action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "bob"},
)
@pytest.mark.asyncio @pytest.mark.asyncio
@ -37,10 +45,14 @@ async def test_database_permission():
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed( assert await ds.allowed(
action="insert-row", resource=TableResource(database="content", table="repos"), actor={"id": "alice"} action="insert-row",
resource=TableResource(database="content", table="repos"),
actor={"id": "alice"},
) )
assert not await ds.allowed( assert not await ds.allowed(
action="insert-row", resource=TableResource(database="content", table="repos"), actor={"id": "bob"} action="insert-row",
resource=TableResource(database="content", table="repos"),
actor={"id": "bob"},
) )
@ -56,10 +68,14 @@ async def test_table_permission():
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed( assert await ds.allowed(
action="delete-row", resource=TableResource(database="content", table="repos"), actor={"id": "alice"} action="delete-row",
resource=TableResource(database="content", table="repos"),
actor={"id": "alice"},
) )
assert not await ds.allowed( assert not await ds.allowed(
action="delete-row", resource=TableResource(database="content", table="repos"), actor={"id": "bob"} action="delete-row",
resource=TableResource(database="content", table="repos"),
actor={"id": "bob"},
) )
@ -71,13 +87,19 @@ async def test_view_table_allow_block():
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed( assert await ds.allowed(
action="view-table", resource=TableResource(database="content", table="repos"), actor={"id": "alice"} action="view-table",
resource=TableResource(database="content", table="repos"),
actor={"id": "alice"},
) )
assert not await ds.allowed( assert not await ds.allowed(
action="view-table", resource=TableResource(database="content", table="repos"), actor={"id": "bob"} action="view-table",
resource=TableResource(database="content", table="repos"),
actor={"id": "bob"},
) )
assert await ds.allowed( assert await ds.allowed(
action="view-table", resource=TableResource(database="content", table="other"), actor={"id": "bob"} action="view-table",
resource=TableResource(database="content", table="other"),
actor={"id": "bob"},
) )
@ -87,7 +109,9 @@ async def test_view_table_allow_false_blocks():
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert not await ds.allowed( assert not await ds.allowed(
action="view-table", resource=TableResource(database="content", table="repos"), actor={"id": "alice"} action="view-table",
resource=TableResource(database="content", table="repos"),
actor={"id": "alice"},
) )
@ -96,18 +120,38 @@ async def test_allow_sql_blocks():
config = {"allow_sql": {"id": "alice"}} config = {"allow_sql": {"id": "alice"}}
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "alice"}) assert await ds.allowed(
assert not await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "bob"}) action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "alice"},
)
assert not await ds.allowed(
action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "bob"},
)
config = {"databases": {"content": {"allow_sql": {"id": "bob"}}}} config = {"databases": {"content": {"allow_sql": {"id": "bob"}}}}
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "bob"}) assert await ds.allowed(
assert not await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "alice"}) action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "bob"},
)
assert not await ds.allowed(
action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "alice"},
)
config = {"allow_sql": False} config = {"allow_sql": False}
ds = await setup_datasette(config=config, databases=["content"]) ds = await setup_datasette(config=config, databases=["content"])
assert not await ds.allowed(action="execute-sql", resource=DatabaseResource(database="content"), actor={"id": "alice"}) assert not await ds.allowed(
action="execute-sql",
resource=DatabaseResource(database="content"),
actor={"id": "alice"},
)
@pytest.mark.asyncio @pytest.mark.asyncio

View file

@ -135,7 +135,9 @@ def test_not_allowed_methods():
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not displayed due to view-query permission, refs #2510") @pytest.mark.xfail(
reason="Canned queries not displayed due to view-query permission, refs #2510"
)
async def test_database_page(ds_client): async def test_database_page(ds_client):
response = await ds_client.get("/fixtures") response = await ds_client.get("/fixtures")
soup = Soup(response.text, "html.parser") soup = Soup(response.text, "html.parser")
@ -264,7 +266,7 @@ def test_query_page_truncates():
pytest.param( pytest.param(
"/fixtures/neighborhood_search", "/fixtures/neighborhood_search",
["query", "db-fixtures", "query-neighborhood_search"], ["query", "db-fixtures", "query-neighborhood_search"],
marks=pytest.mark.xfail(reason="Canned queries not accessible, refs #2510") marks=pytest.mark.xfail(reason="Canned queries not accessible, refs #2510"),
), ),
( (
"/fixtures/table~2Fwith~2Fslashes~2Ecsv", "/fixtures/table~2Fwith~2Fslashes~2Ecsv",
@ -597,7 +599,9 @@ async def test_404_content_type(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_canned_query_default_title(ds_client): async def test_canned_query_default_title(ds_client):
response = await ds_client.get("/fixtures/magic_parameters") response = await ds_client.get("/fixtures/magic_parameters")
assert response.status_code == 200 assert response.status_code == 200
@ -606,7 +610,9 @@ async def test_canned_query_default_title(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_canned_query_with_custom_metadata(ds_client): async def test_canned_query_with_custom_metadata(ds_client):
response = await ds_client.get("/fixtures/neighborhood_search?text=town") response = await ds_client.get("/fixtures/neighborhood_search?text=town")
assert response.status_code == 200 assert response.status_code == 200
@ -669,7 +675,9 @@ async def test_show_hide_sql_query(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_canned_query_with_hide_has_no_hidden_sql(ds_client): async def test_canned_query_with_hide_has_no_hidden_sql(ds_client):
# For a canned query the show/hide should NOT have a hidden SQL field # For a canned query the show/hide should NOT have a hidden SQL field
# https://github.com/simonw/datasette/issues/1411 # https://github.com/simonw/datasette/issues/1411
@ -681,7 +689,9 @@ async def test_canned_query_with_hide_has_no_hidden_sql(ds_client):
] == [(hidden["name"], hidden["value"]) for hidden in hiddens] ] == [(hidden["name"], hidden["value"]) for hidden in hiddens]
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"hide_sql,querystring,expected_hidden,expected_show_hide_link,expected_show_hide_text", "hide_sql,querystring,expected_hidden,expected_show_hide_link,expected_show_hide_text",
( (
@ -931,7 +941,9 @@ def test_base_url_affects_metadata_extra_css_urls(app_client_base_url_prefix):
("/fixtures/magic_parameters", None), ("/fixtures/magic_parameters", None),
], ],
) )
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_edit_sql_link_on_canned_queries(ds_client, path, expected): async def test_edit_sql_link_on_canned_queries(ds_client, path, expected):
response = await ds_client.get(path) response = await ds_client.get(path)
assert response.status_code == 200 assert response.status_code == 200
@ -1037,7 +1049,7 @@ async def test_trace_correctly_escaped(ds_client):
pytest.param( pytest.param(
"/fixtures/neighborhood_search?text=town", "/fixtures/neighborhood_search?text=town",
"http://localhost/fixtures/neighborhood_search.json?text=town", "http://localhost/fixtures/neighborhood_search.json?text=town",
marks=pytest.mark.xfail(reason="Canned queries not accessible, refs #2510") marks=pytest.mark.xfail(reason="Canned queries not accessible, refs #2510"),
), ),
# /-/ pages # /-/ pages
( (

View file

@ -121,7 +121,9 @@ async def test_datasette_check_visibility(
): ):
ds = Datasette([], memory=True, config=config) ds = Datasette([], memory=True, config=config)
await ds.invoke_startup() await ds.invoke_startup()
visible, private = await ds.check_visibility(actor, action=action, resource=resource) visible, private = await ds.check_visibility(
actor, action=action, resource=resource
)
assert visible == should_allow assert visible == should_allow
assert private == expected_private assert private == expected_private

View file

@ -59,7 +59,12 @@ async def perms_ds():
"/-/api", "/-/api",
"/fixtures/compound_three_primary_keys", "/fixtures/compound_three_primary_keys",
"/fixtures/compound_three_primary_keys/a,a,a", "/fixtures/compound_three_primary_keys/a,a,a",
pytest.param("/fixtures/two", marks=pytest.mark.xfail(reason="view-query not yet migrated to new permission system")), # Query pytest.param(
"/fixtures/two",
marks=pytest.mark.xfail(
reason="view-query not yet migrated to new permission system"
),
), # Query
), ),
) )
def test_view_padlock(allow, expected_anon, expected_auth, path, padlock_client): def test_view_padlock(allow, expected_anon, expected_auth, path, padlock_client):
@ -904,6 +909,7 @@ async def test_permissions_in_config(
try: try:
# Convert old-style resource to Resource object # Convert old-style resource to Resource object
from datasette.resources import DatabaseResource, TableResource from datasette.resources import DatabaseResource, TableResource
resource_obj = None resource_obj = None
if resource: if resource:
if isinstance(resource, str): if isinstance(resource, str):
@ -911,7 +917,9 @@ async def test_permissions_in_config(
elif isinstance(resource, tuple) and len(resource) == 2: elif isinstance(resource, tuple) and len(resource) == 2:
resource_obj = TableResource(database=resource[0], table=resource[1]) resource_obj = TableResource(database=resource[0], table=resource[1])
result = await perms_ds.allowed(action=action, resource=resource_obj, actor=actor) result = await perms_ds.allowed(
action=action, resource=resource_obj, actor=actor
)
if result != expected_result: if result != expected_result:
pprint(perms_ds._permission_checks) pprint(perms_ds._permission_checks)
assert result == expected_result assert result == expected_result
@ -1123,7 +1131,16 @@ async def test_view_table_token_can_access_table(perms_ds):
({"a": ["vi"]}, "get", "/perms_ds_one/t1/1.json", None, 403), ({"a": ["vi"]}, "get", "/perms_ds_one/t1/1.json", None, 403),
({"a": ["vi"]}, "get", "/perms_ds_one/v1.json", None, 403), ({"a": ["vi"]}, "get", "/perms_ds_one/v1.json", None, 403),
# Restricted to just view-database # Restricted to just view-database
pytest.param({"a": ["vd"]}, "get", "/.json", None, 200, marks=pytest.mark.xfail(reason="Actor restrictions need additional work, refs #2534")), # Can see instance too pytest.param(
{"a": ["vd"]},
"get",
"/.json",
None,
200,
marks=pytest.mark.xfail(
reason="Actor restrictions need additional work, refs #2534"
),
), # Can see instance too
({"a": ["vd"]}, "get", "/perms_ds_one.json", None, 200), ({"a": ["vd"]}, "get", "/perms_ds_one.json", None, 200),
({"a": ["vd"]}, "get", "/perms_ds_one/t1.json", None, 403), ({"a": ["vd"]}, "get", "/perms_ds_one/t1.json", None, 403),
({"a": ["vd"]}, "get", "/perms_ds_one/t1/1.json", None, 403), ({"a": ["vd"]}, "get", "/perms_ds_one/t1/1.json", None, 403),
@ -1135,7 +1152,9 @@ async def test_view_table_token_can_access_table(perms_ds):
"/.json", "/.json",
None, None,
200, 200,
marks=pytest.mark.xfail(reason="Actor restrictions need additional work, refs #2534") marks=pytest.mark.xfail(
reason="Actor restrictions need additional work, refs #2534"
),
), # Can see instance ), # Can see instance
pytest.param( pytest.param(
{"d": {"perms_ds_one": ["vt"]}}, {"d": {"perms_ds_one": ["vt"]}},
@ -1143,7 +1162,9 @@ async def test_view_table_token_can_access_table(perms_ds):
"/perms_ds_one.json", "/perms_ds_one.json",
None, None,
200, 200,
marks=pytest.mark.xfail(reason="Actor restrictions need additional work, refs #2534") marks=pytest.mark.xfail(
reason="Actor restrictions need additional work, refs #2534"
),
), # and this database ), # and this database
( (
{"d": {"perms_ds_one": ["vt"]}}, {"d": {"perms_ds_one": ["vt"]}},
@ -1175,7 +1196,9 @@ async def test_view_table_token_can_access_table(perms_ds):
"/.json", "/.json",
None, None,
200, 200,
marks=pytest.mark.xfail(reason="Actor restrictions need additional work, refs #2534") marks=pytest.mark.xfail(
reason="Actor restrictions need additional work, refs #2534"
),
), ),
pytest.param( pytest.param(
{"r": {"perms_ds_one": {"t1": ["vt"]}}}, {"r": {"perms_ds_one": {"t1": ["vt"]}}},
@ -1183,7 +1206,9 @@ async def test_view_table_token_can_access_table(perms_ds):
"/perms_ds_one.json", "/perms_ds_one.json",
None, None,
200, 200,
marks=pytest.mark.xfail(reason="Actor restrictions need additional work, refs #2534") marks=pytest.mark.xfail(
reason="Actor restrictions need additional work, refs #2534"
),
), ),
( (
{"r": {"perms_ds_one": {"t1": ["vt"]}}}, {"r": {"perms_ds_one": {"t1": ["vt"]}}},

View file

@ -848,7 +848,9 @@ async def test_hook_startup(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_hook_canned_queries(ds_client): async def test_hook_canned_queries(ds_client):
queries = (await ds_client.get("/fixtures.json")).json()["queries"] queries = (await ds_client.get("/fixtures.json")).json()["queries"]
queries_by_name = {q["name"]: q for q in queries} queries_by_name = {q["name"]: q for q in queries}
@ -865,21 +867,27 @@ async def test_hook_canned_queries(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_hook_canned_queries_non_async(ds_client): async def test_hook_canned_queries_non_async(ds_client):
response = await ds_client.get("/fixtures/from_hook.json?_shape=array") response = await ds_client.get("/fixtures/from_hook.json?_shape=array")
assert [{"1": 1, "actor_id": "null"}] == response.json() assert [{"1": 1, "actor_id": "null"}] == response.json()
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_hook_canned_queries_async(ds_client): async def test_hook_canned_queries_async(ds_client):
response = await ds_client.get("/fixtures/from_async_hook.json?_shape=array") response = await ds_client.get("/fixtures/from_async_hook.json?_shape=array")
assert [{"2": 2}] == response.json() assert [{"2": 2}] == response.json()
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_hook_canned_queries_actor(ds_client): async def test_hook_canned_queries_actor(ds_client):
assert ( assert (
await ds_client.get("/fixtures/from_hook.json?_bot=1&_shape=array") await ds_client.get("/fixtures/from_hook.json?_bot=1&_shape=array")
@ -1541,7 +1549,9 @@ async def test_hook_top_query(ds_client):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.xfail(reason="Canned queries not yet migrated to new permission system, refs #2510") @pytest.mark.xfail(
reason="Canned queries not yet migrated to new permission system, refs #2510"
)
async def test_hook_top_canned_query(ds_client): async def test_hook_top_canned_query(ds_client):
try: try:
pm.register(SlotPlugin(), name="SlotPlugin") pm.register(SlotPlugin(), name="SlotPlugin")