mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 19:02:17 +03:00
Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.
One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.
Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.
514 lines
17 KiB
TypeScript
514 lines
17 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { SignJWT } from "jose";
|
|
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-api-auth-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
process.env.API_KEY_SECRET = "test-api-key-secret";
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
const localDb = await import("../../src/lib/localDb.ts");
|
|
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
|
|
const apiAuth = await import("../../src/shared/utils/apiAuth.ts");
|
|
const { requireManagementAuth } = await import("../../src/lib/api/requireManagementAuth.ts");
|
|
const { getLegacyCliTokenSync, getMachineTokenSync } =
|
|
await import("../../src/lib/machineToken.ts");
|
|
const { CLI_TOKEN_HEADER } = await import("../../src/server/authz/headers.ts");
|
|
|
|
const ORIGINAL_JWT_SECRET = process.env.JWT_SECRET;
|
|
const ORIGINAL_INITIAL_PASSWORD = process.env.INITIAL_PASSWORD;
|
|
|
|
async function resetStorage() {
|
|
core.resetDbInstance();
|
|
apiKeysDb.resetApiKeyState();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
|
delete process.env.JWT_SECRET;
|
|
delete process.env.INITIAL_PASSWORD;
|
|
}
|
|
|
|
function makeCookieRequest(token: string) {
|
|
return {
|
|
cookies: {
|
|
get(name: string) {
|
|
return name === "auth_token" && token ? { value: token } : undefined;
|
|
},
|
|
},
|
|
headers: new Headers(),
|
|
};
|
|
}
|
|
|
|
test.beforeEach(async () => {
|
|
await resetStorage();
|
|
});
|
|
|
|
test.after(() => {
|
|
core.resetDbInstance();
|
|
apiKeysDb.resetApiKeyState();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
|
|
if (ORIGINAL_JWT_SECRET === undefined) {
|
|
delete process.env.JWT_SECRET;
|
|
} else {
|
|
process.env.JWT_SECRET = ORIGINAL_JWT_SECRET;
|
|
}
|
|
|
|
if (ORIGINAL_INITIAL_PASSWORD === undefined) {
|
|
delete process.env.INITIAL_PASSWORD;
|
|
} else {
|
|
process.env.INITIAL_PASSWORD = ORIGINAL_INITIAL_PASSWORD;
|
|
}
|
|
});
|
|
|
|
test("isPublicRoute recognizes allowed API prefixes", () => {
|
|
assert.equal(apiAuth.isPublicRoute("/api/auth/login"), true);
|
|
assert.equal(apiAuth.isPublicRoute("/api/v1/chat/completions"), true);
|
|
assert.equal(apiAuth.isPublicRoute("/api/sync/bundle"), true);
|
|
assert.equal(apiAuth.isPublicRoute("/api/monitoring/health", "GET"), true);
|
|
assert.equal(apiAuth.isPublicRoute("/api/monitoring/health", "DELETE"), false);
|
|
assert.equal(apiAuth.isPublicRoute("/api/settings"), false);
|
|
});
|
|
|
|
test("verifyAuth accepts a valid JWT session cookie", async () => {
|
|
process.env.JWT_SECRET = "jwt-secret-for-tests";
|
|
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
|
|
const token = await new SignJWT({ authenticated: true })
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setIssuedAt()
|
|
.setExpirationTime("1h")
|
|
.sign(secret);
|
|
|
|
const result = await apiAuth.verifyAuth(makeCookieRequest(token));
|
|
|
|
assert.equal(result, null);
|
|
});
|
|
|
|
test("verifyAuth falls back to bearer API key validation after a bad JWT", async () => {
|
|
process.env.JWT_SECRET = "jwt-secret-for-tests";
|
|
const key = await apiKeysDb.createApiKey("integration", "machine1234567890");
|
|
const request = {
|
|
cookies: {
|
|
get() {
|
|
return { value: "definitely-not-a-valid-jwt" };
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: `Bearer ${key.key}` }),
|
|
url: "https://example.com/api/v1/models",
|
|
};
|
|
|
|
const result = await apiAuth.verifyAuth(request);
|
|
|
|
assert.equal(result, null);
|
|
});
|
|
|
|
test("verifyAuth no longer accepts API keys supplied via query string (#3300 follow-up)", async () => {
|
|
// Query-string token fallbacks were removed (credential-in-URL leaks into logs).
|
|
const key = await apiKeysDb.createApiKey("query-auth", "machine1234567890");
|
|
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers(),
|
|
url: `https://example.com/api/v1/models?token=${encodeURIComponent(key.key)}`,
|
|
});
|
|
|
|
// No usable credential → authentication fails (was incorrectly accepted before).
|
|
assert.notEqual(result, null);
|
|
});
|
|
|
|
test("isAuthenticated accepts API keys embedded in vscode path aliases", async () => {
|
|
const key = await apiKeysDb.createApiKey("path-auth", "machine1234567890");
|
|
const request = new Request(
|
|
`https://example.com/api/v1/vscode/${encodeURIComponent(key.key)}/models`
|
|
);
|
|
|
|
const result = await apiAuth.isAuthenticated(request);
|
|
|
|
assert.equal(result, true);
|
|
});
|
|
|
|
test("verifyAuth never honours a URL-borne token on MANAGEMENT routes (#3300 follow-up)", async () => {
|
|
// The historical escalation: a credential in the query string on a management
|
|
// route (/api/* but not /api/v1/*). It must not be extracted at all, so the
|
|
// failure is "Authentication required" (no credential) — NOT "Invalid
|
|
// management token" (which the pre-fix code returned, proving the URL token
|
|
// had been picked up and tried against management validation).
|
|
const key = await apiKeysDb.createApiKey("mgmt-url", "machine1234567890");
|
|
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers(),
|
|
url: `https://example.com/api/providers?token=${encodeURIComponent(key.key)}`,
|
|
});
|
|
|
|
assert.equal(result, "Authentication required");
|
|
});
|
|
|
|
test("verifyAuth rejects bearer API keys on management routes", async () => {
|
|
const key = await apiKeysDb.createApiKey("integration", "machine1234567890");
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: `Bearer ${key.key}` }),
|
|
url: "https://example.com/api/providers",
|
|
});
|
|
|
|
assert.equal(result, "Invalid management token");
|
|
});
|
|
|
|
test("verifyAuth rejects requests without valid credentials", async () => {
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: "Bearer sk-invalid" }),
|
|
url: "https://example.com/api/v1/models",
|
|
});
|
|
|
|
assert.equal(result, "Authentication required");
|
|
});
|
|
|
|
test("isAuthenticated accepts bearer API keys on client-facing routes", async () => {
|
|
const key = await apiKeysDb.createApiKey("integration", "machine1234567890");
|
|
const request = new Request("https://example.com/api/v1/models", {
|
|
headers: { authorization: `Bearer ${key.key}` },
|
|
});
|
|
|
|
const result = await apiAuth.isAuthenticated(request);
|
|
|
|
assert.equal(result, true);
|
|
});
|
|
|
|
test("isAuthenticated rejects bearer API keys on management routes", async () => {
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const key = await apiKeysDb.createApiKey("integration", "machine1234567890");
|
|
const request = new Request("https://example.com/api/providers", {
|
|
headers: { authorization: `Bearer ${key.key}` },
|
|
});
|
|
|
|
const result = await apiAuth.isAuthenticated(request);
|
|
|
|
assert.equal(result, false);
|
|
});
|
|
|
|
test("verifyAuth accepts bearer API keys with manage scope on management routes", async () => {
|
|
const key = await apiKeysDb.createApiKey("mcp-management", "machine1234567890", ["manage"]);
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: `Bearer ${key.key}` }),
|
|
url: "https://example.com/api/providers",
|
|
});
|
|
|
|
assert.equal(result, null);
|
|
});
|
|
|
|
test("verifyAuth accepts bearer API keys with admin scope on management routes", async () => {
|
|
const key = await apiKeysDb.createApiKey("mcp-admin", "machine1234567890", ["admin"]);
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: `Bearer ${key.key}` }),
|
|
url: "https://example.com/api/settings",
|
|
});
|
|
|
|
assert.equal(result, null);
|
|
});
|
|
|
|
test("verifyAuth still rejects unscoped bearer API keys on management routes", async () => {
|
|
const key = await apiKeysDb.createApiKey("integration-no-scope", "machine1234567890");
|
|
const result = await apiAuth.verifyAuth({
|
|
cookies: {
|
|
get() {
|
|
return undefined;
|
|
},
|
|
},
|
|
headers: new Headers({ authorization: `Bearer ${key.key}` }),
|
|
url: "https://example.com/api/providers",
|
|
});
|
|
|
|
assert.equal(result, "Invalid management token");
|
|
});
|
|
|
|
test("isAuthenticated accepts bearer API keys with manage scope on management routes", async () => {
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const key = await apiKeysDb.createApiKey("mcp-management", "machine1234567890", ["manage"]);
|
|
const request = new Request("https://example.com/api/providers", {
|
|
headers: { authorization: `Bearer ${key.key}` },
|
|
});
|
|
|
|
const result = await apiAuth.isAuthenticated(request);
|
|
|
|
assert.equal(result, true);
|
|
});
|
|
|
|
test("monitoring health reset route requires dashboard authentication", async () => {
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const healthRoute = await import("../../src/app/api/monitoring/health/route.ts");
|
|
const response = await healthRoute.DELETE(
|
|
new Request("https://example.com/api/monitoring/health", { method: "DELETE" })
|
|
);
|
|
|
|
assert.equal(response.status, 401);
|
|
});
|
|
|
|
test("isAuthenticated returns false when auth is required without valid credentials", async () => {
|
|
// Force requireLogin to be active
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const request = new Request("https://example.com/api/providers");
|
|
|
|
const result = await apiAuth.isAuthenticated(request);
|
|
|
|
assert.equal(result, false);
|
|
});
|
|
|
|
test("isAuthRequired is disabled when requireLogin is false", async () => {
|
|
await localDb.updateSettings({ requireLogin: false });
|
|
|
|
const result = await apiAuth.isAuthRequired();
|
|
|
|
assert.equal(result, false);
|
|
});
|
|
|
|
test("isAuthRequired is disabled while no password exists", async () => {
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const result = await apiAuth.isAuthRequired();
|
|
|
|
assert.equal(result, false);
|
|
});
|
|
|
|
test("isAuthRequired keeps fresh bootstrap open only on loopback", async () => {
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
assert.equal(await apiAuth.isAuthRequired(new Request("http://localhost/api/providers")), false);
|
|
assert.equal(await apiAuth.isAuthRequired(new Request("http://127.0.0.1/api/providers")), false);
|
|
assert.equal(
|
|
await apiAuth.isAuthRequired(new Request("https://example.com/api/providers")),
|
|
true
|
|
);
|
|
});
|
|
|
|
test("isAuthenticated rejects remote management bootstrap without a configured password", async () => {
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const request = new Request("https://example.com/api/providers");
|
|
|
|
assert.equal(await apiAuth.isAuthenticated(request), false);
|
|
});
|
|
|
|
test("isAuthRequired stays enabled when a password exists", async () => {
|
|
await localDb.updateSettings({ requireLogin: true, password: "hashed-password" });
|
|
|
|
const result = await apiAuth.isAuthRequired();
|
|
|
|
assert.equal(result, true);
|
|
});
|
|
|
|
test("isAuthRequired stays enabled when INITIAL_PASSWORD is present", async () => {
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
|
|
const result = await apiAuth.isAuthRequired();
|
|
|
|
assert.equal(result, true);
|
|
|
|
delete process.env.INITIAL_PASSWORD;
|
|
});
|
|
test("isAuthRequired stays enabled when OIDC is fully configured (replaces password for gate)", async () => {
|
|
await localDb.updateSettings({
|
|
requireLogin: true,
|
|
password: "",
|
|
oidcEnabled: true,
|
|
oidcIssuer: "https://idp.example.com",
|
|
oidcClientId: "client-123",
|
|
oidcClientSecret: "secret-xyz",
|
|
});
|
|
|
|
const result = await apiAuth.isAuthRequired();
|
|
assert.equal(result, true);
|
|
});
|
|
|
|
test("isAuthRequired treats partial OIDC config as not configured (bootstrap behavior preserved)", async () => {
|
|
await localDb.updateSettings({
|
|
requireLogin: true,
|
|
password: "",
|
|
oidcEnabled: true,
|
|
oidcIssuer: "https://idp.example.com",
|
|
// missing clientId + clientSecret
|
|
});
|
|
|
|
// On loopback without full config → bootstrap allowed
|
|
assert.equal(await apiAuth.isAuthRequired(new Request("http://localhost/api/providers")), false);
|
|
// Remote still requires auth
|
|
assert.equal(
|
|
await apiAuth.isAuthRequired(new Request("https://example.com/api/providers")),
|
|
true
|
|
);
|
|
});
|
|
|
|
test("getApiKeyMetadata recognizes OMNIROUTE_API_KEY environment variable", async () => {
|
|
const envKey = "sk-test-env-key-" + Date.now();
|
|
process.env.OMNIROUTE_API_KEY = envKey;
|
|
|
|
const metadata = await apiKeysDb.getApiKeyMetadata(envKey);
|
|
|
|
assert.ok(metadata);
|
|
assert.equal(metadata.id, "env-key");
|
|
assert.equal(metadata.name, "Environment Key");
|
|
|
|
delete process.env.OMNIROUTE_API_KEY;
|
|
});
|
|
|
|
test("getApiKeyMetadata recognizes ROUTER_API_KEY environment variable", async () => {
|
|
const envKey = "sk-test-router-key-" + Date.now();
|
|
process.env.ROUTER_API_KEY = envKey;
|
|
|
|
const metadata = await apiKeysDb.getApiKeyMetadata(envKey);
|
|
|
|
assert.ok(metadata);
|
|
assert.equal(metadata.id, "env-key");
|
|
|
|
delete process.env.ROUTER_API_KEY;
|
|
});
|
|
|
|
// ──── requireManagementAuth ────
|
|
|
|
async function setupAuth() {
|
|
process.env.INITIAL_PASSWORD = "bootstrap-password";
|
|
await localDb.updateSettings({ requireLogin: true, password: "" });
|
|
}
|
|
|
|
function managementRequest(bearerKey?: string) {
|
|
return new Request("https://example.com/api/combos", {
|
|
headers: bearerKey ? { authorization: `Bearer ${bearerKey}` } : {},
|
|
});
|
|
}
|
|
|
|
function managementCliRequest(token: string) {
|
|
const request = new Request("http://localhost:20128/api/combos", {
|
|
headers: { [CLI_TOKEN_HEADER]: token },
|
|
});
|
|
return Object.assign(request, { ip: "127.0.0.1" }) as Request;
|
|
}
|
|
|
|
test("requireManagementAuth returns 401 with no credentials", async () => {
|
|
await setupAuth();
|
|
const res = await requireManagementAuth(managementRequest());
|
|
assert.ok(res);
|
|
assert.equal(res.status, 401);
|
|
});
|
|
|
|
test("requireManagementAuth returns 403 for an invalid management token", async () => {
|
|
await setupAuth();
|
|
const res = await requireManagementAuth(managementRequest("sk-not-a-real-key"));
|
|
assert.ok(res);
|
|
assert.equal(res.status, 403);
|
|
const body = await res.json();
|
|
assert.equal(body.error?.message, "Invalid management token");
|
|
});
|
|
|
|
test("requireManagementAuth returns 403 for valid key without manage scope", async () => {
|
|
await setupAuth();
|
|
const key = await apiKeysDb.createApiKey("inference-only", "machine-test");
|
|
const res = await requireManagementAuth(managementRequest(key.key));
|
|
assert.ok(res);
|
|
assert.equal(res.status, 403);
|
|
const body = await res.json();
|
|
assert.ok(body.error?.message?.includes("manage"));
|
|
});
|
|
|
|
test("requireManagementAuth returns null for valid key with manage scope", async () => {
|
|
await setupAuth();
|
|
const key = await apiKeysDb.createApiKey("admin-key", "machine-test", ["manage"]);
|
|
const res = await requireManagementAuth(managementRequest(key.key));
|
|
assert.equal(res, null);
|
|
});
|
|
|
|
test("requireManagementAuth accepts the 64-character local machine token", async () => {
|
|
await setupAuth();
|
|
const token = getMachineTokenSync();
|
|
assert.equal(token.length, 64);
|
|
const res = await requireManagementAuth(managementCliRequest(token));
|
|
assert.equal(res, null);
|
|
});
|
|
|
|
test("requireManagementAuth accepts the legacy 32-character local CLI token", async () => {
|
|
await setupAuth();
|
|
const token = getLegacyCliTokenSync();
|
|
assert.equal(token.length, 32);
|
|
const res = await requireManagementAuth(managementCliRequest(token));
|
|
assert.equal(res, null);
|
|
});
|
|
|
|
test("requireManagementAuth returns null for OMNIROUTE_API_KEY env passthrough", async () => {
|
|
await setupAuth();
|
|
const envKey = "sk-env-root-" + Date.now();
|
|
process.env.OMNIROUTE_API_KEY = envKey;
|
|
try {
|
|
const res = await requireManagementAuth(managementRequest(envKey));
|
|
assert.equal(res, null);
|
|
} finally {
|
|
delete process.env.OMNIROUTE_API_KEY;
|
|
}
|
|
});
|
|
|
|
test("requireManagementAuth returns 403 for revoked key with manage scope", async () => {
|
|
await setupAuth();
|
|
const key = await apiKeysDb.createApiKey("revoked-admin", "machine-test", ["manage"]);
|
|
await apiKeysDb.revokeApiKey(key.id);
|
|
const res = await requireManagementAuth(managementRequest(key.key));
|
|
assert.ok(res);
|
|
assert.equal(res.status, 403);
|
|
const body = await res.json();
|
|
assert.equal(body.error?.message, "Invalid management token");
|
|
});
|
|
|
|
test("requireManagementAuth returns null for valid JWT cookie", async () => {
|
|
await setupAuth();
|
|
process.env.JWT_SECRET = "jwt-secret-for-tests";
|
|
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
|
|
const token = await new SignJWT({ authenticated: true })
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setIssuedAt()
|
|
.setExpirationTime("1h")
|
|
.sign(secret);
|
|
|
|
const request = {
|
|
cookies: { get: (name: string) => (name === "auth_token" ? { value: token } : undefined) },
|
|
headers: new Headers(),
|
|
url: "https://example.com/api/combos",
|
|
};
|
|
const res = await requireManagementAuth(request as unknown as Request);
|
|
assert.equal(res, null);
|
|
});
|