feat: assert board permissions
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "react-icons/hi2";
|
} from "react-icons/hi2";
|
||||||
|
|
||||||
import Dropdown from "~/components/Dropdown";
|
import Dropdown from "~/components/Dropdown";
|
||||||
|
import { usePermissions } from "~/hooks/usePermissions";
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { usePopup } from "~/providers/popup";
|
import { usePopup } from "~/providers/popup";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
@@ -23,6 +24,7 @@ export default function BoardDropdown({
|
|||||||
workspacePublicId: string;
|
workspacePublicId: string;
|
||||||
}) {
|
}) {
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
|
const { canEditBoard, canDeleteBoard } = usePermissions();
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
@@ -30,25 +32,33 @@ export default function BoardDropdown({
|
|||||||
...(isTemplate
|
...(isTemplate
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
...(canEditBoard
|
||||||
label: t`Make template`,
|
? [
|
||||||
action: () => openModal("CREATE_TEMPLATE"),
|
{
|
||||||
icon: (
|
label: t`Make template`,
|
||||||
<HiOutlineDocumentDuplicate className="h-[16px] w-[16px] text-dark-900" />
|
action: () => openModal("CREATE_TEMPLATE"),
|
||||||
),
|
icon: (
|
||||||
},
|
<HiOutlineDocumentDuplicate className="h-[16px] w-[16px] text-dark-900" />
|
||||||
{
|
),
|
||||||
label: t`Edit board URL`,
|
},
|
||||||
action: () => openModal("UPDATE_BOARD_SLUG"),
|
{
|
||||||
icon: <HiLink className="h-[16px] w-[16px] text-dark-900" />,
|
label: t`Edit board URL`,
|
||||||
},
|
action: () => openModal("UPDATE_BOARD_SLUG"),
|
||||||
|
icon: <HiLink className="h-[16px] w-[16px] text-dark-900" />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
{
|
...(canDeleteBoard
|
||||||
label: isTemplate ? t`Delete template` : t`Delete board`,
|
? [
|
||||||
action: () => openModal("DELETE_BOARD"),
|
{
|
||||||
icon: <HiOutlineTrash className="h-[16px] w-[16px] text-dark-900" />,
|
label: isTemplate ? t`Delete template` : t`Delete board`,
|
||||||
},
|
action: () => openModal("DELETE_BOARD"),
|
||||||
|
icon: <HiOutlineTrash className="h-[16px] w-[16px] text-dark-900" />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<HiEllipsisHorizontal className="h-5 w-5 text-dark-900" />
|
<HiEllipsisHorizontal className="h-5 w-5 text-dark-900" />
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Modal from "~/components/modal";
|
|||||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { Tooltip } from "~/components/Tooltip";
|
import { Tooltip } from "~/components/Tooltip";
|
||||||
|
import { usePermissions } from "~/hooks/usePermissions";
|
||||||
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
|
import { useKeyboardShortcut } from "~/providers/keyboard-shortcuts";
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
@@ -17,12 +18,13 @@ import { NewBoardForm } from "./components/NewBoardForm";
|
|||||||
export default function BoardsPage({ isTemplate }: { isTemplate?: boolean }) {
|
export default function BoardsPage({ isTemplate }: { isTemplate?: boolean }) {
|
||||||
const { openModal, modalContentType, isOpen } = useModal();
|
const { openModal, modalContentType, isOpen } = useModal();
|
||||||
const { workspace } = useWorkspace();
|
const { workspace } = useWorkspace();
|
||||||
|
const { canCreateBoard } = usePermissions();
|
||||||
|
|
||||||
const { tooltipContent: createModalShortcutTooltipContent } =
|
const { tooltipContent: createModalShortcutTooltipContent } =
|
||||||
useKeyboardShortcut({
|
useKeyboardShortcut({
|
||||||
type: "PRESS",
|
type: "PRESS",
|
||||||
stroke: { key: "C" },
|
stroke: { key: "C" },
|
||||||
action: () => openModal("NEW_BOARD"),
|
action: () => canCreateBoard && openModal("NEW_BOARD"),
|
||||||
description: t`Create new ${isTemplate ? "template" : "board"}`,
|
description: t`Create new ${isTemplate ? "template" : "board"}`,
|
||||||
group: "ACTIONS",
|
group: "ACTIONS",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@kan/shared/utils";
|
} from "@kan/shared/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
import { assertUserInWorkspace } from "../utils/auth";
|
import { assertPermission } from "../utils/permissions";
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: protectedProcedure
|
all: protectedProcedure
|
||||||
@@ -58,7 +58,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, workspace.id);
|
await assertPermission(ctx.db, userId, workspace.id, "board:view");
|
||||||
|
|
||||||
const result = boardRepo.getAllByWorkspaceId(ctx.db, workspace.id, {
|
const result = boardRepo.getAllByWorkspaceId(ctx.db, workspace.id, {
|
||||||
type: input.type,
|
type: input.type,
|
||||||
@@ -119,7 +119,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
|
await assertPermission(ctx.db, userId, board.workspaceId, "board:view");
|
||||||
|
|
||||||
// Convert semantic string filters to date ranges expected by the repo
|
// Convert semantic string filters to date ranges expected by the repo
|
||||||
const dueDateFilters = input.dueDateFilters
|
const dueDateFilters = input.dueDateFilters
|
||||||
@@ -255,7 +255,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, workspace.id);
|
await assertPermission(ctx.db, userId, workspace.id, "board:create");
|
||||||
|
|
||||||
// If sourceBoardPublicId is provided, clone the source board
|
// If sourceBoardPublicId is provided, clone the source board
|
||||||
if (input.sourceBoardPublicId) {
|
if (input.sourceBoardPublicId) {
|
||||||
@@ -422,7 +422,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
|
await assertPermission(ctx.db, userId, board.workspaceId, "board:edit");
|
||||||
|
|
||||||
if (input.slug) {
|
if (input.slug) {
|
||||||
const isBoardSlugAvailable = await boardRepo.isBoardSlugAvailable(
|
const isBoardSlugAvailable = await boardRepo.isBoardSlugAvailable(
|
||||||
@@ -491,7 +491,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
|
await assertPermission(ctx.db, userId, board.workspaceId, "board:delete");
|
||||||
|
|
||||||
const listIds = board.lists.map((list) => list.id);
|
const listIds = board.lists.map((list) => list.id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user