diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index d768066fa7..5bbe49cf29 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -347,6 +347,7 @@ export const RAW_SECRETS = { tagIds: "The ID of the tags to be attached to the created secret." }, GET: { + expand: "Whether or not to expand secret references", secretName: "The name of the secret to get.", workspaceId: "The ID of the project to get the secret from.", workspaceSlug: "The slug of the project to get the secret from.", diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index e3f1528ade..b92138ec8f 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -300,6 +300,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath), version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version), type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.GET.type), + expandSecretReferences: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.GET.expand), include_imports: z .enum(["true", "false"]) .default("false") @@ -344,6 +349,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { actor: req.permission.type, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, + expandSecretReferences: req.query.expandSecretReferences, environment, projectId: workspaceId, projectSlug: workspaceSlug, diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index a5a469a8f8..cbfccba91c 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -1078,6 +1078,7 @@ export const secretServiceFactory = ({ actor, environment, projectId: workspaceId, + expandSecretReferences, projectSlug, actorId, actorOrgId, @@ -1091,7 +1092,7 @@ export const secretServiceFactory = ({ const botKey = await projectBotService.getBotKey(projectId); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); - const secret = await getSecretByName({ + const encryptedSecret = await getSecretByName({ actorId, projectId, actorAuthMethod, @@ -1105,7 +1106,46 @@ export const secretServiceFactory = ({ version }); - return decryptSecretRaw(secret, botKey); + const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey); + + if (expandSecretReferences) { + const expandSecrets = interpolateSecrets({ + folderDAL, + projectId, + secretDAL, + secretEncKey: botKey + }); + + const expandSingleSecret = async (secret: { + secretKey: string; + secretValue: string; + secretComment?: string; + secretPath: string; + skipMultilineEncoding: boolean | null | undefined; + }) => { + 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; }; const createSecretRaw = async ({ diff --git a/backend/src/services/secret/secret-types.ts b/backend/src/services/secret/secret-types.ts index 1aac324c56..10df2f2583 100644 --- a/backend/src/services/secret/secret-types.ts +++ b/backend/src/services/secret/secret-types.ts @@ -151,6 +151,7 @@ export type TGetASecretRawDTO = { secretName: string; path: string; environment: string; + expandSecretReferences?: boolean; type: "shared" | "personal"; includeImports?: boolean; version?: number;