Skip to content

Commit f40a311

Browse files
authored
fix: enforce contact book ownership (#341)
1 parent 6786ff0 commit f40a311

File tree

4 files changed

+95
-14
lines changed

4 files changed

+95
-14
lines changed

apps/web/src/server/api/routers/contacts.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { CampaignStatus, Prisma } from "@prisma/client";
2+
import { TRPCError } from "@trpc/server";
23
import { z } from "zod";
34

45
import {
@@ -151,15 +152,40 @@ export const contactsRouter = createTRPCRouter({
151152
subscribed: z.boolean().optional(),
152153
}),
153154
)
154-
.mutation(async ({ input }) => {
155+
.mutation(async ({ ctx: { contactBook }, input }) => {
155156
const { contactId, ...contact } = input;
156-
return contactService.updateContact(contactId, contact);
157+
const updatedContact = await contactService.updateContactInContactBook(
158+
contactId,
159+
contactBook.id,
160+
contact,
161+
);
162+
163+
if (!updatedContact) {
164+
throw new TRPCError({
165+
code: "NOT_FOUND",
166+
message: "Contact not found",
167+
});
168+
}
169+
170+
return updatedContact;
157171
}),
158172

159173
deleteContact: contactBookProcedure
160174
.input(z.object({ contactId: z.string() }))
161-
.mutation(async ({ input }) => {
162-
return contactService.deleteContact(input.contactId);
175+
.mutation(async ({ ctx: { contactBook }, input }) => {
176+
const deletedContact = await contactService.deleteContactInContactBook(
177+
input.contactId,
178+
contactBook.id,
179+
);
180+
181+
if (!deletedContact) {
182+
throw new TRPCError({
183+
code: "NOT_FOUND",
184+
message: "Contact not found",
185+
});
186+
}
187+
188+
return deletedContact;
163189
}),
164190

165191
exportContacts: contactBookProcedure

apps/web/src/server/public-api/api/contacts/delete-contact.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { createRoute, z } from "@hono/zod-openapi";
22
import { PublicAPIApp } from "~/server/public-api/hono";
3-
import { getTeamFromToken } from "~/server/public-api/auth";
4-
import { deleteContact } from "~/server/service/contact-service";
3+
import { deleteContactInContactBook } from "~/server/service/contact-service";
54
import { getContactBook } from "../../api-utils";
5+
import { UnsendApiError } from "../../api-error";
66

77
const route = createRoute({
88
method: "delete",
@@ -41,10 +41,20 @@ function deleteContactHandler(app: PublicAPIApp) {
4141
app.openapi(route, async (c) => {
4242
const team = c.var.team;
4343

44-
await getContactBook(c, team.id);
44+
const contactBook = await getContactBook(c, team.id);
4545
const contactId = c.req.param("contactId");
4646

47-
await deleteContact(contactId);
47+
const deletedContact = await deleteContactInContactBook(
48+
contactId,
49+
contactBook.id,
50+
);
51+
52+
if (!deletedContact) {
53+
throw new UnsendApiError({
54+
code: "NOT_FOUND",
55+
message: "Contact not found",
56+
});
57+
}
4858

4959
return c.json({ success: true });
5060
});

apps/web/src/server/public-api/api/contacts/update-contact.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { createRoute, z } from "@hono/zod-openapi";
22
import { PublicAPIApp } from "~/server/public-api/hono";
3-
import { getTeamFromToken } from "~/server/public-api/auth";
4-
import { updateContact } from "~/server/service/contact-service";
3+
import { updateContactInContactBook } from "~/server/service/contact-service";
54
import { getContactBook } from "../../api-utils";
5+
import { UnsendApiError } from "../../api-error";
66

77
const route = createRoute({
88
method: "patch",
@@ -54,10 +54,21 @@ function updateContactInfo(app: PublicAPIApp) {
5454
app.openapi(route, async (c) => {
5555
const team = c.var.team;
5656

57-
await getContactBook(c, team.id);
57+
const contactBook = await getContactBook(c, team.id);
5858
const contactId = c.req.param("contactId");
5959

60-
const contact = await updateContact(contactId, c.req.valid("json"));
60+
const contact = await updateContactInContactBook(
61+
contactId,
62+
contactBook.id,
63+
c.req.valid("json"),
64+
);
65+
66+
if (!contact) {
67+
throw new UnsendApiError({
68+
code: "NOT_FOUND",
69+
message: "Contact not found",
70+
});
71+
}
6172

6273
return c.json({ contactId: contact.id });
6374
});

apps/web/src/server/service/contact-service.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,32 @@ export async function addOrUpdateContact(
6363
return createdContact;
6464
}
6565

66-
export async function updateContact(
66+
export async function getContactInContactBook(
6767
contactId: string,
68+
contactBookId: string,
69+
) {
70+
return db.contact.findFirst({
71+
where: {
72+
id: contactId,
73+
contactBookId,
74+
},
75+
});
76+
}
77+
78+
export async function updateContactInContactBook(
79+
contactId: string,
80+
contactBookId: string,
6881
contact: Partial<ContactInput>,
6982
) {
83+
const existingContact = await getContactInContactBook(
84+
contactId,
85+
contactBookId,
86+
);
87+
88+
if (!existingContact) {
89+
return null;
90+
}
91+
7092
return db.contact.update({
7193
where: {
7294
id: contactId,
@@ -75,7 +97,19 @@ export async function updateContact(
7597
});
7698
}
7799

78-
export async function deleteContact(contactId: string) {
100+
export async function deleteContactInContactBook(
101+
contactId: string,
102+
contactBookId: string,
103+
) {
104+
const existingContact = await getContactInContactBook(
105+
contactId,
106+
contactBookId,
107+
);
108+
109+
if (!existingContact) {
110+
return null;
111+
}
112+
79113
return db.contact.delete({
80114
where: {
81115
id: contactId,

0 commit comments

Comments
 (0)