forked from bai/curriculum-project-hub
feat: secure organization provider credentials
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import { afterAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { ProviderConnectionService } from "../../src/connections/providerConnections.js";
|
||||
import { DatabaseRuntimeSettings } from "../../src/settings/runtime.js";
|
||||
import type { ProviderUpstreamCredential } from "../../src/connections/providerProxy.js";
|
||||
import { DEFAULT_ORG_ID, prisma, resetDb, seedTestOrganization, testSecretEnvelope } from "./helpers.js";
|
||||
|
||||
const secrets = testSecretEnvelope;
|
||||
|
||||
describe("project-scoped provider runtime settings", () => {
|
||||
beforeEach(async () => {
|
||||
await resetDb();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
it("resolves two projects only through their own Organization connection", async () => {
|
||||
await seedTestOrganization("org_other", "other");
|
||||
await Promise.all([
|
||||
prisma.user.create({ data: { id: "user-a", feishuOpenId: "ou_a", displayName: "A" } }),
|
||||
prisma.user.create({ data: { id: "user-b", feishuOpenId: "ou_b", displayName: "B" } }),
|
||||
prisma.project.create({
|
||||
data: {
|
||||
id: "project-a",
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
name: "A",
|
||||
workspaceDir: "/tmp/project-a",
|
||||
},
|
||||
}),
|
||||
prisma.project.create({
|
||||
data: {
|
||||
id: "project-b",
|
||||
organizationId: "org_other",
|
||||
name: "B",
|
||||
workspaceDir: "/tmp/project-b",
|
||||
},
|
||||
}),
|
||||
]);
|
||||
await Promise.all([
|
||||
prisma.organizationMembership.create({
|
||||
data: { organizationId: DEFAULT_ORG_ID, userId: "user-a", role: "ADMIN" },
|
||||
}),
|
||||
prisma.organizationMembership.create({
|
||||
data: { organizationId: "org_other", userId: "user-b", role: "OWNER" },
|
||||
}),
|
||||
]);
|
||||
const service = new ProviderConnectionService(prisma, secrets, async () => {});
|
||||
await service.rotateByok({
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
providerId: "openrouter",
|
||||
actorUserId: "user-a",
|
||||
baseUrl: "https://provider-a.example/api",
|
||||
authToken: "provider-a-token",
|
||||
anthropicApiKey: "provider-a-api-key",
|
||||
});
|
||||
await service.rotateByok({
|
||||
organizationId: "org_other",
|
||||
providerId: "openrouter",
|
||||
actorUserId: "user-b",
|
||||
baseUrl: "https://provider-b.example/api",
|
||||
authToken: "provider-b-token",
|
||||
});
|
||||
const leasedCredentials: ProviderUpstreamCredential[] = [];
|
||||
const settings = new DatabaseRuntimeSettings(prisma, secrets, {
|
||||
ANTHROPIC_AUTH_TOKEN: "forbidden-global-token",
|
||||
ANTHROPIC_BASE_URL: "https://forbidden-global.example",
|
||||
}, async (credential) => {
|
||||
leasedCredentials.push(credential);
|
||||
return {
|
||||
sdkEnv: {
|
||||
ANTHROPIC_BASE_URL: "http://127.0.0.1:12345",
|
||||
ANTHROPIC_AUTH_TOKEN: `run-capability-${leasedCredentials.length}`,
|
||||
ANTHROPIC_API_KEY: "",
|
||||
},
|
||||
sensitiveValues: [`run-capability-${leasedCredentials.length}`],
|
||||
async close() {},
|
||||
};
|
||||
});
|
||||
|
||||
const providerA = await settings.provider("openrouter", { projectId: "project-a" });
|
||||
const providerB = await settings.provider("openrouter", { projectId: "project-b" });
|
||||
expect(JSON.stringify([providerA, providerB])).not.toContain("provider-a-token");
|
||||
expect(JSON.stringify([providerA, providerB])).not.toContain("provider-b-token");
|
||||
|
||||
const leaseA = await providerA.openAgentLease({ runId: "run-a" });
|
||||
const leaseB = await providerB.openAgentLease({ runId: "run-b" });
|
||||
expect(leaseA.sdkEnv).toMatchObject({
|
||||
ANTHROPIC_BASE_URL: "http://127.0.0.1:12345",
|
||||
ANTHROPIC_AUTH_TOKEN: "run-capability-1",
|
||||
ANTHROPIC_API_KEY: "",
|
||||
});
|
||||
expect(leaseB.sdkEnv.ANTHROPIC_AUTH_TOKEN).toBe("run-capability-2");
|
||||
expect(leasedCredentials).toEqual([
|
||||
{
|
||||
baseUrl: "https://provider-a.example/api",
|
||||
authToken: "provider-a-token",
|
||||
anthropicApiKey: "provider-a-api-key",
|
||||
},
|
||||
{
|
||||
baseUrl: "https://provider-b.example/api",
|
||||
authToken: "provider-b-token",
|
||||
anthropicApiKey: "",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("fails closed without project scope or for missing, inactive, and corrupt connections", async () => {
|
||||
await prisma.user.create({ data: { id: "user-a", feishuOpenId: "ou_a", displayName: "A" } });
|
||||
await prisma.organizationMembership.create({
|
||||
data: { organizationId: DEFAULT_ORG_ID, userId: "user-a", role: "ADMIN" },
|
||||
});
|
||||
await prisma.project.create({
|
||||
data: {
|
||||
id: "project-a",
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
name: "A",
|
||||
workspaceDir: "/tmp/project-a",
|
||||
},
|
||||
});
|
||||
const settings = new DatabaseRuntimeSettings(prisma, secrets, {
|
||||
ANTHROPIC_AUTH_TOKEN: "must-not-be-a-fallback",
|
||||
});
|
||||
|
||||
await expect(settings.provider("openrouter")).rejects.toThrow("projectId is required");
|
||||
await expect(settings.provider("openrouter", { projectId: "project-a" })).rejects.toThrow(
|
||||
"active provider connection not found",
|
||||
);
|
||||
|
||||
const service = new ProviderConnectionService(prisma, secrets, async () => {});
|
||||
await service.rotateByok({
|
||||
organizationId: DEFAULT_ORG_ID,
|
||||
providerId: "openrouter",
|
||||
actorUserId: "user-a",
|
||||
baseUrl: "https://provider-a.example/api",
|
||||
authToken: "provider-a-token",
|
||||
});
|
||||
await prisma.organization.update({
|
||||
where: { id: DEFAULT_ORG_ID },
|
||||
data: { status: "SUSPENDED" },
|
||||
});
|
||||
await expect(settings.provider("openrouter", { projectId: "project-a" })).rejects.toThrow(
|
||||
`organization ${DEFAULT_ORG_ID} is SUSPENDED`,
|
||||
);
|
||||
|
||||
await prisma.organization.update({ where: { id: DEFAULT_ORG_ID }, data: { status: "ACTIVE" } });
|
||||
const connection = await prisma.organizationProviderConnection.findUniqueOrThrow({
|
||||
where: {
|
||||
organizationId_providerId: { organizationId: DEFAULT_ORG_ID, providerId: "openrouter" },
|
||||
},
|
||||
});
|
||||
const version = await prisma.providerCredentialVersion.findUniqueOrThrow({
|
||||
where: { id: connection.activeSecretVersionId ?? "missing" },
|
||||
});
|
||||
const envelope = structuredClone(version.envelope) as {
|
||||
payload: { ciphertext: string };
|
||||
};
|
||||
const bytes = Buffer.from(envelope.payload.ciphertext, "base64");
|
||||
bytes[0] = (bytes[0] ?? 0) ^ 1;
|
||||
envelope.payload.ciphertext = bytes.toString("base64");
|
||||
await prisma.providerCredentialVersion.update({
|
||||
where: { id: version.id },
|
||||
data: { envelope },
|
||||
});
|
||||
const corruptProvider = await settings.provider("openrouter", { projectId: "project-a" });
|
||||
await expect(corruptProvider.openAgentLease({ runId: "run-corrupt" })).rejects.toThrow(
|
||||
"secret envelope authentication failed",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user