Permissions, flags and failures Required
Enforce permissions on the server
Treat hidden and disabled buttons as a convenience only. The server checks the user's permission on every request, whatever the screen shows.
How to do it
Anyone can call the API without the screen, with a browser's developer tools or a script. Check the role for each action and the ownership of each record on the server, as function access and object access describe. Hiding a route in the client never protects its data.
Example
test('viewers cannot delete invoices through the API', async ({ request }) => {
const response = await request.delete('/api/invoices/inv_1042', { headers: { Authorization: `Bearer ${viewerToken}` } });
expect(response.status()).toBe(403);
});
def test_viewer_cannot_delete_invoice(client, viewer_token):
response = client.delete("/api/invoices/inv_1042", headers={"Authorization": f"Bearer {viewer_token}"})
assert response.status_code == 403
assert invoices.get("inv_1042") is not None
How to check
For each action the screen hides from a role, a test calls the API directly as that role. Every call gets 403 or 404, and the data is unchanged.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/permissions-flags-and-failures/server-enforces-permissions.