UNPKG

@shons/next-configify

Version:
118 lines (107 loc) 3.34 kB
import { GetSecretValueCommand, SecretsManagerClient, } from '@aws-sdk/client-secrets-manager'; import { ConfigurationResolver } from '../configuration-resolver.interface'; import { ResolvedValue } from '../resolved-value.interface'; /** * AWS Secrets Manager configuration resolver. */ export class AwsSecretsManagerConfigurationResolver implements ConfigurationResolver { private readonly AWS_SECRETS_AMANGER_YAML_KEY = 'aws-secrets-manager'; private readonly AWS_SECRETS_MANAGER_ENV_PREFIX = 'AWS_SECRETS_MANAGER'; /** * Creates a new instance of secrets manager configuration resolver * * @param {SecretsManagerClient} secretsManager secrets manager client */ constructor(private readonly secretsManager: SecretsManagerClient) {} /** * Fetches secrets and assign it to an object representation * of the configuration. * * @param {Record<string, any>} config the configuration object * @returns {Record<string, any>} the cofiguration with secret values assigned * @throws {Error} if unable to fetch the secret */ async resolve(config: Record<string, any>): Promise<Record<string, any>> { const secrets = this.filterConfiguration(config); const promises = this.buildBulkRequest(secrets); const results = await Promise.all(promises); const errors = results.filter((r) => !r.success); if (errors && errors.length) { throw new Error( `Unable to resolve secrets:\n${errors .map((e) => `${e.key}: ${e.id} - ${e.error.message}`) .join('\n')}`, ); } for (const result of results) { config[result.key] = result.value; } return config; } /** * Filters the configuration object by aws secrets manager keys. * * @param {Record<string, any>} config the configuration object * @returns {Record<string, any>} the filtered object */ private filterConfiguration( config: Record<string, any>, ): Record<string, any> { return Object.fromEntries( Object.entries(config).filter( ([key]) => key.startsWith(this.AWS_SECRETS_AMANGER_YAML_KEY) || key.startsWith(this.AWS_SECRETS_MANAGER_ENV_PREFIX), ), ); } /** * Resolves secret values. * * @param {string} key the object key * @param {string} id the secret id * @returns */ private async resolveSecretValue( key: string, id: string, ): Promise<ResolvedValue> { try { const command = new GetSecretValueCommand({ SecretId: id }); const response = await this.secretsManager.send(command); return { id, key, value: response.SecretString, success: true, }; } catch (e) { return { id, key, error: e, success: false, }; } } /** * Creates a list of promises to get secret values. * * @param {Record<string, any>} config the configuration object * @returns {Promise<ResolvedValue>[]} the get secret value promises */ private buildBulkRequest( config: Record<string, any>, ): Promise<ResolvedValue>[] { const promises = []; for (const [key, value] of Object.entries(config)) { promises.push(this.resolveSecretValue(key, value)); } return promises; } }