mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 23:27:35 +00:00
Merge pull request #2313 from akhilmhdh/feat/test-import
Feat/test import
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
|
|
||||||
|
const createPolicy = async (dto: { name: string; secretPath: string; approvers: string[]; approvals: number }) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v1/secret-approvals`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${jwtAuthToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environment: seedData1.environment.slug,
|
||||||
|
name: dto.name,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
approvers: dto.approvers,
|
||||||
|
approvals: dto.approvals
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
return res.json().approval;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Secret approval policy router", async () => {
|
||||||
|
test("Create policy", async () => {
|
||||||
|
const policy = await createPolicy({
|
||||||
|
secretPath: "/",
|
||||||
|
approvals: 1,
|
||||||
|
approvers: [seedData1.id],
|
||||||
|
name: "test-policy"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(policy.name).toBe("test-policy");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,73 +1,61 @@
|
|||||||
|
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
|
||||||
|
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
|
||||||
|
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
|
||||||
|
|
||||||
import { seedData1 } from "@app/db/seed-data";
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
|
|
||||||
const createSecretImport = async (importPath: string, importEnv: string) => {
|
|
||||||
const res = await testServer.inject({
|
|
||||||
method: "POST",
|
|
||||||
url: `/api/v1/secret-imports`,
|
|
||||||
headers: {
|
|
||||||
authorization: `Bearer ${jwtAuthToken}`
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
workspaceId: seedData1.project.id,
|
|
||||||
environment: seedData1.environment.slug,
|
|
||||||
path: "/",
|
|
||||||
import: {
|
|
||||||
environment: importEnv,
|
|
||||||
path: importPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(res.statusCode).toBe(200);
|
|
||||||
const payload = JSON.parse(res.payload);
|
|
||||||
expect(payload).toHaveProperty("secretImport");
|
|
||||||
return payload.secretImport;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteSecretImport = async (id: string) => {
|
|
||||||
const res = await testServer.inject({
|
|
||||||
method: "DELETE",
|
|
||||||
url: `/api/v1/secret-imports/${id}`,
|
|
||||||
headers: {
|
|
||||||
authorization: `Bearer ${jwtAuthToken}`
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
workspaceId: seedData1.project.id,
|
|
||||||
environment: seedData1.environment.slug,
|
|
||||||
path: "/"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(res.statusCode).toBe(200);
|
|
||||||
const payload = JSON.parse(res.payload);
|
|
||||||
expect(payload).toHaveProperty("secretImport");
|
|
||||||
return payload.secretImport;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("Secret Import Router", async () => {
|
describe("Secret Import Router", async () => {
|
||||||
test.each([
|
test.each([
|
||||||
{ importEnv: "prod", importPath: "/" }, // one in root
|
{ importEnv: "prod", importPath: "/" }, // one in root
|
||||||
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
|
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
|
||||||
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
|
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
|
||||||
// check for default environments
|
// check for default environments
|
||||||
const payload = await createSecretImport(importPath, importEnv);
|
const payload = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath,
|
||||||
|
importEnv
|
||||||
|
});
|
||||||
expect(payload).toEqual(
|
expect(payload).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
importPath: expect.any(String),
|
importPath,
|
||||||
importEnv: expect.objectContaining({
|
importEnv: expect.objectContaining({
|
||||||
name: expect.any(String),
|
name: expect.any(String),
|
||||||
slug: expect.any(String),
|
slug: importEnv,
|
||||||
id: expect.any(String)
|
id: expect.any(String)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await deleteSecretImport(payload.id);
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: payload.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Get secret imports", async () => {
|
test("Get secret imports", async () => {
|
||||||
const createdImport1 = await createSecretImport("/", "prod");
|
const createdImport1 = await createSecretImport({
|
||||||
const createdImport2 = await createSecretImport("/", "staging");
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: "/",
|
||||||
|
importEnv: "prod"
|
||||||
|
});
|
||||||
|
const createdImport2 = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: "/",
|
||||||
|
importEnv: "staging"
|
||||||
|
});
|
||||||
const res = await testServer.inject({
|
const res = await testServer.inject({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/api/v1/secret-imports`,
|
url: `/api/v1/secret-imports`,
|
||||||
@@ -89,25 +77,60 @@ describe("Secret Import Router", async () => {
|
|||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
importPath: expect.any(String),
|
importPath: "/",
|
||||||
importEnv: expect.objectContaining({
|
importEnv: expect.objectContaining({
|
||||||
name: expect.any(String),
|
name: expect.any(String),
|
||||||
slug: expect.any(String),
|
slug: "prod",
|
||||||
|
id: expect.any(String)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
importPath: "/",
|
||||||
|
importEnv: expect.objectContaining({
|
||||||
|
name: expect.any(String),
|
||||||
|
slug: "staging",
|
||||||
id: expect.any(String)
|
id: expect.any(String)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
await deleteSecretImport(createdImport1.id);
|
await deleteSecretImport({
|
||||||
await deleteSecretImport(createdImport2.id);
|
id: createdImport1.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: createdImport2.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Update secret import position", async () => {
|
test("Update secret import position", async () => {
|
||||||
const prodImportDetails = { path: "/", envSlug: "prod" };
|
const prodImportDetails = { path: "/", envSlug: "prod" };
|
||||||
const stagingImportDetails = { path: "/", envSlug: "staging" };
|
const stagingImportDetails = { path: "/", envSlug: "staging" };
|
||||||
|
|
||||||
const createdImport1 = await createSecretImport(prodImportDetails.path, prodImportDetails.envSlug);
|
const createdImport1 = await createSecretImport({
|
||||||
const createdImport2 = await createSecretImport(stagingImportDetails.path, stagingImportDetails.envSlug);
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: prodImportDetails.path,
|
||||||
|
importEnv: prodImportDetails.envSlug
|
||||||
|
});
|
||||||
|
const createdImport2 = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: stagingImportDetails.path,
|
||||||
|
importEnv: stagingImportDetails.envSlug
|
||||||
|
});
|
||||||
|
|
||||||
const updateImportRes = await testServer.inject({
|
const updateImportRes = await testServer.inject({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
@@ -161,22 +184,55 @@ describe("Secret Import Router", async () => {
|
|||||||
expect(secretImportList.secretImports[1].id).toEqual(createdImport1.id);
|
expect(secretImportList.secretImports[1].id).toEqual(createdImport1.id);
|
||||||
expect(secretImportList.secretImports[0].id).toEqual(createdImport2.id);
|
expect(secretImportList.secretImports[0].id).toEqual(createdImport2.id);
|
||||||
|
|
||||||
await deleteSecretImport(createdImport1.id);
|
await deleteSecretImport({
|
||||||
await deleteSecretImport(createdImport2.id);
|
id: createdImport1.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: createdImport2.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Delete secret import position", async () => {
|
test("Delete secret import position", async () => {
|
||||||
const createdImport1 = await createSecretImport("/", "prod");
|
const createdImport1 = await createSecretImport({
|
||||||
const createdImport2 = await createSecretImport("/", "staging");
|
authToken: jwtAuthToken,
|
||||||
const deletedImport = await deleteSecretImport(createdImport1.id);
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: "/",
|
||||||
|
importEnv: "prod"
|
||||||
|
});
|
||||||
|
const createdImport2 = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
importPath: "/",
|
||||||
|
importEnv: "staging"
|
||||||
|
});
|
||||||
|
const deletedImport = await deleteSecretImport({
|
||||||
|
id: createdImport1.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
// check for default environments
|
// check for default environments
|
||||||
expect(deletedImport).toEqual(
|
expect(deletedImport).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
importPath: expect.any(String),
|
importPath: "/",
|
||||||
importEnv: expect.objectContaining({
|
importEnv: expect.objectContaining({
|
||||||
name: expect.any(String),
|
name: expect.any(String),
|
||||||
slug: expect.any(String),
|
slug: "prod",
|
||||||
id: expect.any(String)
|
id: expect.any(String)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -201,6 +257,552 @@ describe("Secret Import Router", async () => {
|
|||||||
expect(secretImportList.secretImports.length).toEqual(1);
|
expect(secretImportList.secretImports.length).toEqual(1);
|
||||||
expect(secretImportList.secretImports[0].position).toEqual(1);
|
expect(secretImportList.secretImports[0].position).toEqual(1);
|
||||||
|
|
||||||
await deleteSecretImport(createdImport2.id);
|
await deleteSecretImport({
|
||||||
|
id: createdImport2.id,
|
||||||
|
workspaceId: seedData1.project.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// dev <- stage <- prod
|
||||||
|
describe.each([{ path: "/" }, { path: "/deep" }])(
|
||||||
|
"Secret import waterfall pattern testing - %path",
|
||||||
|
({ path: testSuitePath }) => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
let prodFolder: { id: string };
|
||||||
|
let stagingFolder: { id: string };
|
||||||
|
let devFolder: { id: string };
|
||||||
|
|
||||||
|
if (testSuitePath !== "/") {
|
||||||
|
prodFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
stagingFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
devFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const devImportFromStage = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "staging"
|
||||||
|
});
|
||||||
|
|
||||||
|
const stageImportFromProd = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "prod"
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: stageImportFromProd.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromStage.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prodFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stagingFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: stagingFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: devFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check one level imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY",
|
||||||
|
value: "stage-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("STAGING_KEY");
|
||||||
|
expect(secret.secretValue).toBe("stage-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "STAGING_KEY",
|
||||||
|
secretValue: "stage-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check two level imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY",
|
||||||
|
value: "prod-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("PROD_KEY");
|
||||||
|
expect(secret.secretValue).toBe("prod-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "PROD_KEY",
|
||||||
|
secretValue: "prod-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// dev <- stage, dev <- prod
|
||||||
|
describe.each([{ path: "/" }, { path: "/deep" }])(
|
||||||
|
"Secret import multiple destination to one source pattern testing - %path",
|
||||||
|
({ path: testSuitePath }) => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
let prodFolder: { id: string };
|
||||||
|
let stagingFolder: { id: string };
|
||||||
|
let devFolder: { id: string };
|
||||||
|
|
||||||
|
if (testSuitePath !== "/") {
|
||||||
|
prodFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
stagingFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
devFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const devImportFromStage = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "staging"
|
||||||
|
});
|
||||||
|
|
||||||
|
const devImportFromProd = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "prod"
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromProd.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromStage.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prodFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stagingFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: stagingFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: devFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY",
|
||||||
|
value: "stage-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY",
|
||||||
|
value: "prod-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("STAGING_KEY");
|
||||||
|
expect(secret.secretValue).toBe("stage-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "STAGING_KEY",
|
||||||
|
secretValue: "stage-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "PROD_KEY",
|
||||||
|
secretValue: "prod-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// dev -> stage, prod
|
||||||
|
describe.each([{ path: "/" }, { path: "/deep" }])(
|
||||||
|
"Secret import one source to multiple destination pattern testing - %path",
|
||||||
|
({ path: testSuitePath }) => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
let prodFolder: { id: string };
|
||||||
|
let stagingFolder: { id: string };
|
||||||
|
let devFolder: { id: string };
|
||||||
|
|
||||||
|
if (testSuitePath !== "/") {
|
||||||
|
prodFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
stagingFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
devFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const stageImportFromDev = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
|
||||||
|
const prodImportFromDev = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: prodImportFromDev.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: stageImportFromDev.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prodFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stagingFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: stagingFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: devFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY",
|
||||||
|
value: "stage-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY",
|
||||||
|
value: "prod-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
const stagingSecret = await getSecretByNameV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stagingSecret.secretKey).toBe("STAGING_KEY");
|
||||||
|
expect(stagingSecret.secretValue).toBe("stage-value");
|
||||||
|
|
||||||
|
const prodSecret = await getSecretByNameV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(prodSecret.secretKey).toBe("PROD_KEY");
|
||||||
|
expect(prodSecret.secretValue).toBe("prod-value");
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -0,0 +1,406 @@
|
|||||||
|
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
|
||||||
|
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
|
||||||
|
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
|
||||||
|
|
||||||
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
|
|
||||||
|
// dev <- stage <- prod
|
||||||
|
describe.each([{ secretPath: "/" }, { secretPath: "/deep" }])(
|
||||||
|
"Secret replication waterfall pattern testing - %secretPath",
|
||||||
|
({ secretPath: testSuitePath }) => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
let prodFolder: { id: string };
|
||||||
|
let stagingFolder: { id: string };
|
||||||
|
let devFolder: { id: string };
|
||||||
|
|
||||||
|
if (testSuitePath !== "/") {
|
||||||
|
prodFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
stagingFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
devFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const devImportFromStage = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "staging",
|
||||||
|
isReplication: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const stageImportFromProd = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "prod",
|
||||||
|
isReplication: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: stageImportFromProd.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromStage.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prodFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stagingFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: stagingFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: devFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check one level imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY",
|
||||||
|
value: "stage-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for 5 second for replication to finish
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 5000); // time to breathe for db
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("STAGING_KEY");
|
||||||
|
expect(secret.secretValue).toBe("stage-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "STAGING_KEY",
|
||||||
|
secretValue: "stage-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check two level imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY",
|
||||||
|
value: "prod-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for 5 second for replication to finish
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 5000); // time to breathe for db
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("PROD_KEY");
|
||||||
|
expect(secret.secretValue).toBe("prod-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "PROD_KEY",
|
||||||
|
secretValue: "prod-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ timeout: 30000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
// dev <- stage, dev <- prod
|
||||||
|
describe.each([{ path: "/" }, { path: "/deep" }])(
|
||||||
|
"Secret replication 1-N pattern testing - %path",
|
||||||
|
({ path: testSuitePath }) => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
let prodFolder: { id: string };
|
||||||
|
let stagingFolder: { id: string };
|
||||||
|
let devFolder: { id: string };
|
||||||
|
|
||||||
|
if (testSuitePath !== "/") {
|
||||||
|
prodFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
stagingFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
devFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const devImportFromStage = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "staging",
|
||||||
|
isReplication: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const devImportFromProd = await createSecretImport({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
importPath: testSuitePath,
|
||||||
|
importEnv: "prod",
|
||||||
|
isReplication: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromProd.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
await deleteSecretImport({
|
||||||
|
id: devImportFromStage.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prodFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stagingFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: stagingFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: "staging"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devFolder) {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: devFolder.id,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
environmentSlug: seedData1.environment.slug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Check imported secret exist", async () => {
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY",
|
||||||
|
value: "stage-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
await createSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY",
|
||||||
|
value: "prod-value"
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for 5 second for replication to finish
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 5000); // time to breathe for db
|
||||||
|
});
|
||||||
|
|
||||||
|
const secret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(secret.secretKey).toBe("STAGING_KEY");
|
||||||
|
expect(secret.secretValue).toBe("stage-value");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "STAGING_KEY",
|
||||||
|
secretValue: "stage-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "PROD_KEY",
|
||||||
|
secretValue: "prod-value"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "staging",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "STAGING_KEY"
|
||||||
|
});
|
||||||
|
await deleteSecretV2({
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
secretPath: testSuitePath,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "PROD_KEY"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ timeout: 30000 }
|
||||||
|
);
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
|
||||||
|
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
|
||||||
|
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
|
||||||
|
|
||||||
|
import { seedData1 } from "@app/db/seed-data";
|
||||||
|
|
||||||
|
describe("Secret expansion", () => {
|
||||||
|
const projectId = seedData1.projectV3.id;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const prodRootFolder = await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
name: "deep"
|
||||||
|
});
|
||||||
|
|
||||||
|
await createFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep",
|
||||||
|
name: "nested"
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
await deleteFolder({
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
secretPath: "/",
|
||||||
|
id: prodRootFolder.id,
|
||||||
|
workspaceId: projectId,
|
||||||
|
environmentSlug: "prod"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Local secret reference", async () => {
|
||||||
|
const secrets = [
|
||||||
|
{
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "HELLO",
|
||||||
|
value: "world"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "TEST",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "hello ${HELLO}"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => createSecretV2(el)));
|
||||||
|
|
||||||
|
const expandedSecret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "TEST"
|
||||||
|
});
|
||||||
|
expect(expandedSecret.secretValue).toBe("hello world");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.secrets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "TEST",
|
||||||
|
secretValue: "hello world"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Cross environment secret reference", async () => {
|
||||||
|
const secrets = [
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "DEEP_KEY_1",
|
||||||
|
value: "testing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_1",
|
||||||
|
value: "reference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_2",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "secret ${NESTED_KEY_1}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "KEY",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "hello ${prod.deep.DEEP_KEY_1} ${prod.deep.nested.NESTED_KEY_2}"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => createSecretV2(el)));
|
||||||
|
|
||||||
|
const expandedSecret = await getSecretByNameV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "KEY"
|
||||||
|
});
|
||||||
|
expect(expandedSecret.secretValue).toBe("hello testing secret reference");
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.secrets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "KEY",
|
||||||
|
secretValue: "hello testing secret reference"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Non replicated secret import secret expansion on local reference and nested reference", async () => {
|
||||||
|
const secrets = [
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "DEEP_KEY_1",
|
||||||
|
value: "testing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_1",
|
||||||
|
value: "reference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_2",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "secret ${NESTED_KEY_1} ${prod.deep.DEEP_KEY_1}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "KEY",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "hello world"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => createSecretV2(el)));
|
||||||
|
const secretImportFromProdToDev = await createSecretImport({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
importEnv: "prod",
|
||||||
|
importPath: "/deep/nested"
|
||||||
|
});
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
environment: "prod",
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "NESTED_KEY_1",
|
||||||
|
secretValue: "reference"
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "NESTED_KEY_2",
|
||||||
|
secretValue: "secret reference testing"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
|
||||||
|
await deleteSecretImport({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
id: secretImportFromProdToDev.id,
|
||||||
|
secretPath: "/"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Replicated secret import secret expansion on local reference and nested reference",
|
||||||
|
async () => {
|
||||||
|
const secrets = [
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "DEEP_KEY_1",
|
||||||
|
value: "testing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_1",
|
||||||
|
value: "reference"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: "prod",
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/deep/nested",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "NESTED_KEY_2",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "secret ${NESTED_KEY_1} ${prod.deep.DEEP_KEY_1}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
key: "KEY",
|
||||||
|
// eslint-disable-next-line
|
||||||
|
value: "hello world"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => createSecretV2(el)));
|
||||||
|
const secretImportFromProdToDev = await createSecretImport({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
importEnv: "prod",
|
||||||
|
importPath: "/deep/nested",
|
||||||
|
isReplication: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for 5 second for replication to finish
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 5000); // time to breathe for db
|
||||||
|
});
|
||||||
|
|
||||||
|
const listSecrets = await getSecretsV2({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
secretPath: "/",
|
||||||
|
authToken: jwtAuthToken
|
||||||
|
});
|
||||||
|
expect(listSecrets.imports).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretPath: `/__reserve_replication_${secretImportFromProdToDev.id}`,
|
||||||
|
environment: seedData1.environment.slug,
|
||||||
|
secrets: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "NESTED_KEY_1",
|
||||||
|
secretValue: "reference"
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
secretKey: "NESTED_KEY_2",
|
||||||
|
secretValue: "secret reference testing"
|
||||||
|
})
|
||||||
|
])
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
|
||||||
|
await deleteSecretImport({
|
||||||
|
environmentSlug: seedData1.environment.slug,
|
||||||
|
workspaceId: projectId,
|
||||||
|
authToken: jwtAuthToken,
|
||||||
|
id: secretImportFromProdToDev.id,
|
||||||
|
secretPath: "/"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ timeout: 10000 }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -8,6 +8,7 @@ type TRawSecret = {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
version: number;
|
version: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSecret = async (dto: { path: string; key: string; value: string; comment: string; type?: SecretType }) => {
|
const createSecret = async (dto: { path: string; key: string; value: string; comment: string; type?: SecretType }) => {
|
||||||
const createSecretReqBody = {
|
const createSecretReqBody = {
|
||||||
workspaceId: seedData1.projectV3.id,
|
workspaceId: seedData1.projectV3.id,
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
type TFolder = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFolder = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
name: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v1/folders`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
name: dto.name,
|
||||||
|
path: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
return res.json().folder as TFolder;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteFolder = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
id: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/v1/folders/${dto.id}`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
path: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
return res.json().folder as TFolder;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listFolders = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/v1/folders`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
path: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
return res.json().folders as TFolder[];
|
||||||
|
};
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
type TSecretImport = {
|
||||||
|
id: string;
|
||||||
|
importEnv: {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
importPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createSecretImport = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
isReplication?: boolean;
|
||||||
|
secretPath: string;
|
||||||
|
importPath: string;
|
||||||
|
importEnv: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v1/secret-imports`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
isReplication: dto.isReplication,
|
||||||
|
path: dto.secretPath,
|
||||||
|
import: {
|
||||||
|
environment: dto.importEnv,
|
||||||
|
path: dto.importPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const payload = JSON.parse(res.payload);
|
||||||
|
expect(payload).toHaveProperty("secretImport");
|
||||||
|
return payload.secretImport as TSecretImport;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSecretImport = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
authToken: string;
|
||||||
|
id: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/v1/secret-imports/${dto.id}`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
path: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const payload = JSON.parse(res.payload);
|
||||||
|
expect(payload).toHaveProperty("secretImport");
|
||||||
|
return payload.secretImport as TSecretImport;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listSecretImport = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const res = await testServer.inject({
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/v1/secret-imports`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
path: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const payload = JSON.parse(res.payload);
|
||||||
|
expect(payload).toHaveProperty("secretImports");
|
||||||
|
return payload.secretImports as TSecretImport[];
|
||||||
|
};
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import { SecretType } from "@app/db/schemas";
|
||||||
|
|
||||||
|
type TRawSecret = {
|
||||||
|
secretKey: string;
|
||||||
|
secretValue: string;
|
||||||
|
secretComment?: string;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createSecretV2 = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
comment?: string;
|
||||||
|
authToken: string;
|
||||||
|
type?: SecretType;
|
||||||
|
}) => {
|
||||||
|
const createSecretReqBody = {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
type: dto.type || SecretType.Shared,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
secretKey: dto.key,
|
||||||
|
secretValue: dto.value,
|
||||||
|
secretComment: dto.comment
|
||||||
|
};
|
||||||
|
const createSecRes = await testServer.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v3/secrets/raw/${dto.key}`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: createSecretReqBody
|
||||||
|
});
|
||||||
|
expect(createSecRes.statusCode).toBe(200);
|
||||||
|
const createdSecretPayload = JSON.parse(createSecRes.payload);
|
||||||
|
expect(createdSecretPayload).toHaveProperty("secret");
|
||||||
|
return createdSecretPayload.secret as TRawSecret;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteSecretV2 = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
key: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const deleteSecRes = await testServer.inject({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/v3/secrets/raw/${dto.key}`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
secretPath: dto.secretPath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(deleteSecRes.statusCode).toBe(200);
|
||||||
|
const updatedSecretPayload = JSON.parse(deleteSecRes.payload);
|
||||||
|
expect(updatedSecretPayload).toHaveProperty("secret");
|
||||||
|
return updatedSecretPayload.secret as TRawSecret;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSecretByNameV2 = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
key: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const response = await testServer.inject({
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/v3/secrets/raw/${dto.key}`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
expandSecretReferences: "true",
|
||||||
|
include_imports: "true"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
const payload = JSON.parse(response.payload);
|
||||||
|
expect(payload).toHaveProperty("secret");
|
||||||
|
return payload.secret as TRawSecret;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSecretsV2 = async (dto: {
|
||||||
|
workspaceId: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
authToken: string;
|
||||||
|
}) => {
|
||||||
|
const getSecretsResponse = await testServer.inject({
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/v3/secrets/raw`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${dto.authToken}`
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
workspaceId: dto.workspaceId,
|
||||||
|
environment: dto.environmentSlug,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
expandSecretReferences: "true",
|
||||||
|
include_imports: "true"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(getSecretsResponse.statusCode).toBe(200);
|
||||||
|
const getSecretsPayload = JSON.parse(getSecretsResponse.payload);
|
||||||
|
expect(getSecretsPayload).toHaveProperty("secrets");
|
||||||
|
expect(getSecretsPayload).toHaveProperty("imports");
|
||||||
|
return getSecretsPayload as {
|
||||||
|
secrets: TRawSecret[];
|
||||||
|
imports: {
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
folderId: string;
|
||||||
|
secrets: TRawSecret[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -11,10 +11,11 @@ import { initLogger } from "@app/lib/logger";
|
|||||||
import { main } from "@app/server/app";
|
import { main } from "@app/server/app";
|
||||||
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { mockQueue } from "./mocks/queue";
|
|
||||||
import { mockSmtpServer } from "./mocks/smtp";
|
import { mockSmtpServer } from "./mocks/smtp";
|
||||||
import { mockKeyStore } from "./mocks/keystore";
|
|
||||||
import { initDbConnection } from "@app/db";
|
import { initDbConnection } from "@app/db";
|
||||||
|
import { queueServiceFactory } from "@app/queue";
|
||||||
|
import { keyStoreFactory } from "@app/keystore/keystore";
|
||||||
|
import { Redis } from "ioredis";
|
||||||
|
|
||||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||||
export default {
|
export default {
|
||||||
@@ -28,19 +29,31 @@ export default {
|
|||||||
dbRootCert: cfg.DB_ROOT_CERT
|
dbRootCert: cfg.DB_ROOT_CERT
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const redis = new Redis(cfg.REDIS_URL);
|
||||||
|
await redis.flushdb("SYNC");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await db.migrate.rollback(
|
||||||
|
{
|
||||||
|
directory: path.join(__dirname, "../src/db/migrations"),
|
||||||
|
extension: "ts",
|
||||||
|
tableName: "infisical_migrations"
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
await db.migrate.latest({
|
await db.migrate.latest({
|
||||||
directory: path.join(__dirname, "../src/db/migrations"),
|
directory: path.join(__dirname, "../src/db/migrations"),
|
||||||
extension: "ts",
|
extension: "ts",
|
||||||
tableName: "infisical_migrations"
|
tableName: "infisical_migrations"
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.seed.run({
|
await db.seed.run({
|
||||||
directory: path.join(__dirname, "../src/db/seeds"),
|
directory: path.join(__dirname, "../src/db/seeds"),
|
||||||
extension: "ts"
|
extension: "ts"
|
||||||
});
|
});
|
||||||
const smtp = mockSmtpServer();
|
const smtp = mockSmtpServer();
|
||||||
const queue = mockQueue();
|
const queue = queueServiceFactory(cfg.REDIS_URL);
|
||||||
const keyStore = mockKeyStore();
|
const keyStore = keyStoreFactory(cfg.REDIS_URL);
|
||||||
const server = await main({ db, smtp, logger, queue, keyStore });
|
const server = await main({ db, smtp, logger, queue, keyStore });
|
||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
globalThis.testServer = server;
|
globalThis.testServer = server;
|
||||||
@@ -58,10 +71,12 @@ export default {
|
|||||||
{ expiresIn: cfg.JWT_AUTH_LIFETIME }
|
{ expiresIn: cfg.JWT_AUTH_LIFETIME }
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line
|
||||||
console.log("[TEST] Error setting up environment", error);
|
console.log("[TEST] Error setting up environment", error);
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom setup
|
// custom setup
|
||||||
return {
|
return {
|
||||||
async teardown() {
|
async teardown() {
|
||||||
@@ -80,6 +95,9 @@ export default {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await redis.flushdb("ASYNC");
|
||||||
|
redis.disconnect();
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+143
-173
@@ -136,7 +136,6 @@
|
|||||||
"tsup": "^8.0.1",
|
"tsup": "^8.0.1",
|
||||||
"tsx": "^4.4.0",
|
"tsx": "^4.4.0",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.2",
|
||||||
"vite-tsconfig-paths": "^4.2.2",
|
|
||||||
"vitest": "^1.2.2"
|
"vitest": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -301,16 +300,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-cloudwatch-logs": {
|
"node_modules/@aws-sdk/client-cloudwatch-logs": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.632.0.tgz",
|
||||||
"integrity": "sha512-M2SGf0B/WmHYNxUhUWKIYI5NW4Si7cyokB6Lt3RtDof3WVHA8L0LLl+EEo1URUkpxH8/F8VH2fTES7ODm2c+7g==",
|
"integrity": "sha512-QrG04Ss2/KDsvGmoBH9QHjaC/wx7Gf9U2F5o8gYbHVU5ZGDW+zMX2Sj/6jjSyZ4qLD4sxK7sRHwK+fYA21OQQA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/client-sso-oidc": "3.635.0",
|
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||||
"@aws-sdk/client-sts": "3.635.0",
|
"@aws-sdk/client-sts": "3.632.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -321,7 +320,7 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/eventstream-serde-browser": "^3.0.6",
|
"@smithy/eventstream-serde-browser": "^3.0.6",
|
||||||
"@smithy/eventstream-serde-config-resolver": "^3.0.3",
|
"@smithy/eventstream-serde-config-resolver": "^3.0.3",
|
||||||
"@smithy/eventstream-serde-node": "^3.0.5",
|
"@smithy/eventstream-serde-node": "^3.0.5",
|
||||||
@@ -330,20 +329,20 @@
|
|||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -561,6 +560,45 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/core": {
|
||||||
|
"version": "3.635.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz",
|
||||||
|
"integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@smithy/core": "^2.4.0",
|
||||||
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
|
"@smithy/property-provider": "^3.1.3",
|
||||||
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
|
"@smithy/signature-v4": "^4.1.0",
|
||||||
|
"@smithy/smithy-client": "^3.2.0",
|
||||||
|
"@smithy/types": "^3.3.0",
|
||||||
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
|
"fast-xml-parser": "4.4.1",
|
||||||
|
"tslib": "^2.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-http": {
|
||||||
|
"version": "3.635.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz",
|
||||||
|
"integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@aws-sdk/types": "3.609.0",
|
||||||
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
|
"@smithy/property-provider": "^3.1.3",
|
||||||
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
|
"@smithy/smithy-client": "^3.2.0",
|
||||||
|
"@smithy/types": "^3.3.0",
|
||||||
|
"@smithy/util-stream": "^3.1.3",
|
||||||
|
"tslib": "^2.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-ini": {
|
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-ini": {
|
||||||
"version": "3.637.0",
|
"version": "3.637.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz",
|
||||||
@@ -659,16 +697,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-iam": {
|
"node_modules/@aws-sdk/client-iam": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.632.0.tgz",
|
||||||
"integrity": "sha512-sflTv6XcwO5UX+U9x31+T6TBEgVIzG61giLgRV51kkFGErni++GpxUcc6O1mpDwb3jpbntJf7QPjrkkj9wsTPA==",
|
"integrity": "sha512-iwivASUliVxCEbT/mu5s03SCyqQKNXbJUpG17ywT4taA2xvLisGRI5iNV3OYT1qDmK9DOLMSJYpeX2GWCijPxw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/client-sso-oidc": "3.635.0",
|
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||||
"@aws-sdk/client-sts": "3.635.0",
|
"@aws-sdk/client-sts": "3.632.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -679,26 +717,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -711,16 +749,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-kms": {
|
"node_modules/@aws-sdk/client-kms": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.632.0.tgz",
|
||||||
"integrity": "sha512-H2qJVXiz3WbBQwtxqfEvuJ9pCKJdqEWkzQ8I4knkXbQkyy78GktfMwBWqFyw3eap/s9rQmsyXbuuhBIozbemOg==",
|
"integrity": "sha512-uMm1fAIdImaBKwKXnpcD1cpRlTAbLisbRbNJqzJdH+snN0jAkukLNUMUheb0XKaczk7eQrp5w4inlWrRvEmjSA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/client-sso-oidc": "3.635.0",
|
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||||
"@aws-sdk/client-sts": "3.635.0",
|
"@aws-sdk/client-sts": "3.632.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -731,26 +769,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -762,16 +800,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-secrets-manager": {
|
"node_modules/@aws-sdk/client-secrets-manager": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.632.0.tgz",
|
||||||
"integrity": "sha512-taa+sa8xFym7ZYzybqkOVy5MAdedcIt2pKEVOReEaNkUuOwMUo+wF4QhJeyhaLPTs2l0rHR1bnwYOG+0fW0Kvg==",
|
"integrity": "sha512-WsQhPHHK1yPfALcP1B7nBSGDzky6vFTUEXnUdfzb5Xy2cT+JTBTS6ChtQGqqOuGHDP/3t/9soqZ+L6rUCYBb/Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/client-sso-oidc": "3.635.0",
|
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||||
"@aws-sdk/client-sts": "3.635.0",
|
"@aws-sdk/client-sts": "3.632.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -782,26 +820,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -814,13 +852,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-sso": {
|
"node_modules/@aws-sdk/client-sso": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.632.0.tgz",
|
||||||
"integrity": "sha512-/Hl69+JpFUo9JNVmh2gSvMgYkE4xjd+1okiRoPBbQqjI7YBP2JWCUDP8IoEkNq3wj0vNTq0OWfn6RpZycIkAXQ==",
|
"integrity": "sha512-iYWHiKBz44m3chCFvtvHnvCpL2rALzyr1e6tOZV3dLlOKtQtDUlPy6OtnXDu4y+wyJCniy8ivG3+LAe4klzn1Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -831,26 +869,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -862,14 +900,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-sso-oidc": {
|
"node_modules/@aws-sdk/client-sso-oidc": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.632.0.tgz",
|
||||||
"integrity": "sha512-RIwDlhzAFttB1vbpznewnPqz7h1H/2UhQLwB38yfZBwYQOxyxVfLV5j5VoUUX3jY4i4qH9wiHc7b02qeAOZY6g==",
|
"integrity": "sha512-Oh1fIWaoZluihOCb/zDEpRTi+6an82fgJz7fyRBugyLhEtDjmvpCQ3oKjzaOhoN+4EvXAm1ZS/ZgpvXBlIRTgw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -880,26 +918,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -910,19 +948,19 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@aws-sdk/client-sts": "^3.635.0"
|
"@aws-sdk/client-sts": "^3.632.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-sts": {
|
"node_modules/@aws-sdk/client-sts": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.632.0.tgz",
|
||||||
"integrity": "sha512-Al2ytE69+cbA44qHlelqhzWwbURikfF13Zkal9utIG5Q6T2c7r8p6sePN92n8l/x1v0FhJ5VTxKak+cPTE0CZQ==",
|
"integrity": "sha512-Ss5cBH09icpTvT+jtGGuQlRdwtO7RyE9BF4ZV/CEPATdd9whtJt4Qxdya8BUnkWR7h5HHTrQHqai3YVYjku41A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
"@aws-crypto/sha256-browser": "5.2.0",
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
"@aws-crypto/sha256-js": "5.2.0",
|
||||||
"@aws-sdk/client-sso-oidc": "3.635.0",
|
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||||
"@aws-sdk/core": "3.635.0",
|
"@aws-sdk/core": "3.629.0",
|
||||||
"@aws-sdk/credential-provider-node": "3.635.0",
|
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||||
"@aws-sdk/middleware-logger": "3.609.0",
|
"@aws-sdk/middleware-logger": "3.609.0",
|
||||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||||
@@ -933,26 +971,26 @@
|
|||||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||||
"@smithy/config-resolver": "^3.0.5",
|
"@smithy/config-resolver": "^3.0.5",
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/hash-node": "^3.0.3",
|
"@smithy/hash-node": "^3.0.3",
|
||||||
"@smithy/invalid-dependency": "^3.0.3",
|
"@smithy/invalid-dependency": "^3.0.3",
|
||||||
"@smithy/middleware-content-length": "^3.0.5",
|
"@smithy/middleware-content-length": "^3.0.5",
|
||||||
"@smithy/middleware-endpoint": "^3.1.0",
|
"@smithy/middleware-endpoint": "^3.1.0",
|
||||||
"@smithy/middleware-retry": "^3.0.15",
|
"@smithy/middleware-retry": "^3.0.14",
|
||||||
"@smithy/middleware-serde": "^3.0.3",
|
"@smithy/middleware-serde": "^3.0.3",
|
||||||
"@smithy/middleware-stack": "^3.0.3",
|
"@smithy/middleware-stack": "^3.0.3",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/url-parser": "^3.0.3",
|
"@smithy/url-parser": "^3.0.3",
|
||||||
"@smithy/util-base64": "^3.0.0",
|
"@smithy/util-base64": "^3.0.0",
|
||||||
"@smithy/util-body-length-browser": "^3.0.0",
|
"@smithy/util-body-length-browser": "^3.0.0",
|
||||||
"@smithy/util-body-length-node": "^3.0.0",
|
"@smithy/util-body-length-node": "^3.0.0",
|
||||||
"@smithy/util-defaults-mode-browser": "^3.0.15",
|
"@smithy/util-defaults-mode-browser": "^3.0.14",
|
||||||
"@smithy/util-defaults-mode-node": "^3.0.15",
|
"@smithy/util-defaults-mode-node": "^3.0.14",
|
||||||
"@smithy/util-endpoints": "^2.0.5",
|
"@smithy/util-endpoints": "^2.0.5",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"@smithy/util-retry": "^3.0.3",
|
"@smithy/util-retry": "^3.0.3",
|
||||||
@@ -964,16 +1002,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/core": {
|
"node_modules/@aws-sdk/core": {
|
||||||
"version": "3.635.0",
|
"version": "3.629.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.629.0.tgz",
|
||||||
"integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==",
|
"integrity": "sha512-+/ShPU/tyIBM3oY1cnjgNA/tFyHtlWq+wXF9xEKRv19NOpYbWQ+xzNwVjGq8vR07cCRqy/sDQLWPhxjtuV/FiQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/core": "^2.4.0",
|
"@smithy/core": "^2.3.2",
|
||||||
"@smithy/node-config-provider": "^3.1.4",
|
"@smithy/node-config-provider": "^3.1.4",
|
||||||
"@smithy/property-provider": "^3.1.3",
|
"@smithy/property-provider": "^3.1.3",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/signature-v4": "^4.1.0",
|
"@smithy/signature-v4": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/util-middleware": "^3.0.3",
|
"@smithy/util-middleware": "^3.0.3",
|
||||||
"fast-xml-parser": "4.4.1",
|
"fast-xml-parser": "4.4.1",
|
||||||
@@ -998,16 +1036,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-http": {
|
"node_modules/@aws-sdk/credential-provider-http": {
|
||||||
"version": "3.635.0",
|
"version": "3.622.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.622.0.tgz",
|
||||||
"integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==",
|
"integrity": "sha512-VUHbr24Oll1RK3WR8XLUugLpgK9ZuxEm/NVeVqyFts1Ck9gsKpRg1x4eH7L7tW3SJ4TDEQNMbD7/7J+eoL2svg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/types": "3.609.0",
|
"@aws-sdk/types": "3.609.0",
|
||||||
"@smithy/fetch-http-handler": "^3.2.4",
|
"@smithy/fetch-http-handler": "^3.2.4",
|
||||||
"@smithy/node-http-handler": "^3.1.4",
|
"@smithy/node-http-handler": "^3.1.4",
|
||||||
"@smithy/property-provider": "^3.1.3",
|
"@smithy/property-provider": "^3.1.3",
|
||||||
"@smithy/protocol-http": "^4.1.0",
|
"@smithy/protocol-http": "^4.1.0",
|
||||||
"@smithy/smithy-client": "^3.2.0",
|
"@smithy/smithy-client": "^3.1.12",
|
||||||
"@smithy/types": "^3.3.0",
|
"@smithy/types": "^3.3.0",
|
||||||
"@smithy/util-stream": "^3.1.3",
|
"@smithy/util-stream": "^3.1.3",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
@@ -1017,14 +1055,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-ini": {
|
"node_modules/@aws-sdk/credential-provider-ini": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.632.0.tgz",
|
||||||
"integrity": "sha512-+OqcNhhOFFY08YHLjO9/Y1n37RKAO7LADnsJ7VTXca7IfvYh27BVBn+FdlqnyEb1MQ5ArHTY4pq3pKRIg6RW4Q==",
|
"integrity": "sha512-m6epoW41xa1ajU5OiHcmQHoGVtrbXBaRBOUhlCLZmcaqMLYsboM4iD/WZP8aatKEON5tTnVXh/4StV8D/+wemw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "3.620.1",
|
"@aws-sdk/credential-provider-env": "3.620.1",
|
||||||
"@aws-sdk/credential-provider-http": "3.635.0",
|
"@aws-sdk/credential-provider-http": "3.622.0",
|
||||||
"@aws-sdk/credential-provider-process": "3.620.1",
|
"@aws-sdk/credential-provider-process": "3.620.1",
|
||||||
"@aws-sdk/credential-provider-sso": "3.635.0",
|
"@aws-sdk/credential-provider-sso": "3.632.0",
|
||||||
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
||||||
"@aws-sdk/types": "3.609.0",
|
"@aws-sdk/types": "3.609.0",
|
||||||
"@smithy/credential-provider-imds": "^3.2.0",
|
"@smithy/credential-provider-imds": "^3.2.0",
|
||||||
@@ -1037,19 +1075,19 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@aws-sdk/client-sts": "^3.635.0"
|
"@aws-sdk/client-sts": "^3.632.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-node": {
|
"node_modules/@aws-sdk/credential-provider-node": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.632.0.tgz",
|
||||||
"integrity": "sha512-bmd23mnb94S6AxmWPgqJTnvT9ONKlTx7EPafE1RNO+vUl6mHih4iyqX6ZPaRcSfaPx4U1R7H1RM8cSnafXgaBg==",
|
"integrity": "sha512-cL8fuJWm/xQBO4XJPkeuZzl3XinIn9EExWgzpG48NRMKR5us1RI/ucv7xFbBBaG+r/sDR2HpYBIA3lVIpm1H3Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "3.620.1",
|
"@aws-sdk/credential-provider-env": "3.620.1",
|
||||||
"@aws-sdk/credential-provider-http": "3.635.0",
|
"@aws-sdk/credential-provider-http": "3.622.0",
|
||||||
"@aws-sdk/credential-provider-ini": "3.635.0",
|
"@aws-sdk/credential-provider-ini": "3.632.0",
|
||||||
"@aws-sdk/credential-provider-process": "3.620.1",
|
"@aws-sdk/credential-provider-process": "3.620.1",
|
||||||
"@aws-sdk/credential-provider-sso": "3.635.0",
|
"@aws-sdk/credential-provider-sso": "3.632.0",
|
||||||
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
||||||
"@aws-sdk/types": "3.609.0",
|
"@aws-sdk/types": "3.609.0",
|
||||||
"@smithy/credential-provider-imds": "^3.2.0",
|
"@smithy/credential-provider-imds": "^3.2.0",
|
||||||
@@ -1078,11 +1116,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/credential-provider-sso": {
|
"node_modules/@aws-sdk/credential-provider-sso": {
|
||||||
"version": "3.635.0",
|
"version": "3.632.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.635.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.632.0.tgz",
|
||||||
"integrity": "sha512-hO/fKyvUaGpK9zyvCnmJz70EputvGWDr2UTOn/RzvcR6UB4yXoFf0QcCMubEsE3v67EsAv6PadgOeJ0vz6IazA==",
|
"integrity": "sha512-P/4wB6j7ym5QCPTL2xlMfvf2NcXSh+z0jmsZP4WW/tVwab4hvgabPPbLeEZDSWZ0BpgtxKGvRq0GSHuGeirQbA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sso": "3.635.0",
|
"@aws-sdk/client-sso": "3.632.0",
|
||||||
"@aws-sdk/token-providers": "3.614.0",
|
"@aws-sdk/token-providers": "3.614.0",
|
||||||
"@aws-sdk/types": "3.609.0",
|
"@aws-sdk/types": "3.609.0",
|
||||||
"@smithy/property-provider": "^3.1.3",
|
"@smithy/property-provider": "^3.1.3",
|
||||||
@@ -11289,12 +11327,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globrex": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/google-auth-library": {
|
"node_modules/google-auth-library": {
|
||||||
"version": "9.9.0",
|
"version": "9.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.9.0.tgz",
|
||||||
@@ -16603,26 +16635,6 @@
|
|||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsconfck": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"tsconfck": "bin/tsconfck.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.13.1 || ^16 || >=18"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^4.3.5 || ^5.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
|
||||||
@@ -17779,48 +17791,6 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vite-tsconfig-paths": {
|
|
||||||
"version": "4.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.2.tgz",
|
|
||||||
"integrity": "sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"globrex": "^0.1.2",
|
|
||||||
"tsconfck": "^2.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vite": "*"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"vite": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite-tsconfig-paths/node_modules/debug": {
|
|
||||||
"version": "4.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "2.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite-tsconfig-paths/node_modules/ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/@esbuild/android-arm": {
|
"node_modules/vite/node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.20.2",
|
"version": "0.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
||||||
|
|||||||
@@ -103,7 +103,6 @@
|
|||||||
"tsup": "^8.0.1",
|
"tsup": "^8.0.1",
|
||||||
"tsx": "^4.4.0",
|
"tsx": "^4.4.0",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.2",
|
||||||
"vite-tsconfig-paths": "^4.2.2",
|
|
||||||
"vitest": "^1.2.2"
|
"vitest": "^1.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -115,7 +115,14 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore because generate schema happens after this
|
// @ts-ignore because generate schema happens after this
|
||||||
approverId: knex(TableName.ProjectMembership)
|
approverId: knex(TableName.ProjectMembership)
|
||||||
.select("id")
|
.join(
|
||||||
|
TableName.SecretApprovalPolicy,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
|
||||||
|
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
|
||||||
|
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
|
||||||
.where("userId", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverUserId`]))
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverUserId`]))
|
||||||
});
|
});
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
@@ -147,13 +154,27 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore because generate schema happens after this
|
// @ts-ignore because generate schema happens after this
|
||||||
committerId: knex(TableName.ProjectMembership)
|
committerId: knex(TableName.ProjectMembership)
|
||||||
.select("id")
|
.join(
|
||||||
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerUserId`])),
|
TableName.SecretApprovalPolicy,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
|
`${TableName.SecretApprovalRequest}.policyId`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
|
||||||
|
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
|
||||||
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerUserId`]))
|
||||||
|
.select(knex.ref("id").withSchema(TableName.ProjectMembership)),
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore because generate schema happens after this
|
// @ts-ignore because generate schema happens after this
|
||||||
statusChangeBy: knex(TableName.ProjectMembership)
|
statusChangeBy: knex(TableName.ProjectMembership)
|
||||||
.select("id")
|
.join(
|
||||||
|
TableName.SecretApprovalPolicy,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
|
`${TableName.SecretApprovalRequest}.policyId`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
|
||||||
|
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
|
||||||
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangedByUserId`]))
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangedByUserId`]))
|
||||||
|
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
|
||||||
});
|
});
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
|
||||||
@@ -177,8 +198,20 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore because generate schema happens after this
|
// @ts-ignore because generate schema happens after this
|
||||||
member: knex(TableName.ProjectMembership)
|
member: knex(TableName.ProjectMembership)
|
||||||
.select("id")
|
.join(
|
||||||
|
TableName.SecretApprovalRequest,
|
||||||
|
`${TableName.SecretApprovalRequest}.id`,
|
||||||
|
`${TableName.SecretApprovalRequestReviewer}.requestId`
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
TableName.SecretApprovalPolicy,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
|
`${TableName.SecretApprovalRequest}.policyId`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
|
||||||
|
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
|
||||||
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`]))
|
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`]))
|
||||||
|
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
|
||||||
});
|
});
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
|
||||||
tb.uuid("member").notNullable().alter();
|
tb.uuid("member").notNullable().alter();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
// TODO(akhilmhdh): Fix this when licence service gets it type
|
// TODO(akhilmhdh): Fix this when license service gets it type
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
|||||||
+3
-1
@@ -26,8 +26,10 @@ export const getDefaultOnPremFeatures = () => {
|
|||||||
status: null,
|
status: null,
|
||||||
trial_end: null,
|
trial_end: null,
|
||||||
has_used_trial: true,
|
has_used_trial: true,
|
||||||
secretApproval: false,
|
secretApproval: true,
|
||||||
secretRotation: true,
|
secretRotation: true,
|
||||||
caCrl: false
|
caCrl: false
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setupLicenseRequestWithStore = () => {};
|
||||||
+9
-9
@@ -49,15 +49,15 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
pkiEst: false
|
pkiEst: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||||
let token: string;
|
let token: string;
|
||||||
const licenceReq = axios.create({
|
const licenseReq = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
timeout: 35 * 1000
|
timeout: 35 * 1000
|
||||||
// signal: AbortSignal.timeout(60 * 1000)
|
// signal: AbortSignal.timeout(60 * 1000)
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshLicence = async () => {
|
const refreshLicense = async () => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const {
|
const {
|
||||||
data: { token: authToken }
|
data: { token: authToken }
|
||||||
@@ -75,7 +75,7 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
|
|||||||
return token;
|
return token;
|
||||||
};
|
};
|
||||||
|
|
||||||
licenceReq.interceptors.request.use(
|
licenseReq.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
if (token && config.headers) {
|
if (token && config.headers) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
@@ -86,7 +86,7 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
|
|||||||
(err) => Promise.reject(err)
|
(err) => Promise.reject(err)
|
||||||
);
|
);
|
||||||
|
|
||||||
licenceReq.interceptors.response.use(
|
licenseReq.interceptors.response.use(
|
||||||
(response) => response,
|
(response) => response,
|
||||||
async (err) => {
|
async (err) => {
|
||||||
const originalRequest = (err as AxiosError).config;
|
const originalRequest = (err as AxiosError).config;
|
||||||
@@ -97,15 +97,15 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
|
|||||||
(originalRequest as any)._retry = true; // injected
|
(originalRequest as any)._retry = true; // injected
|
||||||
|
|
||||||
// refresh
|
// refresh
|
||||||
await refreshLicence();
|
await refreshLicense();
|
||||||
|
|
||||||
licenceReq.defaults.headers.common.Authorization = `Bearer ${token}`;
|
licenseReq.defaults.headers.common.Authorization = `Bearer ${token}`;
|
||||||
return licenceReq(originalRequest!);
|
return licenseReq(originalRequest!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return { request: licenceReq, refreshLicence };
|
return { request: licenseReq, refreshLicense };
|
||||||
};
|
};
|
||||||
@@ -16,8 +16,8 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
|
|||||||
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { getDefaultOnPremFeatures, setupLicenceRequestWithStore } from "./licence-fns";
|
|
||||||
import { TLicenseDALFactory } from "./license-dal";
|
import { TLicenseDALFactory } from "./license-dal";
|
||||||
|
import { getDefaultOnPremFeatures, setupLicenseRequestWithStore } from "./license-fns";
|
||||||
import {
|
import {
|
||||||
InstanceType,
|
InstanceType,
|
||||||
TAddOrgPmtMethodDTO,
|
TAddOrgPmtMethodDTO,
|
||||||
@@ -64,13 +64,13 @@ export const licenseServiceFactory = ({
|
|||||||
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const licenseServerCloudApi = setupLicenceRequestWithStore(
|
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
appCfg.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_CLOUD_LOGIN,
|
LICENSE_SERVER_CLOUD_LOGIN,
|
||||||
appCfg.LICENSE_SERVER_KEY || ""
|
appCfg.LICENSE_SERVER_KEY || ""
|
||||||
);
|
);
|
||||||
|
|
||||||
const licenseServerOnPremApi = setupLicenceRequestWithStore(
|
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
appCfg.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_ON_PREM_LOGIN,
|
LICENSE_SERVER_ON_PREM_LOGIN,
|
||||||
appCfg.LICENSE_KEY || ""
|
appCfg.LICENSE_KEY || ""
|
||||||
@@ -79,7 +79,7 @@ export const licenseServiceFactory = ({
|
|||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
if (appCfg.LICENSE_SERVER_KEY) {
|
if (appCfg.LICENSE_SERVER_KEY) {
|
||||||
const token = await licenseServerCloudApi.refreshLicence();
|
const token = await licenseServerCloudApi.refreshLicense();
|
||||||
if (token) instanceType = InstanceType.Cloud;
|
if (token) instanceType = InstanceType.Cloud;
|
||||||
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
||||||
isValidLicense = true;
|
isValidLicense = true;
|
||||||
@@ -87,7 +87,7 @@ export const licenseServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY) {
|
if (appCfg.LICENSE_KEY) {
|
||||||
const token = await licenseServerOnPremApi.refreshLicence();
|
const token = await licenseServerOnPremApi.refreshLicense();
|
||||||
if (token) {
|
if (token) {
|
||||||
const {
|
const {
|
||||||
data: { currentPlan }
|
data: { currentPlan }
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const getIntegrationSecretsV2 = async (
|
|||||||
environment: string;
|
environment: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
|
secretPath: string;
|
||||||
decryptor: (value: Buffer | null | undefined) => string;
|
decryptor: (value: Buffer | null | undefined) => string;
|
||||||
},
|
},
|
||||||
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find" | "findByFolderId">,
|
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find" | "findByFolderId">,
|
||||||
@@ -306,6 +307,7 @@ export const deleteIntegrationSecrets = async ({
|
|||||||
? await getIntegrationSecretsV2(
|
? await getIntegrationSecretsV2(
|
||||||
{
|
{
|
||||||
environment: integration.environment.id,
|
environment: integration.environment.id,
|
||||||
|
secretPath: integration.secretPath,
|
||||||
projectId: integration.projectId,
|
projectId: integration.projectId,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
|
|||||||
@@ -158,9 +158,12 @@ export const fnSecretsV2FromImports = async ({
|
|||||||
depth?: number;
|
depth?: number;
|
||||||
cyclicDetector?: Set<string>;
|
cyclicDetector?: Set<string>;
|
||||||
decryptor: (value?: Buffer | null) => string;
|
decryptor: (value?: Buffer | null) => string;
|
||||||
expandSecretReferences?: (
|
expandSecretReferences?: (inputSecret: {
|
||||||
secrets: Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }>
|
value?: string;
|
||||||
) => Promise<Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }>>;
|
skipMultilineEncoding?: boolean | null;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
}) => Promise<string | undefined>;
|
||||||
}) => {
|
}) => {
|
||||||
// avoid going more than a depth
|
// avoid going more than a depth
|
||||||
if (depth >= LEVEL_BREAK) return [];
|
if (depth >= LEVEL_BREAK) return [];
|
||||||
@@ -244,26 +247,21 @@ export const fnSecretsV2FromImports = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (expandSecretReferences) {
|
if (expandSecretReferences) {
|
||||||
await Promise.all(
|
await Promise.allSettled(
|
||||||
processedImports.map(async (processedImport) => {
|
processedImports.map((processedImport) =>
|
||||||
const secretsGroupByKey = processedImport.secrets.reduce(
|
Promise.allSettled(
|
||||||
(acc, item) => {
|
processedImport.secrets.map(async (decryptedSecret, index) => {
|
||||||
acc[item.secretKey] = {
|
const expandedSecretValue = await expandSecretReferences({
|
||||||
value: item.secretValue,
|
value: decryptedSecret.secretValue,
|
||||||
comment: item.secretComment,
|
secretPath: processedImport.secretPath,
|
||||||
skipMultilineEncoding: item.skipMultilineEncoding
|
environment: processedImport.environment,
|
||||||
};
|
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
||||||
return acc;
|
});
|
||||||
},
|
// eslint-disable-next-line no-param-reassign
|
||||||
{} as Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean | null }>
|
processedImport.secrets[index].secretValue = expandedSecretValue || "";
|
||||||
);
|
})
|
||||||
// eslint-disable-next-line
|
)
|
||||||
await expandSecretReferences(secretsGroupByKey);
|
)
|
||||||
processedImport.secrets.forEach((decryptedSecret) => {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
decryptedSecret.secretValue = secretsGroupByKey[decryptedSecret.secretKey].value;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -377,150 +377,116 @@ type TInterpolateSecretArg = {
|
|||||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_SECRET_REFERENCE_DEPTH = 10;
|
||||||
export const expandSecretReferencesFactory = ({
|
export const expandSecretReferencesFactory = ({
|
||||||
projectId,
|
projectId,
|
||||||
decryptSecretValue: decryptSecret,
|
decryptSecretValue: decryptSecret,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
folderDAL
|
folderDAL
|
||||||
}: TInterpolateSecretArg) => {
|
}: TInterpolateSecretArg) => {
|
||||||
const fetchSecretFactory = () => {
|
const secretCache: Record<string, Record<string, string>> = {};
|
||||||
const secretCache: Record<string, Record<string, string>> = {};
|
const getCacheUniqueKey = (environment: string, secretPath: string) => `${environment}-${secretPath}`;
|
||||||
|
|
||||||
return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => {
|
const fetchSecret = async (environment: string, secretPath: string, secretKey: string) => {
|
||||||
const referredSecretPathURL = path.join("/", ...secRefPath);
|
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||||
const uniqueKey = `${secRefEnv}-${referredSecretPathURL}`;
|
|
||||||
|
|
||||||
if (secretCache?.[uniqueKey]) {
|
if (secretCache?.[cacheKey]) {
|
||||||
return secretCache[uniqueKey][secRefKey];
|
return secretCache[cacheKey][secretKey] || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, secRefEnv, referredSecretPathURL);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder) return "";
|
if (!folder) return "";
|
||||||
const secrets = await secretDAL.findByFolderId(folder.id);
|
const secrets = await secretDAL.findByFolderId(folder.id);
|
||||||
|
|
||||||
const decryptedSecret = secrets.reduce<Record<string, string>>((prev, secret) => {
|
const decryptedSecret = secrets.reduce<Record<string, string>>((prev, secret) => {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line no-param-reassign
|
||||||
prev[secret.key] = decryptSecret(secret.encryptedValue) || "";
|
prev[secret.key] = decryptSecret(secret.encryptedValue) || "";
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
secretCache[uniqueKey] = decryptedSecret;
|
secretCache[cacheKey] = decryptedSecret;
|
||||||
|
|
||||||
return secretCache[uniqueKey][secRefKey];
|
return secretCache[cacheKey][secretKey] || "";
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursivelyExpandSecret = async (
|
const recursivelyExpandSecret = async (dto: { value?: string; secretPath: string; environment: string }) => {
|
||||||
expandedSec: Record<string, string | undefined>,
|
if (!dto.value) return "";
|
||||||
interpolatedSec: Record<string, string | undefined>,
|
|
||||||
fetchSecret: (env: string, secPath: string[], secKey: string) => Promise<string>,
|
|
||||||
recursionChainBreaker: Record<string, boolean>,
|
|
||||||
key: string
|
|
||||||
): Promise<string | undefined> => {
|
|
||||||
if (expandedSec?.[key] !== undefined) {
|
|
||||||
return expandedSec[key];
|
|
||||||
}
|
|
||||||
if (recursionChainBreaker?.[key]) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
recursionChainBreaker[key] = true;
|
|
||||||
|
|
||||||
let interpolatedValue = interpolatedSec[key];
|
const stack = [{ ...dto, depth: 0 }];
|
||||||
if (!interpolatedValue) {
|
let expandedValue = dto.value;
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`Couldn't find referenced value - ${key}`);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG);
|
while (stack.length) {
|
||||||
if (refs) {
|
const { value, secretPath, environment, depth } = stack.pop()!;
|
||||||
for (const interpolationSyntax of refs) {
|
// eslint-disable-next-line no-continue
|
||||||
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
|
if (depth > MAX_SECRET_REFERENCE_DEPTH) continue;
|
||||||
const entities = interpolationKey.trim().split(".");
|
const refs = value?.match(INTERPOLATION_SYNTAX_REG);
|
||||||
|
|
||||||
if (entities.length === 1) {
|
if (refs) {
|
||||||
// eslint-disable-next-line
|
for (const interpolationSyntax of refs) {
|
||||||
const val = await recursivelyExpandSecret(
|
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
|
||||||
expandedSec,
|
const entities = interpolationKey.trim().split(".");
|
||||||
interpolatedSec,
|
|
||||||
fetchSecret,
|
|
||||||
recursionChainBreaker,
|
|
||||||
interpolationKey
|
|
||||||
);
|
|
||||||
if (val) {
|
|
||||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entities.length > 1) {
|
// eslint-disable-next-line no-continue
|
||||||
const secRefEnv = entities[0];
|
if (!entities.length) continue;
|
||||||
const secRefPath = entities.slice(1, entities.length - 1);
|
|
||||||
const secRefKey = entities[entities.length - 1];
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
if (entities.length === 1) {
|
||||||
const val = await fetchSecret(secRefEnv, secRefPath, secRefKey);
|
const [secretKey] = entities;
|
||||||
if (val) {
|
|
||||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
// eslint-disable-next-line no-continue,no-await-in-loop
|
||||||
|
const referedValue = await fetchSecret(environment, secretPath, secretKey);
|
||||||
|
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||||
|
secretCache[cacheKey][secretKey] = referedValue;
|
||||||
|
if (INTERPOLATION_SYNTAX_REG.test(referedValue)) {
|
||||||
|
stack.push({
|
||||||
|
value: referedValue,
|
||||||
|
secretPath,
|
||||||
|
environment,
|
||||||
|
depth: depth + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue);
|
||||||
|
} else {
|
||||||
|
const secretReferenceEnvironment = entities[0];
|
||||||
|
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
|
||||||
|
const secretReferenceKey = entities[entities.length - 1];
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const referedValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey);
|
||||||
|
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
|
||||||
|
secretCache[cacheKey][secretReferenceKey] = referedValue;
|
||||||
|
if (INTERPOLATION_SYNTAX_REG.test(referedValue)) {
|
||||||
|
stack.push({
|
||||||
|
value: referedValue,
|
||||||
|
secretPath: secretReferencePath,
|
||||||
|
environment: secretReferenceEnvironment,
|
||||||
|
depth: depth + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
return expandedValue;
|
||||||
expandedSec[key] = interpolatedValue;
|
|
||||||
return interpolatedValue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSecret = fetchSecretFactory();
|
const expandSecret = async (inputSecret: {
|
||||||
const expandSecrets = async (
|
value?: string;
|
||||||
inputSecrets: Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }>
|
skipMultilineEncoding?: boolean | null;
|
||||||
) => {
|
secretPath: string;
|
||||||
const expandedSecrets: Record<string, string | undefined> = {};
|
environment: string;
|
||||||
const toBeExpandedSecrets: Record<string, string | undefined> = {};
|
}) => {
|
||||||
|
if (!inputSecret.value) return inputSecret.value;
|
||||||
|
|
||||||
Object.keys(inputSecrets).forEach((key) => {
|
const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
|
||||||
if (inputSecrets[key].value?.match(INTERPOLATION_SYNTAX_REG)) {
|
if (!shouldExpand) return inputSecret.value;
|
||||||
toBeExpandedSecrets[key] = inputSecrets[key].value;
|
|
||||||
} else {
|
|
||||||
expandedSecrets[key] = inputSecrets[key].value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const key of Object.keys(inputSecrets)) {
|
const expandedSecretValue = await recursivelyExpandSecret(inputSecret);
|
||||||
if (expandedSecrets?.[key]) {
|
return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedSecretValue) : expandedSecretValue;
|
||||||
// should not do multi line encoding if user has set it to skip
|
|
||||||
// eslint-disable-next-line
|
|
||||||
inputSecrets[key].value = inputSecrets[key].skipMultilineEncoding
|
|
||||||
? formatMultiValueEnv(expandedSecrets[key])
|
|
||||||
: expandedSecrets[key];
|
|
||||||
// eslint-disable-next-line
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is to avoid recursion loop. So the graph should be direct graph rather than cyclic
|
|
||||||
// so for any recursion building if there is an entity two times same key meaning it will be looped
|
|
||||||
const recursionChainBreaker: Record<string, boolean> = {};
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const expandedVal = await recursivelyExpandSecret(
|
|
||||||
expandedSecrets,
|
|
||||||
toBeExpandedSecrets,
|
|
||||||
fetchSecret,
|
|
||||||
recursionChainBreaker,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
inputSecrets[key].value = inputSecrets[key].skipMultilineEncoding
|
|
||||||
? formatMultiValueEnv(expandedVal)
|
|
||||||
: expandedVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputSecrets;
|
|
||||||
};
|
};
|
||||||
return expandSecrets;
|
return expandSecret;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reshapeBridgeSecret = (
|
export const reshapeBridgeSecret = (
|
||||||
|
|||||||
@@ -521,27 +521,22 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
|
|
||||||
if (shouldExpandSecretReferences) {
|
if (shouldExpandSecretReferences) {
|
||||||
const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
|
const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
|
||||||
for (const secretPathKey in secretsGroupByPath) {
|
await Promise.allSettled(
|
||||||
if (Object.hasOwn(secretsGroupByPath, secretPathKey)) {
|
Object.keys(secretsGroupByPath).map((groupedPath) =>
|
||||||
const secretsGroupByKey = secretsGroupByPath[secretPathKey].reduce(
|
Promise.allSettled(
|
||||||
(acc, item) => {
|
secretsGroupByPath[groupedPath].map(async (decryptedSecret, index) => {
|
||||||
acc[item.secretKey] = {
|
const expandedSecretValue = await expandSecretReferences({
|
||||||
value: item.secretValue,
|
value: decryptedSecret.secretValue,
|
||||||
comment: item.secretComment,
|
secretPath: groupedPath,
|
||||||
skipMultilineEncoding: item.skipMultilineEncoding
|
environment,
|
||||||
};
|
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
||||||
return acc;
|
});
|
||||||
},
|
// eslint-disable-next-line no-param-reassign
|
||||||
{} as Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }>
|
secretsGroupByPath[groupedPath][index].secretValue = expandedSecretValue || "";
|
||||||
);
|
})
|
||||||
// eslint-disable-next-line
|
)
|
||||||
await expandSecretReferences(secretsGroupByKey);
|
)
|
||||||
secretsGroupByPath[secretPathKey].forEach((decryptedSecret) => {
|
);
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
decryptedSecret.secretValue = secretsGroupByKey[decryptedSecret.secretKey].value || "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!includeImports) {
|
if (!includeImports) {
|
||||||
@@ -693,12 +688,14 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
|
||||||
: "";
|
: "";
|
||||||
if (shouldExpandSecretReferences && secretValue) {
|
if (shouldExpandSecretReferences && secretValue) {
|
||||||
const secretReferenceExpandedRecord = {
|
|
||||||
[secret.key]: { value: secretValue }
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
await expandSecretReferences(secretReferenceExpandedRecord);
|
const expandedSecretValue = await expandSecretReferences({
|
||||||
secretValue = secretReferenceExpandedRecord[secret.key].value;
|
environment,
|
||||||
|
secretPath: path,
|
||||||
|
value: secretValue,
|
||||||
|
skipMultilineEncoding: secret.skipMultilineEncoding
|
||||||
|
});
|
||||||
|
secretValue = expandedSecretValue || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return reshapeBridgeSecret(projectId, environment, path, {
|
return reshapeBridgeSecret(projectId, environment, path, {
|
||||||
|
|||||||
@@ -196,6 +196,13 @@ export const recursivelyGetSecretPaths = ({
|
|||||||
return getPaths;
|
return getPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// used to convert multi line ones to quotes ones with \n
|
||||||
|
const formatMultiValueEnv = (val?: string) => {
|
||||||
|
if (!val) return "";
|
||||||
|
if (!val.match("\n")) return val;
|
||||||
|
return `"${val.replace(/\n/g, "\\n")}"`;
|
||||||
|
};
|
||||||
|
|
||||||
type TInterpolateSecretArg = {
|
type TInterpolateSecretArg = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
secretEncKey: string;
|
secretEncKey: string;
|
||||||
@@ -203,162 +210,128 @@ type TInterpolateSecretArg = {
|
|||||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_SECRET_REFERENCE_DEPTH = 5;
|
||||||
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
|
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
|
||||||
export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => {
|
export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => {
|
||||||
const fetchSecretsCrossEnv = () => {
|
const secretCache: Record<string, Record<string, string>> = {};
|
||||||
const fetchCache: Record<string, Record<string, string>> = {};
|
const getCacheUniqueKey = (environment: string, secretPath: string) => `${environment}-${secretPath}`;
|
||||||
|
|
||||||
return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => {
|
const fetchSecret = async (environment: string, secretPath: string, secretKey: string) => {
|
||||||
const secRefPathUrl = path.join("/", ...secRefPath);
|
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||||
const uniqKey = `${secRefEnv}-${secRefPathUrl}`;
|
const uniqKey = `${environment}-${cacheKey}`;
|
||||||
|
|
||||||
if (fetchCache?.[uniqKey]) {
|
if (secretCache?.[uniqKey]) {
|
||||||
return fetchCache[uniqKey][secRefKey];
|
return secretCache[uniqKey][secretKey] || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, secRefEnv, secRefPathUrl);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder) return "";
|
if (!folder) return "";
|
||||||
const secrets = await secretDAL.findByFolderId(folder.id);
|
const secrets = await secretDAL.findByFolderId(folder.id);
|
||||||
|
|
||||||
const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => {
|
const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => {
|
||||||
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
const decryptedSecretKey = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretKeyCiphertext,
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
iv: secret.secretKeyIV,
|
iv: secret.secretKeyIV,
|
||||||
tag: secret.secretKeyTag,
|
tag: secret.secretKeyTag,
|
||||||
key: secretEncKey
|
key: secretEncKey
|
||||||
});
|
});
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
const decryptedSecretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
ciphertext: secret.secretValueCiphertext,
|
ciphertext: secret.secretValueCiphertext,
|
||||||
iv: secret.secretValueIV,
|
iv: secret.secretValueIV,
|
||||||
tag: secret.secretValueTag,
|
tag: secret.secretValueTag,
|
||||||
key: secretEncKey
|
key: secretEncKey
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
prev[secretKey] = secretValue;
|
prev[decryptedSecretKey] = decryptedSecretValue;
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
fetchCache[uniqKey] = decryptedSec;
|
secretCache[uniqKey] = decryptedSec;
|
||||||
|
|
||||||
return fetchCache[uniqKey][secRefKey];
|
return secretCache[uniqKey][secretKey] || "";
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursivelyExpandSecret = async (
|
const recursivelyExpandSecret = async ({
|
||||||
expandedSec: Record<string, string>,
|
value,
|
||||||
interpolatedSec: Record<string, string>,
|
secretPath,
|
||||||
fetchCrossEnv: (env: string, secPath: string[], secKey: string) => Promise<string>,
|
environment,
|
||||||
recursionChainBreaker: Record<string, boolean>,
|
depth = 0
|
||||||
key: string
|
}: {
|
||||||
) => {
|
value?: string;
|
||||||
if (expandedSec?.[key] !== undefined) {
|
secretPath: string;
|
||||||
return expandedSec[key];
|
environment: string;
|
||||||
}
|
depth?: number;
|
||||||
if (recursionChainBreaker?.[key]) {
|
}) => {
|
||||||
return "";
|
if (!value) return "";
|
||||||
}
|
if (depth > MAX_SECRET_REFERENCE_DEPTH) return "";
|
||||||
// eslint-disable-next-line
|
|
||||||
recursionChainBreaker[key] = true;
|
|
||||||
|
|
||||||
let interpolatedValue = interpolatedSec[key];
|
const refs = value.match(INTERPOLATION_SYNTAX_REG);
|
||||||
if (!interpolatedValue) {
|
let expandedValue = value;
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`Couldn't find referenced value - ${key}`);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG);
|
|
||||||
if (refs) {
|
if (refs) {
|
||||||
for (const interpolationSyntax of refs) {
|
for (const interpolationSyntax of refs) {
|
||||||
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
|
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
|
||||||
const entities = interpolationKey.trim().split(".");
|
const entities = interpolationKey.trim().split(".");
|
||||||
|
|
||||||
if (entities.length === 1) {
|
if (entities.length === 1) {
|
||||||
const val = await recursivelyExpandSecret(
|
const [secretKey] = entities;
|
||||||
expandedSec,
|
|
||||||
interpolatedSec,
|
|
||||||
fetchCrossEnv,
|
|
||||||
recursionChainBreaker,
|
|
||||||
interpolationKey
|
|
||||||
);
|
|
||||||
if (val) {
|
|
||||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
continue;
|
let referenceValue = await fetchSecret(environment, secretPath, secretKey);
|
||||||
|
if (INTERPOLATION_SYNTAX_REG.test(referenceValue)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
referenceValue = await recursivelyExpandSecret({
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
value: referenceValue,
|
||||||
|
depth: depth + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||||
|
secretCache[cacheKey][secretKey] = referenceValue;
|
||||||
|
expandedValue = expandedValue.replaceAll(interpolationSyntax, referenceValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entities.length > 1) {
|
if (entities.length > 1) {
|
||||||
const secRefEnv = entities[0];
|
const secretReferenceEnvironment = entities[0];
|
||||||
const secRefPath = entities.slice(1, entities.length - 1);
|
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
|
||||||
const secRefKey = entities[entities.length - 1];
|
const secretReferenceKey = entities[entities.length - 1];
|
||||||
|
|
||||||
const val = await fetchCrossEnv(secRefEnv, secRefPath, secRefKey);
|
// eslint-disable-next-line
|
||||||
if (val) {
|
let referenceValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey);
|
||||||
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
|
if (INTERPOLATION_SYNTAX_REG.test(referenceValue)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
referenceValue = await recursivelyExpandSecret({
|
||||||
|
environment: secretReferenceEnvironment,
|
||||||
|
secretPath: secretReferencePath,
|
||||||
|
value: referenceValue,
|
||||||
|
depth: depth + 1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
|
||||||
|
secretCache[cacheKey][secretReferenceKey] = referenceValue;
|
||||||
|
expandedValue = expandedValue.replaceAll(interpolationSyntax, referenceValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
return expandedValue;
|
||||||
expandedSec[key] = interpolatedValue;
|
|
||||||
return interpolatedValue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// used to convert multi line ones to quotes ones with \n
|
const expandSecret = async (inputSecret: {
|
||||||
const formatMultiValueEnv = (val?: string) => {
|
value?: string;
|
||||||
if (!val) return "";
|
skipMultilineEncoding?: boolean | null;
|
||||||
if (!val.match("\n")) return val;
|
secretPath: string;
|
||||||
return `"${val.replace(/\n/g, "\\n")}"`;
|
environment: string;
|
||||||
|
}) => {
|
||||||
|
if (!inputSecret.value) return inputSecret.value;
|
||||||
|
|
||||||
|
const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
|
||||||
|
if (!shouldExpand) return inputSecret.value;
|
||||||
|
|
||||||
|
const expandedSecretValue = await recursivelyExpandSecret(inputSecret);
|
||||||
|
return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedSecretValue) : expandedSecretValue;
|
||||||
};
|
};
|
||||||
|
return expandSecret;
|
||||||
const expandSecrets = async (
|
|
||||||
secrets: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean | null }>
|
|
||||||
) => {
|
|
||||||
const expandedSec: Record<string, string> = {};
|
|
||||||
const interpolatedSec: Record<string, string> = {};
|
|
||||||
|
|
||||||
const crossSecEnvFetch = fetchSecretsCrossEnv();
|
|
||||||
|
|
||||||
Object.keys(secrets).forEach((key) => {
|
|
||||||
if (secrets[key].value.match(INTERPOLATION_SYNTAX_REG)) {
|
|
||||||
interpolatedSec[key] = secrets[key].value;
|
|
||||||
} else {
|
|
||||||
expandedSec[key] = secrets[key].value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const key of Object.keys(secrets)) {
|
|
||||||
if (expandedSec?.[key]) {
|
|
||||||
// should not do multi line encoding if user has set it to skip
|
|
||||||
// eslint-disable-next-line
|
|
||||||
secrets[key].value = secrets[key].skipMultilineEncoding
|
|
||||||
? formatMultiValueEnv(expandedSec[key])
|
|
||||||
: expandedSec[key];
|
|
||||||
// eslint-disable-next-line
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is to avoid recursion loop. So the graph should be direct graph rather than cyclic
|
|
||||||
// so for any recursion building if there is an entity two times same key meaning it will be looped
|
|
||||||
const recursionChainBreaker: Record<string, boolean> = {};
|
|
||||||
const expandedVal = await recursivelyExpandSecret(
|
|
||||||
expandedSec,
|
|
||||||
interpolatedSec,
|
|
||||||
crossSecEnvFetch,
|
|
||||||
recursionChainBreaker,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
secrets[key].value = secrets[key].skipMultilineEncoding ? formatMultiValueEnv(expandedVal) : expandedVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
return secrets;
|
|
||||||
};
|
|
||||||
return expandSecrets;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const decryptSecretRaw = (
|
export const decryptSecretRaw = (
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ export const secretQueueFactory = ({
|
|||||||
const getIntegrationSecretsV2 = async (dto: {
|
const getIntegrationSecretsV2 = async (dto: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
decryptor: (value: Buffer | null | undefined) => string;
|
decryptor: (value: Buffer | null | undefined) => string;
|
||||||
@@ -269,30 +270,36 @@ export const secretQueueFactory = ({
|
|||||||
);
|
);
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
// process secrets in current folder
|
|
||||||
const secrets = await secretV2BridgeDAL.findByFolderId(dto.folderId);
|
|
||||||
secrets.forEach((secret) => {
|
|
||||||
const secretKey = secret.key;
|
|
||||||
const secretValue = dto.decryptor(secret.encryptedValue);
|
|
||||||
content[secretKey] = { value: secretValue };
|
|
||||||
|
|
||||||
if (secret.encryptedComment) {
|
|
||||||
const commentValue = dto.decryptor(secret.encryptedComment);
|
|
||||||
content[secretKey].comment = commentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
|
||||||
});
|
|
||||||
|
|
||||||
const expandSecretReferences = expandSecretReferencesFactory({
|
const expandSecretReferences = expandSecretReferencesFactory({
|
||||||
decryptSecretValue: dto.decryptor,
|
decryptSecretValue: dto.decryptor,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
projectId: dto.projectId
|
projectId: dto.projectId
|
||||||
});
|
});
|
||||||
|
// process secrets in current folder
|
||||||
|
const secrets = await secretV2BridgeDAL.findByFolderId(dto.folderId);
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
secrets.map(async (secret) => {
|
||||||
|
const secretKey = secret.key;
|
||||||
|
const secretValue = dto.decryptor(secret.encryptedValue);
|
||||||
|
const expandedSecretValue = await expandSecretReferences({
|
||||||
|
environment: dto.environment,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
skipMultilineEncoding: secret.skipMultilineEncoding,
|
||||||
|
value: secretValue
|
||||||
|
});
|
||||||
|
content[secretKey] = { value: expandedSecretValue || "" };
|
||||||
|
|
||||||
|
if (secret.encryptedComment) {
|
||||||
|
const commentValue = dto.decryptor(secret.encryptedComment);
|
||||||
|
content[secretKey].comment = commentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
await expandSecretReferences(content);
|
|
||||||
// check if current folder has any imports from other folders
|
// check if current folder has any imports from other folders
|
||||||
const secretImports = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
|
const secretImports = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
|
||||||
|
|
||||||
@@ -329,6 +336,7 @@ export const secretQueueFactory = ({
|
|||||||
const getIntegrationSecrets = async (dto: {
|
const getIntegrationSecrets = async (dto: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
key: string;
|
key: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
@@ -341,46 +349,52 @@ export const secretQueueFactory = ({
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
// process secrets in current folder
|
const expandSecretReferences = interpolateSecrets({
|
||||||
const secrets = await secretDAL.findByFolderId(dto.folderId);
|
|
||||||
secrets.forEach((secret) => {
|
|
||||||
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretKeyCiphertext,
|
|
||||||
iv: secret.secretKeyIV,
|
|
||||||
tag: secret.secretKeyTag,
|
|
||||||
key: dto.key
|
|
||||||
});
|
|
||||||
|
|
||||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretValueCiphertext,
|
|
||||||
iv: secret.secretValueIV,
|
|
||||||
tag: secret.secretValueTag,
|
|
||||||
key: dto.key
|
|
||||||
});
|
|
||||||
|
|
||||||
content[secretKey] = { value: secretValue };
|
|
||||||
|
|
||||||
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
|
||||||
const commentValue = decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: secret.secretCommentCiphertext,
|
|
||||||
iv: secret.secretCommentIV,
|
|
||||||
tag: secret.secretCommentTag,
|
|
||||||
key: dto.key
|
|
||||||
});
|
|
||||||
content[secretKey].comment = commentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
|
||||||
});
|
|
||||||
|
|
||||||
const expandSecrets = interpolateSecrets({
|
|
||||||
projectId: dto.projectId,
|
projectId: dto.projectId,
|
||||||
secretEncKey: dto.key,
|
secretEncKey: dto.key,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL
|
secretDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandSecrets(content);
|
// process secrets in current folder
|
||||||
|
const secrets = await secretDAL.findByFolderId(dto.folderId);
|
||||||
|
await Promise.allSettled(
|
||||||
|
secrets.map(async (secret) => {
|
||||||
|
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
|
iv: secret.secretKeyIV,
|
||||||
|
tag: secret.secretKeyTag,
|
||||||
|
key: dto.key
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretValueCiphertext,
|
||||||
|
iv: secret.secretValueIV,
|
||||||
|
tag: secret.secretValueTag,
|
||||||
|
key: dto.key
|
||||||
|
});
|
||||||
|
const expandedSecretValue = await expandSecretReferences({
|
||||||
|
environment: dto.environment,
|
||||||
|
secretPath: dto.secretPath,
|
||||||
|
skipMultilineEncoding: secret.skipMultilineEncoding,
|
||||||
|
value: secretValue
|
||||||
|
});
|
||||||
|
|
||||||
|
content[secretKey] = { value: expandedSecretValue || "" };
|
||||||
|
|
||||||
|
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
||||||
|
const commentValue = decryptSymmetric128BitHexKeyUTF8({
|
||||||
|
ciphertext: secret.secretCommentCiphertext,
|
||||||
|
iv: secret.secretCommentIV,
|
||||||
|
tag: secret.secretCommentTag,
|
||||||
|
key: dto.key
|
||||||
|
});
|
||||||
|
content[secretKey].comment = commentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// check if current folder has any imports from other folders
|
// check if current folder has any imports from other folders
|
||||||
const secretImport = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
|
const secretImport = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
|
||||||
@@ -404,7 +418,8 @@ export const secretQueueFactory = ({
|
|||||||
projectId: dto.projectId,
|
projectId: dto.projectId,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
key: dto.key,
|
key: dto.key,
|
||||||
depth: dto.depth + 1
|
depth: dto.depth + 1,
|
||||||
|
secretPath: dto.secretPath
|
||||||
});
|
});
|
||||||
|
|
||||||
// add the imported secrets to the current folder secrets
|
// add the imported secrets to the current folder secrets
|
||||||
@@ -686,6 +701,7 @@ export const secretQueueFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
|
secretPath,
|
||||||
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : "")
|
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : "")
|
||||||
})
|
})
|
||||||
: await getIntegrationSecrets({
|
: await getIntegrationSecrets({
|
||||||
@@ -693,7 +709,8 @@ export const secretQueueFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
key: botKey as string,
|
key: botKey as string,
|
||||||
depth: 1
|
depth: 1,
|
||||||
|
secretPath
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const integration of toBeSyncedIntegrations) {
|
for (const integration of toBeSyncedIntegrations) {
|
||||||
|
|||||||
@@ -482,7 +482,7 @@ export const secretServiceFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
// TODO(akhilmhdh-pg): licence check, posthog service and snapshot
|
// TODO(akhilmhdh-pg): license check, posthog service and snapshot
|
||||||
return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment, secretPath: path };
|
return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment, secretPath: path };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1047,74 +1047,47 @@ export const secretServiceFactory = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const expandSecret = interpolateSecrets({
|
||||||
|
folderDAL,
|
||||||
|
projectId,
|
||||||
|
secretDAL,
|
||||||
|
secretEncKey: botKey
|
||||||
|
});
|
||||||
|
|
||||||
if (expandSecretReferences) {
|
if (expandSecretReferences) {
|
||||||
const expandSecrets = interpolateSecrets({
|
const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
|
||||||
folderDAL,
|
await Promise.allSettled(
|
||||||
projectId,
|
Object.keys(secretsGroupByPath).map((groupedPath) =>
|
||||||
secretDAL,
|
Promise.allSettled(
|
||||||
secretEncKey: botKey
|
secretsGroupByPath[groupedPath].map(async (decryptedSecret, index) => {
|
||||||
});
|
const expandedSecretValue = await expandSecret({
|
||||||
|
value: decryptedSecret.secretValue,
|
||||||
const batchSecretsExpand = async (
|
secretPath: groupedPath,
|
||||||
secretBatch: {
|
environment,
|
||||||
secretKey: string;
|
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
||||||
secretValue: string;
|
});
|
||||||
secretComment?: string;
|
// eslint-disable-next-line no-param-reassign
|
||||||
secretPath: string;
|
secretsGroupByPath[groupedPath][index].secretValue = expandedSecretValue || "";
|
||||||
skipMultilineEncoding: boolean | null | undefined;
|
})
|
||||||
}[]
|
)
|
||||||
) => {
|
)
|
||||||
// Group secrets by secretPath
|
);
|
||||||
const secretsByPath: Record<
|
await Promise.allSettled(
|
||||||
string,
|
processedImports.map((processedImport) =>
|
||||||
{
|
Promise.allSettled(
|
||||||
secretKey: string;
|
processedImport.secrets.map(async (decryptedSecret, index) => {
|
||||||
secretValue: string;
|
const expandedSecretValue = await expandSecret({
|
||||||
secretComment?: string;
|
value: decryptedSecret.secretValue,
|
||||||
skipMultilineEncoding: boolean | null | undefined;
|
secretPath: path,
|
||||||
}[]
|
environment,
|
||||||
> = {};
|
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
||||||
|
});
|
||||||
secretBatch.forEach((secret) => {
|
// eslint-disable-next-line no-param-reassign
|
||||||
if (!secretsByPath[secret.secretPath]) {
|
processedImport.secrets[index].secretValue = expandedSecretValue || "";
|
||||||
secretsByPath[secret.secretPath] = [];
|
})
|
||||||
}
|
)
|
||||||
secretsByPath[secret.secretPath].push(secret);
|
)
|
||||||
});
|
);
|
||||||
|
|
||||||
// Expand secrets for each group
|
|
||||||
for (const secPath in secretsByPath) {
|
|
||||||
if (!Object.hasOwn(secretsByPath, path)) {
|
|
||||||
// eslint-disable-next-line no-continue
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const secretRecord: Record<
|
|
||||||
string,
|
|
||||||
{ value: string; comment?: string; skipMultilineEncoding: boolean | null | undefined }
|
|
||||||
> = {};
|
|
||||||
secretsByPath[secPath].forEach((decryptedSecret) => {
|
|
||||||
secretRecord[decryptedSecret.secretKey] = {
|
|
||||||
value: decryptedSecret.secretValue,
|
|
||||||
comment: decryptedSecret.secretComment,
|
|
||||||
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
await expandSecrets(secretRecord);
|
|
||||||
|
|
||||||
secretsByPath[secPath].forEach((decryptedSecret) => {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
decryptedSecret.secretValue = secretRecord[decryptedSecret.secretKey].value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// expand secrets
|
|
||||||
await batchSecretsExpand(filteredSecrets);
|
|
||||||
|
|
||||||
// expand imports by batch
|
|
||||||
await Promise.all(processedImports.map((processedImport) => batchSecretsExpand(processedImport.secrets)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1177,40 +1150,19 @@ export const secretServiceFactory = ({
|
|||||||
const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey);
|
const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey);
|
||||||
|
|
||||||
if (expandSecretReferences) {
|
if (expandSecretReferences) {
|
||||||
const expandSecrets = interpolateSecrets({
|
const expandSecret = interpolateSecrets({
|
||||||
folderDAL,
|
folderDAL,
|
||||||
projectId,
|
projectId,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretEncKey: botKey
|
secretEncKey: botKey
|
||||||
});
|
});
|
||||||
|
const expandedSecretValue = await expandSecret({
|
||||||
const expandSingleSecret = async (secret: {
|
environment,
|
||||||
secretKey: string;
|
secretPath: path,
|
||||||
secretValue: string;
|
value: decryptedSecret.secretValue,
|
||||||
secretComment?: string;
|
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
|
||||||
secretPath: string;
|
});
|
||||||
skipMultilineEncoding: boolean | null | undefined;
|
decryptedSecret.secretValue = expandedSecretValue || "";
|
||||||
}) => {
|
|
||||||
const secretRecord: Record<
|
|
||||||
string,
|
|
||||||
{ value: string; comment?: string; skipMultilineEncoding: boolean | null | undefined }
|
|
||||||
> = {
|
|
||||||
[secret.secretKey]: {
|
|
||||||
value: secret.secretValue,
|
|
||||||
comment: secret.secretComment,
|
|
||||||
skipMultilineEncoding: secret.skipMultilineEncoding
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await expandSecrets(secretRecord);
|
|
||||||
|
|
||||||
// Update the secret with the expanded value
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
secret.secretValue = secretRecord[secret.secretKey].value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Expand the secret
|
|
||||||
await expandSingleSecret(decryptedSecret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return decryptedSecret;
|
return decryptedSecret;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import tsconfigPaths from "vite-tsconfig-paths"; // only if you are using custom tsconfig paths
|
import path from "path";
|
||||||
import { defineConfig } from "vitest/config";
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -15,7 +15,14 @@ export default defineConfig({
|
|||||||
useAtomics: true,
|
useAtomics: true,
|
||||||
isolate: false
|
isolate: false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
alias: {
|
||||||
|
"./license-fns": path.resolve(__dirname, "./src/ee/services/license/__mocks__/license-fns")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [tsconfigPaths()] // only if you are using custom tsconfig paths,
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@app": path.resolve(__dirname, "./src")
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user