Fix minor irritation with /-/allowed UI

This commit is contained in:
Simon Willison 2025-10-25 18:02:26 -07:00
commit ee62bf9bdc
3 changed files with 32 additions and 28 deletions

View file

@ -42,7 +42,7 @@
<div class="form-section">
<label for="child">Filter by child (optional):</label>
<input type="text" id="child" name="child" placeholder="e.g., table name">
<small>Filter results to a specific child resource (requires parent)</small>
<small>Filter results to a specific child resource (requires parent to be set)</small>
</div>
<div class="form-section">
@ -236,12 +236,15 @@ function displayError(data) {
const parentInput = document.getElementById('parent');
const childInput = document.getElementById('child');
childInput.addEventListener('focus', () => {
parentInput.addEventListener('input', () => {
childInput.disabled = !parentInput.value;
if (!parentInput.value) {
alert('Please specify a parent resource first before filtering by child resource.');
parentInput.focus();
childInput.value = '';
}
});
// Initialize disabled state
childInput.disabled = !parentInput.value;
</script>
{% endblock %}

View file

@ -189,22 +189,6 @@ class AllowedResourcesView(BaseView):
name = "allowed"
has_json_alternate = False
CANDIDATE_SQL = {
"view-table": (
"SELECT database_name AS parent, table_name AS child FROM catalog_tables",
{},
),
"view-database": (
"SELECT database_name AS parent, NULL AS child FROM catalog_databases",
{},
),
"view-instance": ("SELECT NULL AS parent, NULL AS child", {}),
"execute-sql": (
"SELECT database_name AS parent, NULL AS child FROM catalog_databases",
{},
),
}
async def get(self, request):
await self.ds.refresh_schemas()
@ -218,11 +202,29 @@ class AllowedResourcesView(BaseView):
if not as_format:
# Render the HTML form (even if query parameters are present)
# Put most common/interesting actions first
priority_actions = [
"view-instance",
"view-database",
"view-table",
"view-query",
"execute-sql",
"insert-row",
"update-row",
"delete-row",
]
actions = list(self.ds.actions.keys())
# Priority actions first (in order), then remaining alphabetically
sorted_actions = [a for a in priority_actions if a in actions]
sorted_actions.extend(
sorted(a for a in actions if a not in priority_actions)
)
return await self.render(
["debug_allowed.html"],
request,
{
"supported_actions": sorted(self.CANDIDATE_SQL.keys()),
"supported_actions": sorted_actions,
},
)
@ -232,11 +234,6 @@ class AllowedResourcesView(BaseView):
return Response.json({"error": "action parameter is required"}, status=400)
if action not in self.ds.actions:
return Response.json({"error": f"Unknown action: {action}"}, status=404)
if action not in self.CANDIDATE_SQL:
return Response.json(
{"error": f"Action '{action}' is not supported by this endpoint"},
status=400,
)
actor = request.actor if isinstance(request.actor, dict) else None
actor_id = actor.get("id") if actor else None

View file

@ -69,8 +69,12 @@ async def ds_with_permissions():
("/-/allowed.json", 400, {"error"}),
# Invalid action
("/-/allowed.json?action=nonexistent", 404, {"error"}),
# Unsupported action (valid but not in CANDIDATE_SQL)
("/-/allowed.json?action=insert-row", 400, {"error"}),
# Any valid action works, even if no permission rules exist for it
(
"/-/allowed.json?action=insert-row",
200,
{"action", "items", "total", "page"},
),
],
)
async def test_allowed_json_basic(