Skip to content

Commit

Permalink
fix(credential-provider-node): pass client region to inner credential…
Browse files Browse the repository at this point in the history
… client region

test: sts does not resolve aws auth config
  • Loading branch information
kuhe committed Feb 9, 2024
1 parent cbba32c commit 3fb6f1a
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 103 deletions.
5 changes: 3 additions & 2 deletions clients/client-sts/src/defaultRoleAssumers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getDefaultRoleAssumerWithWebIdentity as StsGetDefaultRoleAssumerWithWebIdentity,
RoleAssumer,
RoleAssumerWithWebIdentity,
STSRoleAssumerOptions,
} from "./defaultStsRoleAssumers";
import { ServiceInputTypes, ServiceOutputTypes, STSClient, STSClientConfig } from "./STSClient";

Expand All @@ -32,15 +33,15 @@ const getCustomizableStsClientCtor = (
* The default role assumer that used by credential providers when sts:AssumeRole API is needed.
*/
export const getDefaultRoleAssumer = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler"> = {},
stsOptions: STSRoleAssumerOptions = {},
stsPlugins?: Pluggable<ServiceInputTypes, ServiceOutputTypes>[]
): RoleAssumer => StsGetDefaultRoleAssumer(stsOptions, getCustomizableStsClientCtor(STSClient, stsPlugins));

/**
* The default role assumer that used by credential providers when sts:AssumeRoleWithWebIdentity API is needed.
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler"> = {},
stsOptions: STSRoleAssumerOptions = {},
stsPlugins?: Pluggable<ServiceInputTypes, ServiceOutputTypes>[]
): RoleAssumerWithWebIdentity =>
StsGetDefaultRoleAssumerWithWebIdentity(stsOptions, getCustomizableStsClientCtor(STSClient, stsPlugins));
Expand Down
74 changes: 56 additions & 18 deletions clients/client-sts/src/defaultStsRoleAssumers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// smithy-typescript generated code
// Please do not touch this file. It's generated from template in:
// https://github.com/aws/aws-sdk-js-v3/blob/main/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts
import { AwsCredentialIdentity, Provider } from "@smithy/types";
import type { CredentialProviderOptions } from "@aws-sdk/types";
import { partition } from "@aws-sdk/util-endpoints";
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";

import { AssumeRoleCommand, AssumeRoleCommandInput } from "./commands/AssumeRoleCommand";
import {
Expand All @@ -10,6 +12,14 @@ import {
} from "./commands/AssumeRoleWithWebIdentityCommand";
import type { STSClient, STSClientConfig, STSClientResolvedConfig } from "./STSClient";

/**
* @public
*/
export type STSRoleAssumerOptions = Pick<STSClientConfig, "logger" | "region" | "requestHandler"> & {
credentialProviderLogger?: Logger;
parentClientConfig?: CredentialProviderOptions["parentClientConfig"];
};

