fix: enforce contact book ownership (#341)

This commit is contained in:
KM Koushik
2026-01-17 18:08:05 +11:00
committed by GitHub
parent 6786ff003e
commit f40a311cc9
4 changed files with 95 additions and 14 deletions
+30 -4
View File
@@ -1,4 +1,5 @@
import { CampaignStatus, Prisma } from "@prisma/client";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import {
@@ -151,15 +152,40 @@ export const contactsRouter = createTRPCRouter({
subscribed: z.boolean().optional(),
}),
)
.mutation(async ({ input }) => {
.mutation(async ({ ctx: { contactBook }, input }) => {
const { contactId, ...contact } = input;
return contactService.updateContact(contactId, contact);
const updatedContact = await contactService.updateContactInContactBook(
contactId,
contactBook.id,
contact,
);
if (!updatedContact) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Contact not found",
});
}
return updatedContact;
}),
deleteContact: contactBookProcedure
.input(z.object({ contactId: z.string() }))
.mutation(async ({ input }) => {
return contactService.deleteContact(input.contactId);
.mutation(async ({ ctx: { contactBook }, input }) => {
const deletedContact = await contactService.deleteContactInContactBook(
input.contactId,
contactBook.id,
);
if (!deletedContact) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Contact not found",
});
}
return deletedContact;
}),
exportContacts: contactBookProcedure
@@ -1,8 +1,8 @@
import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "~/server/public-api/hono";
import { getTeamFromToken } from "~/server/public-api/auth";
import { deleteContact } from "~/server/service/contact-service";
import { deleteContactInContactBook } from "~/server/service/contact-service";
import { getContactBook } from "../../api-utils";
import { UnsendApiError } from "../../api-error";
const route = createRoute({
method: "delete",
@@ -41,10 +41,20 @@ function deleteContactHandler(app: PublicAPIApp) {
app.openapi(route, async (c) => {
const team = c.var.team;
await getContactBook(c, team.id);
const contactBook = await getContactBook(c, team.id);
const contactId = c.req.param("contactId");
await deleteContact(contactId);
const deletedContact = await deleteContactInContactBook(
contactId,
contactBook.id,
);
if (!deletedContact) {
throw new UnsendApiError({
code: "NOT_FOUND",
message: "Contact not found",
});
}
return c.json({ success: true });
});
@@ -1,8 +1,8 @@
import { createRoute, z } from "@hono/zod-openapi";
import { PublicAPIApp } from "~/server/public-api/hono";
import { getTeamFromToken } from "~/server/public-api/auth";
import { updateContact } from "~/server/service/contact-service";
import { updateContactInContactBook } from "~/server/service/contact-service";
import { getContactBook } from "../../api-utils";
import { UnsendApiError } from "../../api-error";
const route = createRoute({
method: "patch",
@@ -54,10 +54,21 @@ function updateContactInfo(app: PublicAPIApp) {
app.openapi(route, async (c) => {
const team = c.var.team;
await getContactBook(c, team.id);
const contactBook = await getContactBook(c, team.id);
const contactId = c.req.param("contactId");
const contact = await updateContact(contactId, c.req.valid("json"));
const contact = await updateContactInContactBook(
contactId,
contactBook.id,
c.req.valid("json"),
);
if (!contact) {
throw new UnsendApiError({
code: "NOT_FOUND",
message: "Contact not found",
});
}
return c.json({ contactId: contact.id });
});
+36 -2
View File
@@ -63,10 +63,32 @@ export async function addOrUpdateContact(
return createdContact;
}
export async function updateContact(
export async function getContactInContactBook(
contactId: string,
contactBookId: string,
) {
return db.contact.findFirst({
where: {
id: contactId,
contactBookId,
},
});
}
export async function updateContactInContactBook(
contactId: string,
contactBookId: string,
contact: Partial<ContactInput>,
) {
const existingContact = await getContactInContactBook(
contactId,
contactBookId,
);
if (!existingContact) {
return null;
}
return db.contact.update({
where: {
id: contactId,
@@ -75,7 +97,19 @@ export async function updateContact(
});
}
export async function deleteContact(contactId: string) {
export async function deleteContactInContactBook(
contactId: string,
contactBookId: string,
) {
const existingContact = await getContactInContactBook(
contactId,
contactBookId,
);
if (!existingContact) {
return null;
}
return db.contact.delete({
where: {
id: contactId,