Repository navigation
A token reads a document's extractions and exports its base - #1266
Conversation
WaylandYang
left a comment
There was a problem hiding this comment.
Thank you. Both routes are GETs behind require_reader_kb. Extractions finds the document's base first, as content does, so a token outside kb_ids gets the same 404 whether or not the document exists, and the facts it returns stay in that base because 0070's fact_evidence_same_kb trigger keeps each evidence row's chunk and fact together. Export is a read-only snapshot and starts no work; its one write is the kb.exported row, which now names the token through audit::via_token and is written only after the check passes. Both tests run in CI and fail without the change.
Before merge:
- Please rebase on #1263, #1264 and #1265 once they're in, and fold these two routes into decision 7 ("A token reads the graph") with their reasons. Extend its revision line rather than adding a decision and a revision line of your own; the blank line before the new entry also splits the list. If this ends up the last of the read PRs, it is the one that adds #1262 to the status line.
- Please add extractions and export to the
SessionOrTokencomment inauth.rs(line 179 ondev), so that list stays the place where a reader learns which routes a token reaches. - In decision 7, please say in one sentence why a read token may export the whole base. The route comment in
api/mod.rsalready gives the reason for a Viewer: anything a Viewer sees could be copied out one piece at a time, and with #1263–#1265 in, the same holds for a token. Requiring write scope would only push a mirror to hold a write token for a read-only job, and the audit row naming the token is the control. That reasoning relies on the other reads being open, so this PR should go in last.
Two optional things: token_read_tests from #1263 could take these two GETs instead of a fourth copy of the fixture, and require_reader_kb could return the base it already loaded, which would save the second kbs::get in export.
|
Thanks, agreed it goes last. After #1263–#1265 I'll rebase, fold extractions and export into decision 7 with one sentence on why a read token may export (a Viewer could copy it out piece by piece through the other reads; a write scope would only push a mirror to hold a write token for a read-only job; the audit row naming the token is the control), extend the revision line without the stray blank line, add both to the |
Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Bogdan Szabo <[email protected]>
ed4b9f9 to
155a6dd
Compare
|
Thank you @WaylandYang, both for the careful review and for writing out the export reasoning, which I used almost as you put it. Now that #1265 is in, I've rebased onto
Both GETs sit in |
WaylandYang
left a comment
There was a problem hiding this comment.
Thank you, all three points are in, and both optional ones too. The export reasoning in decision 7 reads right. Merging.
Part of #1262, and the last of its reads.
What happened
A backend that mirrors into a base could not read what extraction made of a document it pushed, and it could not take the base out as RDF. Both reads took a session JWT, so a personal token got 401.
What changed
GET /documents/{id}/extractionsandGET /kbs/{id}/exporttakeSessionOrTokenand callrequire_reader_kb(0052): the owner needs Viewer, any token scope may read, and a token whosekb_idsexclude the base gets 404. Extractions resolves the document's base first, ascontentdoes. Export still writeskb.exportedwith the owner as actor, and when a token was used the row's detail also carries the token id throughaudit::via_token.require_reader_kbnow returns the base it loaded, so export no longer needs a secondkbs::get.Decision 7 in 0014 gets a short paragraph on the two reads, including why a read token may export the whole base: a Viewer could already copy it out piece by piece through the other reads, write scope would only push a mirror to hold a write token for a read-only job, and the audit row naming the token is the control. The revision line gains one sentence and #1262 joins the status line. The
SessionOrTokencomment inauth.rsanddocs/design/access-and-audit.mdname both reads.Tests
Both GETs join
read_pathsinapi::token_read_tests, using a document added to the shared fixture: a viewer's read token gets 200 and a token limited to another base gets 404. A separate test checks that an export by token writeskb.exportedwith the owner as actor and the token id in the detail. Switching either route back to session-only fails the shared tests with 401, and droppingvia_tokenfails the audit test.cargo test -p utopia-serveragainst Postgres withUTOPIA_TEST_REQUIRE_DB=1,cargo fmt --all -- --checkandcargo clippy -p utopia-server --all-targets -- -D warningsare clean.🤖 Generated with Claude Code