fix: monitoring defaults not saving and OIDC redirect issues

This commit is contained in:
LukeGus
2026-03-10 00:09:58 -05:00
parent 5caadf1d5d
commit ea2e59abd8
7 changed files with 97 additions and 52 deletions
+2
View File
@@ -100,6 +100,8 @@ http {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
}
location ~ ^/version(/.*)?$ {
+2
View File
@@ -89,6 +89,8 @@ http {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
}
location ~ ^/version(/.*)?$ {
+6 -9
View File
@@ -43,6 +43,7 @@ import {
generateDeviceFingerprint,
} from "../../utils/user-agent-parser.js";
import { loginRateLimiter } from "../../utils/login-rate-limiter.js";
import { getRequestOriginWithForceHTTPS } from "../../utils/request-origin.js";
const authManager = AuthManager.getInstance();
@@ -798,6 +799,9 @@ router.get("/oidc-config/admin", requireAdmin, async (req, res) => {
*/
router.get("/oidc/authorize", async (req, res) => {
try {
const origin = getRequestOriginWithForceHTTPS(req);
const backendCallbackUri = `${origin}/users/oidc/callback`;
authLogger.info("OIDC authorize request headers", {
protocol: req.protocol,
host: req.get("Host"),
@@ -807,6 +811,8 @@ router.get("/oidc/authorize", async (req, res) => {
"x-forwarded-host": req.get("X-Forwarded-Host"),
"x-forwarded-port": req.get("X-Forwarded-Port"),
secure: req.secure,
calculatedOrigin: origin,
backendCallbackUri: backendCallbackUri,
});
const envConfig = getOIDCConfigFromEnv();
@@ -826,15 +832,6 @@ router.get("/oidc/authorize", async (req, res) => {
const state = nanoid();
const nonce = nanoid();
const protocol =
process.env.OIDC_FORCE_HTTPS === "true"
? "https"
: req.get("X-Forwarded-Proto") || req.protocol;
const host = req.get("Host");
const origin = `${protocol}://${host}`;
const backendCallbackUri = `${origin}/users/oidc/callback`;
const referer = req.get("Referer");
let frontendOrigin;
if (referer) {
+1 -37
View File
@@ -12,6 +12,7 @@ import { FieldCrypto } from "../utils/field-crypto.js";
import { promises as fs } from "fs";
import path from "path";
import axios from "axios";
import { getRequestOrigin } from "../utils/request-origin.js";
const AUTH_TIMEOUT = 60 * 1000;
@@ -48,43 +49,6 @@ interface OPKSSHAuthSession {
const activeAuthSessions = new Map<string, OPKSSHAuthSession>();
const cleanupInProgress = new Set<string>();
export function getRequestOrigin(req: IncomingMessage): string {
const protoHeader =
req.headers["x-forwarded-proto"] ||
((req.socket as unknown as { encrypted?: boolean }).encrypted
? "https"
: "http");
const proto =
typeof protoHeader === "string"
? protoHeader.split(",")[0].trim()
: String(protoHeader);
const portHeader = req.headers["x-forwarded-port"];
const port =
typeof portHeader === "string"
? portHeader.split(",")[0].trim()
: undefined;
const hostHeaderRaw =
req.headers["x-forwarded-host"] || req.headers.host || "localhost";
const hostHeader =
typeof hostHeaderRaw === "string"
? hostHeaderRaw.split(",")[0].trim()
: String(hostHeaderRaw);
if (port) {
const hostWithoutPort = hostHeader.split(":")[0];
const isDefaultPort =
(proto === "http" && port === "80") ||
(proto === "https" && port === "443");
return isDefaultPort
? `${proto}://${hostWithoutPort}`
: `${proto}://${hostWithoutPort}:${port}`;
}
return `${proto}://${hostHeader}`;
}
function getOPKConfigPath(): string {
const dataDir =
process.env.DATA_DIR || path.join(process.cwd(), "db", "data");
+27 -3
View File
@@ -1001,6 +1001,28 @@ class PollingManager {
}
}
refreshAllPolling(): void {
const hostsToRefresh: Array<{
host: SSHHostWithCredentials;
viewerUserId?: string;
}> = [];
for (const [hostId, config] of this.pollingConfigs.entries()) {
hostsToRefresh.push({
host: config.host,
viewerUserId: config.viewerUserId,
});
}
for (const hostId of this.pollingConfigs.keys()) {
this.stopPollingForHost(hostId, false);
}
for (const { host, viewerUserId } of hostsToRefresh) {
this.startPollingForHost(host, { statusOnly: true, viewerUserId });
}
}
registerViewer(hostId: number, sessionId: string, userId: string): void {
if (!this.activeViewers.has(hostId)) {
this.activeViewers.set(hostId, new Set());
@@ -1259,9 +1281,8 @@ async function resolveHostCredentials(
const isSharedHost = userId !== ownerId;
if (isSharedHost) {
const { SharedCredentialManager } = await import(
"../utils/shared-credential-manager.js"
);
const { SharedCredentialManager } =
await import("../utils/shared-credential-manager.js");
const sharedCredManager = SharedCredentialManager.getInstance();
const sharedCred = await sharedCredManager.getSharedCredentialForUser(
host.id as number,
@@ -3185,6 +3206,9 @@ app.post("/global-settings", requireAdmin, async (req, res) => {
.run(String(metricsInterval));
}
// Refresh all active polling to apply new intervals immediately
pollingManager.refreshAllPolling();
res.json({ success: true });
} catch (error) {
statsLogger.error("Failed to save global settings", {
+3 -3
View File
@@ -808,9 +808,9 @@ wss.on("connection", async (ws: WebSocket, req) => {
case "opkssh_start_auth": {
const opksshData = data as { hostId: number };
try {
const { startOPKSSHAuth, getRequestOrigin } = await import(
"./opkssh-auth.js"
);
const { startOPKSSHAuth } = await import("./opkssh-auth.js");
const { getRequestOrigin } =
await import("../utils/request-origin.js");
const db = getDb();
const hostRow = await db
.select()
+56
View File
@@ -0,0 +1,56 @@
import type { Request } from "express";
import type { IncomingMessage } from "http";
export function getRequestOrigin(req: Request | IncomingMessage): string {
let protocol: string;
const protoHeader = req.headers["x-forwarded-proto"];
if (protoHeader) {
protocol =
typeof protoHeader === "string"
? protoHeader.split(",")[0].trim()
: protoHeader[0];
} else if ("protocol" in req && req.protocol) {
protocol = req.protocol;
} else {
protocol = (req.socket as unknown as { encrypted?: boolean }).encrypted
? "https"
: "http";
}
const portHeader = req.headers["x-forwarded-port"];
const port =
typeof portHeader === "string"
? portHeader.split(",")[0].trim()
: undefined;
const hostHeaderRaw =
req.headers["x-forwarded-host"] || req.headers.host || "localhost";
const hostHeader =
typeof hostHeaderRaw === "string"
? hostHeaderRaw.split(",")[0].trim()
: String(hostHeaderRaw);
if (port) {
const hostWithoutPort = hostHeader.split(":")[0];
const isDefaultPort =
(protocol === "http" && port === "80") ||
(protocol === "https" && port === "443");
return isDefaultPort
? `${protocol}://${hostWithoutPort}`
: `${protocol}://${hostWithoutPort}:${port}`;
}
return `${protocol}://${hostHeader}`;
}
export function getRequestOriginWithForceHTTPS(
req: Request | IncomingMessage,
): string {
if (process.env.OIDC_FORCE_HTTPS === "true") {
const origin = getRequestOrigin(req);
return origin.replace(/^http:/, "https:");
}
return getRequestOrigin(req);
}