From cc35dfd1349f2b78113d90abc7d17f81ea2b97df Mon Sep 17 00:00:00 2001 From: alphaeusmote <41258468-alphaeusmote@users.noreply.replit.com> Date: Sat, 12 Apr 2025 00:19:49 +0000 Subject: [PATCH] Refactor: Improve authorization and access control for webhooks and domains by replacing organization IDs with customer IDs. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/9d305f1a-cfc0-4985-9af8-152b65c738cb.jpg --- server/routes.ts | 66 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/server/routes.ts b/server/routes.ts index 634fe49..f97634a 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -707,10 +707,10 @@ export async function registerRoutes(app: Express): Promise { app.get("/api/domains", requireRole(["admin", "manager", "user", "readonly"]), async (req, res) => { try { let domains; - const orgId = req.query.organizationId ? parseInt(req.query.organizationId as string) : undefined; + const customerId = req.query.customerId as string; - if (orgId) { - domains = await storage.getDomainsByOrganization(orgId); + if (customerId) { + domains = await storage.getDomainsByCustomer(customerId); } else { domains = await storage.getAllDomains(); } @@ -744,7 +744,7 @@ export async function registerRoutes(app: Express): Promise { // Create a custom validation schema for the API that makes providerId optional const domainSchema = z.object({ name: z.string(), - organizationId: z.string().uuid(), + customerId: z.string().uuid(), providerId: z.string().uuid().optional(), isActive: z.boolean().optional().default(true) }); @@ -770,7 +770,7 @@ export async function registerRoutes(app: Express): Promise { // Create custom validation schema with optional providerId const domainUpdateSchema = z.object({ name: z.string().optional(), - organizationId: z.string().uuid().optional(), + customerId: z.string().uuid().optional(), providerId: z.string().uuid().optional().nullable(), isActive: z.boolean().optional() }); @@ -1406,9 +1406,16 @@ export async function registerRoutes(app: Express): Promise { return res.status(404).json({ message: "Webhook not found" }); } - // Check authorization - if (req.user?.role !== "admin" && webhook.organizationId !== req.user?.organizationId) { - return res.status(403).json({ message: "Not authorized to access this webhook" }); + // Check authorization for non-admin users + if (req.user?.role !== "admin") { + // Get user's customers + const userCustomers = await storage.getUserCustomers(req.user?.id as string); + const userCustomerIds = userCustomers.map(c => c.id); + + // Check if the user has access to the webhook's customer + if (!userCustomerIds.includes(webhook.customerId)) { + return res.status(403).json({ message: "Not authorized to access this webhook" }); + } } res.json(webhook); @@ -1430,8 +1437,15 @@ export async function registerRoutes(app: Express): Promise { const validatedData = insertWebhookSchema.parse(requestData); // Check authorization for non-admin users - if (req.user?.role !== "admin" && validatedData.organizationId !== req.user?.organizationId) { - return res.status(403).json({ message: "Not authorized to create webhooks for this organization" }); + if (req.user?.role !== "admin") { + // Get user's customers + const userCustomers = await storage.getUserCustomers(req.user?.id as string); + const userCustomerIds = userCustomers.map(c => c.id); + + // Check if the user has access to the customer + if (!userCustomerIds.includes(validatedData.customerId)) { + return res.status(403).json({ message: "Not authorized to create webhooks for this customer" }); + } } const webhook = await storage.createWebhook(validatedData); @@ -1455,16 +1469,23 @@ export async function registerRoutes(app: Express): Promise { return res.status(404).json({ message: "Webhook not found" }); } - // Check authorization - if (req.user?.role !== "admin" && webhook.organizationId !== req.user?.organizationId) { - return res.status(403).json({ message: "Not authorized to modify this webhook" }); + // Check authorization for non-admin users + if (req.user?.role !== "admin") { + // Get user's customers + const userCustomers = await storage.getUserCustomers(req.user?.id as string); + const userCustomerIds = userCustomers.map(c => c.id); + + // Check if the user has access to the webhook's customer + if (!userCustomerIds.includes(webhook.customerId)) { + return res.status(403).json({ message: "Not authorized to modify this webhook" }); + } } const validatedData = insertWebhookSchema.partial().parse(req.body); - // Prevent changing organization for security reasons - if (validatedData.organizationId && validatedData.organizationId !== webhook.organizationId) { - return res.status(400).json({ message: "Cannot change webhook organization" }); + // Prevent changing customer for security reasons + if (validatedData.customerId && validatedData.customerId !== webhook.customerId) { + return res.status(400).json({ message: "Cannot change webhook customer" }); } const updatedWebhook = await storage.updateWebhook(req.params.id, validatedData); @@ -1487,9 +1508,16 @@ export async function registerRoutes(app: Express): Promise { return res.status(404).json({ message: "Webhook not found" }); } - // Check authorization - if (req.user?.role !== "admin" && webhook.organizationId !== req.user?.organizationId) { - return res.status(403).json({ message: "Not authorized to delete this webhook" }); + // Check authorization for non-admin users + if (req.user?.role !== "admin") { + // Get user's customers + const userCustomers = await storage.getUserCustomers(req.user?.id as string); + const userCustomerIds = userCustomers.map(c => c.id); + + // Check if the user has access to the webhook's customer + if (!userCustomerIds.includes(webhook.customerId)) { + return res.status(403).json({ message: "Not authorized to delete this webhook" }); + } } const deleted = await storage.deleteWebhook(req.params.id);