mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Run black formatter
This commit is contained in:
parent
b5f41772ca
commit
60a38cee85
14 changed files with 222 additions and 80 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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=[
|
||||||
|
|
|
||||||
|
|
@ -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":
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"]}}},
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue