Commit Graph

6 Commits

Author SHA1 Message Date
Maurice 1eb29f8b77 fix(trips): confine assignee writes to the people on the trip
Holding packing_edit or todo_edit says you may edit this trip's lists, not that
any user id you name belongs to it. Both updateCategoryAssignees loops wrote the
body's ids straight through, and both end by re-selecting through a JOIN on
users — so an id from anywhere on the instance came back with a display name and
an avatar attached, in the response and in the WebSocket fan-out to the room. The
packing item recipient writes had the same gap.

packing already had tripRosterIds for exactly this and used it only for bag
members; its doc comment said "assigned to a bag", which is probably why the
other three writes grew up without it. The query moves to DatabaseService next to
canAccessTrip, which asks the same question as a predicate, and the helper now
delegates. Every service that needs it already injects DatabaseService.

Off-roster ids drop rather than 400: a copied trip carries assignee ids over
before its members exist, and rejecting the whole request would stall the picker
there. Guests are unaffected — a guest is a trip_members row, so the roster
includes them, which is the point of #1362.
2026-08-16 23:17:50 +02:00
Maurice 8c0280e287 Replace the copied requireTrip helpers with a TripAccessGuard
Every trip-scoped controller carried its own three-line requireTrip, and a
requireEdit next to it: eighteen copies of a security check, each of which had to
remember that no access is a 404 'Trip not found' and never a 403 — the
distinction that stops a stranger learning which trip ids exist. One of them
getting it wrong would have looked like nothing.

TripAccessGuard resolves :tripId once, refuses what the user cannot reach and
hands the row to the handler through a @Trip() param decorator (a param
decorator rather than a req.trip cast, because the request type that used to be
widened for that is gone). @RequirePermission carries the same action string the
domain services pass to checkPermission, so the HTTP and MCP paths cannot drift
apart on which right they demand. Status codes and bodies are unchanged.

Piloted on days, day-notes, todo, packing and files. Two things worth knowing:

Files applies the guard PER HANDLER and leaves the upload route on its own
check. Guards run before interceptors, so a class-level guard would answer 404
before multer had read the multipart body, and a response sent while the client
is still streaming gets the socket destroyed — the caller sees ECONNRESET
instead of the 404. Refusing after the body has been read costs the bandwidth
and keeps the answer readable.

The services keep verifyTripAccess and canEdit: the large majority of their
callers are *.mcp.ts tools, which never pass through an HTTP guard. canEdit lost
its incidental controller-test coverage in the move, so it now has direct tests
asserting the action and the shared flag — the same pair the guard reproduces.

Also fixes the backup zip-slip test that failed only on CI: it asserted a
drive-letter path, which is absolute on win32 and an ordinary relative segment on
Linux, so the branch it was guarding never fired there.
2026-08-09 19:13:21 +02:00
jubnl 530c7cb005 chore: move trip backend to proper DI
Fold the legacy tripService SQL into TripsService; port the trips MCP registrar/resources/trip-summary prompt to trips.mcp.ts and the share-link tools to share.mcp.ts (predicate access for the trips:delete/trips:share/canReadTrips gates, deprecation notice via the attach ctx); adopt trips.dto.ts and clear the seven allow-list entries; delete todo/share bridges with their last consumers. Includes the trailing quirk fixes: transactional deleteTrip/deleteGuest, owner display_name in listMembers.
2026-07-29 01:53:40 +02:00
jubnl a2861d40f3 feat(server): injectable RealtimeService over the ws singleton
Nest services inject RealtimeService (call-time delegation to the
websocket module, DatabaseService pattern) instead of importing
broadcast/broadcastToUser globals. Zero behavior change; test edits
are constructor-arg additions plus RealtimeModule in e2e harness
imports only.
2026-07-29 00:07:51 +02:00
jubnl 1d56de3897 chore: move permissions backend to proper DI 2026-07-27 22:08:37 +02:00
jubnl 79946cc711 chore: move todo backend to proper DI 2026-07-26 14:09:54 +02:00