Files
dockhand-upstream/src/lib/server/ws-auth.ts
T
2026-06-17 08:21:38 +02:00

97 lines
2.9 KiB
TypeScript

import { validateSessionById, isAuthEnabled, SESSION_COOKIE } from './auth';
import { validateApiToken } from './api-tokens';
import { isEnterprise } from './license';
import { userHasAdminRole, userCanAccessEnvironment } from './db';
export interface WsUpgradeAuth {
userId: number;
username: string;
isAdmin: boolean;
authDisabled: boolean;
}
function parseCookieHeader(header: string | undefined): Record<string, string> {
if (!header) return {};
const out: Record<string, string> = {};
for (const part of header.split(';')) {
const eq = part.indexOf('=');
if (eq < 0) continue;
const k = part.slice(0, eq).trim();
let v = part.slice(eq + 1).trim();
if (v.startsWith('"') && v.endsWith('"')) v = v.slice(1, -1);
if (k) out[k] = decodeURIComponent(v);
}
return out;
}
type LowercasedHeaders = Record<string, string | string[] | undefined>;
function pickHeader(headers: LowercasedHeaders, name: string): string | undefined {
const v = headers[name];
if (Array.isArray(v)) return v[0];
return v;
}
export async function authenticateWsUpgrade(
headers: LowercasedHeaders
): Promise<WsUpgradeAuth | null> {
const authEnabled = await isAuthEnabled();
if (!authEnabled) {
return { userId: -1, username: '__bootstrap__', isAdmin: true, authDisabled: true };
}
const cookieHeader = pickHeader(headers, 'cookie');
const cookies = parseCookieHeader(cookieHeader);
const sessionId = cookies[SESSION_COOKIE];
if (sessionId) {
const user = await validateSessionById(sessionId);
if (user) {
const enterprise = await isEnterprise();
const isAdmin = enterprise ? await userHasAdminRole(user.id) : true;
return { userId: user.id, username: user.username, isAdmin, authDisabled: false };
}
}
const authHeader = pickHeader(headers, 'authorization');
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.slice(7).trim();
const user = await validateApiToken(token);
if (user) {
const enterprise = await isEnterprise();
const isAdmin = enterprise ? await userHasAdminRole(user.id) : true;
return { userId: user.id, username: user.username, isAdmin, authDisabled: false };
}
}
return null;
}
export async function canAccessEnvForUser(
auth: WsUpgradeAuth,
environmentId: number | undefined | null
): Promise<boolean> {
if (auth.authDisabled) return true;
if (auth.isAdmin) return true;
const enterprise = await isEnterprise();
if (!enterprise) return true;
if (environmentId == null) {
return false;
}
return userCanAccessEnvironment(auth.userId, environmentId);
}
declare global {
// eslint-disable-next-line no-var
var __authenticateWsUpgrade:
| ((headers: LowercasedHeaders) => Promise<WsUpgradeAuth | null>)
| undefined;
// eslint-disable-next-line no-var
var __canAccessEnvForUser:
| ((auth: WsUpgradeAuth, envId: number | undefined | null) => Promise<boolean>)
| undefined;
}
globalThis.__authenticateWsUpgrade = authenticateWsUpgrade;
globalThis.__canAccessEnvForUser = canAccessEnvForUser;