Both advisories are the same mistake: a route that hands out a bearer token
treated trip membership as authorisation. Membership lets someone read the trip
while they are signed in. It does not let them mint a URL that reads it without
an account, keeps working after they are removed, and can be rotated out from
under the owner's calendar subscriptions.
The calendar feed token now sits behind TripAccessGuard plus
@RequirePermission('share_manage'), the same shape the rest of the trip-scoped
controllers use, which also replaces a hand-written access check that GET never
called at all. GET is gated with the writes because it returns the credential
itself, not its status. The share link's GET does what its own POST and DELETE
already did, and what the invite link next door already does for exactly this
reason.
Two things had to come with it or the gate would have been decoration. The MCP
tool get_share_link read the same token on membership alone, so it would simply
have become the new way in. And feed_token rides along in every trip payload
because TRIP_SELECT reads t.*: any member could have read the value out of
GET /api/trips/:id, the summary tool or the plugin RPC. It is blanked once in
the select and stripped in the two places that select the row directly.
The three service writes take the acting user and scope their UPDATE to a trip
that user can reach. The guard decides who may manage the credential; this only
makes sure a caller arriving another way cannot write to an id it guessed.
Client side, "Subscribe to calendar" is now behind the same permission on
desktop and mobile. Without that a member would have kept an entry whose enable
button silently does nothing, because IcsSubscribeModal swallows every non-200.
The one-off ICS download stays where it is: that is a file they may already read.
Instances that want members to manage these links can lower share_manage to
trip_member in the admin panel, which the tests cover.
3.0 KiB
Admin — Permissions
The Permissions panel, located at the bottom of the Users tab, controls which role level is required to perform each action. Changes apply immediately across the entire instance.
Role model
TREK uses four permission levels, ordered from most to least privileged:
| Level | Who it includes |
|---|---|
admin |
Instance administrators only |
trip_owner |
The user who created the trip |
trip_member |
Any user who is a member of the trip |
everybody |
Any authenticated user (for trip_create: no trip context required; for all other actions: any authenticated user with trip access) |
Each action is assigned a minimum required level. A user whose role is at or above that level can perform the action. Not every level is available for every action — each action exposes only the levels that make sense for it. For example, trip_create only allows everybody or admin, while trip_edit only allows trip_owner or trip_member.
Action categories
Actions are grouped into five categories:
Trip
| Action key | What it controls |
|---|---|
trip_create |
Create a new trip |
trip_edit |
Edit trip name, dates, description, and currency |
trip_delete |
Permanently delete a trip |
trip_archive |
Archive or unarchive a trip |
trip_cover_upload |
Upload or change the cover image for a trip |
Members
| Action key | What it controls |
|---|---|
member_manage |
Invite or remove trip members |
Files
| Action key | What it controls |
|---|---|
file_upload |
Upload files to a trip |
file_edit |
Edit file descriptions and links |
file_delete |
Move files to trash or permanently delete them |
Content & Schedule
| Action key | What it controls |
|---|---|
place_edit |
Add, edit, or delete places |
day_edit |
Edit days, day notes, and place assignments |
reservation_edit |
Create, edit, or delete reservations |
Budget, Packing & Collaboration
| Action key | What it controls |
|---|---|
budget_edit |
Create, edit, or delete budget items |
packing_edit |
Manage packing items and bags |
collab_edit |
Create notes, polls, and send messages |
share_manage |
Read, create or delete public share links and calendar feed links |
Changing permissions
Each action row has a dropdown. Select the minimum role level required. A customized badge appears next to any action that has been changed from its default.
Click Save (top-right of the panel) to persist your changes. Use the Reset to defaults button (circular arrow icon) to revert all actions to their shipped defaults without saving — you still need to click Save after resetting if you want to persist the reset state.
