Better display of recent permissions checks, refs #2543

This commit is contained in:
Simon Willison 2025-10-26 17:42:10 -07:00
commit 5da3c9f4bd
2 changed files with 65 additions and 56 deletions

View file

@ -134,31 +134,33 @@ debugPost.addEventListener('submit', function(ev) {
{% if filter != "only-yours" %}<a href="?filter=only-yours">Only yours</a>{% else %}<strong>Only yours</strong>{% endif %} {% if filter != "only-yours" %}<a href="?filter=only-yours">Only yours</a>{% else %}<strong>Only yours</strong>{% endif %}
</p> </p>
{% for check in permission_checks %} {% if permission_checks %}
<div class="check"> <table class="rows-and-columns permission-checks-table" id="permission-checks-table">
<h2> <thead>
<span class="check-action">{{ check.action }}</span> <tr>
checked at <th>When</th>
<span class="check-when">{{ check.when }}</span> <th>Action</th>
{% if check.result %} <th>Parent</th>
<span class="check-result check-result-true"></span> <th>Child</th>
{% elif check.result is none %} <th>Actor</th>
<span class="check-result check-result-no-opinion">none</span> <th>Result</th>
{% else %} </tr>
<span class="check-result check-result-false"></span> </thead>
{% endif %} <tbody>
</h2> {% for check in permission_checks %}
<p><strong>Actor:</strong> {{ check.actor|tojson }}</p> <tr>
{% if check.parent %} <td><span style="font-size: 0.8em">{{ check.when.split('T', 1)[0] }}</span><br>{{ check.when.split('T', 1)[1].split('+', 1)[0].split('-', 1)[0].split('Z', 1)[0] }}</td>
<p><strong>Resource:</strong> <td><code>{{ check.action }}</code></td>
{% if check.child %} <td>{{ check.parent or '—' }}</td>
{{ check.parent }} / {{ check.child }} <td>{{ check.child or '—' }}</td>
{% else %} <td>{% if check.actor %}<code>{{ check.actor|tojson }}</code>{% else %}<span class="check-actor-anon">anonymous</span>{% endif %}</td>
{{ check.parent }} <td>{% if check.result %}<span class="check-result check-result-true">Allowed</span>{% elif check.result is none %}<span class="check-result check-result-no-opinion">No opinion</span>{% else %}<span class="check-result check-result-false">Denied</span>{% endif %}</td>
{% endif %} </tr>
</p> {% endfor %}
{% endif %} </tbody>
</div> </table>
{% endfor %} {% else %}
<p class="no-results">No permission checks have been recorded yet.</p>
{% endif %}
{% endblock %} {% endblock %}

View file

@ -398,24 +398,27 @@ async def test_permissions_debug(ds_client, filter_):
assert fragment in response.text assert fragment in response.text
# Should show one failure and one success # Should show one failure and one success
soup = Soup(response.text, "html.parser") soup = Soup(response.text, "html.parser")
check_divs = soup.find_all("div", {"class": "check"}) table = soup.find("table", {"id": "permission-checks-table"})
checks = [ rows = table.find("tbody").find_all("tr")
{ checks = []
"action": div.select_one(".check-action").text, for row in rows:
# True = green tick, False = red cross, None = gray None cells = row.find_all("td")
"result": ( result_cell = cells[5]
None if result_cell.select_one(".check-result-true"):
if div.select(".check-result-no-opinion") result = True
else bool(div.select(".check-result-true")) elif result_cell.select_one(".check-result-false"):
), result = False
"actor": json.loads( else:
div.find( result = None
"strong", string=lambda text: text and "Actor" in text actor_code = cells[4].find("code")
).parent.text.split(": ", 1)[1] actor = json.loads(actor_code.text) if actor_code else None
), checks.append(
} {
for div in check_divs "action": cells[1].text.strip(),
] "result": result,
"actor": actor,
}
)
expected_checks = [ expected_checks = [
{ {
"action": "permissions-debug", "action": "permissions-debug",
@ -723,22 +726,26 @@ async def test_actor_restricted_permissions(
}, },
cookies=cookies, cookies=cookies,
) )
# Build expected_resource to match API behavior: # Response mirrors /-/check JSON structure
# - None when no resources if resource_1 is None:
# - Single string when only resource_1 expected_path = "/"
# - List when both resource_1 and resource_2 (JSON serializes tuples as lists) elif resource_2 is None:
if resource_1 and resource_2: expected_path = f"/{resource_1}"
expected_resource = [resource_1, resource_2]
elif resource_1:
expected_resource = resource_1
else: else:
expected_resource = None expected_path = f"/{resource_1}/{resource_2}"
expected = {
"actor": actor, expected_resource = {
"permission": permission, "parent": resource_1,
"resource": expected_resource, "child": resource_2,
"result": expected_result, "path": expected_path,
} }
expected = {
"action": permission,
"allowed": expected_result,
"resource": expected_resource,
}
if actor.get("id"):
expected["actor_id"] = actor["id"]
assert response.json() == expected assert response.json() == expected