fix(documents): linked receipts follow the documents right - #1424
Conversation
Budget entries, shared expenses, settlement proofs and inventory items delivered the name and id of every linked document to anyone who could read the owning module, also with `documents: none` and to tokens without documents:read. The documents-read decision (member right and token scope) now lives once, as mayReadDocuments()/documentViewer() in services/document-links.js, and housekeeping uses it too. Without it links come masked (id and name null), the item history drops them, a settlement's proof_document_id is null, and any non-empty attachment_document_ids or proof_document_id is the same 403 for every id, before visibility and deletion lock. The detail views show "Attached" instead of a link into nothing. Refs #1358
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Code reviewNo issues found. Checked for bugs and CONTRIBUTING.md compliance (this repo has no CLAUDE.md; its contribution rules live in CONTRIBUTING.md on |
…llow-access # Conflicts: # CHANGELOG.md
Code reviewThis push only merges in unrelated upstream changes (the split-expenses ledger-author-on-account-deletion fix for #1309: |
A new receipt link without documents access must answer the same 403 for an unknown or deleted id and for a document in the deletion window as for an existing one. Goes red when the refusal in assertDocumentLinkTargetsAvailable() moves behind visibility and lock. Refs #1358
Code reviewNo issues found. Compared to the last-reviewed state (8054436), this push adds one commit relevant to the PR's actual subject — |
Code reviewCompared to the last-reviewed state ( |
) * fix(documents): linked receipts follow the documents right Budget entries, shared expenses, settlement proofs and inventory items delivered the name and id of every linked document to anyone who could read the owning module, also with `documents: none` and to tokens without documents:read. The documents-read decision (member right and token scope) now lives once, as mayReadDocuments()/documentViewer() in services/document-links.js, and housekeeping uses it too. Without it links come masked (id and name null), the item history drops them, a settlement's proof_document_id is null, and any non-empty attachment_document_ids or proof_document_id is the same 403 for every id, before visibility and deletion lock. The detail views show "Attached" instead of a link into nothing. Refs ulsklyc#1358 * test(housekeeping): unknown, deleted and locked receipt ids refuse alike A new receipt link without documents access must answer the same 403 for an unknown or deleted id and for a document in the deletion window as for an existing one. Goes red when the refusal in assertDocumentLinkTargetsAvailable() moves behind visibility and lock. Refs ulsklyc#1358 --------- Co-authored-by: Ulas Kalayci <ulas.kalayci@icloud.com>
Follow-up to #1413 for the rest of the document links.
Problem. Budget entries, shared expenses (receipts and settlement proofs) and inventory items returned the name and id of every linked document to anyone who could read the owning module - also to members with
documents: noneand to API tokens withoutdocuments:read. The path guard judges by the first segment (/budget,/inventory) and never asks aboutdocuments. Linking worked the same way, and its answers differed between a real and an unknown id.Fix. One decision, in one place:
mayReadDocuments(req)/documentViewer(req)inserver/services/document-links.js(member right and token scope viahiddenModulesFor). Every function there now takes thatviewerinstead of a bare user id, and housekeeping'sreceiptAccessand new-link branch use the same helper - no second copy of the rule.document_id,name,original_name,mime_type,file_sizearenull), the per-document visibility filter stays as before. The inventory history drops document rows then. A settlement'sproof_document_idin the POST and reverse responses isnullunless the caller may read that document (the reverse response also leaked a private proof id to a group manager).attachment_document_idsor aproof_document_idthrowsDocumentLinkRefusedError-> the same 403 body for every id (stored, visible, unknown, or in the deletion window), before visibility and deletion-lock checks and before any row is written. An empty list or a missing field keeps everything. Housekeeping's new-link 403 now uses the same shared body.attachmentLinksNode(budget and split detail views) shows "Attached" (documentAttach.presentHidden, same wording ashousekeeping.receiptPresentin all 24 locales) for a masked link. The inventory detail view had no documents-right check and would have linked to/documents/null/preview. It now follows the same rule: no row atdocuments: none, "Attached" for a masked link.DOCUMENT_LINKS_READ_NOTEplus adocumentLinkRefusal403 on the write operations. The reverse schema notes the nullableproof_document_id. CHANGELOG and SPEC are updated.Tests (red on main, green now).
test:budget-attachments,test:split-expenses-attachments,test:inventory-item-documents: session withdocuments: noneand token withoutdocuments:read, read (list, detail, PUT/POST response, history) and write (POST/PUT with valid, stored and unknown id, and a locked id, give an identical 403 and nothing is created;[]keeps the links). Also a settlement proof, and a reverse by a manager who cannot see the private proof. Red on main: masked fields came back filled (document_id: 20, name: 'Kassenbon Recht'), POST with a receipt was 201 instead of 403.test:budget-readonly-ui: masked link shows "Attached" in budget, split and inventory, and nothing atdocuments: none. Red with the olddocument-attach.jsand the old inventory mapping.mayReadDocuments()returningtrue, 10 tests go red, including 3 of the fix(housekeeping): receipt name follows document access #1413 housekeeping tests.Suites run: housekeeping-routes, budget-attachments, budget-entries-routes, split-expenses-attachments, split-expenses-routes, split-settlement-reversal, inventory-item-documents, inventory-items-routes, inventory-service-log, inventory-tracked-dates, document-folders, budget-readonly-ui, module-write-access, module-readonly-ui, housekeeping-ui, openapi-structure, openapi-coverage, api, changelog, i18n, i18n-translated, frontend-audit, budget-routes-scope, search-permissions, dashboard-permissions, suite-chain.
Not in this PR:
server/routes/tasks.js(GET/PUT /tasks/:id/documents,document_count) has its own document-link code outside document-links.js and the same missing module check.Refs #1358