import { chmodSync, existsSync, linkSync, lstatSync, mkdirSync, opendirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs"; import { join } from "node:path"; import type { WindowsAuthStorageBridge, WindowsAuthStorageDirectory, } from "../../src/auth/windows-auth-storage.js"; // Test-process fixture only. The production POSIX implementation is the Go auth-storage bridge; // the Go package covers retained-descriptor adversarial races separately. export function createFixturePosixAuthStorageBridge(): WindowsAuthStorageBridge { const ensure = async (root: string): Promise => { if (!existsSync(root)) { mkdirSync(root, { mode: 0o700 }); chmodSync(root, 0o700); } for (const name of ["sessions", "oidc"]) { const path = join(root, name); if (!existsSync(path)) { mkdirSync(path, { mode: 0o700 }); chmodSync(path, 0o700); } } }; const path = async (root: string, directory: WindowsAuthStorageDirectory, filename: string): Promise => { await ensure(root); return join(root, directory, filename); }; const names = async (root: string, directory: WindowsAuthStorageDirectory): Promise => { await ensure(root); const handle = opendirSync(join(root, directory)); const result: string[] = []; try { for (;;) { const entry = handle.readSync(); if (entry === null) break; result.push(entry.name); } } finally { handle.closeSync(); } return result.sort(); }; const missing = (error: unknown): boolean => (error as { code?: unknown })?.code === "ENOENT"; return { validateRoot: ensure, ensureLayout: ensure, readAuthConfig: (value) => readFileSync(value), readLocalUsers: async (value) => readFileSync(value), create: async (root, directory, filename, contents) => { try { const value = await path(root, directory, filename); writeFileSync(value, contents, { flag: "wx", mode: 0o600 }); chmodSync(value, 0o600); return true; } catch (error) { if (missing(error) || (error as { code?: unknown })?.code === "EEXIST") return false; throw error; } }, read: async (root, directory, filename) => { try { return readFileSync(await path(root, directory, filename)); } catch (error) { if (missing(error)) return undefined; throw error; } }, replace: async (root, directory, filename, contents) => { const value = await path(root, directory, filename); const temporary = `${value}.fixture-replacement`; writeFileSync(temporary, contents, { flag: "wx", mode: 0o600 }); renameSync(temporary, value); }, remove: async (root, directory, filename) => { try { unlinkSync(await path(root, directory, filename)); return true; } catch (error) { if (missing(error)) return false; throw error; } }, list: async (root, directory, maximumEntries = 256) => { const result = await names(root, directory); if (result.length > maximumEntries) throw new Error("fixture list overflow"); return result.map((name) => ({ name, modifiedUnixMs: lstatSync(join(root, directory, name)).mtimeMs })); }, listPage: async (root, directory, afterName, maximumEntries) => { if (directory !== "sessions") throw new Error("fixture directory invalid"); const selected = (await names(root, directory)).filter((name) => afterName === undefined || name > afterName); return { entries: selected.slice(0, maximumEntries).map((name) => ({ name, modifiedUnixMs: lstatSync(join(root, directory, name)).mtimeMs, })), more: selected.length > maximumEntries, }; }, claimConsume: async (root, filename) => { const source = await path(root, "oidc", filename); const claim = source.replace(/\.json$/, ".claim"); try { linkSync(source, claim); } catch (error) { if (missing(error) || (error as { code?: unknown })?.code === "EEXIST") return undefined; throw error; } const contents = readFileSync(source); unlinkSync(source); unlinkSync(claim); return contents; }, readClaim: async (root, filename) => { const source = await path(root, "oidc", filename); const claim = source.replace(/\.json$/, ".claim"); try { const left = lstatSync(source); const right = lstatSync(claim); if (left.ino !== right.ino || left.dev !== right.dev || left.nlink !== 2 || right.nlink !== 2) return undefined; return readFileSync(source); } catch (error) { if (missing(error)) return undefined; throw error; } }, removeClaim: async (root, filename) => { const source = await path(root, "oidc", filename); const claim = source.replace(/\.json$/, ".claim"); try { const left = lstatSync(source); const right = lstatSync(claim); if (left.ino !== right.ino || left.dev !== right.dev || left.nlink !== 2 || right.nlink !== 2) return false; unlinkSync(source); unlinkSync(claim); return true; } catch (error) { if (missing(error)) return false; throw error; } }, }; }