feat: allow users to delete entities they have created
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
} from "@kan/shared/utils";
|
||||
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { assertPermission } from "../utils/permissions";
|
||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||
|
||||
export const boardRouter = createTRPCRouter({
|
||||
all: protectedProcedure
|
||||
@@ -422,7 +422,13 @@ export const boardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, board.workspaceId, "board:edit");
|
||||
await assertCanEdit(
|
||||
ctx.db,
|
||||
userId,
|
||||
board.workspaceId,
|
||||
"board:edit",
|
||||
board.createdBy ?? null,
|
||||
);
|
||||
|
||||
if (input.slug) {
|
||||
const isBoardSlugAvailable = await boardRepo.isBoardSlugAvailable(
|
||||
@@ -491,7 +497,13 @@ export const boardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, board.workspaceId, "board:delete");
|
||||
await assertCanDelete(
|
||||
ctx.db,
|
||||
userId,
|
||||
board.workspaceId,
|
||||
"board:delete",
|
||||
board.createdBy ?? null,
|
||||
);
|
||||
|
||||
const listIds = board.lists.map((list) => list.id);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { mergeActivities } from "../utils/activities";
|
||||
import { assertPermission } from "../utils/permissions";
|
||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||
import { generateDownloadUrl } from "../utils/s3";
|
||||
|
||||
export const cardRouter = createTRPCRouter({
|
||||
@@ -256,8 +256,6 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, card.workspaceId, "comment:edit");
|
||||
|
||||
const existingComment = await cardCommentRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.commentPublicId,
|
||||
@@ -269,11 +267,13 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
if (existingComment.createdBy !== userId)
|
||||
throw new TRPCError({
|
||||
message: `You do not have permission to update this comment`,
|
||||
code: "FORBIDDEN",
|
||||
});
|
||||
await assertCanEdit(
|
||||
ctx.db,
|
||||
userId,
|
||||
card.workspaceId,
|
||||
"comment:edit",
|
||||
existingComment.createdBy,
|
||||
);
|
||||
|
||||
const updatedComment = await cardCommentRepo.update(ctx.db, {
|
||||
id: existingComment.id,
|
||||
@@ -334,8 +334,6 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, card.workspaceId, "comment:delete");
|
||||
|
||||
const existingComment = await cardCommentRepo.getByPublicId(
|
||||
ctx.db,
|
||||
input.commentPublicId,
|
||||
@@ -347,6 +345,14 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertCanDelete(
|
||||
ctx.db,
|
||||
userId,
|
||||
card.workspaceId,
|
||||
"comment:delete",
|
||||
existingComment.createdBy,
|
||||
);
|
||||
|
||||
const deletedComment = await cardCommentRepo.softDelete(ctx.db, {
|
||||
commentId: existingComment.id,
|
||||
deletedAt: new Date(),
|
||||
@@ -782,7 +788,13 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, card.workspaceId, "card:edit");
|
||||
await assertCanEdit(
|
||||
ctx.db,
|
||||
userId,
|
||||
card.workspaceId,
|
||||
"card:edit",
|
||||
card.createdBy,
|
||||
);
|
||||
|
||||
const existingCard = await cardRepo.getByPublicId(
|
||||
ctx.db,
|
||||
@@ -952,7 +964,13 @@ export const cardRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, card.workspaceId, "card:delete");
|
||||
await assertCanDelete(
|
||||
ctx.db,
|
||||
userId,
|
||||
card.workspaceId,
|
||||
"card:delete",
|
||||
card.createdBy,
|
||||
);
|
||||
|
||||
const deletedAt = new Date();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as activityRepo from "@kan/db/repository/cardActivity.repo";
|
||||
import * as listRepo from "@kan/db/repository/list.repo";
|
||||
|
||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
import { assertPermission } from "../utils/permissions";
|
||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||
|
||||
export const listRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -101,7 +101,13 @@ export const listRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, list.workspaceId, "list:delete");
|
||||
await assertCanDelete(
|
||||
ctx.db,
|
||||
userId,
|
||||
list.workspaceId,
|
||||
"list:delete",
|
||||
list.createdBy,
|
||||
);
|
||||
|
||||
const deletedAt = new Date();
|
||||
|
||||
@@ -183,7 +189,13 @@ export const listRouter = createTRPCRouter({
|
||||
code: "NOT_FOUND",
|
||||
});
|
||||
|
||||
await assertPermission(ctx.db, userId, list.workspaceId, "list:edit");
|
||||
await assertCanEdit(
|
||||
ctx.db,
|
||||
userId,
|
||||
list.workspaceId,
|
||||
"list:edit",
|
||||
list.createdBy,
|
||||
);
|
||||
|
||||
let result: { name: string; publicId: string } | undefined;
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ export async function assertCanManageRole(
|
||||
});
|
||||
}
|
||||
|
||||
const managerRole = managerMember.role as Role;
|
||||
const managerRole = managerMember.role;
|
||||
|
||||
if (!canManageRole(managerRole, targetRoleName as Role)) {
|
||||
throw new TRPCError({
|
||||
@@ -223,8 +223,8 @@ export async function assertCanManageMember(
|
||||
});
|
||||
}
|
||||
|
||||
const managerRole = managerMember.role as Role;
|
||||
const targetRole = targetMember.role as Role;
|
||||
const managerRole = managerMember.role;
|
||||
const targetRole = targetMember.role;
|
||||
|
||||
if (!canManageRole(managerRole, targetRole)) {
|
||||
throw new TRPCError({
|
||||
@@ -233,3 +233,63 @@ export async function assertCanManageMember(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert user can delete an entity - either has the delete permission OR is the creator
|
||||
*/
|
||||
export async function assertCanDelete(
|
||||
db: dbClient,
|
||||
userId: string,
|
||||
workspaceId: number,
|
||||
permission: Permission,
|
||||
createdBy: string | null,
|
||||
): Promise<void> {
|
||||
// Check if user has the general delete permission
|
||||
const hasDeletePermission = await hasPermission(db, userId, workspaceId, permission);
|
||||
|
||||
// If user has permission, allow deletion
|
||||
if (hasDeletePermission) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If user doesn't have permission, check if they are the creator
|
||||
if (createdBy && createdBy === userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Neither condition met - deny deletion
|
||||
throw new TRPCError({
|
||||
message: `You do not have permission to delete this entity (${permission})`,
|
||||
code: "FORBIDDEN",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert user can edit an entity - either has the edit permission OR is the creator
|
||||
*/
|
||||
export async function assertCanEdit(
|
||||
db: dbClient,
|
||||
userId: string,
|
||||
workspaceId: number,
|
||||
permission: Permission,
|
||||
createdBy: string | null,
|
||||
): Promise<void> {
|
||||
// Check if user has the general edit permission
|
||||
const hasEditPermission = await hasPermission(db, userId, workspaceId, permission);
|
||||
|
||||
// If user has permission, allow editing
|
||||
if (hasEditPermission) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If user doesn't have permission, check if they are the creator
|
||||
if (createdBy && createdBy === userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Neither condition met - deny editing
|
||||
throw new TRPCError({
|
||||
message: `You do not have permission to edit this entity (${permission})`,
|
||||
code: "FORBIDDEN",
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user