forked from EduCraft/curriculum-project-hub
feat: secure organization provider credentials
This commit is contained in:
@@ -4,6 +4,15 @@ import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
DEFAULT_ORG_ID,
|
||||
prisma,
|
||||
resetDb,
|
||||
testSecretEnvelope,
|
||||
TEST_SECRET_KEY,
|
||||
TEST_SECRET_KEY_ID,
|
||||
} from "./helpers.js";
|
||||
import { ProviderConnectionService } from "../../src/connections/providerConnections.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const TEST_DATABASE_URL = "postgresql://paradigm:paradigm@127.0.0.1:5432/cph_hub_test";
|
||||
@@ -16,6 +25,7 @@ describe("deployment preflight CLI", () => {
|
||||
let marker: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await resetDb();
|
||||
root = await mkdtemp(join(tmpdir(), "cph-deployment-preflight-"));
|
||||
baseDir = join(root, "deploy");
|
||||
hubDir = join(baseDir, "current", "hub");
|
||||
@@ -93,6 +103,23 @@ describe("deployment preflight CLI", () => {
|
||||
expect(result.stderr).toContain("PostgreSQL authenticated query failed for DATABASE_URL");
|
||||
});
|
||||
|
||||
it("rejects a structurally valid keyring that cannot authenticate stored envelopes", async () => {
|
||||
await prismaActorForEnvelope();
|
||||
const workspaceRoot = join(persistentDir, "workspaces");
|
||||
const fixture = await createFixture({
|
||||
workspaceRoot,
|
||||
marker,
|
||||
keyringKey: Buffer.alloc(32, "wrong"),
|
||||
});
|
||||
|
||||
const result = await runCli(fixture.args);
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.stderr).toContain("stored provider envelope verification failed");
|
||||
expect(result.stderr).toContain("secret envelope authentication failed");
|
||||
expect(result.stderr).not.toContain("stored-envelope-secret");
|
||||
});
|
||||
|
||||
it("loads the actual Hub dependency graph and queries PostgreSQL in the runtime probe", async () => {
|
||||
const result = await execFileAsync(
|
||||
resolve("node_modules/.bin/tsx"),
|
||||
@@ -138,6 +165,7 @@ describe("deployment preflight CLI", () => {
|
||||
marker: string;
|
||||
pgIsReadyScript?: string;
|
||||
databaseUrl?: string;
|
||||
keyringKey?: Buffer;
|
||||
}): Promise<{ args: string[] }> {
|
||||
const binDir = join(root, "bin");
|
||||
const cphBin = join(binDir, "cph");
|
||||
@@ -147,6 +175,7 @@ describe("deployment preflight CLI", () => {
|
||||
const runuserBin = join(binDir, "runuser");
|
||||
const setprivBin = join(binDir, "setpriv");
|
||||
const envFile = join(root, "platform.env");
|
||||
const keyringFile = join(root, "secret-keyring.json");
|
||||
await Promise.all([
|
||||
mkdir(binDir, { recursive: true }),
|
||||
mkdir(join(hubDir, "dist"), { recursive: true }),
|
||||
@@ -180,15 +209,17 @@ describe("deployment preflight CLI", () => {
|
||||
`const { appendFileSync } = require("node:fs");\nappendFileSync(${JSON.stringify(options.marker)}, "service-prisma\\n");\n`,
|
||||
),
|
||||
writeFile(join(hubDir, "prisma", "schema.prisma"), "// fixture\n"),
|
||||
writeFile(keyringFile, JSON.stringify({
|
||||
version: 1,
|
||||
activeKeyId: TEST_SECRET_KEY_ID,
|
||||
keys: { [TEST_SECRET_KEY_ID]: (options.keyringKey ?? TEST_SECRET_KEY).toString("base64") },
|
||||
}), { mode: 0o600 }),
|
||||
]);
|
||||
await writeFile(
|
||||
envFile,
|
||||
[
|
||||
"NODE_ENV=production",
|
||||
`DATABASE_URL=${options.databaseUrl ?? TEST_DATABASE_URL}`,
|
||||
"ANTHROPIC_BASE_URL=https://openrouter.ai/api",
|
||||
"ANTHROPIC_AUTH_TOKEN=provider-token",
|
||||
"ANTHROPIC_API_KEY=",
|
||||
"FEISHU_APP_ID=cli_app_id",
|
||||
"FEISHU_APP_SECRET=feishu-app-secret",
|
||||
"FEISHU_BOT_OPEN_ID=ou_bot",
|
||||
@@ -207,6 +238,8 @@ describe("deployment preflight CLI", () => {
|
||||
args: [
|
||||
"--env-file",
|
||||
envFile,
|
||||
"--keyring-file",
|
||||
keyringFile,
|
||||
"--node-bin",
|
||||
process.execPath,
|
||||
"--runuser-bin",
|
||||
@@ -236,6 +269,20 @@ describe("deployment preflight CLI", () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async function prismaActorForEnvelope(): Promise<void> {
|
||||
await prisma.user.create({ data: { id: "preflight-admin", feishuOpenId: "ou_preflight", displayName: "Preflight" } });
|
||||
await prisma.organizationMembership.create({
|
||||
data: { organizationId: DEFAULT_ORG_ID, userId: "preflight-admin", role: "ADMIN" },
|
||||
});
|
||||
await new ProviderConnectionService(prisma, testSecretEnvelope, async () => {}).rotateByok({
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
providerId: "openrouter",
|
||||
actorUserId: "preflight-admin",
|
||||
baseUrl: "https://provider.example/api",
|
||||
authToken: "stored-envelope-secret",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function executable(path: string, contents: string): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user