mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
fix(collections): moving a place out of a list needs the same role as deleting it
Moving a saved place to another list takes it out of the source list, but updatePlace only asked for edit rights there, so an editor could empty a shared list they may not delete from. The source now takes owner or admin, like a delete, and the target still takes edit rights. REST and the update_collection_place tool share the service path. The Move to list action on desktop and phone follows the delete permission.
This commit is contained in:
@@ -304,7 +304,7 @@ export class CollectionsMcp {
|
||||
@Tool({
|
||||
name: 'update_collection_place',
|
||||
description:
|
||||
"Update a saved place's name, address, coordinates (lat/lng), description, notes, status, category, links, tags, labels, image, or move it to another collection (set collection_id).",
|
||||
"Update a saved place's name, address, coordinates (lat/lng), description, notes, status, category, links, tags, labels, image, or move it to another collection (set collection_id). Moving a place out of a list needs the same role there as deleting it (owner or admin).",
|
||||
inputSchema: { placeId: idSchema, ...collectionPlaceUpdateRequestSchema.shape, links: linksInput },
|
||||
annotations: TOOL_ANNOTATIONS_WRITE,
|
||||
when: collectionsAddonOn,
|
||||
|
||||
@@ -198,9 +198,19 @@ export class CollectionsService {
|
||||
|
||||
/** Delete a place — owner or admin only. */
|
||||
async assertCanDelete(userId: number, collectionId: number): Promise<void> {
|
||||
await this.assertOwnerOrAdmin(userId, collectionId, 'Only an admin can delete places from this list');
|
||||
}
|
||||
|
||||
/** Move a place out of a list to another one. It leaves the list just as a
|
||||
* delete would, so it takes the same role. */
|
||||
private async assertCanMoveOut(userId: number, collectionId: number): Promise<void> {
|
||||
await this.assertOwnerOrAdmin(userId, collectionId, 'Only an admin can move places out of this list');
|
||||
}
|
||||
|
||||
private async assertOwnerOrAdmin(userId: number, collectionId: number, refusal: string): Promise<void> {
|
||||
const r = await this.roleOf(userId, collectionId);
|
||||
if (r === null) httpError(404, 'Collection not found');
|
||||
if (r !== 'owner' && r !== 'admin') httpError(403, 'Only an admin can delete places from this list');
|
||||
if (r !== 'owner' && r !== 'admin') httpError(403, refusal);
|
||||
}
|
||||
|
||||
private async ownerOf(collectionId: number): Promise<number> {
|
||||
@@ -919,6 +929,7 @@ export class CollectionsService {
|
||||
|
||||
let movedTo: number | null = null;
|
||||
if (body.collection_id !== undefined && body.collection_id !== currentCollection) {
|
||||
await this.assertCanMoveOut(userId, currentCollection);
|
||||
await this.assertCanEdit(userId, body.collection_id);
|
||||
write.collection_id = body.collection_id;
|
||||
write.owner_id = await this.ownerOf(body.collection_id);
|
||||
|
||||
Reference in New Issue
Block a user