move action permission check to middleware

This commit is contained in:
Milo Schwartz
2024-11-05 23:55:46 -05:00
parent 03051878ef
commit 372e51c0a5
48 changed files with 266 additions and 936 deletions

View File

@@ -1,17 +1,10 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import {
roles,
userSites,
sites,
roleSites,
exitNodes,
} from "@server/db/schema";
import { roles, userSites, sites, roleSites } from "@server/db/schema";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { eq, and } from "drizzle-orm";
import { getUniqueSiteName } from "@server/db/names";
@@ -22,7 +15,6 @@ const createSiteParamsSchema = z.object({
orgId: z.string(),
});
// Define Zod schema for request body validation
const createSiteSchema = z.object({
name: z.string().min(1).max(255),
exitNodeId: z.number().int().positive(),
@@ -36,9 +28,6 @@ export type CreateSiteResponse = {
siteId: number;
orgId: string;
niceId: string;
// niceId: string;
// subdomain: string;
// subnet: string;
};
export async function createSite(
@@ -47,7 +36,6 @@ export async function createSite(
next: NextFunction
): Promise<any> {
try {
// Validate request body
const parsedBody = createSiteSchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
@@ -60,7 +48,6 @@ export async function createSite(
const { name, subdomain, exitNodeId, pubKey, subnet } = parsedBody.data;
// Validate request params
const parsedParams = createSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
@@ -73,20 +60,6 @@ export async function createSite(
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.createSite,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission perform this action"
)
);
}
if (!req.userOrgRoleId) {
return next(
createHttpError(HttpCode.FORBIDDEN, "User does not have a role")
@@ -95,7 +68,6 @@ export async function createSite(
const niceId = await getUniqueSiteName(orgId);
// Create new site in the database
const [newSite] = await db
.insert(sites)
.values({
@@ -144,8 +116,6 @@ export async function createSite(
niceId: newSite.niceId,
siteId: newSite.siteId,
orgId: newSite.orgId,
// subdomain: newSite.subdomain,
// subnet: newSite.subnet,
},
success: true,
error: false,
@@ -155,10 +125,7 @@ export async function createSite(
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -6,14 +6,12 @@ import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { deletePeer } from "../gerbil/peers";
import { fromError } from "zod-validation-error";
const API_BASE_URL = "http://localhost:3000";
// Define Zod schema for request parameters validation
const deleteSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
@@ -24,7 +22,6 @@ export async function deleteSite(
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = deleteSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
@@ -37,21 +34,6 @@ export async function deleteSite(
const { siteId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.deleteSite,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Delete the site from the database
const [deletedSite] = await db
.delete(sites)
.where(eq(sites.siteId, siteId))
@@ -78,10 +60,7 @@ export async function deleteSite(
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -6,12 +6,10 @@ import { eq, and } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import stoi from "@server/utils/stoi";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const getSiteSchema = z.object({
siteId: z
.string()
@@ -36,7 +34,6 @@ export async function getSite(
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = getSiteSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
@@ -49,22 +46,7 @@ export async function getSite(
const { siteId, niceId, orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateSite,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
let site;
// Fetch the site from the database
if (siteId) {
site = await db
.select()
@@ -107,10 +89,7 @@ export async function getSite(
} catch (error) {
logger.error("Error from getSite: ", error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -6,7 +6,6 @@ import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
@@ -32,20 +31,6 @@ export async function listSiteRoles(
const { siteId } = parsedParams.data;
// Check if the user has permission to list site roles
const hasPermission = await checkUserActionPermission(
ActionsEnum.listSiteRoles,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
const siteRolesList = await db
.select({
roleId: roles.roleId,
@@ -67,10 +52,7 @@ export async function listSiteRoles(
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -1,4 +1,3 @@
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import { db } from "@server/db";
import { orgs, roleSites, sites, userSites } from "@server/db/schema";
import HttpCode from "@server/types/HttpCode";
@@ -45,8 +44,8 @@ function querySites(orgId: string, accessibleSiteIds: number[]) {
.where(
and(
inArray(sites.siteId, accessibleSiteIds),
eq(sites.orgId, orgId),
),
eq(sites.orgId, orgId)
)
);
}
@@ -58,7 +57,7 @@ export type ListSitesResponse = {
export async function listSites(
req: Request,
res: Response,
next: NextFunction,
next: NextFunction
): Promise<any> {
try {
const parsedQuery = listSitesSchema.safeParse(req.query);
@@ -66,8 +65,8 @@ export async function listSites(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedQuery.error),
),
fromError(parsedQuery.error)
)
);
}
const { limit, offset } = parsedQuery.data;
@@ -77,32 +76,18 @@ export async function listSites(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
parsedParams.error.errors.map((e) => e.message).join(", "),
),
parsedParams.error.errors.map((e) => e.message).join(", ")
)
);
}
const { orgId } = parsedParams.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.listSites,
req,
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action",
),
);
}
if (orgId && orgId !== req.userOrgId) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have access to this organization",
),
"User does not have access to this organization"
)
);
}
@@ -115,8 +100,8 @@ export async function listSites(
.where(
or(
eq(userSites.userId, req.user!.userId),
eq(roleSites.roleId, req.userOrgRoleId!),
),
eq(roleSites.roleId, req.userOrgRoleId!)
)
);
const accessibleSiteIds = accessibleSites.map((site) => site.siteId);
@@ -128,8 +113,8 @@ export async function listSites(
.where(
and(
inArray(sites.siteId, accessibleSiteIds),
eq(sites.orgId, orgId),
),
eq(sites.orgId, orgId)
)
);
const sitesList = await baseQuery.limit(limit).offset(offset);
@@ -152,10 +137,7 @@ export async function listSites(
});
} catch (error) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred...",
),
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}

View File

@@ -1,12 +1,10 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { exitNodes, Org, orgs, sites } from "@server/db/schema";
import { exitNodes, sites } from "@server/db/schema";
import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { findNextAvailableCidr } from "@server/utils/ip";
@@ -26,20 +24,6 @@ export async function pickSiteDefaults(
next: NextFunction
): Promise<any> {
try {
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.createSite,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// TODO: more intelligent way to pick the exit node
// make sure there is an exit node by counting the exit nodes table

View File

@@ -6,16 +6,13 @@ import { eq } from "drizzle-orm";
import response from "@server/utils/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
// Define Zod schema for request parameters validation
const updateSiteParamsSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()),
});
// Define Zod schema for request body validation
const updateSiteBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
@@ -36,7 +33,6 @@ export async function updateSite(
next: NextFunction
): Promise<any> {
try {
// Validate request parameters
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
@@ -47,7 +43,6 @@ export async function updateSite(
);
}
// Validate request body
const parsedBody = updateSiteBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
@@ -61,21 +56,6 @@ export async function updateSite(
const { siteId } = parsedParams.data;
const updateData = parsedBody.data;
// Check if the user has permission to list sites
const hasPermission = await checkUserActionPermission(
ActionsEnum.updateSite,
req
);
if (!hasPermission) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have permission to perform this action"
)
);
}
// Update the site in the database
const updatedSite = await db
.update(sites)
.set(updateData)
@@ -101,10 +81,7 @@ export async function updateSite(
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"An error occurred..."
)
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
);
}
}