// Generated, do not edit. sha256:b90a0342814d40cb5e6f51f946a82feca678a01b63d4cdb3e948bcbb7e125654 import { createHash, timingSafeEqual } from 'node:crypto'; import { CONFIG } from '../shared/config.js'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { readSecretsStore, writeSecretsStore } from './keys.mjs'; import { installDir } from './portfolios.mjs'; import { fail } from '../shared/app-error.js'; const AUTH_SLEUTEL = 'assetapp_auth'; const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']); const digest = (waarde) => createHash('sha256').update(String(waarde), 'utf-8').digest(); export function passwordMatches(presented, expected) { if (!expected || !expected.password) return false; if (typeof presented !== 'string' || !presented) return false; return timingSafeEqual(digest(presented), digest(expected.password)); } export function parseCookies(header) { const uit = {}; if (typeof header !== 'string') return uit; for (const deel of header.split(';')) { const i = deel.indexOf('='); if (i < 0) continue; const naam = deel.slice(0, i).trim(); if (!naam || naam in uit) continue; uit[naam] = deel.slice(i + 1).trim(); } return uit; } const vlaggen = (secure) => `Path=/; HttpOnly; SameSite=Lax${secure ? '; Secure' : ''}`; export function sessionCookie(token, { secure = false } = {}) { const seconden = Math.floor(CONFIG.AUTH.SESSION_TTL_MS / 1000); return `${CONFIG.AUTH.COOKIE_NAME}=${token}; Max-Age=${seconden}; ${vlaggen(secure)}`; } export function clearedSessionCookie({ secure = false } = {}) { return `${CONFIG.AUTH.COOKIE_NAME}=; Max-Age=0; ${vlaggen(secure)}`; } export function sessionTokenOf(req) { return parseCookies(req?.headers?.cookie)[CONFIG.AUTH.COOKIE_NAME] || null; } export function sendUnauthorized(res, { document = false } = {}) { if (document) { res.writeHead(302, { Location: CONFIG.AUTH.LOGIN_PATH, 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', }); return res.end(); } res.writeHead(401, { 'Content-Type': 'application/json; charset=utf-8', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'no-store', }); res.end(JSON.stringify({ error: 'Not logged in.' })); } export async function loadCredentials() { const { ASSETAPP_PASSWORD: password } = process.env; if (password) return { password }; const opgeslagen = (await readSecretsStore())[AUTH_SLEUTEL]; if (opgeslagen && opgeslagen.password) return { password: String(opgeslagen.password) }; return null; } export async function credentialsStatus() { const { ASSETAPP_PASSWORD: password } = process.env; if (password) return { ingesteld: true, bron: 'omgeving', wijzigbaar: false }; const opgeslagen = (await readSecretsStore())[AUTH_SLEUTEL]; const ingesteld = Boolean(opgeslagen && opgeslagen.password); return { ingesteld, bron: ingesteld ? 'portefeuille' : 'geen', wijzigbaar: true }; } export async function setCredentials(password) { const w = String(password ?? ''); if (!w) fail('portfolios.passwordRequired', null, 'A password is required.'); const store = await readSecretsStore(); await writeSecretsStore({ ...store, [AUTH_SLEUTEL]: { password: w } }); return { ...await credentialsStatus(), naHerstart: true }; } export async function clearCredentials() { const store = await readSecretsStore(); if (!(AUTH_SLEUTEL in store)) return { ...await credentialsStatus(), naHerstart: false }; delete store[AUTH_SLEUTEL]; await writeSecretsStore(store); return { ...await credentialsStatus(), naHerstart: true }; } const leesOfNull = async (pad) => { try { return await readFile(pad); } catch (e) { if (e.code === 'ENOENT') return null; throw e; } }; export async function loadTlsOptions() { const certPad = process.env.ASSETAPP_TLS_CERT || join(installDir(), 'tls', 'cert.pem'); const keyPad = process.env.ASSETAPP_TLS_KEY || join(installDir(), 'tls', 'key.pem'); const [cert, key] = await Promise.all([leesOfNull(certPad), leesOfNull(keyPad)]); if (!cert && !key) return null; if (!cert) throw new Error(`TLS half configured: key found (${keyPad}) but no certificate (${certPad}).`); if (!key) throw new Error(`TLS half configured: certificate found (${certPad}) but no key (${keyPad}).`); return { cert, key }; } export function resolveBinding({ tls = false, credentials = null, host = null } = {}) { const beschermd = Boolean(tls) && Boolean(credentials); if (host) { if (LOOPBACK.has(host) || beschermd) return { host, waarschuwing: null }; const mist = [Boolean(tls) ? null : 'TLS', credentials ? null : 'inloggegevens'].filter(Boolean).join(' en '); return { host, waarschuwing: `ASSETAPP_HOST=${host} stelt de app open voor het hele netwerk terwijl ${mist} ontbreekt. ` + 'Iedereen die deze machine kan bereiken, kan de app openen.', }; } return { host: beschermd ? '0.0.0.0' : '127.0.0.1', waarschuwing: null }; }