import Fastify from "fastify"; import { afterAll, beforeEach, describe, expect, it } from "vitest"; import { registerAdminPlugin } from "../../src/admin/plugin.js"; import { ProviderConnectionService } from "../../src/connections/providerConnections.js"; import { mintSessionToken, sessionCookieHeader } from "../../src/admin/routes/authRoutes.js"; import { DEFAULT_ORG_ID, prisma, resetDb, testSecretEnvelope } from "./helpers.js"; const SESSION_SECRET = "integration-test-session-secret"; const secrets = testSecretEnvelope; async function buildApp() { const app = Fastify({ logger: false }); await registerAdminPlugin(app, { prisma, sessionSecret: SESSION_SECRET, publicBaseUrl: "http://127.0.0.1:8788", feishuAppId: "cli_test", feishuAppSecret: "secret_test", projectWorkspaceRoot: "/tmp/cph-test-workspaces", secretEnvelope: secrets, providerReadinessProbe: async () => {}, cookieSecure: false, }); await app.ready(); return app; } async function seedUser(role: "OWNER" | "ADMIN" | "MEMBER"): Promise { const id = `u-${role.toLowerCase()}`; const openId = `ou_${role.toLowerCase()}`; await prisma.user.create({ data: { id, feishuOpenId: openId, displayName: id } }); await prisma.organizationMembership.create({ data: { organizationId: DEFAULT_ORG_ID, userId: id, role }, }); return mintSessionToken({ userId: id, feishuOpenId: openId }, SESSION_SECRET); } describe("Organization BYOK provider connections", () => { beforeEach(async () => { await resetDb(); }); afterAll(async () => { await prisma.$disconnect(); }); it("stores write-only credentials in an encrypted immutable version", async () => { const token = await seedUser("OWNER"); const app = await buildApp(); const credential = { baseUrl: "https://provider.example/api", authToken: "org-a-auth-token-never-return", anthropicApiKey: "org-a-api-key-never-return", }; try { const response = await app.inject({ method: "PUT", url: "/api/org/test-default/provider-connections/openrouter", headers: { cookie: sessionCookieHeader(token), "content-type": "application/json", }, payload: credential, }); expect(response.statusCode).toBe(201); expect(response.json()).toMatchObject({ providerId: "openrouter", mode: "BYOK", status: "ACTIVE", activeVersion: 1, keyId: "test-active", }); expect(response.body).not.toContain(credential.authToken); expect(response.body).not.toContain(credential.anthropicApiKey); expect(response.body).not.toContain(credential.baseUrl); const persisted = await prisma.$queryRaw>` SELECT v."envelope", c."activeSecretVersionId" FROM "ProviderCredentialVersion" v JOIN "OrganizationProviderConnection" c ON c."id" = v."connectionId" WHERE c."organizationId" = ${DEFAULT_ORG_ID} AND c."providerId" = 'openrouter' `; expect(persisted).toHaveLength(1); const serialized = JSON.stringify(persisted); expect(serialized).not.toContain(credential.authToken); expect(serialized).not.toContain(credential.anthropicApiKey); expect(serialized).not.toContain(credential.baseUrl); expect(persisted[0]?.activeSecretVersionId).toBeTruthy(); } finally { await app.close(); } }); it("rotates by appending a version and never exposes old or new plaintext", async () => { const token = await seedUser("ADMIN"); const app = await buildApp(); const cookie = sessionCookieHeader(token); try { const first = await app.inject({ method: "PUT", url: "/api/org/test-default/provider-connections/openrouter", headers: { cookie, "content-type": "application/json" }, payload: { baseUrl: "https://one.example", authToken: "first-secret" }, }); const second = await app.inject({ method: "PUT", url: "/api/org/test-default/provider-connections/openrouter", headers: { cookie, "content-type": "application/json" }, payload: { baseUrl: "https://two.example", authToken: "second-secret" }, }); expect(first.statusCode).toBe(201); expect(second.statusCode).toBe(200); expect(second.json()).toMatchObject({ activeVersion: 2, status: "ACTIVE" }); const versions = await prisma.$queryRaw>` SELECT v."version", v."retiredAt" FROM "ProviderCredentialVersion" v JOIN "OrganizationProviderConnection" c ON c."id" = v."connectionId" WHERE c."organizationId" = ${DEFAULT_ORG_ID} ORDER BY v."version" `; expect(versions).toEqual([ { version: 1, retiredAt: expect.any(Date) }, { version: 2, retiredAt: null }, ]); const read = await app.inject({ method: "GET", url: "/api/org/test-default/provider-connections", headers: { cookie }, }); expect(read.statusCode).toBe(200); expect(read.json()).toEqual({ connections: [expect.objectContaining({ activeVersion: 2 })] }); expect(read.body).not.toContain("first-secret"); expect(read.body).not.toContain("second-secret"); } finally { await app.close(); } }); it("rejects members and refuses to overwrite a platform-managed connection", async () => { const memberToken = await seedUser("MEMBER"); const app = await buildApp(); try { const denied = await app.inject({ method: "PUT", url: "/api/org/test-default/provider-connections/openrouter", headers: { cookie: sessionCookieHeader(memberToken), "content-type": "application/json", }, payload: { baseUrl: "https://provider.example", authToken: "denied-secret" }, }); expect(denied.statusCode).toBe(403); const service = new ProviderConnectionService(prisma, secrets, async () => {}); await expect(service.rotateByok({ organizationId: DEFAULT_ORG_ID, providerId: "another-provider", actorUserId: "u-member", baseUrl: "https://provider.example", authToken: "service-bypass-secret", })).rejects.toThrow("requires an active Organization OWNER or ADMIN"); await expect(prisma.organizationProviderConnection.count({ where: { organizationId: DEFAULT_ORG_ID, providerId: "another-provider" }, })).resolves.toBe(0); await prisma.$executeRaw` INSERT INTO "OrganizationProviderConnection" ("id", "organizationId", "providerId", "mode", "status", "createdAt", "updatedAt") VALUES ('platform-connection', ${DEFAULT_ORG_ID}, 'openrouter', 'PLATFORM_MANAGED', 'DRAFT', NOW(), NOW()) `; const ownerToken = await seedUser("OWNER"); const refused = await app.inject({ method: "PUT", url: "/api/org/test-default/provider-connections/openrouter", headers: { cookie: sessionCookieHeader(ownerToken), "content-type": "application/json", }, payload: { baseUrl: "https://provider.example", authToken: "takeover-secret" }, }); expect(refused.statusCode).toBe(403); expect(refused.json()).toMatchObject({ error: { code: "forbidden" }, }); } finally { await app.close(); } }); it("does not activate or persist a credential that fails readiness", async () => { await seedUser("ADMIN"); const service = new ProviderConnectionService(prisma, secrets, async () => { throw new Error("provider credential readiness check failed: status 401"); }); await expect(service.rotateByok({ organizationId: DEFAULT_ORG_ID, providerId: "openrouter", actorUserId: "u-admin", baseUrl: "https://provider.example/api", authToken: "rejected-secret", })).rejects.toThrow("readiness check failed"); await expect(prisma.organizationProviderConnection.count({ where: { organizationId: DEFAULT_ORG_ID, providerId: "openrouter" }, })).resolves.toBe(0); await expect(prisma.providerCredentialVersion.count()).resolves.toBe(0); }); });