feature: deep search for secrets dashboard

This commit is contained in:
Scott Wilson
2024-10-29 15:08:19 -07:00
parent 1309f30af9
commit 21ea7dd317
30 changed files with 1538 additions and 238 deletions
@@ -8,6 +8,8 @@ import { ormify, selectAllTableCols } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { SecretsOrderBy } from "@app/services/secret/secret-types";
import { TFindFoldersDeepByParentIdsDTO } from "./secret-folder-types";
export const validateFolderName = (folderName: string) => {
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
return validNameRegex.test(folderName);
@@ -444,6 +446,48 @@ export const secretFolderDALFactory = (db: TDbClient) => {
}
};
const findByEnvsDeep = async ({ parentIds }: TFindFoldersDeepByParentIdsDTO, tx?: Knex) => {
try {
const folders = await (tx || db.replicaNode())
.withRecursive("parents", (qb) =>
qb
.select(
selectAllTableCols(TableName.SecretFolder),
db.raw("0 as depth"),
db.raw(`'/' as path`),
db.ref(`${TableName.Environment}.slug`).as("environment")
)
.from(TableName.SecretFolder)
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.whereIn(`${TableName.SecretFolder}.id`, parentIds)
.union((un) => {
void un
.select(
selectAllTableCols(TableName.SecretFolder),
db.raw("parents.depth + 1 as depth"),
db.raw(
`CONCAT(
CASE WHEN parents.path = '/' THEN '' ELSE parents.path END,
CASE WHEN ${TableName.SecretFolder}."parentId" is NULL THEN '' ELSE CONCAT('/', secret_folders.name) END
)`
),
db.ref("parents.environment")
)
.from(TableName.SecretFolder)
.join("parents", `${TableName.SecretFolder}.parentId`, "parents.id");
})
)
.select<(TSecretFolders & { path: string; depth: number; environment: string })[]>("*")
.from("parents")
.orderBy("depth")
.orderBy(`name`);
return folders;
} catch (error) {
throw new DatabaseError({ error, name: "FindByEnvsDeep" });
}
};
return {
...secretFolderOrm,
update,
@@ -454,6 +498,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
findSecretPathByFolderIds,
findClosestFolder,
findByProjectId,
findByMultiEnv
findByMultiEnv,
findByEnvsDeep
};
};
@@ -7,7 +7,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrderByDirection } from "@app/lib/types";
import { OrderByDirection, ProjectServiceActor } from "@app/lib/types";
import { TProjectDALFactory } from "../project/project-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
@@ -17,6 +17,7 @@ import {
TDeleteFolderDTO,
TGetFolderByIdDTO,
TGetFolderDTO,
TGetFoldersDeepByEnvsDTO,
TUpdateFolderDTO,
TUpdateManyFoldersDTO
} from "./secret-folder-types";
@@ -511,6 +512,30 @@ export const secretFolderServiceFactory = ({
};
};
const getFoldersDeepByEnvs = async (
{ projectId, environments, secretPath }: TGetFoldersDeepByEnvsDTO,
actor: ProjectServiceActor
) => {
// folder list is allowed to be read by anyone
// permission to check does user have access
await permissionService.getProjectPermission(actor.type, actor.id, projectId, actor.authMethod, actor.orgId);
const envs = await projectEnvDAL.findBySlugs(projectId, environments);
if (!envs.length)
throw new NotFoundError({
message: `Environments '${environments.join(", ")}' not found`,
name: "GetFoldersDeep"
});
const parentFolders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, secretPath);
if (!parentFolders.length) return [];
const folders = await folderDAL.findByEnvsDeep({ parentIds: parentFolders.map((parent) => parent.id) });
return folders;
};
return {
createFolder,
updateFolder,
@@ -519,6 +544,7 @@ export const secretFolderServiceFactory = ({
getFolders,
getFolderById,
getProjectFolderCount,
getFoldersMultiEnv
getFoldersMultiEnv,
getFoldersDeepByEnvs
};
};
@@ -47,3 +47,13 @@ export type TGetFolderDTO = {
export type TGetFolderByIdDTO = {
id: string;
} & Omit<TProjectPermission, "projectId">;
export type TGetFoldersDeepByEnvsDTO = {
projectId: string;
environments: string[];
secretPath: string;
};
export type TFindFoldersDeepByParentIdsDTO = {
parentIds: string[];
};