BUG: Invite route allows editors to invite members, contradicting domain model #5

Closed
opened 2026-05-09 00:27:03 +00:00 by Mark · 1 comment
Owner

Description

The invite API route (/api/trips/[tripId]/invite) allows any editor to invite new members, but the domain entity (Trip.canManageMembers()) specifies that only the owner can manage members.

The Issue

In src/app/api/trips/[tripId]/invite/route.ts (line 19):

if (!membership || membership.role === "viewer") return NextResponse.json({ error: "Forbidden" }, { status: 403 });

This allows editors (role === "editor") to invite members. However, the domain entity in src/domain/entities/trip.ts (line 161-163) says:

canManageMembers(userId: string): boolean {
  return this.data.ownerId === userId;
}

Additionally, the UI only shows the Invite button when canEdit is true (which includes editors), further enabling this behavior.

Impact

  • Editors can invite arbitrary users without owner approval
  • There is no remove-member or change-role endpoint exposed, so an editor could invite other editors who could then modify the trip freely
  • Inconsistent with the domain model's authorization design

Fix

Change the invite route to only allow the trip owner to invite:

const trip = await db.query.trips.findFirst({ where: eq(trips.id, tripId) });
if (!trip || trip.ownerId !== user.id) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
## Description The invite API route (`/api/trips/[tripId]/invite`) allows any editor to invite new members, but the domain entity (`Trip.canManageMembers()`) specifies that only the owner can manage members. ## The Issue In `src/app/api/trips/[tripId]/invite/route.ts` (line 19): ```typescript if (!membership || membership.role === "viewer") return NextResponse.json({ error: "Forbidden" }, { status: 403 }); ``` This allows editors (`role === "editor"`) to invite members. However, the domain entity in `src/domain/entities/trip.ts` (line 161-163) says: ```typescript canManageMembers(userId: string): boolean { return this.data.ownerId === userId; } ``` Additionally, the UI only shows the Invite button when `canEdit` is true (which includes editors), further enabling this behavior. ## Impact - Editors can invite arbitrary users without owner approval - There is no remove-member or change-role endpoint exposed, so an editor could invite other editors who could then modify the trip freely - Inconsistent with the domain model's authorization design ## Fix Change the invite route to only allow the trip owner to invite: ```typescript const trip = await db.query.trips.findFirst({ where: eq(trips.id, tripId) }); if (!trip || trip.ownerId !== user.id) return NextResponse.json({ error: "Forbidden" }, { status: 403 }); ```
Author
Owner

Fixed in commit a5510ba: invite route only blocked viewers, so editors could invite members (and inject any role, including owner). Now owner-only (matches Trip.canManageMembers), role validated to editor/viewer, self-invite rejected.

Fixed in commit a5510ba: invite route only blocked viewers, so editors could invite members (and inject any role, including owner). Now owner-only (matches Trip.canManageMembers), role validated to editor/viewer, self-invite rejected.
Mark closed this issue 2026-08-12 03:47:10 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
AuWoMe/planish#5
No description provided.