🔒 Improve workspace API role filtering
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { z } from 'zod'
|
||||
import { isAdminWriteWorkspaceForbidden } from '../helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
|
||||
export const deleteWorkspace = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -23,6 +25,14 @@ export const deleteWorkspace = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId },
|
||||
include: { members: true },
|
||||
})
|
||||
|
||||
if (!workspace || (await isAdminWriteWorkspaceForbidden(workspace, user)))
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' })
|
||||
|
||||
await prisma.workspace.deleteMany({
|
||||
where: { members: { some: { userId: user.id } }, id: workspaceId },
|
||||
})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Workspace, workspaceSchema } from '@typebot.io/schemas'
|
||||
import { workspaceSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden'
|
||||
|
||||
export const getWorkspace = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -25,11 +26,12 @@ export const getWorkspace = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
const workspace = (await prisma.workspace.findFirst({
|
||||
where: { members: { some: { userId: user.id } }, id: workspaceId },
|
||||
})) as Workspace | null
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId },
|
||||
include: { members: true },
|
||||
})
|
||||
|
||||
if (!workspace)
|
||||
if (!workspace || (await isReadWorkspaceFobidden(workspace, user)))
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' })
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import {
|
||||
WorkspaceInvitation,
|
||||
workspaceInvitationSchema,
|
||||
} from '@typebot.io/schemas'
|
||||
import { workspaceInvitationSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden'
|
||||
|
||||
export const listInvitationsInWorkspace = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -28,19 +26,13 @@ export const listInvitationsInWorkspace = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
const invitations = (await prisma.workspaceInvitation.findMany({
|
||||
where: {
|
||||
workspaceId,
|
||||
workspace: { members: { some: { userId: user.id } } },
|
||||
},
|
||||
select: { createdAt: true, email: true, type: true },
|
||||
})) as WorkspaceInvitation[]
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId },
|
||||
include: { members: true, invitations: true },
|
||||
})
|
||||
|
||||
if (!invitations)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'No invitations found',
|
||||
})
|
||||
if (!workspace || (await isReadWorkspaceFobidden(workspace, user)))
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' })
|
||||
|
||||
return { invitations }
|
||||
return { invitations: workspace.invitations }
|
||||
})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { WorkspaceMember, workspaceMemberSchema } from '@typebot.io/schemas'
|
||||
import { workspaceMemberSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden'
|
||||
|
||||
export const listMembersInWorkspace = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -25,13 +26,25 @@ export const listMembersInWorkspace = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
const members = (await prisma.memberInWorkspace.findMany({
|
||||
where: { userId: user.id, workspaceId },
|
||||
include: { user: { select: { name: true, email: true, image: true } } },
|
||||
})) as WorkspaceMember[]
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId },
|
||||
include: {
|
||||
members: {
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!members)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No members found' })
|
||||
if (!workspace || (await isReadWorkspaceFobidden(workspace, user)))
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' })
|
||||
|
||||
return { members }
|
||||
return {
|
||||
members: workspace.members.map((member) => ({
|
||||
role: member.role,
|
||||
user: member.user,
|
||||
workspaceId,
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Workspace, workspaceSchema } from '@typebot.io/schemas'
|
||||
import { workspaceSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const listWorkspaces = authenticatedProcedure
|
||||
@@ -23,10 +23,10 @@ export const listWorkspaces = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ ctx: { user } }) => {
|
||||
const workspaces = (await prisma.workspace.findMany({
|
||||
const workspaces = await prisma.workspace.findMany({
|
||||
where: { members: { some: { userId: user.id } } },
|
||||
select: { name: true, id: true, icon: true, plan: true },
|
||||
})) as Pick<Workspace, 'id' | 'name' | 'icon' | 'plan'>[]
|
||||
})
|
||||
|
||||
if (!workspaces)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' })
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Workspace, workspaceSchema } from '@typebot.io/schemas'
|
||||
import { workspaceSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { isAdminWriteWorkspaceForbidden } from '../helpers/isAdminWriteWorkspaceForbidden'
|
||||
|
||||
export const updateWorkspace = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -32,13 +33,20 @@ export const updateWorkspace = authenticatedProcedure
|
||||
data: updates,
|
||||
})
|
||||
|
||||
const workspace = (await prisma.workspace.findFirst({
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { members: { some: { userId: user.id } }, id: workspaceId },
|
||||
})) as Workspace | null
|
||||
include: { members: true },
|
||||
})
|
||||
|
||||
if (!workspace)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' })
|
||||
|
||||
if (await isAdminWriteWorkspaceForbidden(workspace, user))
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'You are not allowed to update this workspace',
|
||||
})
|
||||
|
||||
return {
|
||||
workspace,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MemberInWorkspace, User } from '@typebot.io/prisma'
|
||||
|
||||
export const isAdminWriteWorkspaceForbidden = async (
|
||||
workspace: {
|
||||
members: MemberInWorkspace[]
|
||||
},
|
||||
user: Pick<User, 'email' | 'id'>
|
||||
) => {
|
||||
const userRole = workspace.members.find(
|
||||
(member) => member.userId === user.id
|
||||
)?.role
|
||||
return !userRole || userRole !== 'ADMIN'
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MemberInWorkspace, User } from '@typebot.io/prisma'
|
||||
|
||||
export const isReadWorkspaceFobidden = async (
|
||||
workspace: {
|
||||
members: MemberInWorkspace[]
|
||||
},
|
||||
user: Pick<User, 'email' | 'id'>
|
||||
) => {
|
||||
if (
|
||||
process.env.ADMIN_EMAIL === user.email ||
|
||||
workspace.members.find((member) => member.userId === user.id)
|
||||
)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MemberInWorkspace, User } from '@typebot.io/prisma'
|
||||
|
||||
export const isWriteWorkspaceForbidden = async (
|
||||
workspace: {
|
||||
members: MemberInWorkspace[]
|
||||
},
|
||||
user: Pick<User, 'email' | 'id'>
|
||||
) => {
|
||||
const userRole = workspace.members.find(
|
||||
(member) => member.userId === user.id
|
||||
)?.role
|
||||
return !userRole || userRole === 'GUEST'
|
||||
}
|
||||
Reference in New Issue
Block a user