From 0460cc2c06f87068353a3c9244352ff7e3aef31e Mon Sep 17 00:00:00 2001 From: crystall-bitquill <97126568+crystall-bitquill@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:12:17 -0700 Subject: [PATCH] feat: aws secrets manager plugin (#8) --- .../aws_secrets_manager_plugin.ts | 191 ++++ .../iam_authentication_plugin.ts | 3 +- common/lib/aws_client.ts | 13 - common/lib/connection_plugin_chain_builder.ts | 2 + common/lib/plugin_service.ts | 26 +- common/lib/utils/locales/en.json | 4 + common/lib/wrapper_property.ts | 4 + mysql/lib/client.ts | 14 +- package-lock.json | 984 ++++++++++++++---- package.json | 3 +- pg/lib/client.ts | 14 +- tests/unit/aws_secrets_manager_plugin.test.ts | 208 ++++ 12 files changed, 1211 insertions(+), 255 deletions(-) create mode 100644 common/lib/authentication/aws_secrets_manager_plugin.ts create mode 100644 tests/unit/aws_secrets_manager_plugin.test.ts diff --git a/common/lib/authentication/aws_secrets_manager_plugin.ts b/common/lib/authentication/aws_secrets_manager_plugin.ts new file mode 100644 index 00000000..d7f66c4a --- /dev/null +++ b/common/lib/authentication/aws_secrets_manager_plugin.ts @@ -0,0 +1,191 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { + SecretsManagerClientConfig, + SecretsManagerClient, + SecretsManagerServiceException, + GetSecretValueCommand +} from "@aws-sdk/client-secrets-manager"; +import { logger } from "../../logutils"; +import { AbstractConnectionPlugin } from "../abstract_connection_plugin"; +import { ConnectionPlugin } from "../connection_plugin"; +import { HostInfo } from "../host_info"; +import { ConnectionPluginFactory } from "../plugin_factory"; +import { PluginService } from "../plugin_service"; +import { AwsWrapperError } from "../utils/aws_wrapper_error"; +import { Messages } from "../utils/messages"; +import { WrapperProperties } from "../wrapper_property"; + +export class AwsSecretsManagerPlugin extends AbstractConnectionPlugin { + private static SUBSCRIBED_METHODS: Set = new Set(["connect", "forceConnect"]); + private static SECRETS_ARN_PATTERN: RegExp = new RegExp("^arn:aws:secretsmanager:(?[^:\\n]*):[^:\\n]*:([^:/\\n]*[:/])?(.*)$"); + private readonly pluginService: PluginService; + private secret: Secret | null = null; + static secretsCache: Map = new Map(); + secretKey: SecretCacheKey; + secretsManagerClient: SecretsManagerClient; + + constructor(pluginService: PluginService, properties: Map) { + super(); + + this.pluginService = pluginService; + const secretId = WrapperProperties.SECRET_ID.get(properties); + const endpoint = WrapperProperties.SECRET_ENDPOINT.get(properties); + let region = WrapperProperties.SECRET_REGION.get(properties); + const config: SecretsManagerClientConfig = {}; + + if (!secretId) { + throw new AwsWrapperError(Messages.get("AwsSecretsManagerConnectionPlugin.missingRequiredConfigParameter")); + } + + if (!region) { + const groups = secretId.match(AwsSecretsManagerPlugin.SECRETS_ARN_PATTERN)?.groups; + if (groups?.region) { + region = groups.region; + } + config.region = region; + } + + if (endpoint) { + config.endpoint = endpoint; + } + + this.secretKey = new SecretCacheKey(secretId, region); + this.secretsManagerClient = new SecretsManagerClient(config); + } + + getSubscribedMethods(): Set { + return AwsSecretsManagerPlugin.SUBSCRIBED_METHODS; + } + + connect(hostInfo: HostInfo, props: Map, isInitialConnection: boolean, connectFunc: () => Promise): Promise { + return this.connectInternal(hostInfo, props, connectFunc); + } + + forceConnect(hostInfo: HostInfo, props: Map, isInitialConnection: boolean, forceConnectFunc: () => Promise): Promise { + return this.connectInternal(hostInfo, props, forceConnectFunc); + } + + private async connectInternal(hostInfo: HostInfo, props: Map, connectFunc: () => Promise): Promise { + let secretWasFetched = await this.updateSecret(false); + try { + WrapperProperties.USER.set(props, this.secret?.username ?? ""); + WrapperProperties.PASSWORD.set(props, this.secret?.password ?? ""); + this.pluginService.updateConfigWithProperties(props); + return await connectFunc(); + } catch (error) { + if (error instanceof Error) { + if ((error.message.includes("password authentication failed") || error.message.includes("Access denied")) && !secretWasFetched) { + // Login unsuccessful with cached credentials + // Try to re-fetch credentials and try again + + secretWasFetched = await this.updateSecret(true); + if (secretWasFetched) { + WrapperProperties.USER.set(props, this.secret?.username ?? ""); + WrapperProperties.PASSWORD.set(props, this.secret?.password ?? ""); + return await connectFunc(); + } + } + logger.debug(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledException", error.name, error.message)); + } + throw error; + } + } + + private async updateSecret(forceRefresh: boolean): Promise { + let fetched = false; + this.secret = AwsSecretsManagerPlugin.secretsCache.get(JSON.stringify(this.secretKey)) ?? null; + + if (!this.secret || forceRefresh) { + try { + this.secret = await this.fetchLatestCredentials(); + fetched = true; + AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(this.secretKey), this.secret); + } catch (error) { + if (error instanceof SecretsManagerServiceException) { + this.logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials")); + } else if (error instanceof Error && error.message.includes("AWS SDK error")) { + this.logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.endpointOverrideInvalidConnection", error.message)); + } else { + this.logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledException", JSON.stringify(error))); + } + } + } + + return fetched; + } + + private async fetchLatestCredentials(): Promise { + const commandInput = { + SecretId: this.secretKey.secretId + }; + const command = new GetSecretValueCommand(commandInput); + const result = await this.secretsManagerClient.send(command); + const secret = new Secret(JSON.parse(result.SecretString ?? "").username, JSON.parse(result.SecretString ?? "").password); + if (secret && secret.username && secret.password) { + return secret; + } + throw new AwsWrapperError(Messages.get("AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials")); + } + + private logAndThrowError(message: string) { + logger.debug(message); + throw new AwsWrapperError(message); + } +} + +export class SecretCacheKey { + private readonly _secretId: string; + private readonly _region: string | null; + + constructor(secretId: string, region: string) { + this._secretId = secretId; + this._region = region; + } + + get secretId(): string { + return this._secretId; + } + + get region(): string | null { + return this._region; + } +} + +export class Secret { + private readonly _username: string; + private readonly _password: string; + + constructor(username: string, password: string) { + this._username = username; + this._password = password; + } + + get username(): string { + return this._username; + } + + get password(): string | null { + return this._password; + } +} + +export class AwsSecretsManagerPluginFactory implements ConnectionPluginFactory { + getInstance(pluginService: PluginService, properties: Map): ConnectionPlugin { + return new AwsSecretsManagerPlugin(pluginService, new Map(properties)); + } +} diff --git a/common/lib/authentication/iam_authentication_plugin.ts b/common/lib/authentication/iam_authentication_plugin.ts index d0bd3b92..82989c27 100644 --- a/common/lib/authentication/iam_authentication_plugin.ts +++ b/common/lib/authentication/iam_authentication_plugin.ts @@ -79,9 +79,9 @@ export class IamAuthenticationPlugin extends AbstractConnectionPlugin { const token = await this.generateAuthenticationToken(hostInfo, props, host, port, region); logger.debug(Messages.get("IamAuthenticationPlugin.generatedNewIamToken", token)); WrapperProperties.PASSWORD.set(props, token); - this.pluginService.updateCredentials(props); IamAuthenticationPlugin.tokenCache.set(cacheKey, new TokenInfo(token, tokenExpiry)); } + this.pluginService.updateConfigWithProperties(props); try { return connectFunc(); @@ -98,7 +98,6 @@ export class IamAuthenticationPlugin extends AbstractConnectionPlugin { const token = await this.generateAuthenticationToken(hostInfo, props, host, port, region); logger.debug(Messages.get("IamAuthenticationPlugin.generatedNewIamToken", token)); WrapperProperties.PASSWORD.set(props, token); - this.pluginService.updateCredentials(props); IamAuthenticationPlugin.tokenCache.set(cacheKey, new TokenInfo(token, tokenExpiry)); return connectFunc(); } diff --git a/common/lib/aws_client.ts b/common/lib/aws_client.ts index d5ee89d8..e29e8815 100644 --- a/common/lib/aws_client.ts +++ b/common/lib/aws_client.ts @@ -111,19 +111,6 @@ export abstract class AwsClient { return this._connectFunc; } - updateCredentials(properties: Map): void { - const user = WrapperProperties.USER.get(properties); - const pass = WrapperProperties.PASSWORD.get(properties); - - if (this.targetClient.user != user) { - this.targetClient.user = user; - } - - if (this.targetClient.password != pass) { - this.targetClient.password = pass; - } - } - abstract executeQuery(props: Map, sql: string): Promise; abstract end(): Promise; diff --git a/common/lib/connection_plugin_chain_builder.ts b/common/lib/connection_plugin_chain_builder.ts index 19a070a4..d590a798 100644 --- a/common/lib/connection_plugin_chain_builder.ts +++ b/common/lib/connection_plugin_chain_builder.ts @@ -22,6 +22,7 @@ import { WrapperProperties } from "./wrapper_property"; import { AwsWrapperError } from "./utils/aws_wrapper_error"; import { Messages } from "./utils/messages"; import { DefaultPlugin } from "./plugins/default_plugin"; +import { AwsSecretsManagerPluginFactory } from "./authentication/aws_secrets_manager_plugin"; export class PluginFactoryInfo {} @@ -33,6 +34,7 @@ export class ConnectionPluginChainBuilder { static readonly PLUGIN_FACTORIES = new Map([ ["iam", IamAuthenticationPluginFactory], + ["secretsManager", AwsSecretsManagerPluginFactory], ["failover", FailoverPluginFactory] ]); diff --git a/common/lib/plugin_service.ts b/common/lib/plugin_service.ts index 7ec2e2cb..29d4926f 100644 --- a/common/lib/plugin_service.ts +++ b/common/lib/plugin_service.ts @@ -81,10 +81,6 @@ export class PluginService implements ErrorHandler, HostListProviderService { return this._currentClient; } - updateCredentials(properties: Map) { - this.getCurrentClient().updateCredentials(properties); - } - getConnectionUrlParser(): ConnectionUrlParser { return this.getCurrentClient().connectionUrlParser; } @@ -197,6 +193,24 @@ export class PluginService implements ErrorHandler, HostListProviderService { setAvailability(hostAliases: Set, availability: HostAvailability) {} + updateConfigWithProperties(props: Map) { + this._currentClient.config = Object.fromEntries(props.entries()); + } + + replaceTargetClient(props: Map): void { + const createClientFunc = this.getCurrentClient().getCreateClientFunc(); + if (createClientFunc) { + if (this.getCurrentClient().targetClient) { + this.getCurrentClient().end(); + } + const newTargetClient = createClientFunc(Object.fromEntries(props)); + this.getCurrentClient().targetClient = newTargetClient; + return; + } + throw new AwsWrapperError("AwsClient is missing create target client function."); // This should not be reached + } + + // TODO: use replaceTargetClient method instead async createTargetClientAndConnect(hostInfo: HostInfo, props: Map, forceConnect: boolean): Promise { if (this.pluginServiceManagerContainer.pluginManager) { return await this.pluginServiceManagerContainer.pluginManager.createTargetClientAndConnect(hostInfo, props, this._currentClient, forceConnect); @@ -210,7 +224,7 @@ export class PluginService implements ErrorHandler, HostListProviderService { if (connectFunc) { return this.pluginServiceManagerContainer.pluginManager?.connect(hostInfo, props, false, connectFunc); } - throw new AwsWrapperError("AwsClient is missing target client connect functions."); // This should not be reached + throw new AwsWrapperError("AwsClient is missing target client connect function."); // This should not be reached } forceConnect(hostInfo: HostInfo, props: Map) { @@ -218,6 +232,6 @@ export class PluginService implements ErrorHandler, HostListProviderService { if (connectFunc) { return this.pluginServiceManagerContainer.pluginManager?.forceConnect(hostInfo, props, false, connectFunc); } - throw new AwsWrapperError("AwsClient is missing target client connect functions."); // This should not be reached + throw new AwsWrapperError("AwsClient is missing target client connect function."); // This should not be reached } } diff --git a/common/lib/utils/locales/en.json b/common/lib/utils/locales/en.json index 8356367c..d5a4205e 100644 --- a/common/lib/utils/locales/en.json +++ b/common/lib/utils/locales/en.json @@ -8,6 +8,10 @@ "IamAuthenticationPlugin.invalidPort": "Port number: %d is not valid. Port number should be greater than zero. Falling back to default port.", "IamAuthenticationPlugin.unhandledException": "Unhandled exception: %s", "IamAuthenticationPlugin.connectException": "Error occurred while opening a connection: %s", + "AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials": "Was not able to either fetch or read the database credentials from AWS Secrets Manager. Ensure the correct secretId and region properties have been provided.", + "AwsSecretsManagerConnectionPlugin.missingRequiredConfigParameter": "Configuration parameter 'secretId' is required.", + "AwsSecretsManagerConnectionPlugin.unhandledException": "Unhandled exception: '%s'", + "AwsSecretsManagerConnectionPlugin.endpointOverrideInvalidConnection": "A connection to the provided endpoint could not be established: '%s'.", "PluginManager.PipelineNone": "A pipeline was requested but the created pipeline evaluated to None.", "ClusterAwareReaderFailoverHandler.invalidTopology": "'%s' was called with an invalid (null or empty) topology", "ClusterAwareReaderFailoverHandler.attemptingReaderConnection": "Trying to connect to reader: '%s', with properties '%s'", diff --git a/common/lib/wrapper_property.ts b/common/lib/wrapper_property.ts index ad45bffa..0dfd12f1 100644 --- a/common/lib/wrapper_property.ts +++ b/common/lib/wrapper_property.ts @@ -66,6 +66,10 @@ export class WrapperProperties { WrapperProperties.DEFAULT_TOKEN_EXPIRATION_SEC ); + static readonly SECRET_ID = new WrapperProperty("secretId", "The name or the ARN of the secret to retrieve.", null); + static readonly SECRET_REGION = new WrapperProperty("secretRegion", "The region of the secret to retrieve.", null); + static readonly SECRET_ENDPOINT = new WrapperProperty("secretEndpoint", "The endpoint of the secret to retrieve.", null); + static readonly CLUSTER_TOPOLOGY_REFRESH_RATE_MS = new WrapperProperty( "clusterTopologyRefreshRateMs", "Cluster topology refresh rate in millis. " + diff --git a/mysql/lib/client.ts b/mysql/lib/client.ts index 1a709b46..cfa10bbc 100644 --- a/mysql/lib/client.ts +++ b/mysql/lib/client.ts @@ -26,7 +26,6 @@ export class AwsMySQLClient extends AwsClient { constructor(config: any) { super(config, new MySQLErrorHandler(), new AuroraMySQLDatabaseDialect(), new MySQLConnectionUrlParser()); this.config = config; - this.targetClient = createConnection(WrapperProperties.removeWrapperProperties(config)); this._createClientFunc = (config: any) => { return createConnection(WrapperProperties.removeWrapperProperties(config)); }; @@ -44,17 +43,26 @@ export class AwsMySQLClient extends AwsClient { async connect(): Promise { await this.internalConnect(); const conn: Promise = this.pluginManager.connect(this.pluginService.getCurrentHostInfo(), this.properties, true, async () => { + this.targetClient = createConnection(WrapperProperties.removeWrapperProperties(this.config)); return this.targetClient.promise().connect(); }); this.isConnected = true; return conn; } - executeQuery(props: Map, sql: string): Promise { + async executeQuery(props: Map, sql: string): Promise { + if (!this.isConnected) { + await this.connect(); // client.connect is not required for MySQL clients + this.isConnected = true; + } return this.targetClient.promise().query({ sql: sql }); } - query(options: QueryOptions, callback?: any): Promise { + async query(options: QueryOptions, callback?: any): Promise { + if (!this.isConnected) { + await this.connect(); // client.connect is not required for MySQL clients + this.isConnected = true; + } const host = this.pluginService.getCurrentHostInfo(); return this.pluginManager.execute( host, diff --git a/package-lock.json b/package-lock.json index ca6d7d05..64c78ab0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "mysql" ], "devDependencies": { + "@aws-sdk/client-secrets-manager": "^3.511.0", "@aws-sdk/credential-providers": "^3.461.0", "@aws-sdk/rds-signer": "^3.461.0", "@types/i18n": "^0.13.10", @@ -467,6 +468,534 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.511.0.tgz", + "integrity": "sha512-HI03pQK6EyBunogeGWAylSZU866yv5vOEr7cku8UGNUxDt5yP+xctXusdYIggl4jIRTwaqg/0IGmZDs8mXTFtg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.511.0", + "@aws-sdk/core": "3.511.0", + "@aws-sdk/credential-provider-node": "3.511.0", + "@aws-sdk/middleware-host-header": "3.511.0", + "@aws-sdk/middleware-logger": "3.511.0", + "@aws-sdk/middleware-recursion-detection": "3.511.0", + "@aws-sdk/middleware-signing": "3.511.0", + "@aws-sdk/middleware-user-agent": "3.511.0", + "@aws-sdk/region-config-resolver": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@aws-sdk/util-endpoints": "3.511.0", + "@aws-sdk/util-user-agent-browser": "3.511.0", + "@aws-sdk/util-user-agent-node": "3.511.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.511.0.tgz", + "integrity": "sha512-v1f5ZbuZWpad+fgTOpgFyIZT3A37wdqoSPh0hl+cKRu5kPsz96xCe9+UvLx+HdN2yJ/mV0UZcMq6ysj4xAGIEg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.511.0", + "@aws-sdk/middleware-host-header": "3.511.0", + "@aws-sdk/middleware-logger": "3.511.0", + "@aws-sdk/middleware-recursion-detection": "3.511.0", + "@aws-sdk/middleware-user-agent": "3.511.0", + "@aws-sdk/region-config-resolver": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@aws-sdk/util-endpoints": "3.511.0", + "@aws-sdk/util-user-agent-browser": "3.511.0", + "@aws-sdk/util-user-agent-node": "3.511.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.511.0.tgz", + "integrity": "sha512-cITRRq54eTrq7ll9li+yYnLbNHKXG2P+ovdZSDiQ6LjCYBdcD4ela30qbs87Yye9YsopdslDzBhHHtrf5oiuMw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.511.0", + "@aws-sdk/core": "3.511.0", + "@aws-sdk/middleware-host-header": "3.511.0", + "@aws-sdk/middleware-logger": "3.511.0", + "@aws-sdk/middleware-recursion-detection": "3.511.0", + "@aws-sdk/middleware-signing": "3.511.0", + "@aws-sdk/middleware-user-agent": "3.511.0", + "@aws-sdk/region-config-resolver": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@aws-sdk/util-endpoints": "3.511.0", + "@aws-sdk/util-user-agent-browser": "3.511.0", + "@aws-sdk/util-user-agent-node": "3.511.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.511.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sts": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.511.0.tgz", + "integrity": "sha512-lwVEEXK+1auEwmBuTv35m2GvbxPthi8SjNUpU4pRetZPVbGhnhCN6H7JqeMDP6GLf81Io2eySXRsmLMt7l/fjg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.511.0", + "@aws-sdk/middleware-host-header": "3.511.0", + "@aws-sdk/middleware-logger": "3.511.0", + "@aws-sdk/middleware-recursion-detection": "3.511.0", + "@aws-sdk/middleware-user-agent": "3.511.0", + "@aws-sdk/region-config-resolver": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@aws-sdk/util-endpoints": "3.511.0", + "@aws-sdk/util-user-agent-browser": "3.511.0", + "@aws-sdk/util-user-agent-node": "3.511.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.511.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/core": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.511.0.tgz", + "integrity": "sha512-0gbDvQhToyLxPyr/7KP6uavrBYKh7exld2lju1Lp65U61XgEjTVP/thJmHTvH4BAKGSqeIz/rrwJ0KrC8nwBtw==", + "dev": true, + "dependencies": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.511.0.tgz", + "integrity": "sha512-4VUsnLRox8YzxnZwnFrfZM4bL5KKLhsjjjX7oiuLyzFkhauI4HFYt7rTB8YNGphpqAg/Wzw5DBZfO3Bw1iR1HA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.511.0.tgz", + "integrity": "sha512-y83Gt8GPpgMe/lMFxIq+0G2rbzLTC6lhrDocHUzqcApLD6wet8Esy2iYckSRlJgYY+qsVAzpLrSMtt85DwRPTw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.511.0.tgz", + "integrity": "sha512-AgIOCtYzm61jbTQCY/2Vf/yu7DeLG0TLZa05a3VVRN9XE4ERtEnMn7TdbxM+hS24MTX8xI0HbMcWxCBkXRIg9w==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sts": "3.511.0", + "@aws-sdk/credential-provider-env": "3.511.0", + "@aws-sdk/credential-provider-process": "3.511.0", + "@aws-sdk/credential-provider-sso": "3.511.0", + "@aws-sdk/credential-provider-web-identity": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.511.0.tgz", + "integrity": "sha512-5JDZXsSluliJmxOF+lYYFgJdSKQfVLQyic5NxScHULTERGoEwEHMgucFGwJ9MV9FoINjNTQLfAiWlJL/kGkCEQ==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.511.0", + "@aws-sdk/credential-provider-http": "3.511.0", + "@aws-sdk/credential-provider-ini": "3.511.0", + "@aws-sdk/credential-provider-process": "3.511.0", + "@aws-sdk/credential-provider-sso": "3.511.0", + "@aws-sdk/credential-provider-web-identity": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.511.0.tgz", + "integrity": "sha512-88hLUPqcTwjSubPS+34ZfmglnKeLny8GbmZsyllk96l26PmDTAqo5RScSA8BWxL0l5pRRWGtcrFyts+oibHIuQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.511.0.tgz", + "integrity": "sha512-aEei9UdXYEE2e0Htf28/IcuHcWk3VkUkpcg3KDR/AyzXA3i/kxmixtAgRmHOForC5CMqoJjzVPFUITNkAscyag==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.511.0", + "@aws-sdk/token-providers": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.511.0.tgz", + "integrity": "sha512-/3XMyN7YYefAsES/sMMY5zZGRmZ5QJisJw798DdMYmYMsb1dt0Qy8kZTu+59ZzOiVIcznsjSTCEB81QmGtDKcA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sts": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.511.0.tgz", + "integrity": "sha512-DbBzQP/6woSHR/+g9dHN3YiYaLIqFw9u8lQFMxi3rT3hqITFVYLzzXtEaHjDD6/is56pNT84CIKbyJ6/gY5d1Q==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-logger": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.511.0.tgz", + "integrity": "sha512-EYU9dBlJXvQcCsM2Tfgi0NQoXrqovfDv/fDy8oGJgZFrgNuHDti8tdVVxeJTUJNEAF67xlDl5o+rWEkKthkYGQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.511.0.tgz", + "integrity": "sha512-PlNPCV/6zpDVdNx1K69xDTh/wPNU4WyP4qa6hUo2/+4/PNG5HI9xbCWtpb4RjhdTRw6qDtkBNcPICHbtWx5aHg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-signing": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.511.0.tgz", + "integrity": "sha512-IMijFLfm+QQHD6NNDX9k3op9dpBSlWKnqjcMU38Tytl2nbqV4gktkarOK1exHAmH7CdoYR5BufVtBzbASNSF/A==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.511.0.tgz", + "integrity": "sha512-eLs+CxP2QCXh3tCGYCdAml3oyWj8MSIwKbH+8rKw0k/5vmY1YJDBy526whOxx61ivhz2e0muuijN4X5EZZ2Pnw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@aws-sdk/util-endpoints": "3.511.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.511.0.tgz", + "integrity": "sha512-RzBLSNaRd4iEkQyEGfiSNvSnWU/x23rsiFgA9tqYFA0Vqx7YmzSWC8QBUxpwybB8HkbbL9wNVKQqTbhI3mYneQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/token-providers": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.511.0.tgz", + "integrity": "sha512-92dXjMHBJcRoUkJHc0Bvtsz7Sal8t6VASRJ5vfs5c2ZpTVgLpVnM4dBmwUgGUdnvHov0cZTXbbadTJ/qOWx5Zw==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.511.0", + "@aws-sdk/types": "3.511.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/types": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.511.0.tgz", + "integrity": "sha512-P03ufufxmkvd7nO46oOeEqYIMPJ8qMCKxAsfJk1JBVPQ1XctVntbail4/UFnrnzij8DTl4Mk/D62uGo7+RolXA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-endpoints": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.511.0.tgz", + "integrity": "sha512-J/5hsscJkg2pAOdLx1YKlyMCk5lFRxRxEtup9xipzOxVBlqOIE72Tuu31fbxSlF8XzO/AuCJcZL4m1v098K9oA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.511.0.tgz", + "integrity": "sha512-5LuESdwtIcA10aHcX7pde7aCIijcyTPBXFuXmFlDTgm/naAayQxelQDpvgbzuzGLgePf8eTyyhDKhzwPZ2EqiQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.511.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.511.0.tgz", + "integrity": "sha512-UopdlRvYY5mxlS4wwFv+QAWL6/T302wmoQj7i+RY+c/D3Ej3PKBb/mW3r2wEOgZLJmPpeeM1SYMk+rVmsW1rqw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.511.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-sso": { "version": "3.460.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.460.0.tgz", @@ -2811,12 +3340,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.14.tgz", - "integrity": "sha512-zXtteuYLWbSXnzI3O6xq3FYvigYZFW8mdytGibfarLL2lxHto9L3ILtGVnVGmFZa7SDh62l39EnU5hesLN87Fw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.1.tgz", + "integrity": "sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2824,15 +3353,34 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.19.tgz", - "integrity": "sha512-JsghnQ5zjWmjEVY8TFOulLdEOCj09SjRLugrHlkPZTIBBm7PQitCFVLThbsKPZQOP7N3ME1DU1nKUc1UaVnBog==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.1.tgz", + "integrity": "sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw==", "dev": true, "dependencies": { - "@smithy/node-config-provider": "^2.1.6", - "@smithy/types": "^2.6.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.7", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.2.tgz", + "integrity": "sha512-tYDmTp0f2TZVE18jAOH1PnmkngLQ+dOGUlMd1u67s87ieueNeyqhja6z/Z4MxhybEiXKOWFOmGjfTZWFxljwJw==", + "dev": true, + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2840,15 +3388,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.2.tgz", - "integrity": "sha512-Y62jBWdoLPSYjr9fFvJf+KwTa1EunjVr6NryTEWCnwIY93OJxwV4t0qxjwdPl/XMsUkq79ppNJSEQN6Ohnhxjw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.1.tgz", + "integrity": "sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA==", "dev": true, "dependencies": { - "@smithy/node-config-provider": "^2.1.6", - "@smithy/property-provider": "^2.0.15", - "@smithy/types": "^2.6.0", - "@smithy/url-parser": "^2.0.14", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2856,39 +3404,39 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.14.tgz", - "integrity": "sha512-g/OU/MeWGfHDygoXgMWfG/Xb0QqDnAGcM9t2FRrVAhleXYRddGOEnfanR5cmHgB9ue52MJsyorqFjckzXsylaA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.1.tgz", + "integrity": "sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw==", "dev": true, "dependencies": { "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.6.0", - "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/fetch-http-handler": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.7.tgz", - "integrity": "sha512-iSDBjxuH9TgrtMYAr7j5evjvkvgwLY3y+9D547uep+JNkZ1ZT+BaeU20j6I/bO/i26ilCWFImrlXTPsfQtZdIQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.1.tgz", + "integrity": "sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg==", "dev": true, "dependencies": { - "@smithy/protocol-http": "^3.0.10", - "@smithy/querystring-builder": "^2.0.14", - "@smithy/types": "^2.6.0", - "@smithy/util-base64": "^2.0.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/hash-node": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.16.tgz", - "integrity": "sha512-Wbi9A0PacMYUOwjAulQP90Wl3mQ6NDwnyrZQzFjDz+UzjXOSyQMgBrTkUBz+pVoYVlX3DUu24gWMZBcit+wOGg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.1.tgz", + "integrity": "sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/types": "^2.9.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2896,19 +3444,19 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.14.tgz", - "integrity": "sha512-d8ohpwZo9RzTpGlAfsWtfm1SHBSU7+N4iuZ6MzR10xDTujJJWtmXYHK1uzcr7rggbpUTaWyHpPFgnf91q0EFqQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.1.tgz", + "integrity": "sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -2918,13 +3466,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.16.tgz", - "integrity": "sha512-9ddDia3pp1d3XzLXKcm7QebGxLq9iwKf+J1LapvlSOhpF8EM9SjMeSrMOOFgG+2TfW5K3+qz4IAJYYm7INYCng==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.1.tgz", + "integrity": "sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g==", "dev": true, "dependencies": { - "@smithy/protocol-http": "^3.0.10", - "@smithy/types": "^2.6.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2932,17 +3480,17 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.1.tgz", - "integrity": "sha512-dVDS7HNJl/wb0lpByXor6whqDbb1YlLoaoWYoelyYzLHioXOE7y/0iDwJWtDcN36/tVCw9EPBFZ3aans84jLpg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.1.tgz", + "integrity": "sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q==", "dev": true, "dependencies": { - "@smithy/middleware-serde": "^2.0.14", - "@smithy/node-config-provider": "^2.1.6", - "@smithy/shared-ini-file-loader": "^2.2.5", - "@smithy/types": "^2.6.0", - "@smithy/url-parser": "^2.0.14", - "@smithy/util-middleware": "^2.0.7", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2950,17 +3498,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.21.tgz", - "integrity": "sha512-EZS1EXv1k6IJX6hyu/0yNQuPcPaXwG8SWljQHYueyRbOxmqYgoWMWPtfZj0xRRQ4YtLawQSpBgAeiJltq8/MPw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.1.tgz", + "integrity": "sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA==", "dev": true, "dependencies": { - "@smithy/node-config-provider": "^2.1.6", - "@smithy/protocol-http": "^3.0.10", - "@smithy/service-error-classification": "^2.0.7", - "@smithy/types": "^2.6.0", - "@smithy/util-middleware": "^2.0.7", - "@smithy/util-retry": "^2.0.7", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", "tslib": "^2.5.0", "uuid": "^8.3.2" }, @@ -2968,22 +3517,13 @@ "node": ">=14.0.0" } }, - "node_modules/@smithy/middleware-retry/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@smithy/middleware-serde": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.14.tgz", - "integrity": "sha512-hFi3FqoYWDntCYA2IGY6gJ6FKjq2gye+1tfxF2HnIJB5uW8y2DhpRNBSUMoqP+qvYzRqZ6ntv4kgbG+o3pX57g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.1.tgz", + "integrity": "sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2991,12 +3531,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.8.tgz", - "integrity": "sha512-7/N59j0zWqVEKExJcA14MrLDZ/IeN+d6nbkN8ucs+eURyaDUXWYlZrQmMOd/TyptcQv0+RDlgag/zSTTV62y/Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.1.tgz", + "integrity": "sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3004,14 +3544,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.6.tgz", - "integrity": "sha512-HLqTs6O78m3M3z1cPLFxddxhEPv5MkVatfPuxoVO3A+cHZanNd/H5I6btcdHy6N2CB1MJ/lihJC92h30SESsBA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.1.tgz", + "integrity": "sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg==", "dev": true, "dependencies": { - "@smithy/property-provider": "^2.0.15", - "@smithy/shared-ini-file-loader": "^2.2.5", - "@smithy/types": "^2.6.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3019,15 +3559,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.10.tgz", - "integrity": "sha512-lkALAwtN6odygIM4nB8aHDahINM6WXXjNrZmWQAh0RSossySRT2qa31cFv0ZBuAYVWeprskRk13AFvvLmf1WLw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.3.1.tgz", + "integrity": "sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA==", "dev": true, "dependencies": { - "@smithy/abort-controller": "^2.0.14", - "@smithy/protocol-http": "^3.0.10", - "@smithy/querystring-builder": "^2.0.14", - "@smithy/types": "^2.6.0", + "@smithy/abort-controller": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3035,12 +3575,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.15.tgz", - "integrity": "sha512-YbRFBn8oiiC3o1Kn3a4KjGa6k47rCM9++5W9cWqYn9WnkyH+hBWgfJAckuxpyA2Hq6Ys4eFrWzXq6fqHEw7iew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.1.tgz", + "integrity": "sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3048,12 +3588,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.10.tgz", - "integrity": "sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.1.1.tgz", + "integrity": "sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3061,13 +3601,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.14.tgz", - "integrity": "sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.1.tgz", + "integrity": "sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", - "@smithy/util-uri-escape": "^2.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-uri-escape": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3075,12 +3615,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.14.tgz", - "integrity": "sha512-+cbtXWI9tNtQjlgQg3CA+pvL3zKTAxPnG3Pj6MP89CR3vi3QMmD0SOWoq84tqZDnJCxlsusbgIXk1ngMReXo+A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.1.tgz", + "integrity": "sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3088,24 +3628,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.7.tgz", - "integrity": "sha512-LLxgW12qGz8doYto15kZ4x1rHjtXl0BnCG6T6Wb8z2DI4PT9cJfOSvzbuLzy7+5I24PAepKgFeWHRd9GYy3Z9w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.1.tgz", + "integrity": "sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0" + "@smithy/types": "^2.9.1" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.5.tgz", - "integrity": "sha512-LHA68Iu7SmNwfAVe8egmjDCy648/7iJR/fK1UnVw+iAOUJoEYhX2DLgVd5pWllqdDiRbQQzgaHLcRokM+UFR1w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.1.tgz", + "integrity": "sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3113,18 +3653,18 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.16.tgz", - "integrity": "sha512-ilLY85xS2kZZzTb83diQKYLIYALvart0KnBaKnIRnMBHAGEio5aHSlANQoxVn0VsonwmQ3CnWhnCT0sERD8uTg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.1.tgz", + "integrity": "sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg==", "dev": true, "dependencies": { - "@smithy/eventstream-codec": "^2.0.14", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.6.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.7", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3132,14 +3672,16 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.16.tgz", - "integrity": "sha512-Lw67+yQSpLl4YkDLUzI2KgS8TXclXmbzSeOJUmRFS4ueT56B4pw3RZRF/SRzvgyxM/HxgkUan8oSHXCujPDafQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.3.1.tgz", + "integrity": "sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA==", "dev": true, "dependencies": { - "@smithy/middleware-stack": "^2.0.8", - "@smithy/types": "^2.6.0", - "@smithy/util-stream": "^2.0.21", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3147,9 +3689,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.6.0.tgz", - "integrity": "sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.9.1.tgz", + "integrity": "sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3159,23 +3701,23 @@ } }, "node_modules/@smithy/url-parser": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.14.tgz", - "integrity": "sha512-kbu17Y1AFXi5lNlySdDj7ZzmvupyWKCX/0jNZ8ffquRyGdbDZb+eBh0QnWqsSmnZa/ctyWaTf7n4l/pXLExrnw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.1.tgz", + "integrity": "sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q==", "dev": true, "dependencies": { - "@smithy/querystring-parser": "^2.0.14", - "@smithy/types": "^2.6.0", + "@smithy/querystring-parser": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", "dev": true, "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3183,18 +3725,18 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", "dev": true, "dependencies": { "tslib": "^2.5.0" } }, "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3204,12 +3746,12 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", "dev": true, "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", + "@smithy/is-array-buffer": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3217,9 +3759,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3229,14 +3771,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.20.tgz", - "integrity": "sha512-QJtnbTIl0/BbEASkx1MUFf6EaoWqWW1/IM90N++8NNscePvPf77GheYfpoPis6CBQawUWq8QepTP2QUSAdrVkw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.1.tgz", + "integrity": "sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA==", "dev": true, "dependencies": { - "@smithy/property-provider": "^2.0.15", - "@smithy/smithy-client": "^2.1.16", - "@smithy/types": "^2.6.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", "bowser": "^2.11.0", "tslib": "^2.5.0" }, @@ -3245,17 +3787,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.26.tgz", - "integrity": "sha512-lGFPOFCHv1ql019oegYqa54BZH7HREw6EBqjDLbAr0wquMX0BDi2sg8TJ6Eq+JGLijkZbJB73m4+aK8OFAapMg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.0.tgz", + "integrity": "sha512-iFJp/N4EtkanFpBUtSrrIbtOIBf69KNuve03ic1afhJ9/korDxdM0c6cCH4Ehj/smI9pDCfVv+bqT3xZjF2WaA==", "dev": true, "dependencies": { - "@smithy/config-resolver": "^2.0.19", - "@smithy/credential-provider-imds": "^2.1.2", - "@smithy/node-config-provider": "^2.1.6", - "@smithy/property-provider": "^2.0.15", - "@smithy/smithy-client": "^2.1.16", - "@smithy/types": "^2.6.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3263,13 +3805,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.5.tgz", - "integrity": "sha512-K7qNuCOD5K/90MjHvHm9kJldrfm40UxWYQxNEShMFxV/lCCCRIg8R4uu1PFAxRvPxNpIdcrh1uK6I1ISjDXZJw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.1.tgz", + "integrity": "sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw==", "dev": true, "dependencies": { - "@smithy/node-config-provider": "^2.1.6", - "@smithy/types": "^2.6.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3277,9 +3819,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3289,12 +3831,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.7.tgz", - "integrity": "sha512-tRINOTlf1G9B0ECarFQAtTgMhpnrMPSa+5j4ZEwEawCLfTFTavk6757sxhE4RY5RMlD/I3x+DCS8ZUiR8ho9Pw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.1.tgz", + "integrity": "sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA==", "dev": true, "dependencies": { - "@smithy/types": "^2.6.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3302,13 +3844,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.7.tgz", - "integrity": "sha512-fIe5yARaF0+xVT1XKcrdnHKTJ1Vc4+3e3tLDjCuIcE9b6fkBzzGFY7AFiX4M+vj6yM98DrwkuZeHf7/hmtVp0Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.1.tgz", + "integrity": "sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA==", "dev": true, "dependencies": { - "@smithy/service-error-classification": "^2.0.7", - "@smithy/types": "^2.6.0", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -3316,18 +3858,18 @@ } }, "node_modules/@smithy/util-stream": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.21.tgz", - "integrity": "sha512-0BUE16d7n1x7pi1YluXJdB33jOTyBChT0j/BlOkFa9uxfg6YqXieHxjHNuCdJRARa7AZEj32LLLEPJ1fSa4inA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.1.tgz", + "integrity": "sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ==", "dev": true, "dependencies": { - "@smithy/fetch-http-handler": "^2.2.7", - "@smithy/node-http-handler": "^2.1.10", - "@smithy/types": "^2.6.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3335,9 +3877,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -3347,12 +3889,12 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", "dev": true, "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -11369,17 +11911,6 @@ "which": "^2.0.2" } }, - "node_modules/node-notifier/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/node-releases": { "version": "2.0.13", "dev": true, @@ -13939,6 +14470,15 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "dev": true, diff --git a/package.json b/package.json index 24b6789a..44e46995 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "ts-mockito": "^2.6.1", "ts-node": "^10.9.1", "tsx": "^3.12.7", - "typescript": "^4.2.3" + "typescript": "^4.2.3", + "@aws-sdk/client-secrets-manager": "^3.511.0" } } diff --git a/pg/lib/client.ts b/pg/lib/client.ts index f4eb868b..701a2dcb 100644 --- a/pg/lib/client.ts +++ b/pg/lib/client.ts @@ -25,23 +25,21 @@ export class AwsPGClient extends AwsClient { constructor(config: any) { super(config, new PgErrorHandler(), new AuroraPgDatabaseDialect(), new PgConnectionUrlParser()); this.config = config; - this.targetClient = new Client(WrapperProperties.removeWrapperProperties(config)); this.isConnected = false; this._createClientFunc = (config: any) => { return new Client(WrapperProperties.removeWrapperProperties(config)); }; - this._connectFunc = async () => { - return await this.targetClient.connect().catch((error: any) => { - throw error; - }); + this._connectFunc = () => { + return this.targetClient.connect(); }; } async connect(): Promise { await this.internalConnect(); - const res: Promise = this.pluginManager.connect(this.pluginService.getCurrentHostInfo(), this.properties, true, () => - this.targetClient.connect() - ); + const res: Promise = this.pluginManager.connect(this.pluginService.getCurrentHostInfo(), this.properties, true, () => { + this.targetClient = new Client(WrapperProperties.removeWrapperProperties(this.config)); + return this.targetClient.connect(); + }); this.isConnected = true; return res; } diff --git a/tests/unit/aws_secrets_manager_plugin.test.ts b/tests/unit/aws_secrets_manager_plugin.test.ts new file mode 100644 index 00000000..5c3cdb93 --- /dev/null +++ b/tests/unit/aws_secrets_manager_plugin.test.ts @@ -0,0 +1,208 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { SecretsManagerServiceException, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; +import { AwsSecretsManagerPlugin, Secret, SecretCacheKey } from "aws-wrapper-common-lib/lib/authentication/aws_secrets_manager_plugin"; +import { AwsClient } from "aws-wrapper-common-lib/lib/aws_client"; +import { SimpleHostAvailabilityStrategy } from "aws-wrapper-common-lib/lib/host_availability/simple_host_availability_strategy"; +import { HostInfo } from "aws-wrapper-common-lib/lib/host_info"; +import { HostInfoBuilder } from "aws-wrapper-common-lib/lib/host_info_builder"; +import { PluginService } from "aws-wrapper-common-lib/lib/plugin_service"; +import { AwsWrapperError } from "aws-wrapper-common-lib/lib/utils/aws_wrapper_error"; +import { Messages } from "aws-wrapper-common-lib/lib/utils/messages"; +import { WrapperProperties } from "aws-wrapper-common-lib/lib/wrapper_property"; +import { anything, instance, mock, reset, verify, when } from "ts-mockito"; + +const secretsManagerClientException: SecretsManagerServiceException = new SecretsManagerServiceException({ + message: "message", + name: "name", + $fault: "server", + $metadata: {} +}); + +const TEST_PROPS = new Map(); +const TEST_SECRET_ID = "testSecret"; +const TEST_SECRET_REGION = "us-east-2"; +const TEST_USERNAME = "myUsername"; +const TEST_PASSWORD = "myPassword"; +const TEST_HOST = "host"; +const TEST_ARN_1 = "arn:aws:secretsmanager:us-east-2:123456789012:secret:foo"; +const TEST_ARN_2 = "arn:aws:secretsmanager:us-west-1:123456789012:secret:boo"; +const TEST_ARN_3 = "arn:aws:secretsmanager:us-east-2:123456789012:secret:rds!cluster-bar-foo"; +const TEST_HOSTINFO: HostInfo = new HostInfoBuilder({ + hostAvailabilityStrategy: new SimpleHostAvailabilityStrategy(), + host: TEST_HOST +}).build(); +const TEST_SECRET = new Secret(TEST_USERNAME, TEST_PASSWORD); +const TEST_SECRET_CACHE_KEY = new SecretCacheKey(TEST_SECRET_ID, TEST_SECRET_REGION); + +const VALID_SECRET_RESPONSE = { + SecretString: '{"username": "' + TEST_USERNAME + '", "password": "' + TEST_PASSWORD + '"}', + $metadata: {} +}; + +const mockPluginService: PluginService = mock(PluginService); +const mockClient: AwsClient = mock(AwsClient); +const mockSecretsManagerClient: SecretsManagerClient = mock(SecretsManagerClient); +const MYSQL_AUTH_ERROR = new Error("Access denied for user ''@'' (using password: NO)"); +const PG_AUTH_ERROR = new Error('password authentication failed for user ""'); + +let plugin: AwsSecretsManagerPlugin; + +let connectCounter = 0; +function mockConnectFunction(): Promise { + return Promise.resolve(); +} + +function mockConnectFunctionThrowsUnhandledError(): Promise { + throw new Error("test"); +} + +describe("testSecretsManager", () => { + beforeEach(() => { + connectCounter = 0; + TEST_PROPS.set(WrapperProperties.SECRET_ID.name, TEST_SECRET_ID); + TEST_PROPS.set(WrapperProperties.SECRET_REGION.name, TEST_SECRET_REGION); + const mockClientInstance = instance(mockClient); + when(mockPluginService.getCurrentClient()).thenReturn(mockClientInstance); + const mockPluginServiceInstance = instance(mockPluginService); + plugin = new AwsSecretsManagerPlugin(mockPluginServiceInstance, TEST_PROPS); + }); + + afterEach(() => { + TEST_PROPS.clear(); + reset(mockSecretsManagerClient); + AwsSecretsManagerPlugin.secretsCache.clear(); + }); + + // The plugin will successfully open a connection with a cached secret. + it("connect with cached secrets", async () => { + // Add initial cached secret to be used for a connection. + AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(TEST_SECRET_CACHE_KEY), TEST_SECRET); + + await plugin.connect(TEST_HOSTINFO, TEST_PROPS, true, mockConnectFunction); + + expect(AwsSecretsManagerPlugin.secretsCache.size).toBe(1); + verify(mockSecretsManagerClient.send(anything())).never(); + expect(TEST_PROPS.get(WrapperProperties.USER.name)).toBe(TEST_USERNAME); + expect(TEST_PROPS.get(WrapperProperties.PASSWORD.name)).toBe(TEST_PASSWORD); + }); + + // The plugin will attempt to open a connection with an empty secret cache. The plugin will fetch the + // secret from the AWS Secrets Manager. + it("connect with new secrets", async () => { + when(mockSecretsManagerClient.send(anything())).thenResolve(VALID_SECRET_RESPONSE); + plugin.secretsManagerClient = instance(mockSecretsManagerClient); + + await plugin.connect(TEST_HOSTINFO, TEST_PROPS, true, mockConnectFunction); + + expect(AwsSecretsManagerPlugin.secretsCache.size).toBe(1); + verify(mockSecretsManagerClient.send(anything())).once(); + expect(TEST_PROPS.get(WrapperProperties.USER.name)).toBe(TEST_USERNAME); + expect(TEST_PROPS.get(WrapperProperties.PASSWORD.name)).toBe(TEST_PASSWORD); + }); + + it("missing required parameters", () => { + expect(async () => { + await new AwsSecretsManagerPlugin(mockPluginService, new Map()).connect(TEST_HOSTINFO, TEST_PROPS, true, mockConnectFunction); + }).rejects.toStrictEqual(new AwsWrapperError(Messages.get("AwsSecretsManagerConnectionPlugin.missingRequiredConfigParameter"))); + }); + + // The plugin will attempt to open a connection with a cached secret, but it will fail with a unhandled error. + // In this case, the plugin will rethrow the error back to the user. + it("failed initial connection with unhandled error", async () => { + // Add initial cached secret to be used for a connection. + AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(TEST_SECRET_CACHE_KEY), TEST_SECRET); + + await expect(async () => { + await plugin.connect(TEST_HOSTINFO, TEST_PROPS, true, mockConnectFunctionThrowsUnhandledError); + }).rejects.toStrictEqual(new Error("test")); + + expect(AwsSecretsManagerPlugin.secretsCache.size).toBe(1); + verify(mockSecretsManagerClient.send(anything())).never(); + expect(TEST_PROPS.get(WrapperProperties.USER.name)).toBe(TEST_USERNAME); + expect(TEST_PROPS.get(WrapperProperties.PASSWORD.name)).toBe(TEST_PASSWORD); + }); + + // The plugin will attempt to open a connection with a cached secret, but it will fail due to authorization errors. + // In this case, the plugin will fetch the secret and will retry the connection. + it.each([MYSQL_AUTH_ERROR, PG_AUTH_ERROR])("connect with new secret after trying with cached secrets", async (error) => { + // Add initial cached secret to be used for a connection. + AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(TEST_SECRET_CACHE_KEY), new Secret("", "")); + when(mockSecretsManagerClient.send(anything())).thenResolve(VALID_SECRET_RESPONSE); + plugin.secretsManagerClient = instance(mockSecretsManagerClient); + + let connectCounter = 0; + function testConnectFunction(): Promise { + if (connectCounter === 0) { + connectCounter++; + throw error; + } + connectCounter++; + return Promise.resolve(); + } + + // Fail the initial connection attempt with cached secret. + // Second attempt should be successful. + await plugin.connect(TEST_HOSTINFO, TEST_PROPS, true, testConnectFunction); + expect(AwsSecretsManagerPlugin.secretsCache.size).toBe(1); + verify(mockSecretsManagerClient.send(anything())).once(); + expect(TEST_PROPS.get(WrapperProperties.USER.name)).toBe(TEST_USERNAME); + expect(TEST_PROPS.get(WrapperProperties.PASSWORD.name)).toBe(TEST_PASSWORD); + }); + + // The plugin will attempt to open a connection after fetching a secret, but it will fail because an exception was + // thrown by the AWS Secrets Manager. + it("failed to get secrets", async () => { + when(mockSecretsManagerClient.send(anything())).thenThrow(secretsManagerClientException); + plugin.secretsManagerClient = instance(mockSecretsManagerClient); + + await expect(async () => { + await plugin.connect(TEST_HOSTINFO, TEST_PROPS, true, mockConnectFunction); + }).rejects.toThrow(new AwsWrapperError(Messages.get("AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials"))); + + expect(AwsSecretsManagerPlugin.secretsCache.size).toBe(0); + verify(mockSecretsManagerClient.send(anything())).once(); + }); + + it.each([ + [TEST_ARN_1, "us-east-2"], + [TEST_ARN_2, "us-west-1"], + [TEST_ARN_3, "us-east-2"] + ])("connect using arn", async (arn, expectedRegionParsedFromARN) => { + const props = new Map(); + WrapperProperties.SECRET_ID.set(props, arn); + const testPlugin = new AwsSecretsManagerPlugin(mockPluginService, props); + const secretKey = testPlugin.secretKey; + expect(secretKey.region).toBe(expectedRegionParsedFromARN); + }); + + it.each([ + [TEST_ARN_1, "us-east-2"], + [TEST_ARN_2, "us-west-1"], + [TEST_ARN_3, "us-east-2"] + ])("connect using region and arn", async (arn, regionFromArn) => { + const expectedRegion = "us-iso-east-1"; + const props = new Map(); + WrapperProperties.SECRET_ID.set(props, arn); + WrapperProperties.SECRET_REGION.set(props, expectedRegion); + const testPlugin = new AwsSecretsManagerPlugin(mockPluginService, props); + const secretKey = testPlugin.secretKey; + // The region specified in `secretsManagerRegion` should override the region parsed from ARN. + expect(secretKey.region).not.toBe(regionFromArn); + expect(secretKey.region).toBe(expectedRegion); + }); +});