🔒 Improve workspace API role filtering

This commit is contained in:
Baptiste Arnaud
2023-08-17 15:11:50 +02:00
parent 8810aa8ddb
commit 906845bd76
19 changed files with 288 additions and 138 deletions

View File

@@ -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 },
})

View File

@@ -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 {

View File

@@ -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 }
})

View File

@@ -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,
})),
}
})

View File

@@ -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' })

View File

@@ -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,
}

View File

@@ -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'
}

View File

@@ -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
}

View File

@@ -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'
}