feat: assert permission for remaining routes

This commit is contained in:
Henry
2026-01-28 12:03:21 +00:00
parent 7239f958c0
commit 0b734bd460
6 changed files with 37 additions and 52 deletions

View File

@@ -8,7 +8,7 @@ import * as workspaceRepo from "@kan/db/repository/workspace.repo";
import { generateUID } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { assertPermission } from "../utils/permissions";
import { deleteObject, generateUploadUrl } from "../utils/s3";
export const attachmentRouter = createTRPCRouter({
@@ -55,8 +55,7 @@ export const attachmentRouter = createTRPCRouter({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, card.workspaceId);
await assertPermission(ctx.db, userId, card.workspaceId, "card:edit");
// Get workspace publicId
const workspace = await workspaceRepo.getById(ctx.db, card.workspaceId);
@@ -131,8 +130,7 @@ export const attachmentRouter = createTRPCRouter({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, card.workspaceId);
await assertPermission(ctx.db, userId, card.workspaceId, "card:edit");
const attachment = await cardAttachmentRepo.create(ctx.db, {
cardId: card.id,
@@ -186,8 +184,7 @@ export const attachmentRouter = createTRPCRouter({
});
const workspaceId = attachment.card.list.board.workspaceId;
await assertUserInWorkspace(ctx.db, userId, workspaceId);
await assertPermission(ctx.db, userId, workspaceId, "card:edit");
const bucket = process.env.NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME;
if (bucket) {

View File

@@ -6,7 +6,7 @@ import * as cardActivityRepo from "@kan/db/repository/cardActivity.repo";
import * as checklistRepo from "@kan/db/repository/checklist.repo";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { assertPermission } from "../utils/permissions";
const checklistSchema = z.object({
publicId: z.string().length(12),
@@ -57,8 +57,7 @@ export const checklistRouter = createTRPCRouter({
message: `Card with public ID ${input.cardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, card.workspaceId);
await assertPermission(ctx.db, userId, card.workspaceId, "card:edit");
const newChecklist = await checklistRepo.create(ctx.db, {
name: input.name,
@@ -106,11 +105,11 @@ export const checklistRouter = createTRPCRouter({
message: `Checklist with public ID ${input.checklistPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(
await assertPermission(
ctx.db,
userId,
checklist.card.list.board.workspace.id,
"card:edit",
);
const previousName = checklist.name;
@@ -166,11 +165,11 @@ export const checklistRouter = createTRPCRouter({
message: `Checklist with public ID ${input.checklistPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(
await assertPermission(
ctx.db,
userId,
checklist.card.list.board.workspace.id,
"card:edit",
);
await checklistRepo.softDeleteAllItemsByChecklistId(ctx.db, {
@@ -237,11 +236,11 @@ export const checklistRouter = createTRPCRouter({
message: `Checklist with public ID ${input.checklistPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(
await assertPermission(
ctx.db,
userId,
checklist.card.list.board.workspace.id,
"card:edit",
);
const newChecklistItem = await checklistRepo.createItem(ctx.db, {
@@ -304,11 +303,11 @@ export const checklistRouter = createTRPCRouter({
message: `Checklist item with public ID ${input.checklistItemPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(
await assertPermission(
ctx.db,
userId,
item.checklist.card.list.board.workspace.id,
"card:edit",
);
const previousTitle = item.title;
@@ -394,11 +393,11 @@ export const checklistRouter = createTRPCRouter({
message: `Checklist item with public ID ${input.checklistItemPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(
await assertPermission(
ctx.db,
userId,
item.checklist.card.list.board.workspace.id,
"card:edit",
);
const deleted = await checklistRepo.softDeleteItemById(ctx.db, {

View File

@@ -14,7 +14,7 @@ import { colours } from "@kan/shared/constants";
import { generateUID } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { assertPermission } from "../utils/permissions";
import { apiKeys, urls } from "./integration";
export interface TrelloBoard {
@@ -180,8 +180,7 @@ export const importRouter = createTRPCRouter({
message: `Workspace with public ID ${input.workspacePublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id);
await assertPermission(ctx.db, userId, workspace.id, "board:create");
const newImport = await importRepo.create(ctx.db, {
source: "trello",

View File

@@ -6,7 +6,7 @@ import * as cardRepo from "@kan/db/repository/card.repo";
import * as labelRepo from "@kan/db/repository/label.repo";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { assertPermission } from "../utils/permissions";
const labelSchema = z.object({
publicId: z.string(),
@@ -47,8 +47,7 @@ export const labelRouter = createTRPCRouter({
message: `Label with public ID ${input.labelPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, label.workspaceId);
await assertPermission(ctx.db, userId, label.workspaceId, "board:view");
const result = await labelRepo.getByPublicId(ctx.db, input.labelPublicId);
@@ -102,8 +101,7 @@ export const labelRouter = createTRPCRouter({
message: `Board with public ID ${input.boardPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
await assertPermission(ctx.db, userId, board.workspaceId, "board:edit");
const result = await labelRepo.create(ctx.db, {
name: input.name,
@@ -162,8 +160,7 @@ export const labelRouter = createTRPCRouter({
message: `Label with public ID ${input.labelPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, label.workspaceId);
await assertPermission(ctx.db, userId, label.workspaceId, "board:edit");
const result = await labelRepo.update(ctx.db, input);
@@ -211,8 +208,7 @@ export const labelRouter = createTRPCRouter({
message: `Label with public ID ${input.labelPublicId} not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, label.workspaceId);
await assertPermission(ctx.db, userId, label.workspaceId, "board:edit");
await cardRepo.hardDeleteAllCardLabelRelationships(ctx.db, label.id);

View File

@@ -16,7 +16,6 @@ import {
import { updateSubscriptionSeats } from "@kan/stripe";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import {
assertCanManageMember,
assertCanManageRole,
@@ -62,7 +61,7 @@ export const memberRouter = createTRPCRouter({
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
await assertPermission(ctx.db, userId, workspace.id, "member:invite");
const isInvitedEmailAlreadyMember = workspace.members.some(
(member) => member.email === input.email,
@@ -196,7 +195,7 @@ export const memberRouter = createTRPCRouter({
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
await assertPermission(ctx.db, userId, workspace.id, "member:remove");
const member = await memberRepo.getByPublicId(
ctx.db,
@@ -298,8 +297,8 @@ export const memberRouter = createTRPCRouter({
code: "NOT_FOUND",
});
// Check if user is in workspace
await assertUserInWorkspace(ctx.db, userId, workspace.id);
// Check if user can view members
await assertPermission(ctx.db, userId, workspace.id, "member:view");
// Get active invite link for this workspace
const activeInviteLink = await inviteLinkRepo.getActiveForWorkspace(
@@ -366,8 +365,8 @@ export const memberRouter = createTRPCRouter({
code: "NOT_FOUND",
});
// Check if user is in workspace
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
// Check if user can edit members (admin-equivalent)
await assertPermission(ctx.db, userId, workspace.id, "member:edit");
// Check subscription for cloud environment
if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") {
@@ -467,8 +466,8 @@ export const memberRouter = createTRPCRouter({
code: "NOT_FOUND",
});
// Check if user is in workspace
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
// Check if user can edit members (admin-equivalent)
await assertPermission(ctx.db, userId, workspace.id, "member:edit");
// Deactivate all active invite links
await inviteLinkRepo.deactivateAllActiveForWorkspace(ctx.db, {

View File

@@ -7,7 +7,7 @@ import * as workspaceSlugRepo from "@kan/db/repository/workspaceSlug.repo";
import { generateUID } from "@kan/shared/utils";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
import { assertUserInWorkspace } from "../utils/auth";
import { assertPermission } from "../utils/permissions";
export const workspaceRouter = createTRPCRouter({
all: protectedProcedure
@@ -74,8 +74,7 @@ export const workspaceRouter = createTRPCRouter({
message: `Workspace not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, result.id);
await assertPermission(ctx.db, userId, result.id, "workspace:view");
// Check if user is an admin
const userMember = result.members.find(
@@ -164,8 +163,7 @@ export const workspaceRouter = createTRPCRouter({
message: `Workspace not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, result.id);
await assertPermission(ctx.db, userId, result.id, "workspace:view");
return result;
}),
@@ -296,8 +294,7 @@ export const workspaceRouter = createTRPCRouter({
message: `Workspace not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
await assertPermission(ctx.db, userId, workspace.id, "workspace:edit");
if (input.slug) {
const reservedOrPremiumWorkspaceSlug =
@@ -379,8 +376,7 @@ export const workspaceRouter = createTRPCRouter({
message: `Workspace not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
await assertPermission(ctx.db, userId, workspace.id, "workspace:delete");
const result = await workspaceRepo.hardDelete(
ctx.db,
@@ -518,8 +514,7 @@ export const workspaceRouter = createTRPCRouter({
message: `Workspace not found`,
code: "NOT_FOUND",
});
await assertUserInWorkspace(ctx.db, userId, workspace.id);
await assertPermission(ctx.db, userId, workspace.id, "workspace:view");
const result = await workspaceRepo.searchBoardsAndCards(
ctx.db,