/**
* @internal
*/
Expand All @@ -21,40 +31,63 @@ export type RoleAssumer = (
const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";

/**
* Inject the fallback STS region of us-east-1.
* @internal
*
* Default to the us-east-1 region for aws partition,
* or default to the parent client region otherwise.
*/
const decorateDefaultRegion = (region: string | Provider<string> | undefined): string | Provider<string> => {
if (typeof region !== "function") {
return region === undefined ? ASSUME_ROLE_DEFAULT_REGION : region;
const resolveRegion = async (
_region: string | Provider<string> | undefined,
_parentRegion: string | Provider<string> | undefined,
credentialProviderLogger?: Logger
): Promise<string> => {
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;

if (!parentRegion || partition(parentRegion).name === "aws") {
credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
);
return region ?? ASSUME_ROLE_DEFAULT_REGION;
} else {
credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${parentRegion} (parent client)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
);
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
}
return async () => {
try {
return await region();
} catch (e) {
return ASSUME_ROLE_DEFAULT_REGION;
}
};
};

/**
* The default role assumer that used by credential providers when sts:AssumeRole API is needed.
* @internal
*/
export const getDefaultRoleAssumer = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler">,
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
): RoleAssumer => {
let stsClient: STSClient;
let closureSourceCreds: AwsCredentialIdentity;
return async (sourceCreds, params) => {
closureSourceCreds = sourceCreds;
if (!stsClient) {
const { logger, region, requestHandler } = stsOptions;
const { logger, region, requestHandler, credentialProviderLogger } = stsOptions;
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
);
stsClient = new stsClientCtor({
logger,
// A hack to make sts client uses the credential in current closure.
credentialDefaultProvider: () => async () => closureSourceCreds,
region: decorateDefaultRegion(region || stsOptions.region),
region: resolvedRegion,
...(requestHandler ? { requestHandler } : {}),
});
}
Expand Down Expand Up @@ -85,16 +118,21 @@ export type RoleAssumerWithWebIdentity = (
* @internal
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler">,
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
): RoleAssumerWithWebIdentity => {
let stsClient: STSClient;
return async (params) => {
if (!stsClient) {
const { logger, region, requestHandler } = stsOptions;
const { logger, region, requestHandler, credentialProviderLogger } = stsOptions;
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
);
stsClient = new stsClientCtor({
logger,
region: decorateDefaultRegion(region || stsOptions.region),
region: resolvedRegion,
...(requestHandler ? { requestHandler } : {}),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getDefaultRoleAssumerWithWebIdentity as StsGetDefaultRoleAssumerWithWebIdentity,
RoleAssumer,
RoleAssumerWithWebIdentity,
STSRoleAssumerOptions,
} from "./defaultStsRoleAssumers";
import { ServiceInputTypes, ServiceOutputTypes, STSClient, STSClientConfig } from "./STSClient";

Expand All @@ -29,15 +30,15 @@ const getCustomizableStsClientCtor = (
* The default role assumer that used by credential providers when sts:AssumeRole API is needed.
*/
export const getDefaultRoleAssumer = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler"> = {},
stsOptions: STSRoleAssumerOptions = {},
stsPlugins?: Pluggable<ServiceInputTypes, ServiceOutputTypes>[]
): RoleAssumer => StsGetDefaultRoleAssumer(stsOptions, getCustomizableStsClientCtor(STSClient, stsPlugins));

/**
* The default role assumer that used by credential providers when sts:AssumeRoleWithWebIdentity API is needed.
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler"> = {},
stsOptions: STSRoleAssumerOptions = {},
stsPlugins?: Pluggable<ServiceInputTypes, ServiceOutputTypes>[]
): RoleAssumerWithWebIdentity =>
StsGetDefaultRoleAssumerWithWebIdentity(stsOptions, getCustomizableStsClientCtor(STSClient, stsPlugins));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { AwsCredentialIdentity, Provider } from "@smithy/types";
import type { CredentialProviderOptions } from "@aws-sdk/types";
import { partition } from "@aws-sdk/util-endpoints";
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";

import { AssumeRoleCommand, AssumeRoleCommandInput } from "./commands/AssumeRoleCommand";
import {
Expand All @@ -7,6 +9,14 @@ import {
} from "./commands/AssumeRoleWithWebIdentityCommand";
import type { STSClient, STSClientConfig, STSClientResolvedConfig } from "./STSClient";

/**
* @public
*/
export type STSRoleAssumerOptions = Pick<STSClientConfig, "logger" | "region" | "requestHandler"> & {
credentialProviderLogger?: Logger;
parentClientConfig?: CredentialProviderOptions["parentClientConfig"];
};

/**
* @internal
*/
Expand All @@ -18,40 +28,63 @@ export type RoleAssumer = (
const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";

/**
* Inject the fallback STS region of us-east-1.
* @internal
*
* Default to the us-east-1 region for aws partition,
* or default to the parent client region otherwise.
*/
const decorateDefaultRegion = (region: string | Provider<string> | undefined): string | Provider<string> => {
if (typeof region !== "function") {
return region === undefined ? ASSUME_ROLE_DEFAULT_REGION : region;
const resolveRegion = async (
_region: string | Provider<string> | undefined,
_parentRegion: string | Provider<string> | undefined,
credentialProviderLogger?: Logger
): Promise<string> => {
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;

if (!parentRegion || partition(parentRegion).name === "aws") {
credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
);
return region ?? ASSUME_ROLE_DEFAULT_REGION;
} else {
credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${parentRegion} (parent client)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
);
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
}
return async () => {
try {
return await region();
} catch (e) {
return ASSUME_ROLE_DEFAULT_REGION;
}
};
};

/**
* The default role assumer that used by credential providers when sts:AssumeRole API is needed.
* @internal
*/
export const getDefaultRoleAssumer = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler">,
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
): RoleAssumer => {
let stsClient: STSClient;
let closureSourceCreds: AwsCredentialIdentity;
return async (sourceCreds, params) => {
closureSourceCreds = sourceCreds;
if (!stsClient) {
const { logger, region, requestHandler } = stsOptions;
const { logger, region, requestHandler, credentialProviderLogger } = stsOptions;
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
);
stsClient = new stsClientCtor({
logger,
// A hack to make sts client uses the credential in current closure.
credentialDefaultProvider: () => async () => closureSourceCreds,
region: decorateDefaultRegion(region || stsOptions.region),
region: resolvedRegion,
...(requestHandler ? { requestHandler } : {}),
});
}
Expand Down Expand Up @@ -82,16 +115,21 @@ export type RoleAssumerWithWebIdentity = (
* @internal
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: Pick<STSClientConfig, "logger" | "region" | "requestHandler">,
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
): RoleAssumerWithWebIdentity => {
let stsClient: STSClient;
return async (params) => {
if (!stsClient) {
const { logger, region, requestHandler } = stsOptions;
const { logger, region, requestHandler, credentialProviderLogger } = stsOptions;
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
);
stsClient = new stsClientCtor({
logger,
region: decorateDefaultRegion(region || stsOptions.region),
region: resolvedRegion,
...(requestHandler ? { requestHandler } : {}),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,17 @@ export const resolveAwsSdkSigV4Config = <T>(
// credentialDefaultProvider should always be populated, but in case
// it isn't, set a default identity provider that throws an error
if (config.credentialDefaultProvider) {
normalizedCreds = normalizeProvider(config.credentialDefaultProvider(config as any));
normalizedCreds = normalizeProvider(
config.credentialDefaultProvider(
Object.assign({}, config as any, {
parentClientConfig: config,
})
)
);
} else {
normalizedCreds = async () => { throw new Error("`credentials` is missing") };
normalizedCreds = async () => {
throw new Error("`credentials` is missing");
};
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ export function fromCognitoIdentity(parameters: FromCognitoIdentityParameters):
SecretKey = throwOnMissingSecretKey(),
SessionToken,
} = throwOnMissingCredentials(),
} = await (parameters.client ?? new CognitoIdentityClient(parameters.clientConfig ?? {})).send(
} = await (
parameters.client ??
new CognitoIdentityClient(
Object.assign({}, parameters.clientConfig ?? {}, {
region: parameters.clientConfig?.region ?? parameters.parentClientConfig?.region,
})
)
).send(
new GetCredentialsForIdentityCommand({
CustomRoleArn: parameters.customRoleArn,
IdentityId: parameters.identityId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function fromCognitoIdentityPool({
logins,
userIdentifier = !logins || Object.keys(logins).length === 0 ? "ANONYMOUS" : undefined,
logger,
parentClientConfig,
}: FromCognitoIdentityPoolParameters): CognitoIdentityCredentialProvider {
logger?.debug("@aws-sdk/credential-provider-cognito-identity", "fromCognitoIdentity");
const cacheKey: string | undefined = userIdentifier
Expand All @@ -35,7 +36,11 @@ export function fromCognitoIdentityPool({

let provider: CognitoIdentityCredentialProvider = async () => {
const { GetIdCommand, CognitoIdentityClient } = await import("./loadCognitoIdentity");
const _client = client ?? new CognitoIdentityClient(clientConfig ?? {});
const _client =
client ??
new CognitoIdentityClient(
Object.assign({}, clientConfig ?? {}, { region: clientConfig?.region ?? parentClientConfig?.region })
);

let identityId: string | undefined = (cacheKey && (await cache.getItem(cacheKey))) as string | undefined;
if (!identityId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ export const resolveAssumeRoleCredentials = async (

if (!options.roleAssumer) {
const { getDefaultRoleAssumer } = await import("./loadSts");
options.roleAssumer = getDefaultRoleAssumer(options.clientConfig, options.clientPlugins);
options.roleAssumer = getDefaultRoleAssumer(
{
...options.clientConfig,
credentialProviderLogger: options.logger,
parentClientConfig: options?.parentClientConfig,
},
options.clientPlugins
);
}

const { source_profile } = data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ export const resolveWebIdentityCredentials = async (
roleSessionName: profile.role_session_name,
roleAssumerWithWebIdentity: options.roleAssumerWithWebIdentity,
logger: options.logger,
parentClientConfig: options.parentClientConfig,
})()
);
Loading

0 comments on commit 3fb6f1a

Please sign in to comment.