From 6ff584c0e2ae637a7e13966e586b26d1eb2838c3 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Sat, 25 Jan 2025 02:55:54 +0100 Subject: [PATCH] feat(secrets): get secrets with imports --- README.md | 27 +++++++++++++++++++++++++++ src/custom/secrets.ts | 39 ++++++++++++++++++++++++++++++++++++++- src/custom/util.ts | 13 +++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c44c0d7..0fa96d6 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,33 @@ const allSecrets = await client.secrets().listSecrets({ **Returns:** - `ApiV3SecretsRawGet200Response`: The response containing the list of secrets. +#### List secrets with imports + +The `listSecretsWithImports` method makes it easier to get all your secrets at once. The imported secrets will automatically be added to the secrets returned. The secrets in the selected environment will take precedence over the imported secrets. This means if you have secrets with conflicting names, the secret from the environment the import was imported into, will take precedence. + +```typescript +const allSecrets = await client.secrets().listSecretsWithImports({ + environment: "dev", + projectId: "", + expandSecretReferences: true, + recursive: false, + secretPath: "/foo/bar" +}); +``` + +**Parameters:** +- `projectId` (string): The ID of your project. +- `environment` (string): The environment in which to list secrets (e.g., "dev"). +- `secretPath` (str): The path to the secrets. +- `expandSecretReferences` (bool): Whether to expand secret references. +- `recursive` (bool): Whether to list secrets recursively. +- `tagFilters` (string[]): Tags to filter secrets. + +**Returns:** +- `ApiV1DashboardSecretsOverviewGet200ResponseSecretsInner`: The response containing the list of secrets, with imports. + + + #### Create Secret ```typescript diff --git a/src/custom/secrets.ts b/src/custom/secrets.ts index eeb75a2..d5a6839 100644 --- a/src/custom/secrets.ts +++ b/src/custom/secrets.ts @@ -9,6 +9,7 @@ import type { DefaultApiApiV3SecretsRawSecretNamePostRequest } from "../infisicalapi_client"; import { newInfisicalError } from "./errors"; +import { getUniqueSecretsByKey } from "./util"; type SecretType = "shared" | "personal"; @@ -51,7 +52,7 @@ export type UpdateSecretResult = ApiV3SecretsRawSecretNamePost200Response; export type CreateSecretResult = ApiV3SecretsRawSecretNamePost200Response; export type DeleteSecretResult = ApiV3SecretsRawSecretNamePost200Response; -const convertBool = (value: boolean | undefined) => (value ? "true" : "false"); +const convertBool = (value?: boolean) => (value ? "true" : "false"); export default class SecretsClient { #apiInstance: InfisicalApi; @@ -81,6 +82,42 @@ export default class SecretsClient { } }; + listSecretsWithImports = async (options: Omit): Promise => { + const res = await this.listSecrets({ + ...options, + includeImports: true + }); + + let { imports, secrets } = res; + if (imports) { + if (options.recursive) { + secrets = getUniqueSecretsByKey(secrets); + } + + for (const imp of imports) { + for (const importedSecret of imp.secrets) { + // CASE: We need to ensure that the imported values don't override the "base" secrets. + // Priority order is: + // Local/Preset variables -> Actual secrets -> Imported secrets (high->low) + + // Check if the secret already exists in the secrets list + if (!secrets.find(s => s.secretKey === importedSecret.secretKey)) { + secrets.push({ + ...importedSecret, + secretPath: imp.secretPath, + // These fields are not returned by the API + updatedAt: new Date().toISOString(), + createdAt: new Date().toISOString(), + tags: [] + }); + } + } + } + } + + return secrets; + }; + getSecret = async (options: GetSecretOptions): Promise => { try { const res = await this.#apiInstance.apiV3SecretsRawSecretNameGet( diff --git a/src/custom/util.ts b/src/custom/util.ts index 6a6ccc8..de85346 100644 --- a/src/custom/util.ts +++ b/src/custom/util.ts @@ -2,6 +2,19 @@ import axios from "axios"; import { AWS_IDENTITY_DOCUMENT_URI, AWS_TOKEN_METADATA_URI } from "./constants"; import AWS from "aws-sdk"; import { InfisicalSDKError } from "./errors"; +import { ApiV3SecretsRawGet200Response } from "../infisicalapi_client"; + +type Secret = ApiV3SecretsRawGet200Response["secrets"][number]; + +export const getUniqueSecretsByKey = (secrets: Secret[]) => { + const secretMap = new Map(); + + for (const secret of secrets) { + secretMap.set(secret.secretKey, secret); + } + + return Array.from(secretMap.values()); +}; export const getAwsRegion = async () => { const region = process.env.AWS_REGION; // Typically found in lambda runtime environment