Skip to content

A token reads the entities of the bases it reaches - #1263

Merged
WaylandYang merged 2 commits into
deeplethe:devfrom
gedaiu:feat/token-reads-entities
Oct 19, 2026
Merged

WaylandYang merged 2 commits into
deeplethe:devfrom
gedaiu:feat/token-reads-entities

Conversation

@gedaiu

@gedaiu gedaiu commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Part of #1262.

What happened

A backend that mirrors into a base (#1243) could not read the base back: every graph read takes a session JWT, and a personal token gets 401.

What changed

Four reads now take SessionOrToken, acting as the token's owner: entity search (GET /kbs/{id}/entities), entity detail (GET /kbs/{id}/entities/{eid}), its history (…/history) and a fact's evidence (GET /kbs/{id}/facts/{fid}/evidence). They call require_reader_kb, the check document content already uses (0052): the owner needs Viewer on the base, any token scope may read, and a token whose kb_ids exclude the base gets 404. The reading logic is unchanged, and PATCH on the entity path still takes a session. require_reader_kb becomes pub(crate) where it is.

0014 gains decision 7, "A token reads the graph", with a reason per route; docs/design/access-and-audit.md names the four reads.

Fact evidence is only as narrow as its lookup: #1261 makes it answer within its own base, which matters more once a token limited by kb_ids can call it.

Tests

api::token_read_tests, against a real database: a read token of a base viewer gets 200 on all four routes of its base; a token limited to another base gets 404 on all four. Both fail without the change (401).

Run in rust:1-slim-bookworm against Postgres with UTOPIA_TEST_REQUIRE_DB=1: cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D warnings clean; cargo test -p utopia-server token_ 15 passed; cargo test -p utopia-server -- graph_routes documents_routes 17 passed.

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. The four routes are GETs only and use the same require_reader_kb as document content: the owner's role is the ceiling, and a base outside kb_ids is a 404. Every store read underneath is bound to the base in the path, so another base's entity or history can't be reached by id. The PATCH on the entity path stays session-only, and both tests run in CI and fail without the change.

One thing before merge: please rebase on dev now that #1261 is in. The two conflict in the fact_evidence handler, and the order matters. Without #1261, a token limited to base A could read base B's quotes through /kbs/A/facts/<B's fact>/evidence, so opening that route to tokens first would turn #1260 into a way around kb_ids. After the rebase the handler should call fact_evidence(&state.pool, kb_id, fact_id).

Two small things while you're there. status() in token_read_tests.rs takes a method and builds a JSON body for non-GET requests, but every call is a GET, so please drop that. And the 0014 status line lists #1262 as implemented while the graph-view, ontology and export reads are still open; it would read truer to add #1262 there with the last of those PRs.

@gedaiu

gedaiu commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks. Rebased on dev with #1261 in: the evidence handler now calls fact_evidence(&state.pool, kb_id, fact_id). token_read_tests has a plain get helper instead of status(method, …), and #1262 is off the 0014 status line; it will go there with the last of the read PRs. fmt, clippy and token_read (2 passed) rerun on the rebased branch.

… reaches, so a service mirroring into Utopia can read its own base back without a person's session

Signed-off-by: Bogdan Szabo <[email protected]>
@gedaiu

gedaiu commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on dev again; the only conflict was the 0014 revision list, where #1274's line and this PR's line now sit one after the other. fmt, clippy (-D warnings) and cargo test --workspace are clean. Thanks for your patience with this one.

@gedaiu

gedaiu commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi, and thank you again for the careful reviews this week. Just a gentle note that this one is rebased on dev and green, whenever you have a moment, no rush at all. Once it's in, I'll bring #1264–#1266 back one at a time, each folded into the same decision and the shared token_read_tests fixture as you suggested. Have a lovely weekend!

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the rebase and the cleanups. The evidence handler now calls fact_evidence(&state.pool, kb_id, fact_id) on top of #1261, so a token limited to one base can't reach another base's quotes through the fact id; the test helper is a plain GET; and #1262 stays off the 0014 status line until the last read PR. The four routes each go through require_reader_kb, every store read underneath is bound to the base in the path, and PATCH stays session-only. One small thing for a follow-up rather than this PR: require_reader_kb is now shared, so it would sit more naturally next to SessionOrToken than in documents_routes.rs. Merging.

@WaylandYang
WaylandYang merged commit 68cb3ff into deeplethe:dev Oct 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants