fix(dashboard): DataTables queries count as reads for read-only API keys

POST /api/tables/... only reads, so read-only keys may use it (and the
API docs list it for them). Keys limited to some addresses can't open
the WebSocket, whose address isn't checked, nor keys whose allowed
paths leave out /ws. Refusals are audited without an account target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 08:51:53 -05:00
parent fc4e4eda9f
commit 66ce5f6c4f
6 changed files with 20 additions and 12 deletions

View File

@@ -109,14 +109,13 @@ namespace {
if (!context.apiKey) return true;
const auto& key = *context.apiKey;
if (ApiKeyService::SessionOnlyPath(doc.path)) return false;
if (key.readOnly && doc.method != "GET") return false;
if (key.readOnly && !doc.reads) return false;
return doc.permission.empty() ? (doc.minGmLevel <= 0 || key.allPermissions) : key.Has(doc.permission);
}
// Register a DataTables endpoint. The fetcher returns the DB layer's JSON string. Access: a GM level or a Perm.
template<typename Access>
void TableRoute(const std::string& path, const Access& access, const std::string& description, TableFetcher fetcher) {
ReadRoutes reads; // the query is a POST body, but it only reads
Route(eHTTPMethod::POST, path, access, description, [fetcher = std::move(fetcher)](HTTPReply& reply, const HTTPContext& context) {
const auto request = ParseDataTablesRequest(context.body);
const auto body = ParseBody(context);