/api/visits, /api/cameras, /api/sites, /api/visitors and /api/reports/footfall, all in production, all before today's work. A platform admin is defined by having NO client, and every tenant query scopes on client_id = $1::uuid - so the empty string reaches Postgres as ''::uuid, which is a cast ERROR rather than an empty result. Found by calling them while verifying the new routes, which have the same shape and were failing the same way. tenantOnly is the guard, beside adminOnly and for the opposite audience. Per-query casts would have been the wrong fix twice over: it is a fix the next query forgets, and the next query would then 500 in production exactly as these did. 403, not adminOnly's 404, because the two hide opposite things. A tenant must not learn a platform surface exists. A platform admin already knows the tenant surface does - they are reading its data through /api/admin - so nothing is concealed by pretending otherwise, and the refusal names the route to use instead. "Forbidden" alone sends somebody hunting a permissions problem that does not exist. /api/auth/* stays on plain authed: a session is not a company's data, and signing out or revoking a lost device must keep working for an account with no tenant. The fake could not have caught this either - it compares client ids as strings and is perfectly content with "". The test asserts the contract (403 and a message naming /api/admin) and a third case that matters more than either: an ordinary tenant user still reaches all of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
2.4 KiB
2.4 KiB