fix(plugins): trips.getById returns the actual trip row, not the access check

canAccessTrip only returns { id, user_id } (it's a membership check), but the
rpc-host's trips.getById returned it verbatim — so plugins saw a trip with no
title/start_date/etc. Fetch the real row after the access check. Also fix the
reference plugin to read t.title (the trips column is 'title', not 'name').
This commit is contained in:
mauriceboe
2026-07-03 19:01:23 +02:00
parent 2d492d4f4e
commit 1a7f7a9340
3 changed files with 12 additions and 5 deletions
@@ -31,7 +31,7 @@ module.exports = definePlugin({
return {
status: 200,
headers: json(),
body: JSON.stringify({ trip: t ? { name: t.name, days } : null }),
body: JSON.stringify({ trip: t ? { name: t.title, days } : null }),
}
},
},
+3 -1
View File
@@ -71,7 +71,9 @@ export class PluginRpcHost {
}
if (has('db:read:trips')) {
this.methods.set('trips.getById', (p) => this.tripRead(p, () => deps.canAccessTrip(num(p.tripId, 'tripId'), num(p.asUserId, 'asUserId'))));
this.methods.set('trips.getById', (p) =>
this.tripRead(p, () => deps.db.prepare('SELECT * FROM trips WHERE id = ?').get(num(p.tripId, 'tripId'))),
);
this.methods.set('trips.getPlaces', (p) =>
this.tripRead(p, () => deps.db.prepare('SELECT * FROM places WHERE trip_id = ? ORDER BY day_id, position').all(num(p.tripId, 'tripId'))),
);
+8 -3
View File
@@ -18,9 +18,12 @@ function makeDeps(): HostDeps {
close: vi.fn(),
} as unknown as HostDeps['data'],
db: {
prepare: vi.fn(() => ({
prepare: vi.fn((sql: string) => ({
all: () => [{ id: 7, name: 'Place' }],
get: () => ({ id: 3, username: 'ada', display_name: 'Ada', avatar: null }),
get: () =>
sql.includes('FROM trips')
? { id: 1, title: 'Japan', start_date: '2027-01-01' }
: { id: 3, username: 'ada', display_name: 'Ada', avatar: null },
})),
},
// trip 1 is accessible to user 42; everything else is not
@@ -63,7 +66,9 @@ describe('PluginRpcHost — capability enforcement', () => {
const host = new PluginRpcHost('p', new Set(['db:read:trips']), deps);
const res = await host.dispatch(req('trips.getById', { tripId: 1, asUserId: 42 }));
expect(ok(res)).toBe(true);
expect((res as RpcResponse).result).toEqual({ id: 1 });
// returns the ACTUAL trip row (title/start_date), not the access-check object
expect((res as RpcResponse).result).toMatchObject({ id: 1, title: 'Japan', start_date: '2027-01-01' });
expect(deps.db.prepare).toHaveBeenCalledWith(expect.stringContaining('FROM trips'));
});
it('db:read:trips is still RESOURCE_FORBIDDEN when the user is not a member', async () => {