mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 18:15:53 +00:00
feature: server-side pagination/filtering/sorting for secrets overview and main pages
This commit is contained in:
@@ -59,7 +59,7 @@ type TSecretV2BridgeServiceFactoryDep = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||
folderDAL: Pick<
|
||||
TSecretFolderDALFactory,
|
||||
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find"
|
||||
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" | "findBySecretPathMultiEnv"
|
||||
>;
|
||||
secretImportDAL: Pick<TSecretImportDALFactory, "find" | "findByFolderIds">;
|
||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "handleSecretReminder" | "removeSecretReminder">;
|
||||
@@ -431,6 +431,175 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
// get unique secrets count for multiple envs
|
||||
const getSecretsCountMultiEnv = async ({
|
||||
actorId,
|
||||
path,
|
||||
|
||||
projectId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
environments,
|
||||
...params
|
||||
}: Pick<TGetSecretsDTO, "actorId" | "actor" | "path" | "projectId" | "actorOrgId" | "actorAuthMethod" | "search"> & {
|
||||
environments: string[];
|
||||
}) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
// verify user has access to all environments
|
||||
environments.forEach((environment) =>
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
|
||||
)
|
||||
);
|
||||
|
||||
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, path);
|
||||
if (!folders.length) return 0;
|
||||
|
||||
const count = await secretDAL.countByFolderIds(
|
||||
folders.map((folder) => folder.id),
|
||||
actorId,
|
||||
undefined,
|
||||
params
|
||||
);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
// get secret count for individual env
|
||||
const getSecretsCount = async ({
|
||||
actorId,
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
...params
|
||||
}: Pick<
|
||||
TGetSecretsDTO,
|
||||
| "actorId"
|
||||
| "actor"
|
||||
| "path"
|
||||
| "projectId"
|
||||
| "actorOrgId"
|
||||
| "actorAuthMethod"
|
||||
| "tagSlugs"
|
||||
| "environment"
|
||||
| "search"
|
||||
>) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
|
||||
);
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
|
||||
if (!folder) return 0;
|
||||
|
||||
const count = await secretDAL.countByFolderIds([folder.id], actorId, undefined, params);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
// get secrets for multiple envs
|
||||
const getSecretsMultiEnv = async ({
|
||||
actorId,
|
||||
path,
|
||||
environments,
|
||||
projectId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
...params
|
||||
}: Pick<TGetSecretsDTO, "actorId" | "actor" | "path" | "projectId" | "actorOrgId" | "actorAuthMethod" | "search"> & {
|
||||
environments: string[];
|
||||
}) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
let paths: { folderId: string; path: string; environment: string }[] = [];
|
||||
|
||||
// verify user has access to all environments
|
||||
environments.forEach((environment) =>
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
|
||||
)
|
||||
);
|
||||
|
||||
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, path);
|
||||
|
||||
const data: { [key: string]: typeof decryptedSecrets } = {};
|
||||
|
||||
if (!folders.length) {
|
||||
environments.forEach((env) => {
|
||||
data[env] = [];
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
paths = folders.map((folder) => ({ folderId: folder.id, path, environment: folder.environment.slug }));
|
||||
|
||||
const groupedPaths = groupBy(paths, (p) => p.folderId);
|
||||
|
||||
const secrets = await secretDAL.findByFolderIds(
|
||||
paths.map((p) => p.folderId),
|
||||
actorId,
|
||||
undefined,
|
||||
params
|
||||
);
|
||||
|
||||
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
});
|
||||
|
||||
const decryptedSecrets = secrets.map((secret) =>
|
||||
reshapeBridgeSecret(
|
||||
projectId,
|
||||
groupedPaths[secret.folderId][0].environment,
|
||||
groupedPaths[secret.folderId][0].path,
|
||||
{
|
||||
...secret,
|
||||
value: secret.encryptedValue
|
||||
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
|
||||
: "",
|
||||
comment: secret.encryptedComment
|
||||
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString()
|
||||
: ""
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
decryptedSecrets.forEach((secret) => {
|
||||
data[secret.environment] = [...(data[secret.environment] ?? []), secret];
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getSecrets = async ({
|
||||
actorId,
|
||||
path,
|
||||
@@ -441,8 +610,8 @@ export const secretV2BridgeServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
includeImports,
|
||||
recursive,
|
||||
tagSlugs = [],
|
||||
expandSecretReferences: shouldExpandSecretReferences
|
||||
expandSecretReferences: shouldExpandSecretReferences,
|
||||
...params
|
||||
}: TGetSecretsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
@@ -490,7 +659,9 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
const secrets = await secretDAL.findByFolderIds(
|
||||
paths.map((p) => p.folderId),
|
||||
actorId
|
||||
actorId,
|
||||
undefined,
|
||||
params
|
||||
);
|
||||
|
||||
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
@@ -509,9 +680,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
: ""
|
||||
})
|
||||
);
|
||||
const filteredSecrets = tagSlugs.length
|
||||
? decryptedSecrets.filter((secret) => Boolean(secret.tags?.find((el) => tagSlugs.includes(el.slug))))
|
||||
: decryptedSecrets;
|
||||
|
||||
const expandSecretReferences = expandSecretReferencesFactory({
|
||||
projectId,
|
||||
folderDAL,
|
||||
@@ -520,7 +689,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
|
||||
if (shouldExpandSecretReferences) {
|
||||
const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
|
||||
const secretsGroupByPath = groupBy(decryptedSecrets, (i) => i.secretPath);
|
||||
await Promise.allSettled(
|
||||
Object.keys(secretsGroupByPath).map((groupedPath) =>
|
||||
Promise.allSettled(
|
||||
@@ -541,7 +710,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
if (!includeImports) {
|
||||
return {
|
||||
secrets: filteredSecrets
|
||||
secrets: decryptedSecrets
|
||||
};
|
||||
}
|
||||
|
||||
@@ -569,7 +738,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
|
||||
return {
|
||||
secrets: filteredSecrets,
|
||||
secrets: decryptedSecrets,
|
||||
imports: importedSecrets
|
||||
};
|
||||
};
|
||||
@@ -1416,6 +1585,9 @@ export const secretV2BridgeServiceFactory = ({
|
||||
getSecrets,
|
||||
getSecretVersions,
|
||||
backfillSecretReferences,
|
||||
moveSecrets
|
||||
moveSecrets,
|
||||
getSecretsCount,
|
||||
getSecretsCountMultiEnv,
|
||||
getSecretsMultiEnv
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user