diff --git a/scripts/hydrate.ts b/scripts/hydrate.ts index 456b52f1..9c278539 100644 --- a/scripts/hydrate.ts +++ b/scripts/hydrate.ts @@ -1,159 +1,130 @@ -// import { -// orgs, -// sites, -// resources, -// exitNodes, -// routes, -// targets, -// } from "@server/db/schema"; +// import { orgs, sites, resources, exitNodes, targets } from "@server/db/schema"; // import db from "@server/db"; -// import { createSuperUserRole } from "@server/db/ensureActions"; +// import { crateAdminRole } from "@server/db/ensureActions"; -async function insertDummyData() { - // // Insert dummy orgs - // const org1 = db - // .insert(orgs) - // .values({ - // orgId: "default", - // name: "Default", - // domain: "fosrl.io", - // }) - // .returning() - // .get(); +// async function insertDummyData() { +// const org1 = db +// .insert(orgs) +// .values({ +// orgId: "fossorial", +// name: "Fossorial", +// domain: "fossorial.io", +// }) +// .returning() +// .get(); - // await createSuperUserRole(org1.orgId!); +// await crateAdminRole(org1.orgId!); - // const org2 = db - // .insert(orgs) - // .values({ - // orgId: "fossorial", - // name: "Fossorial", - // domain: "fossorial.io", - // }) - // .returning() - // .get(); +// // Insert dummy exit nodes +// const exitNode1 = db +// .insert(exitNodes) +// .values({ +// name: "Exit Node 1", +// address: "10.0.0.1/24", +// publicKey: "sKQlCNErB2n+dV8eLp5Yw/avsjK/zkrxJE0n48hjb10=", +// listenPort: 51820, +// endpoint: "exitnode1.fossorial.io", +// }) +// .returning() +// .get(); - // await createSuperUserRole(org2.orgId!); +// // Insert dummy sites +// const site1 = db +// .insert(sites) +// .values({ +// orgId: org1.orgId, - // // Insert dummy exit nodes - // const exitNode1 = db - // .insert(exitNodes) - // .values({ - // name: "Exit Node 1", - // address: "10.0.0.1/24", - // privateKey: "sKQlCNErB2n+dV8eLp5Yw/avsjK/zkrxJE0n48hjb10=", - // listenPort: 51820, - // }) - // .returning() - // .get(); +// exitNodeId: exitNode1.exitNodeId, +// name: "Main Site", +// subdomain: "main", +// pubKey: "Kn4eD0kvcTwjO//zqH/CtNVkMNdMiUkbqFxysEym2D8=", +// subnet: "10.0.0.16/28", +// }) +// .returning() +// .get(); - // const exitNode2 = db - // .insert(exitNodes) - // .values({ - // name: "Exit Node 2", - // address: "172.16.1.1/24", - // privateKey: "ACaw+q5vHVm8Xb0jIgIkMzlkJiriC7cURuOiNbGsGHg=", - // listenPort: 51820, - // }) - // .returning() - // .get(); +// const site2 = db +// .insert(sites) +// .values({ +// orgId: org2.orgId, +// exitNode: exitNode2.exitNodeId, +// name: "Dev Site", +// subdomain: "dev", +// pubKey: "V329Uf/vhnBwYxAuT/ZlMZuLokHy5tug/sGsLfIMK1w=", +// subnet: "172.16.1.16/28", +// }) +// .returning() +// .get(); - // // Insert dummy sites - // const site1 = db - // .insert(sites) - // .values({ - // orgId: org1.orgId, - // exitNode: exitNode1.exitNodeId, - // name: "Main Site", - // subdomain: "main", - // pubKey: "Kn4eD0kvcTwjO//zqH/CtNVkMNdMiUkbqFxysEym2D8=", - // subnet: "10.0.0.16/28", - // }) - // .returning() - // .get(); +// // Insert dummy resources +// const resource1 = db +// .insert(resources) +// .values({ +// resourceId: `web.${site1.subdomain}.${org1.domain}`, +// siteId: site1.siteId, +// orgId: site1.orgId, +// name: "Web Server", +// subdomain: "web", +// }) +// .returning() +// .get(); - // const site2 = db - // .insert(sites) - // .values({ - // orgId: org2.orgId, - // exitNode: exitNode2.exitNodeId, - // name: "Dev Site", - // subdomain: "dev", - // pubKey: "V329Uf/vhnBwYxAuT/ZlMZuLokHy5tug/sGsLfIMK1w=", - // subnet: "172.16.1.16/28", - // }) - // .returning() - // .get(); +// const resource2 = db +// .insert(resources) +// .values({ +// resourceId: `web2.${site1.subdomain}.${org1.domain}`, +// siteId: site1.siteId, +// orgId: site1.orgId, +// name: "Web Server 2", +// subdomain: "web2", +// }) +// .returning() +// .get(); - // // Insert dummy resources - // const resource1 = db - // .insert(resources) - // .values({ - // resourceId: `web.${site1.subdomain}.${org1.domain}`, - // siteId: site1.siteId, - // orgId: site1.orgId, - // name: "Web Server", - // subdomain: "web", - // }) - // .returning() - // .get(); +// const resource3 = db +// .insert(resources) +// .values({ +// resourceId: `db.${site2.subdomain}.${org2.domain}`, +// siteId: site2.siteId, +// orgId: site2.orgId, +// name: "Database", +// subdomain: "db", +// }) +// .returning() +// .get(); - // const resource2 = db - // .insert(resources) - // .values({ - // resourceId: `web2.${site1.subdomain}.${org1.domain}`, - // siteId: site1.siteId, - // orgId: site1.orgId, - // name: "Web Server 2", - // subdomain: "web2", - // }) - // .returning() - // .get(); +// // Insert dummy routes +// await db.insert(routes).values([ +// { exitNodeId: exitNode1.exitNodeId, subnet: "10.0.0.0/24" }, +// { exitNodeId: exitNode2.exitNodeId, subnet: "172.16.1.1/24" }, +// ]); - // const resource3 = db - // .insert(resources) - // .values({ - // resourceId: `db.${site2.subdomain}.${org2.domain}`, - // siteId: site2.siteId, - // orgId: site2.orgId, - // name: "Database", - // subdomain: "db", - // }) - // .returning() - // .get(); +// // Insert dummy targets +// await db.insert(targets).values([ +// { +// resourceId: resource1.resourceId, +// ip: "10.0.0.16", +// method: "http", +// port: 4200, +// protocol: "TCP", +// }, +// { +// resourceId: resource2.resourceId, +// ip: "10.0.0.17", +// method: "https", +// port: 443, +// protocol: "TCP", +// }, +// { +// resourceId: resource3.resourceId, +// ip: "172.16.1.16", +// method: "http", +// port: 80, +// protocol: "TCP", +// }, +// ]); - // // Insert dummy routes - // await db.insert(routes).values([ - // { exitNodeId: exitNode1.exitNodeId, subnet: "10.0.0.0/24" }, - // { exitNodeId: exitNode2.exitNodeId, subnet: "172.16.1.1/24" }, - // ]); +// console.log("Dummy data inserted successfully"); +// } - // // Insert dummy targets - // await db.insert(targets).values([ - // { - // resourceId: resource1.resourceId, - // ip: "10.0.0.16", - // method: "http", - // port: 4200, - // protocol: "TCP", - // }, - // { - // resourceId: resource2.resourceId, - // ip: "10.0.0.17", - // method: "https", - // port: 443, - // protocol: "TCP", - // }, - // { - // resourceId: resource3.resourceId, - // ip: "172.16.1.16", - // method: "http", - // port: 80, - // protocol: "TCP", - // }, - // ]); - - // console.log("Dummy data inserted successfully"); -} - -insertDummyData().catch(console.error); +// insertDummyData().catch(console.error); diff --git a/server/db/ensureActions.ts b/server/db/ensureActions.ts index 7d96ee55..9d2c6011 100644 --- a/server/db/ensureActions.ts +++ b/server/db/ensureActions.ts @@ -7,15 +7,19 @@ import logger from "@server/logger"; export async function ensureActions() { const actionIds = Object.values(ActionsEnum); const existingActions = await db.select().from(actions).execute(); - const existingActionIds = existingActions.map(action => action.actionId); + const existingActionIds = existingActions.map((action) => action.actionId); - const actionsToAdd = actionIds.filter(id => !existingActionIds.includes(id)); - const actionsToRemove = existingActionIds.filter(id => !actionIds.includes(id as ActionsEnum)); + const actionsToAdd = actionIds.filter( + (id) => !existingActionIds.includes(id) + ); + const actionsToRemove = existingActionIds.filter( + (id) => !actionIds.includes(id as ActionsEnum) + ); const defaultRoles = await db .select() .from(roles) - .where(eq(roles.isSuperUserRole, true)) + .where(eq(roles.isAdmin, true)) .execute(); // Add new actions @@ -24,29 +28,42 @@ export async function ensureActions() { await db.insert(actions).values({ actionId }).execute(); // Add new actions to the Default role if (defaultRoles.length != 0) { - await db.insert(roleActions) - .values(defaultRoles.map(role => ({ roleId: role.roleId!, actionId, orgId: role.orgId! }))) + await db + .insert(roleActions) + .values( + defaultRoles.map((role) => ({ + roleId: role.roleId!, + actionId, + orgId: role.orgId!, + })) + ) .execute(); } } // Remove deprecated actions if (actionsToRemove.length > 0) { - logger.debug(`Removing actions: ${actionsToRemove.join(', ')}`); - await db.delete(actions).where(inArray(actions.actionId, actionsToRemove)).execute(); - await db.delete(roleActions).where(inArray(roleActions.actionId, actionsToRemove)).execute(); + logger.debug(`Removing actions: ${actionsToRemove.join(", ")}`); + await db + .delete(actions) + .where(inArray(actions.actionId, actionsToRemove)) + .execute(); + await db + .delete(roleActions) + .where(inArray(roleActions.actionId, actionsToRemove)) + .execute(); } } -export async function createSuperUserRole(orgId: string) { +export async function createAdminRole(orgId: string) { // Create the Default role if it doesn't exist const [insertedRole] = await db .insert(roles) .values({ orgId, - isSuperUserRole: true, - name: 'Super User', - description: 'Super User role with all actions' + isAdmin: true, + name: "Admin", + description: "Admin role most permissions", }) .returning({ roleId: roles.roleId }) .execute(); @@ -56,13 +73,20 @@ export async function createSuperUserRole(orgId: string) { const actionIds = await db.select().from(actions).execute(); if (actionIds.length === 0) { - logger.info('No actions to assign to the Super User role'); + logger.info("No actions to assign to the Admin role"); return; } - await db.insert(roleActions) - .values(actionIds.map(action => ({ roleId, actionId: action.actionId, orgId }))) + await db + .insert(roleActions) + .values( + actionIds.map((action) => ({ + roleId, + actionId: action.actionId, + orgId, + })) + ) .execute(); return roleId; -} \ No newline at end of file +} diff --git a/server/db/schema.ts b/server/db/schema.ts index 493fa955..3e7e8206 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -99,6 +99,7 @@ export const userOrgs = sqliteTable("userOrgs", { roleId: integer("roleId") .notNull() .references(() => roles.roleId), + isOwner: integer("isOwner", { mode: "boolean" }).notNull().default(false), }); export const emailVerificationCodes = sqliteTable("emailVerificationCodes", { @@ -131,7 +132,7 @@ export const roles = sqliteTable("roles", { orgId: text("orgId").references(() => orgs.orgId, { onDelete: "cascade", }), - isSuperUserRole: integer("isSuperUserRole", { mode: "boolean" }), + isAdmin: integer("isAdmin", { mode: "boolean" }), name: text("name").notNull(), description: text("description"), }); @@ -241,3 +242,4 @@ export type RoleResource = InferSelectModel; export type UserResource = InferSelectModel; export type Limit = InferSelectModel; export type UserInvite = InferSelectModel; +export type UserOrg = InferSelectModel; diff --git a/server/index.ts b/server/index.ts index d96fc4e1..909bf796 100644 --- a/server/index.ts +++ b/server/index.ts @@ -12,9 +12,9 @@ import { } from "@server/middlewares"; import internal from "@server/routers/internal"; import { authenticated, unauthenticated } from "@server/routers/external"; -import { router as wsRouter, handleWSUpgrade } from '@server/routers/ws'; +import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws"; import cookieParser from "cookie-parser"; -import { User } from "@server/db/schema"; +import { User, UserOrg } from "@server/db/schema"; import { ensureActions } from "./db/ensureActions"; import { logIncomingMiddleware } from "./middlewares/logIncoming"; @@ -52,15 +52,15 @@ app.prepare().then(() => { externalServer.use(prefix, unauthenticated); externalServer.use(prefix, authenticated); externalServer.use(`${prefix}/ws`, wsRouter); - + externalServer.use(notFoundMiddleware); - + // We are using NEXT from here on externalServer.all("*", (req: Request, res: Response) => { const parsedUrl = parse(req.url!, true); handle(req, res, parsedUrl); }); - + const httpServer = externalServer.listen(externalPort, (err?: any) => { if (err) throw err; logger.info( @@ -98,6 +98,7 @@ declare global { namespace Express { interface Request { user?: User; + userOrg?: UserOrg; userOrgRoleId?: number; userOrgId?: string; userOrgIds?: string[]; diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index c7a95cfb..c90006d2 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -11,7 +11,7 @@ export * from "./verifyResourceAccess"; export * from "./verifyTargetAccess"; export * from "./verifyRoleAccess"; export * from "./verifyUserAccess"; -export * from "./verifySuperUser"; +export * from "./verifyAdmin"; export * from "./verifyEmail"; export * from "./requestEmailVerificationCode"; export * from "./changePassword"; diff --git a/server/routers/auth/verifyAdmin.ts b/server/routers/auth/verifyAdmin.ts new file mode 100644 index 00000000..579a63e3 --- /dev/null +++ b/server/routers/auth/verifyAdmin.ts @@ -0,0 +1,63 @@ +import { Request, Response, NextFunction } from "express"; +import { db } from "@server/db"; +import { roles, userOrgs } from "@server/db/schema"; +import { and, eq } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; + +export async function verifyAdmin( + req: Request, + res: Response, + next: NextFunction +) { + const userId = req.user?.userId; + const orgId = req.userOrgId; + let userOrg = req.userOrg; + + if (!userId) { + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User does not have orgId") + ); + } + + if (!userId) { + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); + } + + if (!userOrg) { + const userOrgRes = await db + .select() + .from(userOrgs) + .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId!))) + .limit(1); + userOrg = userOrgRes[0]; + } + + if (!userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + const userRole = await db + .select() + .from(roles) + .where(eq(roles.roleId, userOrg.roleId)) + .limit(1); + + if (userRole.length === 0 || !userRole[0].isAdmin) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have Admin access" + ) + ); + } + + return next(); +} diff --git a/server/routers/auth/verifyOrgAccess.ts b/server/routers/auth/verifyOrgAccess.ts index 1d04e4ca..a6b93f65 100644 --- a/server/routers/auth/verifyOrgAccess.ts +++ b/server/routers/auth/verifyOrgAccess.ts @@ -4,15 +4,15 @@ import { userOrgs } from "@server/db/schema"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; -import { AuthenticatedRequest } from "@server/types/Auth"; -export function verifyOrgAccess( +export async function verifyOrgAccess( req: Request, res: Response, next: NextFunction ) { - const userId = req.user!.userId; // Assuming you have user information in the request + const userId = req.user!.userId; const orgId = req.params.orgId; + let userOrg = req.userOrg; if (!userId) { return next( @@ -26,30 +26,36 @@ export function verifyOrgAccess( ); } - db.select() - .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId))) - .then((result) => { - if (result.length === 0) { - next( - createHttpError( - HttpCode.FORBIDDEN, - "User does not have access to this organization" - ) + try { + if (!userOrg) { + const userOrgRes = await db + .select() + .from(userOrgs) + .where( + and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId)) ); - } else { - // User has access, attach the user's role to the request for potential future use - req.userOrgRoleId = result[0].roleId; - req.userOrgId = orgId; - next(); - } - }) - .catch((error) => { + userOrg = userOrgRes[0]; + } + + if (!userOrg) { next( createHttpError( - HttpCode.INTERNAL_SERVER_ERROR, - "Error verifying organization access" + HttpCode.FORBIDDEN, + "User does not have access to this organization" ) ); - }); + } else { + // User has access, attach the user's role to the request for potential future use + req.userOrgRoleId = userOrg.roleId; + req.userOrgId = orgId; + return next(); + } + } catch (e) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying organization access" + ) + ); + } } diff --git a/server/routers/auth/verifyResourceAccess.ts b/server/routers/auth/verifyResourceAccess.ts index 777e6fb7..1ddbe69b 100644 --- a/server/routers/auth/verifyResourceAccess.ts +++ b/server/routers/auth/verifyResourceAccess.ts @@ -1,49 +1,85 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { resources, userOrgs, userResources, roleResources } from '@server/db/schema'; -import { and, eq } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; +import { Request, Response, NextFunction } from "express"; +import { db } from "@server/db"; +import { + resources, + userOrgs, + userResources, + roleResources, +} from "@server/db/schema"; +import { and, eq } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; -export async function verifyResourceAccess(req: Request, res: Response, next: NextFunction) { - const userId = req.user!.userId; // Assuming you have user information in the request - const resourceId = req.params.resourceId || req.body.resourceId || req.query.resourceId; +export async function verifyResourceAccess( + req: Request, + res: Response, + next: NextFunction +) { + const userId = req.user!.userId; + const resourceId = + req.params.resourceId || req.body.resourceId || req.query.resourceId; + let userOrg = req.userOrg; if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated')); + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); } try { - // Get the resource - const resource = await db.select() + const resource = await db + .select() .from(resources) .where(eq(resources.resourceId, resourceId)) .limit(1); if (resource.length === 0) { - return next(createHttpError(HttpCode.NOT_FOUND, `Resource with ID ${resourceId} not found`)); + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Resource with ID ${resourceId} not found` + ) + ); } if (!resource[0].orgId) { - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Resource with ID ${resourceId} does not have an organization ID`)); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `Resource with ID ${resourceId} does not have an organization ID` + ) + ); } - // Get user's role ID in the organization - const userOrgRole = await db.select() - .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId))) - .limit(1); - - if (userOrgRole.length === 0) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); + if (!userOrg) { + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, resource[0].orgId) + ) + ) + .limit(1); + userOrg = userOrgRole[0]; } - const userOrgRoleId = userOrgRole[0].roleId; + if (!userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + const userOrgRoleId = userOrg.roleId; req.userOrgRoleId = userOrgRoleId; req.userOrgId = resource[0].orgId; - // Check role-based resource access first - const roleResourceAccess = await db.select() + const roleResourceAccess = await db + .select() .from(roleResources) .where( and( @@ -54,25 +90,36 @@ export async function verifyResourceAccess(req: Request, res: Response, next: Ne .limit(1); if (roleResourceAccess.length > 0) { - // User's role has access to the resource return next(); } - // If role doesn't have access, check user-specific resource access - const userResourceAccess = await db.select() + const userResourceAccess = await db + .select() .from(userResources) - .where(and(eq(userResources.userId, userId), eq(userResources.resourceId, resourceId))) + .where( + and( + eq(userResources.userId, userId), + eq(userResources.resourceId, resourceId) + ) + ) .limit(1); if (userResourceAccess.length > 0) { - // User has direct access to the resource return next(); } - // If we reach here, the user doesn't have access to the resource - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this resource')); - + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this resource" + ) + ); } catch (error) { - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying resource access')); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying resource access" + ) + ); } } diff --git a/server/routers/auth/verifyRoleAccess.ts b/server/routers/auth/verifyRoleAccess.ts index c520a330..bd74226f 100644 --- a/server/routers/auth/verifyRoleAccess.ts +++ b/server/routers/auth/verifyRoleAccess.ts @@ -1,50 +1,82 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { roles, userOrgs } from '@server/db/schema'; -import { and, eq } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; -import logger from '@server/logger'; +import { Request, Response, NextFunction } from "express"; +import { db } from "@server/db"; +import { roles, userOrgs } from "@server/db/schema"; +import { and, eq } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; +import logger from "@server/logger"; -export async function verifyRoleAccess(req: Request, res: Response, next: NextFunction) { - const userId = req.user?.userId; // Assuming you have user information in the request - const roleId = parseInt(req.params.roleId || req.body.roleId || req.query.roleId); +export async function verifyRoleAccess( + req: Request, + res: Response, + next: NextFunction +) { + const userId = req.user?.userId; + const roleId = parseInt( + req.params.roleId || req.body.roleId || req.query.roleId + ); + let userOrg = req.userOrg; if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated')); + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); } if (isNaN(roleId)) { - return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid role ID')); + return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid role ID")); } try { - // Check if the role exists and belongs to the specified organization - const role = await db.select() + const role = await db + .select() .from(roles) .where(eq(roles.roleId, roleId)) .limit(1); if (role.length === 0) { - return next(createHttpError(HttpCode.NOT_FOUND, `Role with ID ${roleId} not found`)); + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Role with ID ${roleId} not found` + ) + ); } - // Check if the user has a role in the organization - const userOrgRole = await db.select() - .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, role[0].orgId!))) - .limit(1); - - if (userOrgRole.length === 0) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); + if (!userOrg) { + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, role[0].orgId!) + ) + ) + .limit(1); + userOrg = userOrgRole[0]; } - req.userOrgRoleId = userOrgRole[0].roleId; - req.userOrgId = userOrgRole[0].orgId; + if (!userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + req.userOrgRoleId = userOrg.roleId; + req.userOrgId = userOrg.orgId; return next(); } catch (error) { - logger.error('Error verifying role access:', error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying role access')); + logger.error("Error verifying role access:", error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying role access" + ) + ); } } diff --git a/server/routers/auth/verifySiteAccess.ts b/server/routers/auth/verifySiteAccess.ts index 7648c001..7d4e9690 100644 --- a/server/routers/auth/verifySiteAccess.ts +++ b/server/routers/auth/verifySiteAccess.ts @@ -1,42 +1,81 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { sites, userOrgs, userSites, roleSites, roles } from '@server/db/schema'; -import { and, eq, or } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; +import { Request, Response, NextFunction } from "express"; +import { db } from "@server/db"; +import { + sites, + userOrgs, + userSites, + roleSites, + roles, +} from "@server/db/schema"; +import { and, eq, or } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; -export async function verifySiteAccess(req: Request, res: Response, next: NextFunction) { +export async function verifySiteAccess( + req: Request, + res: Response, + next: NextFunction +) { const userId = req.user!.userId; // Assuming you have user information in the request - const siteId = parseInt(req.params.siteId || req.body.siteId || req.query.siteId); + const siteId = parseInt( + req.params.siteId || req.body.siteId || req.query.siteId + ); if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated')); + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); } if (isNaN(siteId)) { - return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid site ID')); + return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid site ID")); } try { // Get the site - const site = await db.select().from(sites).where(eq(sites.siteId, siteId)).limit(1); + const site = await db + .select() + .from(sites) + .where(eq(sites.siteId, siteId)) + .limit(1); if (site.length === 0) { - return next(createHttpError(HttpCode.NOT_FOUND, `Site with ID ${siteId} not found`)); + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Site with ID ${siteId} not found` + ) + ); } if (!site[0].orgId) { - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, `Site with ID ${siteId} does not have an organization ID`)); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `Site with ID ${siteId} does not have an organization ID` + ) + ); } // Get user's role ID in the organization - const userOrgRole = await db.select() + const userOrgRole = await db + .select() .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, site[0].orgId))) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, site[0].orgId) + ) + ) .limit(1); if (userOrgRole.length === 0) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); } const userOrgRoleId = userOrgRole[0].roleId; @@ -44,7 +83,8 @@ export async function verifySiteAccess(req: Request, res: Response, next: NextFu req.userOrgId = site[0].orgId; // Check role-based site access first - const roleSiteAccess = await db.select() + const roleSiteAccess = await db + .select() .from(roleSites) .where( and( @@ -60,9 +100,12 @@ export async function verifySiteAccess(req: Request, res: Response, next: NextFu } // If role doesn't have access, check user-specific site access - const userSiteAccess = await db.select() + const userSiteAccess = await db + .select() .from(userSites) - .where(and(eq(userSites.userId, userId), eq(userSites.siteId, siteId))) + .where( + and(eq(userSites.userId, userId), eq(userSites.siteId, siteId)) + ) .limit(1); if (userSiteAccess.length > 0) { @@ -71,9 +114,18 @@ export async function verifySiteAccess(req: Request, res: Response, next: NextFu } // If we reach here, the user doesn't have access to the site - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this site')); - + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this site" + ) + ); } catch (error) { - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying site access')); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying site access" + ) + ); } } diff --git a/server/routers/auth/verifySuperuser.ts b/server/routers/auth/verifySuperuser.ts deleted file mode 100644 index 5455d4a4..00000000 --- a/server/routers/auth/verifySuperuser.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { roles, userOrgs } from '@server/db/schema'; -import { and, eq } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; -import logger from '@server/logger'; - -export async function verifySuperUser(req: Request, res: Response, next: NextFunction) { - const userId = req.user?.userId; // Assuming you have user information in the request - const orgId = req.userOrgId; - - if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User does not have orgId')); - } - - if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated')); - } - - try { - // Check if the user has a role in the organization - const userOrgRole = await db.select() - .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, orgId!))) - .limit(1); - - if (userOrgRole.length === 0) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); - } - - // get userOrgRole[0].roleId - // Check if the user's role in the organization is a Super User role - const userRole = await db.select() - .from(roles) - .where(eq(roles.roleId, userOrgRole[0].roleId)) - .limit(1); - - if (userRole.length === 0 || !userRole[0].isSuperUserRole) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have Super User access')); - } - - return next(); - } catch (error) { - logger.error('Error verifying role access:', error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying role access')); - } -} diff --git a/server/routers/auth/verifyTargetAccess.ts b/server/routers/auth/verifyTargetAccess.ts index 2f6a0658..87557e87 100644 --- a/server/routers/auth/verifyTargetAccess.ts +++ b/server/routers/auth/verifyTargetAccess.ts @@ -1,23 +1,33 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { resources, targets, userOrgs } from '@server/db/schema'; -import { and, eq } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; +import { Request, Response, NextFunction } from "express"; +import { db } from "@server/db"; +import { resources, targets, userOrgs } from "@server/db/schema"; +import { and, eq } from "drizzle-orm"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; -export async function verifyTargetAccess(req: Request, res: Response, next: NextFunction) { - const userId = req.user!.userId; // Assuming you have user information in the request +export async function verifyTargetAccess( + req: Request, + res: Response, + next: NextFunction +) { + const userId = req.user!.userId; const targetId = parseInt(req.params.targetId); + let userOrg = req.userOrg; if (!userId) { - return next(createHttpError(HttpCode.UNAUTHORIZED, 'User not authenticated')); + return next( + createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") + ); } if (isNaN(targetId)) { - return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid organization ID')); + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID") + ); } - const target = await db.select() + const target = await db + .select() .from(targets) .where(eq(targets.targetId, targetId)) .limit(1); @@ -42,43 +52,62 @@ export async function verifyTargetAccess(req: Request, res: Response, next: Next ); } - const resource = await db.select() - .from(resources) - .where(eq(resources.resourceId, resourceId!)) - .limit(1); + try { + const resource = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId!)) + .limit(1); - if (resource.length === 0) { - return next( - createHttpError( - HttpCode.NOT_FOUND, - `resource with ID ${resourceId} not found` - ) - ); - } + if (resource.length === 0) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Resource with ID ${resourceId} not found` + ) + ); + } - if (!resource[0].orgId) { + if (!resource[0].orgId) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + `resource with ID ${resourceId} does not have an organization ID` + ) + ); + } + + if (!userOrg) { + const res = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, resource[0].orgId) + ) + ); + userOrg = res[0]; + } + + if (!userOrg) { + next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } else { + req.userOrgRoleId = userOrg.roleId; + req.userOrgId = resource[0].orgId!; + next(); + } + } catch (e) { return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - `resource with ID ${resourceId} does not have an organization ID` + "Error verifying organization access" ) ); } - - db.select() - .from(userOrgs) - .where(and(eq(userOrgs.userId, userId), eq(userOrgs.orgId, resource[0].orgId))) - .then((result) => { - if (result.length === 0) { - next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); - } else { - // User has access, attach the user's role to the request for potential future use - req.userOrgRoleId = result[0].roleId; - req.userOrgId = resource[0].orgId!; - next(); - } - }) - .catch((error) => { - next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying organization access')); - }); } diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 22473301..341c6680 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -25,7 +25,7 @@ export type VerifyTotpResponse = { export async function verifyTotp( req: Request, res: Response, - next: NextFunction, + next: NextFunction ): Promise { const parsedBody = verifyTotpBody.safeParse(req.body); @@ -33,8 +33,8 @@ export async function verifyTotp( return next( createHttpError( HttpCode.BAD_REQUEST, - fromError(parsedBody.error).toString(), - ), + fromError(parsedBody.error).toString() + ) ); } @@ -46,8 +46,8 @@ export async function verifyTotp( return next( createHttpError( HttpCode.BAD_REQUEST, - "Two-factor authentication is already enabled", - ), + "Two-factor authentication is already enabled" + ) ); } @@ -55,13 +55,17 @@ export async function verifyTotp( return next( createHttpError( HttpCode.BAD_REQUEST, - "User has not requested two-factor authentication", - ), + "User has not requested two-factor authentication" + ) ); } try { - const valid = await verifyTotpCode(code, user.twoFactorSecret, user.userId); + const valid = await verifyTotpCode( + code, + user.twoFactorSecret, + user.userId + ); let codes; if (valid) { @@ -101,8 +105,8 @@ export async function verifyTotp( return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - "Failed to verify two-factor authentication code", - ), + "Failed to verify two-factor authentication code" + ) ); } } diff --git a/server/routers/auth/verifyUserAccess.ts b/server/routers/auth/verifyUserAccess.ts index c9d40793..c9fa4634 100644 --- a/server/routers/auth/verifyUserAccess.ts +++ b/server/routers/auth/verifyUserAccess.ts @@ -1,12 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { - sites, - userOrgs, - userSites, - roleSites, - roles, -} from "@server/db/schema"; +import { userOrgs } from "@server/db/schema"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; @@ -16,9 +10,11 @@ export async function verifyUserAccess( res: Response, next: NextFunction ) { - const userId = req.user!.userId; // Assuming you have user information in the request + const userId = req.user!.userId; const reqUserId = req.params.userId || req.body.userId || req.query.userId; + let userOrg = req.userOrg; + if (!userId) { return next( createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated") @@ -30,18 +26,21 @@ export async function verifyUserAccess( } try { - const userOrg = await db - .select() - .from(userOrgs) - .where( - and( - eq(userOrgs.userId, reqUserId), - eq(userOrgs.orgId, req.userOrgId!) + if (!userOrg) { + const res = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, reqUserId), + eq(userOrgs.orgId, req.userOrgId!) + ) ) - ) - .limit(1); + .limit(1); + userOrg = res[0]; + } - if (userOrg.length === 0) { + if (userOrg) { return next( createHttpError( HttpCode.FORBIDDEN, diff --git a/server/routers/auth/verifyUserInRole.ts b/server/routers/auth/verifyUserInRole.ts index b38baea5..2a153114 100644 --- a/server/routers/auth/verifyUserInRole.ts +++ b/server/routers/auth/verifyUserInRole.ts @@ -1,31 +1,51 @@ -import { Request, Response, NextFunction } from 'express'; -import { db } from '@server/db'; -import { roles, userOrgs } from '@server/db/schema'; -import { and, eq } from 'drizzle-orm'; -import createHttpError from 'http-errors'; -import HttpCode from '@server/types/HttpCode'; -import logger from '@server/logger'; +import { Request, Response, NextFunction } from "express"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; +import logger from "@server/logger"; -export async function verifyUserInRole(req: Request, res: Response, next: NextFunction) { +export async function verifyUserInRole( + req: Request, + res: Response, + next: NextFunction +) { try { - const roleId = parseInt(req.params.roleId || req.body.roleId || req.query.roleId); + const roleId = parseInt( + req.params.roleId || req.body.roleId || req.query.roleId + ); const userRoleId = req.userOrgRoleId; if (isNaN(roleId)) { - return next(createHttpError(HttpCode.BAD_REQUEST, 'Invalid role ID')); + return next( + createHttpError(HttpCode.BAD_REQUEST, "Invalid role ID") + ); } if (!userRoleId) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this organization')); + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); } if (userRoleId !== roleId) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have access to this role')); + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this role" + ) + ); } return next(); } catch (error) { - logger.error('Error verifying role access:', error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Error verifying role access')); + logger.error("Error verifying role access:", error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying role access" + ) + ); } -} \ No newline at end of file +} diff --git a/server/routers/external.ts b/server/routers/external.ts index 0aa38896..4ccab1fa 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -19,7 +19,7 @@ import { verifyResourceAccess, verifyTargetAccess, verifyRoleAccess, - verifySuperUser, + verifyAdmin, verifyUserInRole, verifyUserAccess, } from "./auth"; @@ -121,7 +121,7 @@ authenticated.delete( // authenticated.put( // "/org/:orgId/role", // verifyOrgAccess, -// verifySuperUser, +// verifyAdmin, // role.createRole // ); // authenticated.get("/org/:orgId/roles", verifyOrgAccess, role.listRoles); @@ -134,13 +134,13 @@ authenticated.delete( // authenticated.post( // "/role/:roleId", // verifyRoleAccess, -// verifySuperUser, +// verifyAdmin, // role.updateRole // ); // authenticated.delete( // "/role/:roleId", // verifyRoleAccess, -// verifySuperUser, +// verifyAdmin, // role.deleteRole // ); @@ -190,14 +190,14 @@ authenticated.delete( // "/role/:roleId/action", // verifyRoleAccess, // verifyUserInRole, -// verifySuperUser, +// verifyAdmin, // role.removeRoleAction // ); // authenticated.get( // "/role/:roleId/actions", // verifyRoleAccess, // verifyUserInRole, -// verifySuperUser, +// verifyAdmin, // role.listRoleActions // ); @@ -239,14 +239,14 @@ authenticated.delete( // "/org/:orgId/user/:userId/action", // verifyOrgAccess, // verifyUserAccess, -// verifySuperUser, +// verifyAdmin, // role.addRoleAction // ); // authenticated.delete( // "/org/:orgId/user/:userId/action", // verifyOrgAccess, // verifyUserAccess, -// verifySuperUser, +// verifyAdmin, // role.removeRoleAction // ); diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index 23a79620..090818d0 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -1,16 +1,16 @@ -import { Request, Response, NextFunction } from 'express'; -import { z } from 'zod'; -import { db } from '@server/db'; -import { eq } from 'drizzle-orm'; -import { orgs, userOrgs } from '@server/db/schema'; +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { eq } from "drizzle-orm"; +import { orgs, userOrgs } 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 { createSuperUserRole } from '@server/db/ensureActions'; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions"; +import logger from "@server/logger"; +import { createAdminRole } from "@server/db/ensureActions"; import config, { APP_PATH } from "@server/config"; -import { fromError } from 'zod-validation-error'; +import { fromError } from "zod-validation-error"; const createOrgSchema = z.object({ orgId: z.string(), @@ -20,7 +20,11 @@ const createOrgSchema = z.object({ const MAX_ORGS = 5; -export async function createOrg(req: Request, res: Response, next: NextFunction): Promise { +export async function createOrg( + req: Request, + res: Response, + next: NextFunction +): Promise { try { const parsedBody = createOrgSchema.safeParse(req.body); if (!parsedBody.success) { @@ -43,7 +47,7 @@ export async function createOrg(req: Request, res: Response, next: NextFunction) } // TODO: we cant do this when they create an org because they are not in an org yet... maybe we need to make the org id optional on the userActions table - // Check if the user has permission + // Check if the user has permission // const hasPermission = await checkUserActionPermission(ActionsEnum.createOrg, req); // if (!hasPermission) { // return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action')); @@ -52,11 +56,12 @@ export async function createOrg(req: Request, res: Response, next: NextFunction) const { orgId, name } = parsedBody.data; // make sure the orgId is unique - const orgExists = await db.select() + const orgExists = await db + .select() .from(orgs) .where(eq(orgs.orgId, orgId)) .limit(1); - + if (orgExists.length > 0) { return next( createHttpError( @@ -69,29 +74,34 @@ export async function createOrg(req: Request, res: Response, next: NextFunction) // create a url from config.app.base_url and get the hostname const domain = new URL(config.app.base_url).hostname; - const newOrg = await db.insert(orgs).values({ - orgId, - name, - domain - }).returning(); + const newOrg = await db + .insert(orgs) + .values({ + orgId, + name, + domain, + }) + .returning(); - const roleId = await createSuperUserRole(newOrg[0].orgId); + const roleId = await createAdminRole(newOrg[0].orgId); if (!roleId) { return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - `Error creating Super User role` + `Error creating Admin role` ) ); } - // put the user in the super user role - await db.insert(userOrgs).values({ - userId: req.user!.userId, - orgId: newOrg[0].orgId, - roleId: roleId, - }).execute(); + await db + .insert(userOrgs) + .values({ + userId: req.user!.userId, + orgId: newOrg[0].orgId, + roleId: roleId, + }) + .execute(); return response(res, { data: newOrg[0], @@ -102,6 +112,11 @@ export async function createOrg(req: Request, res: Response, next: NextFunction) }); } catch (error) { logger.error(error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred...")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); } } diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index 24f306aa..5bbecb9a 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -1,20 +1,24 @@ -import { Request, Response, NextFunction } from 'express'; -import { z } from 'zod'; -import { db } from '@server/db'; -import { orgs } from '@server/db/schema'; -import { eq } from 'drizzle-orm'; +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { orgs, userActions } 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 { fromError } from 'zod-validation-error'; +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"; const deleteOrgSchema = z.object({ - orgId: z.string() + orgId: z.string(), }); -export async function deleteOrg(req: Request, res: Response, next: NextFunction): Promise { +export async function deleteOrg( + req: Request, + res: Response, + next: NextFunction +): Promise { try { const parsedParams = deleteOrgSchema.safeParse(req.params); if (!parsedParams.success) { @@ -28,13 +32,22 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction) const { orgId } = parsedParams.data; - // Check if the user has permission to list sites - const hasPermission = await checkUserActionPermission(ActionsEnum.deleteOrg, req); - if (!hasPermission) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action')); - } + // // Check if the user has permission to list sites + // const hasPermission = await checkUserActionPermission( + // ActionsEnum.deleteOrg, + // req + // ); + // if (!hasPermission) { + // return next( + // createHttpError( + // HttpCode.FORBIDDEN, + // "User does not have permission to perform this action" + // ) + // ); + // } - const deletedOrg = await db.delete(orgs) + const deletedOrg = await db + .delete(orgs) .where(eq(orgs.orgId, orgId)) .returning(); @@ -56,6 +69,11 @@ export async function deleteOrg(req: Request, res: Response, next: NextFunction) }); } catch (error) { logger.error(error); - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred...")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); } } diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index c58d7343..79b936c2 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -1,19 +1,29 @@ -import { Request, Response, NextFunction } from 'express'; -import { z } from 'zod'; -import { db } from '@server/db'; -import { orgs, resources, roleResources, roles, userResources } from '@server/db/schema'; +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { + orgs, + resources, + roleResources, + roles, + userResources, +} 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 stoi from '@server/utils/stoi'; -import { fromError } from 'zod-validation-error'; +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 stoi from "@server/utils/stoi"; +import { fromError } from "zod-validation-error"; const createResourceParamsSchema = z.object({ - siteId: z.string().optional().transform(stoi).pipe(z.number().int().positive().optional()), - orgId: z.string() + siteId: z + .string() + .optional() + .transform(stoi) + .pipe(z.number().int().positive().optional()), + orgId: z.string(), }); // Define Zod schema for request body validation @@ -22,7 +32,11 @@ const createResourceSchema = z.object({ subdomain: z.string().min(1).max(255).optional(), }); -export async function createResource(req: Request, res: Response, next: NextFunction): Promise { +export async function createResource( + req: Request, + res: Response, + next: NextFunction +): Promise { try { // Validate request body const parsedBody = createResourceSchema.safeParse(req.body); @@ -51,17 +65,28 @@ export async function createResource(req: Request, res: Response, next: NextFunc const { siteId, orgId } = parsedParams.data; // Check if the user has permission to list sites - const hasPermission = await checkUserActionPermission(ActionsEnum.createResource, req); + const hasPermission = await checkUserActionPermission( + ActionsEnum.createResource, + req + ); if (!hasPermission) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have permission to perform this action')); + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have permission to perform this action" + ) + ); } if (!req.userOrgRoleId) { - return next(createHttpError(HttpCode.FORBIDDEN, 'User does not have a role')); + return next( + createHttpError(HttpCode.FORBIDDEN, "User does not have a role") + ); } // get the org - const org = await db.select() + const org = await db + .select() .from(orgs) .where(eq(orgs.orgId, orgId)) .limit(1); @@ -79,35 +104,35 @@ export async function createResource(req: Request, res: Response, next: NextFunc const fullDomain = `${subdomain}.${org[0].domain}`; // Create new resource in the database - const newResource = await db.insert(resources).values({ - fullDomain, - siteId, - orgId, - name, - subdomain, - }).returning(); + const newResource = await db + .insert(resources) + .values({ + fullDomain, + siteId, + orgId, + name, + subdomain, + }) + .returning(); - // find the Super User roleId and also add the resource to the Super User role - const superUserRole = await db.select() + const adminRole = await db + .select() .from(roles) - .where(and(eq(roles.isSuperUserRole, true), eq(roles.orgId, orgId))) + .where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId))) .limit(1); - if (superUserRole.length === 0) { + if (adminRole.length === 0) { return next( - createHttpError( - HttpCode.NOT_FOUND, - `Super User role not found` - ) + createHttpError(HttpCode.NOT_FOUND, `Admin role not found`) ); } await db.insert(roleResources).values({ - roleId: superUserRole[0].roleId, + roleId: adminRole[0].roleId, resourceId: newResource[0].resourceId, }); - if (req.userOrgRoleId != superUserRole[0].roleId) { + if (req.userOrgRoleId != adminRole[0].roleId) { // make sure the user can access the resource await db.insert(userResources).values({ userId: req.user?.userId!, @@ -124,6 +149,11 @@ export async function createResource(req: Request, res: Response, next: NextFunc }); } catch (error) { throw error; - return next(createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred...")); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred..." + ) + ); } } diff --git a/server/routers/resource/listResourceRoles.ts b/server/routers/resource/listResourceRoles.ts index c3d880f3..83c565ce 100644 --- a/server/routers/resource/listResourceRoles.ts +++ b/server/routers/resource/listResourceRoles.ts @@ -51,7 +51,7 @@ export async function listResourceRoles( roleId: roles.roleId, name: roles.name, description: roles.description, - isSuperUserRole: roles.isSuperUserRole, + isAdmin: roles.isAdmin, }) .from(roleResources) .innerJoin(roles, eq(roleResources.roleId, roles.roleId)) diff --git a/server/routers/role/deleteRole.ts b/server/routers/role/deleteRole.ts index 24a650f0..4700ccc1 100644 --- a/server/routers/role/deleteRole.ts +++ b/server/routers/role/deleteRole.ts @@ -61,11 +61,11 @@ export async function deleteRole( ); } - if (role[0].isSuperUserRole) { + if (role[0].isAdmin) { return next( createHttpError( HttpCode.FORBIDDEN, - `Cannot delete a Super User role` + `Cannot delete a Admin role` ) ); } diff --git a/server/routers/role/listRoles.ts b/server/routers/role/listRoles.ts index f73f8af0..7ffc9dcf 100644 --- a/server/routers/role/listRoles.ts +++ b/server/routers/role/listRoles.ts @@ -80,7 +80,7 @@ export async function listRoles( .select({ roleId: roles.roleId, orgId: roles.orgId, - isSuperUserRole: roles.isSuperUserRole, + isAdmin: roles.isAdmin, name: roles.name, description: roles.description, orgName: orgs.name, diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index 04ce2478..740c8fcc 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -81,11 +81,11 @@ export async function updateRole( ); } - if (role[0].isSuperUserRole) { + if (role[0].isAdmin) { return next( createHttpError( HttpCode.FORBIDDEN, - `Cannot update a Super User role` + `Cannot update a Admin role` ) ); } diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index b35793d9..c5bf7641 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -107,25 +107,24 @@ export async function createSite( subnet, }) .returning(); - // find the Super User roleId and also add the resource to the Super User role - const superUserRole = await db + const adminRole = await db .select() .from(roles) - .where(and(eq(roles.isSuperUserRole, true), eq(roles.orgId, orgId))) + .where(and(eq(roles.isAdmin, true), eq(roles.orgId, orgId))) .limit(1); - if (superUserRole.length === 0) { + if (adminRole.length === 0) { return next( - createHttpError(HttpCode.NOT_FOUND, `Super User role not found`) + createHttpError(HttpCode.NOT_FOUND, `Admin role not found`) ); } await db.insert(roleSites).values({ - roleId: superUserRole[0].roleId, + roleId: adminRole[0].roleId, siteId: newSite.siteId, }); - if (req.userOrgRoleId != superUserRole[0].roleId) { + if (req.userOrgRoleId != adminRole[0].roleId) { // make sure the user can access the site db.insert(userSites).values({ userId: req.user?.userId!, diff --git a/server/routers/site/listSiteRoles.ts b/server/routers/site/listSiteRoles.ts index 1c255124..7e5c5fb6 100644 --- a/server/routers/site/listSiteRoles.ts +++ b/server/routers/site/listSiteRoles.ts @@ -51,7 +51,7 @@ export async function listSiteRoles( roleId: roles.roleId, name: roles.name, description: roles.description, - isSuperUserRole: roles.isSuperUserRole, + isAdmin: roles.isAdmin, }) .from(roleSites) .innerJoin(roles, eq(roleSites.roleId, roles.roleId)) diff --git a/src/app/[orgId]/settings/users/components/InviteUserForm.tsx b/src/app/[orgId]/settings/users/components/InviteUserForm.tsx index f3cf8073..fb3cb051 100644 --- a/src/app/[orgId]/settings/users/components/InviteUserForm.tsx +++ b/src/app/[orgId]/settings/users/components/InviteUserForm.tsx @@ -57,13 +57,7 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) { const [loading, setLoading] = useState(false); const [expiresInDays, setExpiresInDays] = useState(1); - const roles = [ - { roleId: 1, name: "Super User" }, - { roleId: 2, name: "Admin" }, - { roleId: 3, name: "Power User" }, - { roleId: 4, name: "User" }, - { roleId: 5, name: "Guest" }, - ]; + const roles = [{ roleId: 1, name: "Admin" }]; const validFor = [ { hours: 24, name: "1 day" }, @@ -122,13 +116,16 @@ export default function InviteUserForm({ open, setOpen }: InviteUserFormProps) { return ( <> - { - setOpen(val); - setInviteLink(null); - setLoading(false); - setExpiresInDays(1); - form.reset(); - }}> + { + setOpen(val); + setInviteLink(null); + setLoading(false); + setExpiresInDays(1); + form.reset(); + }} + > Invite User