fix team auth

This commit is contained in:
Alex Holliday
2025-07-21 13:58:01 -07:00
parent f2037bf5fe
commit fdff3be316
3 changed files with 71 additions and 83 deletions

View File

@@ -20,6 +20,7 @@ import axios from "axios";
import seedDb from "../db/mongo/utils/seedDb.js";
import pkg from "papaparse";
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
import { fetchMonitorCertificate } from "./controllerUtils.js";
const SERVICE_NAME = "monitorController";
class MonitorController {
@@ -31,6 +32,15 @@ class MonitorController {
this.emailService = emailService;
}
async verifyTeamAccess(teamId, monitorId) {
const monitor = await this.db.getMonitorById(monitorId);
if (!monitor.teamId.equals(teamId)) {
const error = new Error("Unauthorized");
error.status = 403;
throw error;
}
}
/**
* Returns all monitors
* @async
@@ -52,32 +62,18 @@ class MonitorController {
"getAllMonitors"
);
/**
* Returns all monitors with uptime stats for 1,7,30, and 90 days
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @param {function} next
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
getAllMonitorsWithUptimeStats = asyncHandler(
async (req, res, next) => {
const monitors = await this.db.getAllMonitorsWithUptimeStats();
return res.success({
msg: this.stringService.monitorGetAll,
data: monitors,
});
},
SERVICE_NAME,
"getAllMonitorsWithUptimeStats"
);
getUptimeDetailsById = asyncHandler(
async (req, res, next) => {
const { monitorId } = req.params;
const { dateRange, normalize } = req.query;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const data = await this.db.getUptimeDetailsById({
monitorId,
dateRange,
@@ -109,6 +105,13 @@ class MonitorController {
let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query;
const { monitorId } = req.params;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const monitorStats = await this.db.getMonitorStatsById({
monitorId,
limit,
@@ -140,8 +143,14 @@ class MonitorController {
await getHardwareDetailsByIdParamValidation.validateAsync(req.params);
await getHardwareDetailsByIdQueryValidation.validateAsync(req.query);
const { monitorId } = req.params;
const { dateRange } = req.query;
const monitorId = req?.params?.monitorId;
const dateRange = req?.query?.dateRange;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const monitor = await this.db.getHardwareDetailsById({ monitorId, dateRange });
return res.success({
msg: this.stringService.monitorGetByIdSuccess,
@@ -153,7 +162,7 @@ class MonitorController {
);
getMonitorCertificate = asyncHandler(
async (req, res, next, fetchMonitorCertificate) => {
async (req, res, next) => {
await getCertificateParamValidation.validateAsync(req.params);
const { monitorId } = req.params;
@@ -188,6 +197,18 @@ class MonitorController {
await getMonitorByIdQueryValidation.validateAsync(req.query);
const monitor = await this.db.getMonitorById(req.params.monitorId);
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
if (!monitor.teamId.equals(teamId)) {
const error = new Error("Unauthorized");
error.status = 403;
throw error;
}
return res.success({
msg: this.stringService.monitorGetByIdSuccess,
data: monitor,
@@ -367,6 +388,13 @@ class MonitorController {
async (req, res, next) => {
await getMonitorByIdParamValidation.validateAsync(req.params);
const monitorId = req.params.monitorId;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const monitor = await this.db.deleteMonitor({ monitorId });
await this.jobQueue.deleteJob(monitor);
await this.db.deleteStatusPagesByMonitorId(monitor._id);
@@ -389,7 +417,7 @@ class MonitorController {
*/
deleteAllMonitors = asyncHandler(
async (req, res, next) => {
const { teamId } = req.user;
const teamId = req?.user?.teamId;
const { monitors, deletedCount } = await this.db.deleteAllMonitors(teamId);
await Promise.all(
monitors.map(async (monitor) => {
@@ -431,7 +459,14 @@ class MonitorController {
async (req, res, next) => {
await getMonitorByIdParamValidation.validateAsync(req.params);
await editMonitorBodyValidation.validateAsync(req.body);
const { monitorId } = req.params;
const monitorId = req?.params?.monitorId;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const editedMonitor = await this.db.editMonitor(monitorId, req.body);
@@ -462,6 +497,13 @@ class MonitorController {
await pauseMonitorParamValidation.validateAsync(req.params);
const monitorId = req.params.monitorId;
const teamId = req?.user?.teamId;
if (!teamId) {
throw new Error("Team ID is required");
}
await this.verifyTeamAccess(teamId, monitorId);
const monitor = await this.db.pauseMonitor({ monitorId });
monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor);

View File

@@ -56,53 +56,6 @@ const getAllMonitors = async (req, res) => {
}
};
/**
* Get all monitors with uptime stats for 1,7,30, and 90 days
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Array<Monitor>>}
* @throws {Error}
*/
const getAllMonitorsWithUptimeStats = async () => {
const timeRanges = {
1: new Date(Date.now() - 24 * 60 * 60 * 1000),
7: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
30: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
90: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
};
try {
const monitors = await Monitor.find();
const monitorsWithStats = await Promise.all(
monitors.map(async (monitor) => {
const model = CHECK_MODEL_LOOKUP[monitor.type];
const uptimeStats = await Promise.all(
Object.entries(timeRanges).map(async ([days, startDate]) => {
const checks = await model.find({
monitorId: monitor._id,
createdAt: { $gte: startDate },
});
return [days, getUptimePercentage(checks)];
})
);
return {
...monitor.toObject(),
...Object.fromEntries(uptimeStats),
};
})
);
return monitorsWithStats;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "getAllMonitorsWithUptimeStats";
throw error;
}
};
/**
* Function to calculate uptime duration based on the most recent check.
* @param {Array} checks Array of check objects.
@@ -761,7 +714,6 @@ const pauseMonitor = async ({ monitorId }) => {
export {
getAllMonitors,
getAllMonitorsWithUptimeStats,
getMonitorStatsById,
getMonitorById,
getMonitorsByIds,

View File

@@ -20,10 +20,9 @@ class MonitorRoutes {
// Team routes
this.router.get("/team", this.monitorController.getMonitorsByTeamId);
this.router.get("/team/with-checks", this.monitorController.getMonitorsWithChecksByTeamId);
this.router.get("/team/summary", this.monitorController.getMonitorsAndSummaryByTeamId); // TODO should be /team/summary
this.router.get("/team/summary", this.monitorController.getMonitorsAndSummaryByTeamId);
// Uptime routes
this.router.get("/uptime", this.monitorController.getAllMonitorsWithUptimeStats);
this.router.get("/uptime/details/:monitorId", this.monitorController.getUptimeDetailsById);
// Hardware routes
@@ -53,13 +52,8 @@ class MonitorRoutes {
// Individual monitor CRUD routes
this.router.get("/:monitorId", this.monitorController.getMonitorById);
this.router.put("/:monitorId", verifyTeamAccess(Monitor, "monitorId"), isAllowed(["admin", "superadmin"]), this.monitorController.editMonitor);
this.router.delete(
"/:monitorId",
verifyOwnership(Monitor, "monitorId"),
isAllowed(["admin", "superadmin"]),
this.monitorController.deleteMonitor
);
this.router.put("/:monitorId", isAllowed(["admin", "superadmin"]), this.monitorController.editMonitor);
this.router.delete("/:monitorId", isAllowed(["admin", "superadmin"]), this.monitorController.deleteMonitor);
}
getRouter() {