Wire provider vaults into secrets runtime

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-05-07 18:59:05 -05:00
1 parent ef1f56ef4c
commit 57db4863ba
7 files changed
+474 -60

No files matched your search

@@ -81,6 +81,88 @@ describe("awsSecretsManagerProvider", () => {
expect(prepared.providerVersionRef).toBe("aws-version-1");
});
it("creates AWS secrets from selected provider vault config without deployment env fallback", async () => {
delete process.env.PAPERCLIP_SECRETS_AWS_REGION;
delete process.env.AWS_REGION;
delete process.env.AWS_DEFAULT_REGION;
delete process.env.PAPERCLIP_SECRETS_AWS_DEPLOYMENT_ID;
delete process.env.PAPERCLIP_SECRETS_AWS_KMS_KEY_ID;
const calls: Array<{ op: string; input: Record<string, unknown> }> = [];
const provider = createAwsSecretsManagerProvider({
gateway: {
async createSecret(input) {
calls.push({ op: "createSecret", input });
return {
ARN: "arn:aws:secretsmanager:us-west-2:123456789012:secret:clip/prod-us-west/company-1/openai-api-key",
VersionId: "aws-version-1",
};
},
async putSecretValue(input) {
calls.push({ op: "putSecretValue", input });
return { ARN: String(input.SecretId), VersionId: "unused" };
},
async getSecretValue(input) {
calls.push({ op: "getSecretValue", input });
return { SecretString: "resolved-value", VersionId: "unused" };
},
async deleteSecret(input) {
calls.push({ op: "deleteSecret", input });
return {};
},
},
});
const providerConfig = {
id: "vault-1",
provider: "aws_secrets_manager" as const,
status: "ready",
config: {
region: "us-west-2",
namespace: "prod-us-west",
secretNamePrefix: "clip",
ownerTag: "platform",
environmentTag: "production",
},
};
const health = await provider.healthCheck({ providerConfig });
const prepared = await provider.createSecret({
value: "super-secret-value",
providerConfig,
context: {
companyId: "company-1",
secretKey: "openai-api-key",
secretName: "OpenAI API Key",
version: 1,
},
});
expect(health.status).toBe("ok");
expect(health.details).toMatchObject({
region: "us-west-2",
prefix: "clip",
deploymentId: "prod-us-west",
kmsKeyConfigured: false,
});
expect(calls).toEqual([
expect.objectContaining({
op: "createSecret",
input: expect.objectContaining({
Name: "clip/prod-us-west/company-1/openai-api-key",
SecretString: "super-secret-value",
Tags: expect.arrayContaining([
{ Key: "paperclip:provider-owner", Value: "platform" },
{ Key: "paperclip:environment", Value: "production" },
]),
}),
}),
]);
expect(calls[0]?.input).not.toHaveProperty("KmsKeyId");
expect(JSON.stringify(prepared)).not.toContain("super-secret-value");
expect(prepared.externalRef).toContain("clip/prod-us-west/company-1/openai-api-key");
});
it("creates new AWS secret versions against a namespace-valid existing secret reference", async () => {
const calls: Array<{ op: string; input: Record<string, unknown> }> = [];
const provider = createAwsSecretsManagerProvider({
@@ -324,6 +406,22 @@ describe("awsSecretsManagerProvider", () => {
const health = await provider.healthCheck();
expect(health.status).toBe("warn");
expect(health.message).toContain("missing PAPERCLIP_SECRETS_AWS_REGION");
expect(health.warnings).toEqual(
expect.arrayContaining([
expect.stringContaining("Missing required non-secret AWS provider config"),
expect.stringContaining("AWS bootstrap credentials must be available"),
expect.stringContaining("Do not store AWS root credentials"),
]),
);
expect(health.details).toMatchObject({
missingConfig: [
"PAPERCLIP_SECRETS_AWS_REGION or AWS_REGION/AWS_DEFAULT_REGION",
"PAPERCLIP_SECRETS_AWS_DEPLOYMENT_ID",
"PAPERCLIP_SECRETS_AWS_KMS_KEY_ID",
],
credentialSource: "AWS SDK default credential provider chain",
});
await expect(
provider.createSecret({
value: "super-secret-value",
@@ -255,6 +255,72 @@ describeEmbeddedPostgres("secretService", () => {
).rejects.toThrow(/coming soon/i);
});
it("passes selected provider vault config through create, rotate, and resolve", async () => {
const companyId = await seedCompany();
const svc = secretService(db);
const awsVault = await svc.createProviderConfig(companyId, {
provider: "aws_secrets_manager",
displayName: "AWS production",
config: {
region: "us-east-1",
namespace: "prod-use1",
secretNamePrefix: "paperclip",
},
});
const createSpy = vi.spyOn(awsSecretsManagerProvider, "createSecret").mockResolvedValue({
material: {
scheme: "aws_secrets_manager_v1",
secretId: "arn:aws:secretsmanager:us-east-1:123456789012:secret:paperclip/prod-use1/company/openai-api-key",
versionId: "aws-version-1",
source: "managed",
},
valueSha256: "value-sha-1",
fingerprintSha256: "fingerprint-sha-1",
externalRef: "arn:aws:secretsmanager:us-east-1:123456789012:secret:paperclip/prod-use1/company/openai-api-key",
providerVersionRef: "aws-version-1",
});
const createVersionSpy = vi.spyOn(awsSecretsManagerProvider, "createVersion").mockResolvedValue({
material: {
scheme: "aws_secrets_manager_v1",
secretId: "arn:aws:secretsmanager:us-east-1:123456789012:secret:paperclip/prod-use1/company/openai-api-key",
versionId: "aws-version-2",
source: "managed",
},
valueSha256: "value-sha-2",
fingerprintSha256: "fingerprint-sha-2",
externalRef: "arn:aws:secretsmanager:us-east-1:123456789012:secret:paperclip/prod-use1/company/openai-api-key",
providerVersionRef: "aws-version-2",
});
const resolveSpy = vi.spyOn(awsSecretsManagerProvider, "resolveVersion").mockResolvedValue("resolved-secret");
const secret = await svc.create(companyId, {
name: `aws-managed-${randomUUID()}`,
provider: "aws_secrets_manager",
providerConfigId: awsVault.id,
value: "runtime-secret",
});
const rotated = await svc.rotate(secret.id, { value: "rotated-runtime-secret" });
const resolved = await svc.resolveSecretValue(companyId, rotated.id, "latest");
expect(resolved).toBe("resolved-secret");
expect(createSpy).toHaveBeenCalledWith(expect.objectContaining({
providerConfig: expect.objectContaining({
id: awsVault.id,
provider: "aws_secrets_manager",
config: expect.objectContaining({ region: "us-east-1", namespace: "prod-use1" }),
}),
}));
expect(createVersionSpy).toHaveBeenCalledWith(expect.objectContaining({
providerConfig: expect.objectContaining({ id: awsVault.id }),
}));
expect(resolveSpy).toHaveBeenCalledWith(expect.objectContaining({
providerConfig: expect.objectContaining({ id: awsVault.id }),
providerVersionRef: "aws-version-2",
}));
expect(JSON.stringify(resolveSpy.mock.calls[0]?.[0])).not.toContain("resolved-secret");
});
it("rejects externalRef overrides on managed secrets", async () => {
const companyId = await seedCompany();
const svc = secretService(db);
@@ -340,6 +406,7 @@ describeEmbeddedPostgres("secretService", () => {
version: 1,
},
mode: "delete",
providerConfig: null,
});
expect(persisted).toBeNull();
});