mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Update test infrastructure to use register_actions hook
- Consolidated register_permissions and register_actions hooks in my_plugin.py - Added permission_resources_sql hook to provide SQL-based permission rules - Updated conftest.py to reference datasette.actions instead of datasette.permissions - Updated fixtures.py to include permission_resources_sql hook and remove register_permissions - Added backwards compatibility support for old datasette-register-permissions config - Converted test actions (this_is_allowed, this_is_denied, etc.) to use permission_resources_sql 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
59ccf797c4
commit
fe2084df66
3 changed files with 89 additions and 34 deletions
|
|
@ -147,8 +147,8 @@ def check_permission_actions_are_documented():
|
||||||
def before(hook_name, hook_impls, kwargs):
|
def before(hook_name, hook_impls, kwargs):
|
||||||
if hook_name == "permission_allowed":
|
if hook_name == "permission_allowed":
|
||||||
datasette = kwargs["datasette"]
|
datasette = kwargs["datasette"]
|
||||||
assert kwargs["action"] in datasette.permissions, (
|
assert kwargs["action"] in datasette.actions, (
|
||||||
"'{}' has not been registered with register_permissions()".format(
|
"'{}' has not been registered with register_actions()".format(
|
||||||
kwargs["action"]
|
kwargs["action"]
|
||||||
)
|
)
|
||||||
+ " (or maybe a test forgot to do await ds.invoke_startup())"
|
+ " (or maybe a test forgot to do await ds.invoke_startup())"
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,13 @@ EXPECTED_PLUGINS = [
|
||||||
"homepage_actions",
|
"homepage_actions",
|
||||||
"menu_links",
|
"menu_links",
|
||||||
"permission_allowed",
|
"permission_allowed",
|
||||||
|
"permission_resources_sql",
|
||||||
"prepare_connection",
|
"prepare_connection",
|
||||||
"prepare_jinja2_environment",
|
"prepare_jinja2_environment",
|
||||||
"query_actions",
|
"query_actions",
|
||||||
"register_actions",
|
"register_actions",
|
||||||
"register_facet_classes",
|
"register_facet_classes",
|
||||||
"register_magic_parameters",
|
"register_magic_parameters",
|
||||||
"register_permissions",
|
|
||||||
"register_routes",
|
"register_routes",
|
||||||
"render_cell",
|
"render_cell",
|
||||||
"row_actions",
|
"row_actions",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from datasette import hookimpl, Permission
|
from datasette import hookimpl
|
||||||
from datasette.facets import Facet
|
from datasette.facets import Facet
|
||||||
from datasette import tracer
|
from datasette import tracer
|
||||||
from datasette.permissions import Action
|
from datasette.permissions import Action
|
||||||
from datasette.resources import DatabaseResource
|
from datasette.resources import DatabaseResource, InstanceResource
|
||||||
from datasette.utils import path_with_added_args
|
from datasette.utils import path_with_added_args
|
||||||
from datasette.utils.asgi import asgi_send_json, Response
|
from datasette.utils.asgi import asgi_send_json, Response
|
||||||
import base64
|
import base64
|
||||||
|
|
@ -474,37 +474,20 @@ def skip_csrf(scope):
|
||||||
return scope["path"] == "/skip-csrf"
|
return scope["path"] == "/skip-csrf"
|
||||||
|
|
||||||
|
|
||||||
@hookimpl
|
|
||||||
def register_permissions(datasette):
|
|
||||||
extras = datasette.plugin_config("datasette-register-permissions") or {}
|
|
||||||
permissions = [
|
|
||||||
Permission(
|
|
||||||
name="permission-from-plugin",
|
|
||||||
abbr="np",
|
|
||||||
description="New permission added by a plugin",
|
|
||||||
takes_database=True,
|
|
||||||
takes_resource=False,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if extras:
|
|
||||||
permissions.extend(
|
|
||||||
Permission(
|
|
||||||
name=p["name"],
|
|
||||||
abbr=p["abbr"],
|
|
||||||
description=p["description"],
|
|
||||||
takes_database=p["takes_database"],
|
|
||||||
takes_resource=p["takes_resource"],
|
|
||||||
default=p["default"],
|
|
||||||
)
|
|
||||||
for p in extras["permissions"]
|
|
||||||
)
|
|
||||||
return permissions
|
|
||||||
|
|
||||||
|
|
||||||
@hookimpl
|
@hookimpl
|
||||||
def register_actions(datasette):
|
def register_actions(datasette):
|
||||||
return [
|
extras_old = datasette.plugin_config("datasette-register-permissions") or {}
|
||||||
|
extras_new = datasette.plugin_config("datasette-register-actions") or {}
|
||||||
|
|
||||||
|
actions = [
|
||||||
|
Action(
|
||||||
|
name="action-from-plugin",
|
||||||
|
abbr="ap",
|
||||||
|
description="New action added by a plugin",
|
||||||
|
takes_parent=True,
|
||||||
|
takes_child=False,
|
||||||
|
resource_class=DatabaseResource,
|
||||||
|
),
|
||||||
Action(
|
Action(
|
||||||
name="view-collection",
|
name="view-collection",
|
||||||
abbr="vc",
|
abbr="vc",
|
||||||
|
|
@ -514,3 +497,75 @@ def register_actions(datasette):
|
||||||
resource_class=DatabaseResource,
|
resource_class=DatabaseResource,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Support old-style config for backwards compatibility
|
||||||
|
if extras_old:
|
||||||
|
for p in extras_old["permissions"]:
|
||||||
|
# Map old takes_database/takes_resource to new takes_parent/takes_child
|
||||||
|
actions.append(
|
||||||
|
Action(
|
||||||
|
name=p["name"],
|
||||||
|
abbr=p["abbr"],
|
||||||
|
description=p["description"],
|
||||||
|
takes_parent=p.get("takes_database", False),
|
||||||
|
takes_child=p.get("takes_resource", False),
|
||||||
|
resource_class=DatabaseResource if p.get("takes_database") else InstanceResource,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Support new-style config
|
||||||
|
if extras_new:
|
||||||
|
for a in extras_new["actions"]:
|
||||||
|
# Map string resource_class to actual class
|
||||||
|
resource_class_map = {
|
||||||
|
"InstanceResource": InstanceResource,
|
||||||
|
"DatabaseResource": DatabaseResource,
|
||||||
|
}
|
||||||
|
resource_class = resource_class_map.get(a.get("resource_class", "InstanceResource"), InstanceResource)
|
||||||
|
|
||||||
|
actions.append(
|
||||||
|
Action(
|
||||||
|
name=a["name"],
|
||||||
|
abbr=a["abbr"],
|
||||||
|
description=a["description"],
|
||||||
|
takes_parent=a.get("takes_parent", False),
|
||||||
|
takes_child=a.get("takes_child", False),
|
||||||
|
resource_class=resource_class,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return actions
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def permission_resources_sql(datasette, actor, action):
|
||||||
|
from datasette.permissions import PermissionSQL
|
||||||
|
|
||||||
|
# Handle test actions used in test_hook_permission_allowed
|
||||||
|
if action == "this_is_allowed":
|
||||||
|
sql = "SELECT NULL AS parent, NULL AS child, 1 AS allow, 'test plugin allows this_is_allowed' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
elif action == "this_is_denied":
|
||||||
|
sql = "SELECT NULL AS parent, NULL AS child, 0 AS allow, 'test plugin denies this_is_denied' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
elif action == "this_is_allowed_async":
|
||||||
|
sql = "SELECT NULL AS parent, NULL AS child, 1 AS allow, 'test plugin allows this_is_allowed_async' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
elif action == "this_is_denied_async":
|
||||||
|
sql = "SELECT NULL AS parent, NULL AS child, 0 AS allow, 'test plugin denies this_is_denied_async' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
elif action == "view-database-download":
|
||||||
|
# Return rule based on actor's can_download permission
|
||||||
|
if actor and actor.get("can_download"):
|
||||||
|
sql = "SELECT NULL AS parent, NULL AS child, 1 AS allow, 'actor has can_download' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
else:
|
||||||
|
return None # No opinion
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
elif action in ("insert-row", "create-table", "drop-table", "delete-row", "update-row"):
|
||||||
|
# Special permissions for latest.datasette.io demos
|
||||||
|
actor_id = actor.get("id") if actor else None
|
||||||
|
if actor_id == "todomvc":
|
||||||
|
sql = f"SELECT NULL AS parent, NULL AS child, 1 AS allow, 'todomvc actor allowed for {action}' AS reason, 'my_plugin' AS source_plugin"
|
||||||
|
return PermissionSQL(source="my_plugin", sql=sql, params={})
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue