mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #1735 from akhilmhdh/import/recursive
feat(server): recursive imported secret fetch for api
This commit is contained in:
@@ -1,33 +1,66 @@
|
|||||||
import { SecretType, TSecretImports } from "@app/db/schemas";
|
import { SecretType, TSecretImports, TSecrets } from "@app/db/schemas";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
|
|
||||||
import { TSecretDALFactory } from "../secret/secret-dal";
|
import { TSecretDALFactory } from "../secret/secret-dal";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
|
import { TSecretImportDALFactory } from "./secret-import-dal";
|
||||||
|
|
||||||
|
type TSecretImportSecrets = {
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
environmentInfo: {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
folderId: string | undefined;
|
||||||
|
importFolderId: string;
|
||||||
|
secrets: (TSecrets & { workspace: string; environment: string; _id: string })[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const LEVEL_BREAK = 10;
|
||||||
|
const getImportUniqKey = (envSlug: string, path: string) => `${envSlug}=${path}`;
|
||||||
export const fnSecretsFromImports = async ({
|
export const fnSecretsFromImports = async ({
|
||||||
allowedImports,
|
allowedImports: possibleCyclicImports,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL
|
secretDAL,
|
||||||
|
secretImportDAL,
|
||||||
|
depth = 0,
|
||||||
|
cyclicDetector = new Set()
|
||||||
}: {
|
}: {
|
||||||
allowedImports: (Omit<TSecretImports, "importEnv"> & {
|
allowedImports: (Omit<TSecretImports, "importEnv"> & {
|
||||||
importEnv: { id: string; slug: string; name: string };
|
importEnv: { id: string; slug: string; name: string };
|
||||||
})[];
|
})[];
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "findByManySecretPath">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findByManySecretPath">;
|
||||||
secretDAL: Pick<TSecretDALFactory, "find">;
|
secretDAL: Pick<TSecretDALFactory, "find">;
|
||||||
|
secretImportDAL: Pick<TSecretImportDALFactory, "findByFolderIds">;
|
||||||
|
depth?: number;
|
||||||
|
cyclicDetector?: Set<string>;
|
||||||
}) => {
|
}) => {
|
||||||
const importedFolders = await folderDAL.findByManySecretPath(
|
// avoid going more than a depth
|
||||||
allowedImports.map(({ importEnv, importPath }) => ({
|
if (depth >= LEVEL_BREAK) return [];
|
||||||
envId: importEnv.id,
|
|
||||||
secretPath: importPath
|
const allowedImports = possibleCyclicImports.filter(
|
||||||
}))
|
({ importPath, importEnv }) => !cyclicDetector.has(getImportUniqKey(importEnv.slug, importPath))
|
||||||
);
|
);
|
||||||
const folderIds = importedFolders.map((el) => el?.id).filter(Boolean) as string[];
|
|
||||||
if (!folderIds.length) {
|
const importedFolders = (
|
||||||
|
await folderDAL.findByManySecretPath(
|
||||||
|
allowedImports.map(({ importEnv, importPath }) => ({
|
||||||
|
envId: importEnv.id,
|
||||||
|
secretPath: importPath
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
).filter(Boolean); // remove undefined ones
|
||||||
|
if (!importedFolders.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importedFolderIds = importedFolders.map((el) => el?.id) as string[];
|
||||||
|
const importedFolderGroupBySourceImport = groupBy(importedFolders, (i) => `${i?.envId}-${i?.path}`);
|
||||||
const importedSecrets = await secretDAL.find(
|
const importedSecrets = await secretDAL.find(
|
||||||
{
|
{
|
||||||
$in: { folderId: folderIds },
|
$in: { folderId: importedFolderIds },
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -35,18 +68,50 @@ export const fnSecretsFromImports = async ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const importedSecsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId);
|
const importedSecretsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId);
|
||||||
return allowedImports.map(({ importPath, importEnv }, i) => ({
|
|
||||||
secretPath: importPath,
|
allowedImports.forEach(({ importPath, importEnv }) => {
|
||||||
environment: importEnv.slug,
|
cyclicDetector.add(getImportUniqKey(importEnv.slug, importPath));
|
||||||
environmentInfo: importEnv,
|
});
|
||||||
folderId: importedFolders?.[i]?.id,
|
// now we need to check recursively deeper imports made inside other imports
|
||||||
// this will ensure for cases when secrets are empty. Could be due to missing folder for a path or when emtpy secrets inside a given path
|
// we go level wise meaning we take all imports of a tree level and then go deeper ones level by level
|
||||||
secrets: (importedSecsGroupByFolderId?.[importedFolders?.[i]?.id as string] || []).map((item) => ({
|
const deeperImports = await secretImportDAL.findByFolderIds(importedFolderIds);
|
||||||
...item,
|
let secretsFromDeeperImports: TSecretImportSecrets[] = [];
|
||||||
|
if (deeperImports.length) {
|
||||||
|
secretsFromDeeperImports = await fnSecretsFromImports({
|
||||||
|
allowedImports: deeperImports,
|
||||||
|
secretImportDAL,
|
||||||
|
folderDAL,
|
||||||
|
secretDAL,
|
||||||
|
depth: depth + 1,
|
||||||
|
cyclicDetector
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const secretsFromdeeperImportGroupedByFolderId = groupBy(secretsFromDeeperImports, (i) => i.importFolderId);
|
||||||
|
|
||||||
|
const secrets = allowedImports.map(({ importPath, importEnv, id, folderId }, i) => {
|
||||||
|
const sourceImportFolder = importedFolderGroupBySourceImport[`${importEnv.id}-${importPath}`][0];
|
||||||
|
const folderDeeperImportSecrets =
|
||||||
|
secretsFromdeeperImportGroupedByFolderId?.[sourceImportFolder?.id || ""]?.[0]?.secrets || [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
secretPath: importPath,
|
||||||
environment: importEnv.slug,
|
environment: importEnv.slug,
|
||||||
workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend.
|
environmentInfo: importEnv,
|
||||||
_id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend.
|
folderId: importedFolders?.[i]?.id,
|
||||||
}))
|
id,
|
||||||
}));
|
importFolderId: folderId,
|
||||||
|
// this will ensure for cases when secrets are empty. Could be due to missing folder for a path or when emtpy secrets inside a given path
|
||||||
|
secrets: (importedSecretsGroupByFolderId?.[importedFolders?.[i]?.id as string] || [])
|
||||||
|
.map((item) => ({
|
||||||
|
...item,
|
||||||
|
environment: importEnv.slug,
|
||||||
|
workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend.
|
||||||
|
_id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend.
|
||||||
|
}))
|
||||||
|
.concat(folderDeeperImportSecrets)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return secrets;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ export const secretImportServiceFactory = ({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL });
|
return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL });
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -525,7 +525,8 @@ export const secretServiceFactory = ({
|
|||||||
const importedSecrets = await fnSecretsFromImports({
|
const importedSecrets = await fnSecretsFromImports({
|
||||||
allowedImports,
|
allowedImports,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
folderDAL
|
folderDAL,
|
||||||
|
secretImportDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -630,7 +631,8 @@ export const secretServiceFactory = ({
|
|||||||
const importedSecrets = await fnSecretsFromImports({
|
const importedSecrets = await fnSecretsFromImports({
|
||||||
allowedImports,
|
allowedImports,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
folderDAL
|
folderDAL,
|
||||||
|
secretImportDAL
|
||||||
});
|
});
|
||||||
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
|
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
|
||||||
for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) {
|
for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user