Files
ThothII/backend/test/fixtures/posix-auth-storage-bridge.mts

151 lines
5.3 KiB
TypeScript

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<void> => {
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<string> => {
await ensure(root);
return join(root, directory, filename);
};
const names = async (root: string, directory: WindowsAuthStorageDirectory): Promise<string[]> => {
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;
}
},
};
}