refactor(server): trip-access primitives async at every caller

This commit is contained in:
jubnl
2026-09-23 02:46:39 +02:00
parent 8efb3c8623
commit 769b9577ec
59 changed files with 472 additions and 140 deletions
@@ -36,7 +36,7 @@ export class PluginGuards {
if (ctx.actingUserId === undefined) {
throw new ForbiddenResource('trip reads require an authenticated user context');
}
if (!this.db.canAccessTrip(tripId, ctx.actingUserId)) {
if (!(await this.db.canAccessTrip(tripId, ctx.actingUserId))) {
throw new ForbiddenResource(`no access to trip ${tripId}`);
}
// The read runs only for a bound, membership-checked user, so hand the id through
@@ -57,7 +57,7 @@ export class PluginGuards {
/** A write is allowed only if the acting user can access AND edit the trip. */
async requireTripEdit(tripId: number, userId: number, action: string): Promise<void> {
if (!this.db.canAccessTrip(tripId, userId)) throw new ForbiddenResource(`no access to trip ${tripId}`);
if (!(await this.db.canAccessTrip(tripId, userId))) throw new ForbiddenResource(`no access to trip ${tripId}`);
if (!(await this.canEditAs(action, tripId, userId))) throw new ForbiddenResource(`no permission to edit trip ${tripId}`);
}
@@ -67,7 +67,7 @@ export class PluginGuards {
* message the refusal carries.
*/
async canEditAs(action: string, tripId: number, userId: number): Promise<boolean> {
const trip = this.db.canAccessTrip(tripId, userId);
const trip = await this.db.canAccessTrip(tripId, userId);
if (!trip) return false;
const user = this.db.prepare('SELECT role FROM users WHERE id = ?').get(userId) as { role?: string } | undefined;
if (!user) return false;
@@ -62,7 +62,7 @@ export class HostSurfaceRpc {
// Namespacing the event type alone does not cross the membership boundary.
const tripId = num(params.tripId, 'tripId');
if (ctx.actingUserId === undefined) throw new ForbiddenResource('broadcasts require an authenticated user context');
if (!this.db.canAccessTrip(tripId, ctx.actingUserId)) throw new ForbiddenResource(`no access to trip ${tripId}`);
if (!(await this.db.canAccessTrip(tripId, ctx.actingUserId))) throw new ForbiddenResource(`no access to trip ${tripId}`);
// The host forces the plugin:{id}:{event} namespace, so a plugin cannot forge a
// core event.
this.realtime.broadcast(tripId, `plugin:${ctx.pluginId}:${str(params.event, 'event')}`, asPayload(params.data));
@@ -101,7 +101,7 @@ export class HostSurfaceRpc {
if (scope !== 'user' && scope !== 'trip') throw new BadParams("scope must be 'user' or 'trip'");
const targetId = num(input.targetId, 'targetId');
if (scope === 'user' && targetId !== actor) throw new ForbiddenResource('a plugin may only notify the acting user');
if (scope === 'trip' && !this.db.canAccessTrip(targetId, actor)) {
if (scope === 'trip' && !(await this.db.canAccessTrip(targetId, actor))) {
throw new ForbiddenResource('the acting user is not a member of that trip');
}
const link = this.safeLink(input.link);
+1 -1
View File
@@ -130,7 +130,7 @@ export class MetaRpc {
const entityId = num(params.entityId, 'entityId');
if (ctx.actingUserId === undefined) throw new ForbiddenResource('metadata requires an authenticated user context');
const tripId = await this.entityTrip(entityType, entityId);
if (tripId === undefined || !this.db.canAccessTrip(tripId, ctx.actingUserId)) {
if (tripId === undefined || !(await this.db.canAccessTrip(tripId, ctx.actingUserId))) {
throw new ForbiddenResource(`no access to ${entityType} ${entityId}`);
}
if (write && !(await this.guards.canEditAs(EDIT_ACTION[entityType], tripId, ctx.actingUserId))) {