diff --git a/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts b/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts index 00651eacbf4d2..7b90018f43289 100644 --- a/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts +++ b/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts @@ -1,6 +1,6 @@ import type { CredentialProviderOptions } from "@aws-sdk/types"; import { CredentialsProviderError } from "@smithy/property-provider"; -import { AwsCredentialIdentity, Provider } from "@smithy/types"; +import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types"; import { CognitoProviderParameters } from "./CognitoProviderParameters"; import { resolveLogins } from "./resolveLogins"; @@ -30,16 +30,16 @@ export type CognitoIdentityCredentialProvider = Provider => { - parameters.logger?.debug("@aws-sdk/credential-provider-cognito-identity", "fromCognitoIdentity"); + parameters.logger?.debug("@aws-sdk/credential-provider-cognito-identity - fromCognitoIdentity"); const { GetCredentialsForIdentityCommand, CognitoIdentityClient } = await import("./loadCognitoIdentity"); const { Credentials: { - AccessKeyId = throwOnMissingAccessKeyId(), + AccessKeyId = throwOnMissingAccessKeyId(parameters.logger), Expiration, - SecretKey = throwOnMissingSecretKey(), + SecretKey = throwOnMissingSecretKey(parameters.logger), SessionToken, - } = throwOnMissingCredentials(), + } = throwOnMissingCredentials(parameters.logger), } = await ( parameters.client ?? new CognitoIdentityClient( @@ -76,14 +76,14 @@ export interface FromCognitoIdentityParameters extends CognitoProviderParameters identityId: string; } -function throwOnMissingAccessKeyId(): never { - throw new CredentialsProviderError("Response from Amazon Cognito contained no access key ID"); +function throwOnMissingAccessKeyId(logger?: Logger): never { + throw new CredentialsProviderError("Response from Amazon Cognito contained no access key ID", { logger }); } -function throwOnMissingCredentials(): never { - throw new CredentialsProviderError("Response from Amazon Cognito contained no credentials"); +function throwOnMissingCredentials(logger?: Logger): never { + throw new CredentialsProviderError("Response from Amazon Cognito contained no credentials", { logger }); } -function throwOnMissingSecretKey(): never { - throw new CredentialsProviderError("Response from Amazon Cognito contained no secret key"); +function throwOnMissingSecretKey(logger?: Logger): never { + throw new CredentialsProviderError("Response from Amazon Cognito contained no secret key", { logger }); } diff --git a/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts b/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts index fc1e1db9e925e..559b281897274 100644 --- a/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts +++ b/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts @@ -1,5 +1,6 @@ import type { CredentialProviderOptions } from "@aws-sdk/types"; import { CredentialsProviderError } from "@smithy/property-provider"; +import { Logger } from "@smithy/types"; import { CognitoProviderParameters } from "./CognitoProviderParameters"; import { CognitoIdentityCredentialProvider, fromCognitoIdentity } from "./fromCognitoIdentity"; @@ -29,7 +30,7 @@ export function fromCognitoIdentityPool({ logger, parentClientConfig, }: FromCognitoIdentityPoolParameters): CognitoIdentityCredentialProvider { - logger?.debug("@aws-sdk/credential-provider-cognito-identity", "fromCognitoIdentity"); + logger?.debug("@aws-sdk/credential-provider-cognito-identity - fromCognitoIdentity"); const cacheKey: string | undefined = userIdentifier ? `aws:cognito-identity-credentials:${identityPoolId}:${userIdentifier}` : undefined; @@ -44,7 +45,7 @@ export function fromCognitoIdentityPool({ let identityId: string | undefined = (cacheKey && (await cache.getItem(cacheKey))) as string | undefined; if (!identityId) { - const { IdentityId = throwOnMissingId() } = await _client.send( + const { IdentityId = throwOnMissingId(logger) } = await _client.send( new GetIdCommand({ AccountId: accountId, IdentityPoolId: identityPoolId, @@ -116,6 +117,6 @@ export interface FromCognitoIdentityPoolParameters extends CognitoProviderParame userIdentifier?: string; } -function throwOnMissingId(): never { - throw new CredentialsProviderError("Response from Amazon Cognito contained no identity ID"); +function throwOnMissingId(logger?: Logger): never { + throw new CredentialsProviderError("Response from Amazon Cognito contained no identity ID", { logger }); } diff --git a/packages/credential-provider-env/src/fromEnv.ts b/packages/credential-provider-env/src/fromEnv.ts index 2d47bd54107ed..0b9aeeb58eef7 100644 --- a/packages/credential-provider-env/src/fromEnv.ts +++ b/packages/credential-provider-env/src/fromEnv.ts @@ -35,7 +35,7 @@ export const ENV_CREDENTIAL_SCOPE = "AWS_CREDENTIAL_SCOPE"; export const fromEnv = (init?: FromEnvInit): AwsCredentialIdentityProvider => async () => { - init?.logger?.debug("@aws-sdk/credential-provider-env", "fromEnv"); + init?.logger?.debug("@aws-sdk/credential-provider-env - fromEnv"); const accessKeyId: string | undefined = process.env[ENV_KEY]; const secretAccessKey: string | undefined = process.env[ENV_SECRET]; const sessionToken: string | undefined = process.env[ENV_SESSION]; @@ -52,5 +52,5 @@ export const fromEnv = }; } - throw new CredentialsProviderError("Unable to find environment variable credentials."); + throw new CredentialsProviderError("Unable to find environment variable credentials.", { logger: init?.logger }); }; diff --git a/packages/credential-provider-http/src/fromHttp/checkUrl.ts b/packages/credential-provider-http/src/fromHttp/checkUrl.ts index 805706c0c68a9..737a48883943c 100644 --- a/packages/credential-provider-http/src/fromHttp/checkUrl.ts +++ b/packages/credential-provider-http/src/fromHttp/checkUrl.ts @@ -1,4 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { Logger } from "@smithy/types"; /** * @internal @@ -28,9 +29,10 @@ const EKS_CONTAINER_HOST_IPv6 = "[fd00:ec2::23]"; * @internal * * @param url - to be validated. + * @param logger - passed to CredentialsProviderError. * @throws if not acceptable to this provider. */ -export const checkUrl = (url: URL): void => { +export const checkUrl = (url: URL, logger?: Logger): void => { if (url.protocol === "https:") { // no additional requirements for HTTPS. return; @@ -74,6 +76,7 @@ export const checkUrl = (url: URL): void => { `URL not accepted. It must either be HTTPS or match one of the following: - loopback CIDR 127.0.0.0/8 or [::1/128] - ECS container host 169.254.170.2 - - EKS container host 169.254.170.23 or [fd00:ec2::23]` + - EKS container host 169.254.170.23 or [fd00:ec2::23]`, + { logger } ); }; diff --git a/packages/credential-provider-http/src/fromHttp/fromHttp.browser.ts b/packages/credential-provider-http/src/fromHttp/fromHttp.browser.ts index e35e72a93c66e..7068a9e10b37f 100644 --- a/packages/credential-provider-http/src/fromHttp/fromHttp.browser.ts +++ b/packages/credential-provider-http/src/fromHttp/fromHttp.browser.ts @@ -10,8 +10,8 @@ import { retryWrapper } from "./retry-wrapper"; /** * Creates a provider that gets credentials via HTTP request. */ -export const fromHttp = (options: FromHttpOptions): AwsCredentialIdentityProvider => { - options.logger?.debug("@aws-sdk/credential-provider-http", "fromHttp"); +export const fromHttp = (options: FromHttpOptions = {}): AwsCredentialIdentityProvider => { + options.logger?.debug("@aws-sdk/credential-provider-http - fromHttp"); let host: string; const full = options.credentialsFullUri; @@ -19,14 +19,14 @@ export const fromHttp = (options: FromHttpOptions): AwsCredentialIdentityProvide if (full) { host = full; } else { - throw new CredentialsProviderError("No HTTP credential provider host provided."); + throw new CredentialsProviderError("No HTTP credential provider host provided.", { logger: options.logger }); } // throws if invalid format. const url = new URL(host); // throws if not to spec for provider. - checkUrl(url); + checkUrl(url, options.logger); const requestHandler = new FetchHttpHandler(); diff --git a/packages/credential-provider-http/src/fromHttp/fromHttp.ts b/packages/credential-provider-http/src/fromHttp/fromHttp.ts index eafe5c045b68d..287ab3a12a931 100644 --- a/packages/credential-provider-http/src/fromHttp/fromHttp.ts +++ b/packages/credential-provider-http/src/fromHttp/fromHttp.ts @@ -17,8 +17,8 @@ const AWS_CONTAINER_AUTHORIZATION_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN"; /** * Creates a provider that gets credentials via HTTP request. */ -export const fromHttp = (options: FromHttpOptions): AwsCredentialIdentityProvider => { - options.logger?.debug("@aws-sdk/credential-provider-http", "fromHttp"); +export const fromHttp = (options: FromHttpOptions = {}): AwsCredentialIdentityProvider => { + options.logger?.debug("@aws-sdk/credential-provider-http - fromHttp"); let host: string; const relative = options.awsContainerCredentialsRelativeUri ?? process.env[AWS_CONTAINER_CREDENTIALS_RELATIVE_URI]; @@ -26,20 +26,23 @@ export const fromHttp = (options: FromHttpOptions): AwsCredentialIdentityProvide const token = options.awsContainerAuthorizationToken ?? process.env[AWS_CONTAINER_AUTHORIZATION_TOKEN]; const tokenFile = options.awsContainerAuthorizationTokenFile ?? process.env[AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE]; + const warn: (warning: string) => void = + options.logger?.constructor?.name === "NoOpLogger" || !options.logger ? console.warn : options.logger.warn; + if (relative && full) { - console.warn( - "AWS SDK HTTP credentials provider:", - "you have set both awsContainerCredentialsRelativeUri and awsContainerCredentialsFullUri." + warn( + "@aws-sdk/credential-provider-http: " + + "you have set both awsContainerCredentialsRelativeUri and awsContainerCredentialsFullUri." ); - console.warn("awsContainerCredentialsFullUri will take precedence."); + warn("awsContainerCredentialsFullUri will take precedence."); } if (token && tokenFile) { - console.warn( - "AWS SDK HTTP credentials provider:", - "you have set both awsContainerAuthorizationToken and awsContainerAuthorizationTokenFile." + warn( + "@aws-sdk/credential-provider-http: " + + "you have set both awsContainerAuthorizationToken and awsContainerAuthorizationTokenFile." ); - console.warn("awsContainerAuthorizationToken will take precedence."); + warn("awsContainerAuthorizationToken will take precedence."); } if (full) { @@ -49,7 +52,8 @@ export const fromHttp = (options: FromHttpOptions): AwsCredentialIdentityProvide } else { throw new CredentialsProviderError( `No HTTP credential provider host provided. -Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.` +Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.`, + { logger: options.logger } ); } @@ -57,7 +61,7 @@ Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI const url = new URL(host); // throws if not to spec for provider. - checkUrl(url); + checkUrl(url, options.logger); const requestHandler = new NodeHttpHandler({ requestTimeout: options.timeout ?? 1000, @@ -79,7 +83,7 @@ Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI const result = await requestHandler.handle(request); return getCredentials(result.response); } catch (e: unknown) { - throw new CredentialsProviderError(String(e)); + throw new CredentialsProviderError(String(e), { logger: options.logger }); } }, options.maxRetries ?? 3, diff --git a/packages/credential-provider-http/src/fromHttp/requestHelpers.ts b/packages/credential-provider-http/src/fromHttp/requestHelpers.ts index 594de86868178..9fb0c0afb35de 100644 --- a/packages/credential-provider-http/src/fromHttp/requestHelpers.ts +++ b/packages/credential-provider-http/src/fromHttp/requestHelpers.ts @@ -2,7 +2,7 @@ import { AwsCredentialIdentity } from "@aws-sdk/types"; import { CredentialsProviderError } from "@smithy/property-provider"; import { HttpRequest } from "@smithy/protocol-http"; import { parseRfc3339DateTime } from "@smithy/smithy-client"; -import { HttpResponse } from "@smithy/types"; +import { HttpResponse, Logger } from "@smithy/types"; import { sdkStreamMixin } from "@smithy/util-stream"; import { HttpProviderCredentials } from "./fromHttpTypes"; @@ -27,11 +27,13 @@ export function createGetRequest(url: URL): HttpRequest { /** * @internal */ -export async function getCredentials(response: HttpResponse): Promise { +export async function getCredentials(response: HttpResponse, logger?: Logger): Promise { const contentType = response?.headers["content-type"] ?? response?.headers["Content-Type"] ?? ""; if (!contentType.includes("json")) { - console.warn( + const warn: (warning: string) => void = + logger?.constructor?.name === "NoOpLogger" || !logger ? console.warn : logger.warn; + warn( "HTTP credential provider response header content-type was not application/json. Observed: " + contentType + "." ); } @@ -50,7 +52,8 @@ export async function getCredentials(response: HttpResponse): Promise async () => { - init.logger?.debug("@aws-sdk/credential-provider-ini", "fromIni"); + init.logger?.debug("@aws-sdk/credential-provider-ini - fromIni"); const profiles = await parseKnownFiles(init); return resolveProfileData(getProfileName(init), profiles, init); }; diff --git a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts index 772443e6b6b04..52adc6ebb9085 100644 --- a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts @@ -162,7 +162,9 @@ describe(resolveAssumeRoleCredentials.name, () => { source_profile: mockProfileName, ...mockRoleAssumeParams, }, - [mockProfileName]: {}, + [mockProfileName]: { + role_arn: "mock_role_arn", + }, }; const receivedCreds = await resolveAssumeRoleCredentials(mockProfileCurrent, mockProfilesWithSource, mockOptions); @@ -186,7 +188,7 @@ describe(resolveAssumeRoleCredentials.name, () => { const receivedCreds = await resolveAssumeRoleCredentials(mockProfileName, mockProfilesWithCredSource, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveProfileData).not.toHaveBeenCalled(); - expect(resolveCredentialSource).toHaveBeenCalledWith(mockCredentialSource, mockProfileName); + expect(resolveCredentialSource).toHaveBeenCalledWith(mockCredentialSource, mockProfileName, undefined); expect(mockOptions.roleAssumer).toHaveBeenCalledWith(mockSourceCredsFromCredential, { RoleArn: mockRoleAssumeParams.role_arn, RoleSessionName: mockRoleAssumeParams.role_session_name, diff --git a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts index e768d7284d67e..0357c9564d8a2 100644 --- a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts +++ b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts @@ -1,6 +1,6 @@ import { CredentialsProviderError } from "@smithy/property-provider"; import { getProfileName } from "@smithy/shared-ini-file-loader"; -import { AwsCredentialIdentity, ParsedIniData, Profile } from "@smithy/types"; +import { AwsCredentialIdentity, Logger, ParsedIniData, Profile } from "@smithy/types"; import { FromIniInit } from "./fromIni"; import { resolveCredentialSource } from "./resolveCredentialSource"; @@ -59,20 +59,42 @@ interface AssumeRoleWithProviderProfile extends Profile { /** * @internal */ -export const isAssumeRoleProfile = (arg: any) => - Boolean(arg) && - typeof arg === "object" && - typeof arg.role_arn === "string" && - ["undefined", "string"].indexOf(typeof arg.role_session_name) > -1 && - ["undefined", "string"].indexOf(typeof arg.external_id) > -1 && - ["undefined", "string"].indexOf(typeof arg.mfa_serial) > -1 && - (isAssumeRoleWithSourceProfile(arg) || isAssumeRoleWithProviderProfile(arg)); +export const isAssumeRoleProfile = ( + arg: any, + { profile = "default", logger }: { profile?: string; logger?: Logger } = {} +) => { + return ( + Boolean(arg) && + typeof arg === "object" && + typeof arg.role_arn === "string" && + ["undefined", "string"].indexOf(typeof arg.role_session_name) > -1 && + ["undefined", "string"].indexOf(typeof arg.external_id) > -1 && + ["undefined", "string"].indexOf(typeof arg.mfa_serial) > -1 && + (isAssumeRoleWithSourceProfile(arg, { profile, logger }) || isCredentialSourceProfile(arg, { profile, logger })) + ); +}; -const isAssumeRoleWithSourceProfile = (arg: any): arg is AssumeRoleWithSourceProfile => - typeof arg.source_profile === "string" && typeof arg.credential_source === "undefined"; +const isAssumeRoleWithSourceProfile = ( + arg: any, + { profile, logger }: { profile: string; logger?: Logger } +): arg is AssumeRoleWithSourceProfile => { + const withSourceProfile = typeof arg.source_profile === "string" && typeof arg.credential_source === "undefined"; + if (withSourceProfile) { + logger?.debug?.(` ${profile} isAssumeRoleWithSourceProfile source_profile=${arg.source_profile}`); + } + return withSourceProfile; +}; -const isAssumeRoleWithProviderProfile = (arg: any): arg is AssumeRoleWithProviderProfile => - typeof arg.credential_source === "string" && typeof arg.source_profile === "undefined"; +const isCredentialSourceProfile = ( + arg: any, + { profile, logger }: { profile: string; logger?: Logger } +): arg is AssumeRoleWithProviderProfile => { + const withProviderProfile = typeof arg.credential_source === "string" && typeof arg.source_profile === "undefined"; + if (withProviderProfile) { + logger?.debug?.(` ${profile} isCredentialSourceProfile credential_source=${arg.credential_source}`); + } + return withProviderProfile; +}; /** * @internal @@ -83,7 +105,7 @@ export const resolveAssumeRoleCredentials = async ( options: FromIniInit, visitedProfiles: Record = {} ) => { - options.logger?.debug("@aws-sdk/credential-provider-ini", "resolveAssumeRoleCredentials (STS)"); + options.logger?.debug("@aws-sdk/credential-provider-ini - resolveAssumeRoleCredentials (STS)"); const data = profiles[profileName]; if (!options.roleAssumer) { @@ -105,16 +127,36 @@ export const resolveAssumeRoleCredentials = async ( `Detected a cycle attempting to resolve credentials for profile` + ` ${getProfileName(options)}. Profiles visited: ` + Object.keys(visitedProfiles).join(", "), - false + { logger: options.logger } ); } + options.logger?.debug( + `@aws-sdk/credential-provider-ini - finding credential resolver using ${ + source_profile ? `source_profile=[${source_profile}]` : `profile=[${profileName}]` + }` + ); + const sourceCredsProvider: Promise = source_profile - ? resolveProfileData(source_profile, profiles, options, { - ...visitedProfiles, - [source_profile]: true, - }) - : (await resolveCredentialSource(data.credential_source!, profileName)(options))(); + ? resolveProfileData( + source_profile, + { + ...profiles, + [source_profile]: { + ...profiles[source_profile], + // This assigns the role_arn of the "root" profile + // to the credential_source profile so this recursive call knows + // what role to assume. + role_arn: data.role_arn ?? profiles[source_profile].role_arn, + }, + }, + options, + { + ...visitedProfiles, + [source_profile]: true, + } + ) + : (await resolveCredentialSource(data.credential_source!, profileName, options.logger)(options))(); const params: AssumeRoleParams = { RoleArn: data.role_arn!, @@ -128,7 +170,7 @@ export const resolveAssumeRoleCredentials = async ( if (!options.mfaCodeProvider) { throw new CredentialsProviderError( `Profile ${profileName} requires multi-factor authentication, but no MFA code callback was provided.`, - false + { logger: options.logger, tryNextLink: false } ); } params.SerialNumber = mfa_serial; diff --git a/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts b/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts index 5e0923b2f3aae..ce117bd7e8fe2 100644 --- a/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts +++ b/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts @@ -1,7 +1,9 @@ jest.mock("@aws-sdk/credential-provider-env"); +jest.mock("@aws-sdk/credential-provider-http"); jest.mock("@smithy/credential-provider-imds"); import { fromEnv } from "@aws-sdk/credential-provider-env"; +import { fromHttp } from "@aws-sdk/credential-provider-http"; import { fromContainerMetadata, fromInstanceMetadata } from "@smithy/credential-provider-imds"; import { CredentialsProviderError } from "@smithy/property-provider"; @@ -23,6 +25,9 @@ describe(resolveCredentialSource.name, () => { beforeEach(() => { (fromEnv as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); (fromContainerMetadata as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); + (fromHttp as jest.Mock).mockReturnValue(() => { + throw new CredentialsProviderError("try next", {}); + }); (fromInstanceMetadata as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); }); diff --git a/packages/credential-provider-ini/src/resolveCredentialSource.ts b/packages/credential-provider-ini/src/resolveCredentialSource.ts index 2cbc2db38b869..8c4efb47617f0 100644 --- a/packages/credential-provider-ini/src/resolveCredentialSource.ts +++ b/packages/credential-provider-ini/src/resolveCredentialSource.ts @@ -1,6 +1,6 @@ import type { CredentialProviderOptions } from "@aws-sdk/types"; -import { CredentialsProviderError } from "@smithy/property-provider"; -import { AwsCredentialIdentityProvider } from "@smithy/types"; +import { chain, CredentialsProviderError } from "@smithy/property-provider"; +import { AwsCredentialIdentityProvider, Logger } from "@smithy/types"; /** * @internal @@ -13,22 +13,34 @@ import { AwsCredentialIdentityProvider } from "@smithy/types"; */ export const resolveCredentialSource = ( credentialSource: string, - profileName: string + profileName: string, + logger?: Logger ): ((options?: CredentialProviderOptions) => Promise) => { const sourceProvidersMap = { - EcsContainer: (options?: CredentialProviderOptions) => - import("@smithy/credential-provider-imds").then(({ fromContainerMetadata }) => fromContainerMetadata(options)), - Ec2InstanceMetadata: (options?: CredentialProviderOptions) => - import("@smithy/credential-provider-imds").then(({ fromInstanceMetadata }) => fromInstanceMetadata(options)), - Environment: (options?: CredentialProviderOptions) => - import("@aws-sdk/credential-provider-env").then(({ fromEnv }) => fromEnv(options)), + EcsContainer: async (options?: CredentialProviderOptions) => { + const { fromHttp } = await import("@aws-sdk/credential-provider-http"); + const { fromContainerMetadata } = await import("@smithy/credential-provider-imds"); + logger?.debug("@aws-sdk/credential-provider-ini - credential_source is EcsContainer"); + return chain(fromHttp(options ?? {}), fromContainerMetadata(options)); + }, + Ec2InstanceMetadata: async (options?: CredentialProviderOptions) => { + logger?.debug("@aws-sdk/credential-provider-ini - credential_source is Ec2InstanceMetadata"); + const { fromInstanceMetadata } = await import("@smithy/credential-provider-imds"); + return fromInstanceMetadata(options); + }, + Environment: async (options?: CredentialProviderOptions) => { + logger?.debug("@aws-sdk/credential-provider-ini - credential_source is Environment"); + const { fromEnv } = await import("@aws-sdk/credential-provider-env"); + return fromEnv(options); + }, }; if (credentialSource in sourceProvidersMap) { return sourceProvidersMap[credentialSource as keyof typeof sourceProvidersMap]; } else { throw new CredentialsProviderError( `Unsupported credential source in profile ${profileName}. Got ${credentialSource}, ` + - `expected EcsContainer or Ec2InstanceMetadata or Environment.` + `expected EcsContainer or Ec2InstanceMetadata or Environment.`, + { logger } ); } }; diff --git a/packages/credential-provider-ini/src/resolveProfileData.spec.ts b/packages/credential-provider-ini/src/resolveProfileData.spec.ts index 24f24402a04c8..6867998e30491 100644 --- a/packages/credential-provider-ini/src/resolveProfileData.spec.ts +++ b/packages/credential-provider-ini/src/resolveProfileData.spec.ts @@ -20,7 +20,7 @@ describe(resolveProfileData.name, () => { roleAssumerWithWebIdentity: jest.fn(), }; const mockError = new CredentialsProviderError( - `Profile ${mockProfileName} could not be found or parsed in shared credentials file.` + `Could not resolve credentials using profile: [${mockProfileName}] in configuration/credentials file(s).` ); const mockCreds = { diff --git a/packages/credential-provider-ini/src/resolveProfileData.ts b/packages/credential-provider-ini/src/resolveProfileData.ts index fb4eb179c3ec2..ea35a74ffe2fe 100644 --- a/packages/credential-provider-ini/src/resolveProfileData.ts +++ b/packages/credential-provider-ini/src/resolveProfileData.ts @@ -28,7 +28,7 @@ export const resolveProfileData = async ( // If this is the first profile visited, role assumption keys should be // given precedence over static credentials. - if (isAssumeRoleProfile(data)) { + if (isAssumeRoleProfile(data, { profile: profileName, logger: options.logger })) { return resolveAssumeRoleCredentials(profileName, profiles, options, visitedProfiles); } @@ -59,5 +59,8 @@ export const resolveProfileData = async ( // terminal resolution error if a profile has been specified by the user // (whether via a parameter, an environment variable, or another profile's // `source_profile` key). - throw new CredentialsProviderError(`Profile ${profileName} could not be found or parsed in shared credentials file.`); + throw new CredentialsProviderError( + `Could not resolve credentials using profile: [${profileName}] in configuration/credentials file(s).`, + { logger: options.logger } + ); }; diff --git a/packages/credential-provider-ini/src/resolveStaticCredentials.ts b/packages/credential-provider-ini/src/resolveStaticCredentials.ts index 012dda732f063..c9ced1a540c38 100644 --- a/packages/credential-provider-ini/src/resolveStaticCredentials.ts +++ b/packages/credential-provider-ini/src/resolveStaticCredentials.ts @@ -29,7 +29,7 @@ export const resolveStaticCredentials = ( profile: StaticCredsProfile, options?: FromIniInit ): Promise => { - options?.logger?.debug("@aws-sdk/credential-provider-ini", "resolveStaticCredentials"); + options?.logger?.debug("@aws-sdk/credential-provider-ini - resolveStaticCredentials"); return Promise.resolve({ accessKeyId: profile.aws_access_key_id, secretAccessKey: profile.aws_secret_access_key, diff --git a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts index 624b42f7bde45..3b83e504cc33c 100644 --- a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts +++ b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts @@ -1,4 +1,5 @@ import { STS } from "@aws-sdk/client-sts"; +import * as credentialProviderHttp from "@aws-sdk/credential-provider-http"; import { HttpResponse } from "@smithy/protocol-http"; import type { SourceProfileInit } from "@smithy/shared-ini-file-loader"; import type { HttpRequest, NodeHttpHandlerOptions, ParsedIniData } from "@smithy/types"; @@ -490,6 +491,44 @@ describe("credential-provider-node integration test", () => { credentialScope: "us-sso-1-us-sso-region-1", }); }); + + it("should be able to combine a source_profile having credential_source with an origin profile having role_arn and source_profile", async () => { + process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI = "http://169.254.170.23"; + process.env.AWS_CONTAINER_AUTHORIZATION_TOKEN = "container-authorization"; + iniProfileData.default.source_profile = "credential_source_profile"; + iniProfileData.default.role_arn = "ROLE_ARN"; + iniProfileData.credential_source_profile = { + credential_source: "EcsContainer", + }; + const spy = jest.spyOn(credentialProviderHttp, "fromHttp"); + sts = new STS({ + region: "us-west-2", + requestHandler: mockRequestHandler, + credentials: defaultProvider({ + awsContainerCredentialsFullUri: process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI, + awsContainerAuthorizationToken: process.env.AWS_CONTAINER_AUTHORIZATION_TOKEN, + clientConfig: { + region: "us-west-2", + }, + }), + }); + await sts.getCallerIdentity({}); + const credentials = await sts.config.credentials(); + expect(credentials).toEqual({ + accessKeyId: "STS_AR_ACCESS_KEY_ID", + secretAccessKey: "STS_AR_SECRET_ACCESS_KEY", + sessionToken: "STS_AR_SESSION_TOKEN", + expiration: new Date("3000-01-01T00:00:00.000Z"), + credentialScope: "us-stsar-1__us-west-2", + }); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + awsContainerCredentialsFullUri: process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI, + awsContainerAuthorizationToken: process.env.AWS_CONTAINER_AUTHORIZATION_TOKEN, + }) + ); + spy.mockClear(); + }); }); describe("fromProcess", () => { diff --git a/packages/credential-provider-node/src/defaultProvider.ts b/packages/credential-provider-node/src/defaultProvider.ts index bf1754da06f4b..65b8c4b2cf29e 100644 --- a/packages/credential-provider-node/src/defaultProvider.ts +++ b/packages/credential-provider-node/src/defaultProvider.ts @@ -1,4 +1,5 @@ import { fromEnv } from "@aws-sdk/credential-provider-env"; +import type { FromHttpOptions } from "@aws-sdk/credential-provider-http"; import type { FromIniInit } from "@aws-sdk/credential-provider-ini"; import type { FromProcessInit } from "@aws-sdk/credential-provider-process"; import type { FromSSOInit, SsoCredentialsParameters } from "@aws-sdk/credential-provider-sso"; @@ -14,6 +15,7 @@ import { remoteProvider } from "./remoteProvider"; * @public */ export type DefaultProviderInit = FromIniInit & + FromHttpOptions & RemoteProviderInit & FromProcessInit & (FromSSOInit & Partial) & @@ -60,42 +62,46 @@ export const defaultProvider = (init: DefaultProviderInit = {}): MemoizedProvide ? [] : [ async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::fromEnv"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromEnv"); return fromEnv(init)(); }, ]), async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::fromSSO"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromSSO"); const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoSession } = init; if (!ssoStartUrl && !ssoAccountId && !ssoRegion && !ssoRoleName && !ssoSession) { throw new CredentialsProviderError( - "Skipping SSO provider in default chain (inputs do not include SSO fields)." + "Skipping SSO provider in default chain (inputs do not include SSO fields).", + { logger: init.logger } ); } const { fromSSO } = await import("@aws-sdk/credential-provider-sso"); return fromSSO(init)(); }, async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::fromIni"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromIni"); const { fromIni } = await import("@aws-sdk/credential-provider-ini"); return fromIni(init)(); }, async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::fromProcess"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromProcess"); const { fromProcess } = await import("@aws-sdk/credential-provider-process"); return fromProcess(init)(); }, async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::fromTokenFile"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::fromTokenFile"); const { fromTokenFile } = await import("@aws-sdk/credential-provider-web-identity"); return fromTokenFile(init)(); }, async () => { - init.logger?.debug("@aws-sdk/credential-provider-node", "defaultProvider::remoteProvider"); + init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::remoteProvider"); return (await remoteProvider(init))(); }, async () => { - throw new CredentialsProviderError("Could not load credentials from any providers", false); + throw new CredentialsProviderError("Could not load credentials from any providers", { + tryNextLink: false, + logger: init.logger, + }); } ), credentialsTreatedAsExpired, diff --git a/packages/credential-provider-node/src/remoteProvider.ts b/packages/credential-provider-node/src/remoteProvider.ts index 318d8fdbe9388..27dce9a2de8e7 100644 --- a/packages/credential-provider-node/src/remoteProvider.ts +++ b/packages/credential-provider-node/src/remoteProvider.ts @@ -1,3 +1,4 @@ +import type { FromHttpOptions } from "@aws-sdk/credential-provider-http"; import type { RemoteProviderInit } from "@smithy/credential-provider-imds"; import { chain, CredentialsProviderError } from "@smithy/property-provider"; import type { AwsCredentialIdentityProvider } from "@smithy/types"; @@ -10,23 +11,25 @@ export const ENV_IMDS_DISABLED = "AWS_EC2_METADATA_DISABLED"; /** * @internal */ -export const remoteProvider = async (init: RemoteProviderInit): Promise => { +export const remoteProvider = async ( + init: RemoteProviderInit | FromHttpOptions +): Promise => { const { ENV_CMDS_FULL_URI, ENV_CMDS_RELATIVE_URI, fromContainerMetadata, fromInstanceMetadata } = await import( "@smithy/credential-provider-imds" ); if (process.env[ENV_CMDS_RELATIVE_URI] || process.env[ENV_CMDS_FULL_URI]) { - init.logger?.debug("@aws-sdk/credential-provider-node", "remoteProvider::fromHttp/fromContainerMetadata"); + init.logger?.debug("@aws-sdk/credential-provider-node - remoteProvider::fromHttp/fromContainerMetadata"); const { fromHttp } = await import("@aws-sdk/credential-provider-http"); return chain(fromHttp(init), fromContainerMetadata(init)); } if (process.env[ENV_IMDS_DISABLED]) { return async () => { - throw new CredentialsProviderError("EC2 Instance Metadata Service access disabled"); + throw new CredentialsProviderError("EC2 Instance Metadata Service access disabled", { logger: init.logger }); }; } - init.logger?.debug("@aws-sdk/credential-provider-node", "remoteProvider::fromInstanceMetadata"); + init.logger?.debug("@aws-sdk/credential-provider-node - remoteProvider::fromInstanceMetadata"); return fromInstanceMetadata(init); }; diff --git a/packages/credential-provider-process/src/fromProcess.spec.ts b/packages/credential-provider-process/src/fromProcess.spec.ts index daacfb026047e..b61e39dfa8e79 100644 --- a/packages/credential-provider-process/src/fromProcess.spec.ts +++ b/packages/credential-provider-process/src/fromProcess.spec.ts @@ -47,7 +47,7 @@ describe(fromProcess.name, () => { } expect(parseKnownFiles).toHaveBeenCalledWith(mockInit); expect(getProfileName).toHaveBeenCalledWith(mockInit); - expect(resolveProcessCredentials).toHaveBeenCalledWith(mockMasterProfileName, mockProfiles); + expect(resolveProcessCredentials).toHaveBeenCalledWith(mockMasterProfileName, mockProfiles, undefined); }); it("returns resolved process creds", async () => { @@ -60,6 +60,6 @@ describe(fromProcess.name, () => { expect(receivedCreds).toStrictEqual(expectedCreds); expect(parseKnownFiles).toHaveBeenCalledWith(mockInit); expect(getProfileName).toHaveBeenCalledWith(mockInit); - expect(resolveProcessCredentials).toHaveBeenCalledWith(mockMasterProfileName, mockProfiles); + expect(resolveProcessCredentials).toHaveBeenCalledWith(mockMasterProfileName, mockProfiles, undefined); }); }); diff --git a/packages/credential-provider-process/src/fromProcess.ts b/packages/credential-provider-process/src/fromProcess.ts index 719a2958c6388..ec03d36ac8c8d 100644 --- a/packages/credential-provider-process/src/fromProcess.ts +++ b/packages/credential-provider-process/src/fromProcess.ts @@ -18,7 +18,7 @@ export interface FromProcessInit extends SourceProfileInit, CredentialProviderOp export const fromProcess = (init: FromProcessInit = {}): AwsCredentialIdentityProvider => async () => { - init.logger?.debug("@aws-sdk/credential-provider-process", "fromProcess"); + init.logger?.debug("@aws-sdk/credential-provider-process - fromProcess"); const profiles = await parseKnownFiles(init); - return resolveProcessCredentials(getProfileName(init), profiles); + return resolveProcessCredentials(getProfileName(init), profiles, init.logger); }; diff --git a/packages/credential-provider-process/src/resolveProcessCredentials.ts b/packages/credential-provider-process/src/resolveProcessCredentials.ts index 28a5e56f7a49b..c0df0c38da100 100644 --- a/packages/credential-provider-process/src/resolveProcessCredentials.ts +++ b/packages/credential-provider-process/src/resolveProcessCredentials.ts @@ -1,5 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; -import { AwsCredentialIdentity, ParsedIniData } from "@smithy/types"; +import { AwsCredentialIdentity, Logger, ParsedIniData } from "@smithy/types"; import { exec } from "child_process"; import { promisify } from "util"; @@ -11,7 +11,8 @@ import { ProcessCredentials } from "./ProcessCredentials"; */ export const resolveProcessCredentials = async ( profileName: string, - profiles: ParsedIniData + profiles: ParsedIniData, + logger?: Logger ): Promise => { const profile = profiles[profileName]; @@ -29,16 +30,18 @@ export const resolveProcessCredentials = async ( } return getValidatedProcessCredentials(profileName, data as ProcessCredentials); } catch (error) { - throw new CredentialsProviderError(error.message); + throw new CredentialsProviderError(error.message, { logger }); } } else { - throw new CredentialsProviderError(`Profile ${profileName} did not contain credential_process.`); + throw new CredentialsProviderError(`Profile ${profileName} did not contain credential_process.`, { logger }); } } else { // If the profile cannot be parsed or does not contain the default or // specified profile throw an error. This should be considered a terminal // resolution error if a profile has been specified by the user (whether via // a parameter, anenvironment variable, or another profile's `source_profile` key). - throw new CredentialsProviderError(`Profile ${profileName} could not be found in shared credentials file.`); + throw new CredentialsProviderError(`Profile ${profileName} could not be found in shared credentials file.`, { + logger, + }); } }; diff --git a/packages/credential-provider-sso/src/fromSSO.spec.ts b/packages/credential-provider-sso/src/fromSSO.spec.ts index bd86bc8ee370a..537f93dc3ea7c 100644 --- a/packages/credential-provider-sso/src/fromSSO.spec.ts +++ b/packages/credential-provider-sso/src/fromSSO.spec.ts @@ -98,7 +98,7 @@ describe(fromSSO.name, () => { } catch (error) { expect(error).toStrictEqual(expectedError); } - expect(validateSsoProfile).toHaveBeenCalledWith(mockSsoProfile); + expect(validateSsoProfile).toHaveBeenCalledWith(mockSsoProfile, undefined); }); it("calls resolveSSOCredentials with values from validated SSO profile", async () => { diff --git a/packages/credential-provider-sso/src/fromSSO.ts b/packages/credential-provider-sso/src/fromSSO.ts index 7ba762e292a9d..0e1d3ab56aae2 100644 --- a/packages/credential-provider-sso/src/fromSSO.ts +++ b/packages/credential-provider-sso/src/fromSSO.ts @@ -81,7 +81,7 @@ export interface FromSSOInit extends SourceProfileInit, CredentialProviderOption export const fromSSO = (init: FromSSOInit & Partial = {}): AwsCredentialIdentityProvider => async () => { - init.logger?.debug("@aws-sdk/credential-provider-sso", "fromSSO"); + init.logger?.debug("@aws-sdk/credential-provider-sso - fromSSO"); const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoSession } = init; const { ssoClient } = init; const profileName = getProfileName(init); @@ -92,11 +92,13 @@ export const fromSSO = const profile = profiles[profileName]; if (!profile) { - throw new CredentialsProviderError(`Profile ${profileName} was not found.`); + throw new CredentialsProviderError(`Profile ${profileName} was not found.`, { logger: init.logger }); } if (!isSsoProfile(profile)) { - throw new CredentialsProviderError(`Profile ${profileName} is not configured with SSO credentials.`); + throw new CredentialsProviderError(`Profile ${profileName} is not configured with SSO credentials.`, { + logger: init.logger, + }); } if (profile?.sso_session) { @@ -104,16 +106,25 @@ export const fromSSO = const session = ssoSessions[profile.sso_session]; const conflictMsg = ` configurations in profile ${profileName} and sso-session ${profile.sso_session}`; if (ssoRegion && ssoRegion !== session.sso_region) { - throw new CredentialsProviderError(`Conflicting SSO region` + conflictMsg, false); + throw new CredentialsProviderError(`Conflicting SSO region` + conflictMsg, { + tryNextLink: false, + logger: init.logger, + }); } if (ssoStartUrl && ssoStartUrl !== session.sso_start_url) { - throw new CredentialsProviderError(`Conflicting SSO start_url` + conflictMsg, false); + throw new CredentialsProviderError(`Conflicting SSO start_url` + conflictMsg, { + tryNextLink: false, + logger: init.logger, + }); } profile.sso_region = session.sso_region; profile.sso_start_url = session.sso_start_url; } - const { sso_start_url, sso_account_id, sso_region, sso_role_name, sso_session } = validateSsoProfile(profile); + const { sso_start_url, sso_account_id, sso_region, sso_role_name, sso_session } = validateSsoProfile( + profile, + init.logger + ); return resolveSSOCredentials({ ssoStartUrl: sso_start_url, ssoSession: sso_session, @@ -127,7 +138,8 @@ export const fromSSO = } else if (!ssoStartUrl || !ssoAccountId || !ssoRegion || !ssoRoleName) { throw new CredentialsProviderError( "Incomplete configuration. The fromSSO() argument hash must include " + - '"ssoStartUrl", "ssoAccountId", "ssoRegion", "ssoRoleName"' + '"ssoStartUrl", "ssoAccountId", "ssoRegion", "ssoRoleName"', + { tryNextLink: false, logger: init.logger } ); } else { return resolveSSOCredentials({ diff --git a/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts b/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts index 4c198153faf95..3431dd19863f3 100644 --- a/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts +++ b/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts @@ -117,7 +117,9 @@ describe(resolveSSOCredentials.name, () => { await resolveSSOCredentials(mockOptions); fail(`expected ${expectedError}`); } catch (error) { - expect(error).toStrictEqual(CredentialsProviderError.from(expectedError, SHOULD_FAIL_CREDENTIAL_CHAIN)); + expect(error).toStrictEqual( + new CredentialsProviderError(expectedError.toString(), SHOULD_FAIL_CREDENTIAL_CHAIN) + ); } }); diff --git a/packages/credential-provider-sso/src/resolveSSOCredentials.ts b/packages/credential-provider-sso/src/resolveSSOCredentials.ts index d7e78ffdf999f..b1c7a4692a64b 100644 --- a/packages/credential-provider-sso/src/resolveSSOCredentials.ts +++ b/packages/credential-provider-sso/src/resolveSSOCredentials.ts @@ -20,6 +20,7 @@ export const resolveSSOCredentials = async ({ ssoClient, clientConfig, profile, + logger, }: FromSSOInit & SsoCredentialsParameters): Promise => { let token: SSOToken; const refreshMessage = `To refresh this SSO session run aws sso login with the corresponding profile.`; @@ -32,24 +33,27 @@ export const resolveSSOCredentials = async ({ expiresAt: new Date(_token.expiration!).toISOString(), }; } catch (e) { - throw new CredentialsProviderError(e.message, SHOULD_FAIL_CREDENTIAL_CHAIN); + throw new CredentialsProviderError(e.message, { + tryNextLink: SHOULD_FAIL_CREDENTIAL_CHAIN, + logger, + }); } } else { try { token = await getSSOTokenFromFile(ssoStartUrl); } catch (e) { - throw new CredentialsProviderError( - `The SSO session associated with this profile is invalid. ${refreshMessage}`, - SHOULD_FAIL_CREDENTIAL_CHAIN - ); + throw new CredentialsProviderError(`The SSO session associated with this profile is invalid. ${refreshMessage}`, { + tryNextLink: SHOULD_FAIL_CREDENTIAL_CHAIN, + logger, + }); } } if (new Date(token.expiresAt).getTime() - Date.now() <= 0) { - throw new CredentialsProviderError( - `The SSO session associated with this profile has expired. ${refreshMessage}`, - SHOULD_FAIL_CREDENTIAL_CHAIN - ); + throw new CredentialsProviderError(`The SSO session associated with this profile has expired. ${refreshMessage}`, { + tryNextLink: SHOULD_FAIL_CREDENTIAL_CHAIN, + logger, + }); } const { accessToken } = token; @@ -73,7 +77,10 @@ export const resolveSSOCredentials = async ({ }) ); } catch (e) { - throw CredentialsProviderError.from(e, SHOULD_FAIL_CREDENTIAL_CHAIN); + throw new CredentialsProviderError(e, { + tryNextLink: SHOULD_FAIL_CREDENTIAL_CHAIN, + logger, + }); } const { roleCredentials: { accessKeyId, secretAccessKey, sessionToken, expiration, credentialScope } = {} } = @@ -88,7 +95,10 @@ export const resolveSSOCredentials = async ({ }; if (!accessKeyId || !secretAccessKey || !sessionToken || !expiration) { - throw new CredentialsProviderError("SSO returns an invalid temporary credential.", SHOULD_FAIL_CREDENTIAL_CHAIN); + throw new CredentialsProviderError("SSO returns an invalid temporary credential.", { + tryNextLink: SHOULD_FAIL_CREDENTIAL_CHAIN, + logger, + }); } return { accessKeyId, secretAccessKey, sessionToken, expiration: new Date(expiration), credentialScope }; diff --git a/packages/credential-provider-sso/src/validateSsoProfile.ts b/packages/credential-provider-sso/src/validateSsoProfile.ts index 9ce6400145be1..d9e9e9b83561b 100644 --- a/packages/credential-provider-sso/src/validateSsoProfile.ts +++ b/packages/credential-provider-sso/src/validateSsoProfile.ts @@ -1,11 +1,12 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { Logger } from "@smithy/types"; import { SsoProfile } from "./types"; /** * @internal */ -export const validateSsoProfile = (profile: Partial): SsoProfile => { +export const validateSsoProfile = (profile: Partial, logger?: Logger): SsoProfile => { const { sso_start_url, sso_account_id, sso_region, sso_role_name } = profile; if (!sso_start_url || !sso_account_id || !sso_region || !sso_role_name) { throw new CredentialsProviderError( @@ -13,7 +14,7 @@ export const validateSsoProfile = (profile: Partial): SsoProfile => `"sso_region", "sso_role_name", "sso_start_url". Got ${Object.keys(profile).join( ", " )}\nReference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html`, - false + { tryNextLink: false, logger } ); } return profile as SsoProfile; diff --git a/packages/credential-provider-web-identity/src/fromTokenFile.ts b/packages/credential-provider-web-identity/src/fromTokenFile.ts index 2b73cc9d0e044..fc39fc626e305 100644 --- a/packages/credential-provider-web-identity/src/fromTokenFile.ts +++ b/packages/credential-provider-web-identity/src/fromTokenFile.ts @@ -29,13 +29,15 @@ export interface FromTokenFileInit export const fromTokenFile = (init: FromTokenFileInit = {}): AwsCredentialIdentityProvider => async () => { - init.logger?.debug("@aws-sdk/credential-provider-web-identity", "fromTokenFile"); + init.logger?.debug("@aws-sdk/credential-provider-web-identity - fromTokenFile"); const webIdentityTokenFile = init?.webIdentityTokenFile ?? process.env[ENV_TOKEN_FILE]; const roleArn = init?.roleArn ?? process.env[ENV_ROLE_ARN]; const roleSessionName = init?.roleSessionName ?? process.env[ENV_ROLE_SESSION_NAME]; if (!webIdentityTokenFile || !roleArn) { - throw new CredentialsProviderError("Web identity configuration not specified"); + throw new CredentialsProviderError("Web identity configuration not specified", { + logger: init.logger, + }); } return fromWebToken({ diff --git a/packages/credential-provider-web-identity/src/fromWebToken.ts b/packages/credential-provider-web-identity/src/fromWebToken.ts index c94b3725e6a60..cc550ea8f977e 100644 --- a/packages/credential-provider-web-identity/src/fromWebToken.ts +++ b/packages/credential-provider-web-identity/src/fromWebToken.ts @@ -153,7 +153,7 @@ export interface FromWebTokenInit export const fromWebToken = (init: FromWebTokenInit): AwsCredentialIdentityProvider => async () => { - init.logger?.debug("@aws-sdk/credential-provider-web-identity", "fromWebToken"); + init.logger?.debug("@aws-sdk/credential-provider-web-identity - fromWebToken"); const { roleArn, roleSessionName, webIdentityToken, providerId, policyArns, policy, durationSeconds } = init; let { roleAssumerWithWebIdentity } = init; diff --git a/packages/credential-providers/src/fromTemporaryCredentials.ts b/packages/credential-providers/src/fromTemporaryCredentials.ts index fc48c992383a2..a20ace99d1712 100644 --- a/packages/credential-providers/src/fromTemporaryCredentials.ts +++ b/packages/credential-providers/src/fromTemporaryCredentials.ts @@ -54,13 +54,16 @@ export interface FromTemporaryCredentialsOptions extends CredentialProviderOptio export const fromTemporaryCredentials = (options: FromTemporaryCredentialsOptions): AwsCredentialIdentityProvider => { let stsClient: STSClient; return async (): Promise => { - options.logger?.debug("@aws-sdk/credential-providers", "fromTemporaryCredentials (STS)"); + options.logger?.debug("@aws-sdk/credential-providers - fromTemporaryCredentials (STS)"); const params = { ...options.params, RoleSessionName: options.params.RoleSessionName ?? "aws-sdk-js-" + Date.now() }; if (params?.SerialNumber) { if (!options.mfaCodeProvider) { throw new CredentialsProviderError( `Temporary credential requires multi-factor authentication,` + ` but no MFA code callback was provided.`, - false + { + tryNextLink: false, + logger: options.logger, + } ); } params.TokenCode = await options.mfaCodeProvider(params?.SerialNumber); @@ -76,7 +79,9 @@ export const fromTemporaryCredentials = (options: FromTemporaryCredentialsOption } const { Credentials } = await stsClient.send(new AssumeRoleCommand(params)); if (!Credentials || !Credentials.AccessKeyId || !Credentials.SecretAccessKey) { - throw new CredentialsProviderError(`Invalid response from STS.assumeRole call with role ${params.RoleArn}`); + throw new CredentialsProviderError(`Invalid response from STS.assumeRole call with role ${params.RoleArn}`, { + logger: options.logger, + }); } return { accessKeyId: Credentials.AccessKeyId, diff --git a/packages/middleware-eventstream/src/middleware-eventstream.integ.spec.ts b/packages/middleware-eventstream/src/middleware-eventstream.integ.spec.ts index 3861c94804c45..8bafcb49fe091 100644 --- a/packages/middleware-eventstream/src/middleware-eventstream.integ.spec.ts +++ b/packages/middleware-eventstream/src/middleware-eventstream.integ.spec.ts @@ -5,10 +5,18 @@ import { TranscribeStreaming } from "@aws-sdk/client-transcribe-streaming"; import { requireRequestsFrom } from "../../../private/aws-util-test/src"; describe("middleware-eventstream", () => { + const logger = { + trace() {}, + debug() {}, + info() {}, + warn() {}, + error() {}, + }; + // TODO: http2 in CI xdescribe(LexRuntimeV2.name, () => { it("should set streaming headers", async () => { - const client = new LexRuntimeV2({ region: "us-west-2" }); + const client = new LexRuntimeV2({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ headers: { @@ -39,7 +47,7 @@ describe("middleware-eventstream", () => { describe(RekognitionStreaming.name, () => { it("should set streaming headers", async () => { - const client = new RekognitionStreaming({ region: "us-west-2" }); + const client = new RekognitionStreaming({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ headers: { @@ -71,7 +79,7 @@ describe("middleware-eventstream", () => { // TODO: http2 in CI xdescribe(TranscribeStreaming.name, () => { it("should set streaming headers", async () => { - const client = new TranscribeStreaming({ region: "us-west-2" }); + const client = new TranscribeStreaming({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ headers: { diff --git a/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts b/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts index ea3de965cd1be..f65c31caf8cb3 100644 --- a/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts +++ b/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts @@ -4,9 +4,17 @@ import { Transform } from "stream"; import { requireRequestsFrom } from "../../../private/aws-util-test/src"; describe("middleware-flexible-checksums", () => { + const logger = { + trace() {}, + debug() {}, + info() {}, + warn() {}, + error() {}, + }; + describe(S3.name, () => { it("should set flexible checksums (SHA256)", async () => { - const client = new S3({ region: "us-west-2" }); + const client = new S3({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ method: "PUT", @@ -44,7 +52,7 @@ describe("middleware-flexible-checksums", () => { }); it("should set flexible checksums (SHA1)", async () => { - const client = new S3({ region: "us-west-2" }); + const client = new S3({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ method: "PUT", @@ -83,7 +91,7 @@ describe("middleware-flexible-checksums", () => { }); it("should not set binary file content length", async () => { - const client = new S3({ region: "us-west-2" }); + const client = new S3({ region: "us-west-2", logger }); requireRequestsFrom(client).toMatch({ method: "PUT", diff --git a/packages/middleware-sdk-sqs/src/middleware-sdk-sqs.integ.spec.ts b/packages/middleware-sdk-sqs/src/middleware-sdk-sqs.integ.spec.ts index 7a6b7b7148949..12b0bc0c0e58d 100644 --- a/packages/middleware-sdk-sqs/src/middleware-sdk-sqs.integ.spec.ts +++ b/packages/middleware-sdk-sqs/src/middleware-sdk-sqs.integ.spec.ts @@ -110,6 +110,14 @@ describe("middleware-sdk-sqs", () => { secretAccessKey: "integration_test", }; + const logger = { + trace() {}, + debug() {}, + info() {}, + warn() {}, + error() {}, + }; + describe(SQS.name + ` w/ useAwsQuery: ${useAwsQuery}`, () => { describe("correct md5 hashes", () => { beforeEach(() => { @@ -120,6 +128,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -143,6 +152,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -167,6 +177,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -210,6 +221,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -237,6 +249,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -265,6 +278,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, requestHandler: new (class implements HttpHandler { async handle(): Promise { const r = responses(); @@ -309,6 +323,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, }); requireRequestsFrom(client).toMatch({ @@ -346,6 +361,7 @@ describe("middleware-sdk-sqs", () => { const client = new SQS({ region: "us-west-2", credentials: mockCredentials, + logger, endpoint: "https://custom-endpoint.com/", }); diff --git a/packages/middleware-websocket/src/middleware-websocket.integ.spec.ts b/packages/middleware-websocket/src/middleware-websocket.integ.spec.ts index fe7e82abbe932..2c4b888ca4c5f 100644 --- a/packages/middleware-websocket/src/middleware-websocket.integ.spec.ts +++ b/packages/middleware-websocket/src/middleware-websocket.integ.spec.ts @@ -3,10 +3,18 @@ import { RekognitionStreaming } from "@aws-sdk/client-rekognitionstreaming"; import { requireRequestsFrom } from "../../../private/aws-util-test/src"; describe("middleware-websocket", () => { + const logger = { + trace() {}, + debug() {}, + info() {}, + warn() {}, + error() {}, + }; describe(RekognitionStreaming.name, () => { it("sets protocol, headers, moves params to query, and signs in the query", async () => { const client = new RekognitionStreaming({ region: "us-west-2", + logger, }); requireRequestsFrom(client).toMatch({ protocol: "wss:", diff --git a/packages/token-providers/src/fromSso.ts b/packages/token-providers/src/fromSso.ts index 91aada276a2fb..a65e6febd3efb 100644 --- a/packages/token-providers/src/fromSso.ts +++ b/packages/token-providers/src/fromSso.ts @@ -28,7 +28,7 @@ export interface FromSsoInit extends SourceProfileInit, CredentialProviderOption export const fromSso = (init: FromSsoInit = {}): TokenIdentityProvider => async () => { - init.logger?.debug("@aws-sdk/token-providers", "fromSso"); + init.logger?.debug("@aws-sdk/token-providers - fromSso"); const profiles = await parseKnownFiles(init); const profileName = getProfileName(init); diff --git a/packages/token-providers/src/fromStatic.ts b/packages/token-providers/src/fromStatic.ts index 58e738ca0c7f5..308b8e6fc927a 100644 --- a/packages/token-providers/src/fromStatic.ts +++ b/packages/token-providers/src/fromStatic.ts @@ -11,7 +11,7 @@ export interface FromStaticInit extends CredentialProviderOptions { export const fromStatic = ({ token, logger }: FromStaticInit): TokenIdentityProvider => async () => { - logger?.debug("@aws-sdk/token-providers", "fromStatic"); + logger?.debug("@aws-sdk/token-providers - fromStatic"); if (!token || !token.token) { throw new TokenProviderError(`Please pass a valid token to fromStatic`, false); } diff --git a/scripts/runtime-dependency-version-check/check-dependencies.js b/scripts/runtime-dependency-version-check/check-dependencies.js index c7e08656002b6..c806252aeee02 100644 --- a/scripts/runtime-dependency-version-check/check-dependencies.js +++ b/scripts/runtime-dependency-version-check/check-dependencies.js @@ -83,8 +83,8 @@ const node_libraries = [ const importedDependencies = []; importedDependencies.push( ...new Set( - [...(contents.toString().match(/(from |import\()"(.*?)";/g) || [])] - .map((_) => _.replace(/from "/g, "").replace(/";$/, "")) + [...(contents.toString().match(/(from |import\()"(.*?)"\)?;/g) ?? [])] + .map((_) => _.replace(/(from ")|(import\(")/g, "").replace(/"\)?;$/, "")) .filter((_) => !_.startsWith(".") && !node_libraries.includes(_)) ) );