bug fixes.
This commit is contained in:
@@ -16,6 +16,10 @@ export async function handleChatRoutes(
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasPermission(user.role, "chat:write", user.userId)) {
|
||||
return new Response("Forbidden: Chat access denied", { status: 403 });
|
||||
}
|
||||
|
||||
const beforeParam = url.searchParams.get("before");
|
||||
const limitParam = url.searchParams.get("limit");
|
||||
const before = beforeParam ? Number(beforeParam) : undefined;
|
||||
|
||||
@@ -158,11 +158,8 @@ export async function handleClientRoutes(
|
||||
const user = await authenticateRequest(req);
|
||||
if (!user) return new Response("Unauthorized", { status: 401 });
|
||||
|
||||
try {
|
||||
requirePermission(user, "clients:control");
|
||||
} catch (error) {
|
||||
if (error instanceof Response) return error;
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
if (user.role !== "admin") {
|
||||
return new Response("Forbidden: Admin access required", { status: 403 });
|
||||
}
|
||||
|
||||
if (req.method === "GET") {
|
||||
|
||||
@@ -346,11 +346,8 @@ export async function handleEnrollmentRoutes(
|
||||
if (req.method === "GET" && url.pathname === "/api/enrollment/banned-ips") {
|
||||
const user = await authenticateRequest(req);
|
||||
if (!user) return new Response("Unauthorized", { status: 401 });
|
||||
try {
|
||||
requirePermission(user, "clients:enroll");
|
||||
} catch (error) {
|
||||
if (error instanceof Response) return error;
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
if (user.role !== "admin") {
|
||||
return new Response("Forbidden: Admin access required", { status: 403 });
|
||||
}
|
||||
|
||||
return Response.json({ items: listBannedIps() });
|
||||
@@ -360,11 +357,8 @@ export async function handleEnrollmentRoutes(
|
||||
if (req.method === "DELETE" && url.pathname === "/api/enrollment/banned-ips") {
|
||||
const user = await authenticateRequest(req);
|
||||
if (!user) return new Response("Unauthorized", { status: 401 });
|
||||
try {
|
||||
requirePermission(user, "clients:enroll");
|
||||
} catch (error) {
|
||||
if (error instanceof Response) return error;
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
if (user.role !== "admin") {
|
||||
return new Response("Forbidden: Admin access required", { status: 403 });
|
||||
}
|
||||
|
||||
const ipToUnban = (url.searchParams.get("ip") || "").trim();
|
||||
@@ -390,11 +384,8 @@ export async function handleEnrollmentRoutes(
|
||||
if (req.method === "POST" && url.pathname === "/api/enrollment/ban-ip") {
|
||||
const user = await authenticateRequest(req);
|
||||
if (!user) return new Response("Unauthorized", { status: 401 });
|
||||
try {
|
||||
requirePermission(user, "clients:enroll");
|
||||
} catch (error) {
|
||||
if (error instanceof Response) return error;
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
if (user.role !== "admin") {
|
||||
return new Response("Forbidden: Admin access required", { status: 403 });
|
||||
}
|
||||
|
||||
let body: any;
|
||||
|
||||
@@ -2,15 +2,17 @@ import { authenticateRequest } from "../../auth";
|
||||
import { AuditAction, logAudit } from "../../auditLog";
|
||||
import * as clientManager from "../../clientManager";
|
||||
import { getConfig, updateNotificationsConfig } from "../../config";
|
||||
import { savePushSubscription, deletePushSubscription, getPushSubscriptionsByUser } from "../../db";
|
||||
import { savePushSubscription, deletePushSubscriptionForUser, getPushSubscriptionsByUser } from "../../db";
|
||||
import { encodeMessage } from "../../protocol";
|
||||
import {
|
||||
canUserAccessClient,
|
||||
getUserNotificationSettings,
|
||||
updateUserNotificationSettings,
|
||||
} from "../../users";
|
||||
import {
|
||||
DEFAULT_WEBHOOK_TEMPLATE,
|
||||
DEFAULT_TELEGRAM_TEMPLATE,
|
||||
isPrivateOrInternalHostname,
|
||||
renderNotificationTemplate,
|
||||
} from "../notification-delivery";
|
||||
import { getVapidPublicKey } from "../web-push";
|
||||
@@ -20,7 +22,7 @@ type RequestIpProvider = {
|
||||
};
|
||||
|
||||
type NotificationsRouteDeps = {
|
||||
getNotificationScreenshot: (notificationId: string) => { format?: string; bytes: Uint8Array } | null;
|
||||
getNotificationScreenshot: (notificationId: string) => { format?: string; bytes: Uint8Array; clientId?: string } | null;
|
||||
secureHeaders: (contentType?: string) => HeadersInit;
|
||||
};
|
||||
|
||||
@@ -45,6 +47,13 @@ export async function handleNotificationsConfigRoutes(
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
if (
|
||||
screenshot.clientId &&
|
||||
!canUserAccessClient(user.userId, user.role, screenshot.clientId)
|
||||
) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
const format = (screenshot.format || "jpeg").toLowerCase();
|
||||
const contentType = format === "jpg" || format === "jpeg"
|
||||
? "image/jpeg"
|
||||
@@ -67,8 +76,8 @@ export async function handleNotificationsConfigRoutes(
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
if (user.role === "viewer") {
|
||||
return new Response("Forbidden: Viewer access denied", { status: 403 });
|
||||
if (user.role !== "admin") {
|
||||
return new Response("Forbidden: Admin access required", { status: 403 });
|
||||
}
|
||||
return Response.json({ notifications: getConfig().notifications });
|
||||
}
|
||||
@@ -348,6 +357,13 @@ export async function handleNotificationsConfigRoutes(
|
||||
return Response.json({ error: "Invalid webhook URL" }, { status: 400 });
|
||||
}
|
||||
|
||||
if (isPrivateOrInternalHostname(parsed.hostname.toLowerCase())) {
|
||||
return Response.json(
|
||||
{ error: "Webhook URL points at a private/internal address" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const sampleRecord = {
|
||||
id: "preview-notification",
|
||||
clientId: "abc123def456",
|
||||
@@ -489,7 +505,7 @@ export async function handleNotificationsConfigRoutes(
|
||||
return Response.json({ error: "Missing endpoint" }, { status: 400 });
|
||||
}
|
||||
|
||||
deletePushSubscription(endpoint);
|
||||
deletePushSubscriptionForUser(user.userId, endpoint);
|
||||
return Response.json({ ok: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ export async function handleSavedScriptsRoutes(
|
||||
: "powershell";
|
||||
|
||||
const item = saveSavedScript(id, user.userId, name, content, scriptType);
|
||||
if (!item) {
|
||||
return Response.json({ error: "Script id already in use" }, { status: 409 });
|
||||
}
|
||||
return Response.json({ ok: true, item });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { authenticateRequest } from "../../auth";
|
||||
import { logger } from "../../logger";
|
||||
import { isIpBanned } from "../../db";
|
||||
import { canUserAccessClient, canUserAccessFeature } from "../../users";
|
||||
import { canUserAccessClient, canUserAccessFeature, hasPermission } from "../../users";
|
||||
import type { FeatureName } from "../../users";
|
||||
|
||||
type RequestServer = {
|
||||
@@ -296,6 +296,9 @@ export async function handleWsUpgradeRoutes(
|
||||
if (!user) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
if (!hasPermission(user.role, "chat:write", user.userId)) {
|
||||
return new Response("Forbidden: Chat access denied", { status: 403 });
|
||||
}
|
||||
const ip = server.requestIP(req)?.address || "";
|
||||
if (server.upgrade(req, { data: { role: "chat_viewer", clientId: "", ip, userRole: user.role, userId: user.userId } })) {
|
||||
return new Response();
|
||||
|
||||
Reference in New Issue
Block a user