From c51d9246b996a2831c9bd6a1e205f6cb48b9a5f3 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Wed, 2 Nov 2022 22:10:07 -0700 Subject: [PATCH] Permission check testing tool, refs #1881 --- datasette/permissions.py | 19 +++++ datasette/templates/permissions_debug.html | 86 ++++++++++++++++++++++ datasette/views/special.py | 34 ++++++++- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 datasette/permissions.py diff --git a/datasette/permissions.py b/datasette/permissions.py new file mode 100644 index 00000000..91c9e774 --- /dev/null +++ b/datasette/permissions.py @@ -0,0 +1,19 @@ +import collections + +Permission = collections.namedtuple( + "Permission", ("name", "abbr", "takes_database", "takes_table", "default") +) + +PERMISSIONS = ( + Permission("view-instance", "vi", False, False, True), + Permission("view-database", "vd", True, False, True), + Permission("view-database-download", "vdd", True, False, True), + Permission("view-table", "vt", True, True, True), + Permission("view-query", "vq", True, True, True), + Permission("insert-row", "ir", True, True, False), + Permission("delete-row", "dr", True, True, False), + Permission("drop-table", "dt", True, True, False), + Permission("execute-sql", "es", True, False, True), + Permission("permissions-debug", "pd", False, False, False), + Permission("debug-menu", "dm", False, False, False), +) diff --git a/datasette/templates/permissions_debug.html b/datasette/templates/permissions_debug.html index b0f50f0e..36a12acc 100644 --- a/datasette/templates/permissions_debug.html +++ b/datasette/templates/permissions_debug.html @@ -19,11 +19,97 @@ .check-action, .check-when, .check-result { font-size: 1.3em; } +textarea { + height: 10em; + width: 95%; + box-sizing: border-box; + padding: 0.5em; + border: 2px dotted black; +} +.two-col { + display: inline-block; + width: 48%; +} +.two-col label { + width: 48%; +} +@media only screen and (max-width: 576px) { + .two-col { + width: 100%; + } +} {% endblock %} {% block content %} +

Permission check testing tool

+ +

This tool lets you simulate an actor and a permission check for that actor.

+ +
+ +
+

+ +
+
+

+ +

+

+
+
+ +
+

+
+ + +

Recent permissions checks

{% for check in permission_checks %} diff --git a/datasette/views/special.py b/datasette/views/special.py index 9922a621..ff014246 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -1,6 +1,8 @@ import json +from datasette.permissions import PERMISSIONS from datasette.utils.asgi import Response, Forbidden from datasette.utils import actor_matches_allow, add_cors_headers +from datasette.permissions import PERMISSIONS from .base import BaseView import secrets import time @@ -103,7 +105,37 @@ class PermissionsDebugView(BaseView): ["permissions_debug.html"], request, # list() avoids error if check is performed during template render: - {"permission_checks": list(reversed(self.ds._permission_checks))}, + { + "permission_checks": list(reversed(self.ds._permission_checks)), + "permissions": PERMISSIONS, + }, + ) + + async def post(self, request): + await self.ds.ensure_permissions(request.actor, ["view-instance"]) + if not await self.ds.permission_allowed(request.actor, "permissions-debug"): + raise Forbidden("Permission denied") + vars = await request.post_vars() + actor = json.loads(vars["actor"]) + permission = vars["permission"] + resource_1 = vars["resource_1"] + resource_2 = vars["resource_2"] + resource = [] + if resource_1: + resource.append(resource_1) + if resource_2: + resource.append(resource_2) + resource = tuple(resource) + result = await self.ds.permission_allowed( + actor, permission, resource, default="USE_DEFAULT" + ) + return Response.json( + { + "actor": actor, + "permission": permission, + "resource": resource, + "result": result, + } )