Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions clients/client-sts/src/defaultStsRoleAssumers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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 { setCredentialFeature } from "@aws-sdk/core/client";
import { stsRegionDefaultResolver } from "@aws-sdk/region-config-resolver";
import type { CredentialProviderOptions } from "@aws-sdk/types";
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";

Expand All @@ -28,8 +29,6 @@ export type RoleAssumer = (
params: AssumeRoleCommandInput
) => Promise<AwsCredentialIdentity>;

const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";

interface AssumedRoleUser {
/**
* The ARN of the temporary security credentials that are returned from the AssumeRole action.
Expand Down Expand Up @@ -63,19 +62,21 @@ const getAccountIdFromAssumedRoleUser = (assumedRoleUser?: AssumedRoleUser) => {
const resolveRegion = async (
_region: string | Provider<string> | undefined,
_parentRegion: string | Provider<string> | undefined,
credentialProviderLogger?: Logger
credentialProviderLogger?: Logger,
loaderConfig: Parameters<typeof stsRegionDefaultResolver>[0] = {}
): Promise<string> => {
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;
const stsDefaultRegion = await stsRegionDefaultResolver(loaderConfig)();

credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${parentRegion} (parent client)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
`${region} (credential provider clientConfig)`,
`${parentRegion} (contextual client)`,
`${stsDefaultRegion} (STS default: AWS_REGION, profile region, or us-east-1)`
);
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
return region ?? parentRegion ?? stsDefaultRegion;
};

/**
Expand All @@ -101,7 +102,11 @@ export const getDefaultRoleAssumer = (
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
credentialProviderLogger,
{
logger,
profile,
}
);
const isCompatibleRequestHandler = !isH2(requestHandler);

Expand Down Expand Up @@ -164,7 +169,11 @@ export const getDefaultRoleAssumerWithWebIdentity = (
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
credentialProviderLogger,
{
logger,
profile,
}
);
const isCompatibleRequestHandler = !isH2(requestHandler);

Expand Down
2 changes: 1 addition & 1 deletion clients/client-sts/test/defaultRoleAssumers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe("getDefaultRoleAssumer", () => {
requestHandler: handler,
parentClientConfig: {
region: "some-other-region",
logger: null,
logger: null as any,
requestHandler: null,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe("getDefaultRoleAssumer", () => {
requestHandler: handler,
parentClientConfig: {
region: "some-other-region",
logger: null,
logger: null as any,
requestHandler: null,
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { setCredentialFeature } from "@aws-sdk/core/client";
import { stsRegionDefaultResolver } from "@aws-sdk/region-config-resolver";
import type { CredentialProviderOptions } from "@aws-sdk/types";
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";

Expand All @@ -25,8 +26,6 @@ export type RoleAssumer = (
params: AssumeRoleCommandInput
) => Promise<AwsCredentialIdentity>;

const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";

interface AssumedRoleUser {
/**
* The ARN of the temporary security credentials that are returned from the AssumeRole action.
Expand Down Expand Up @@ -60,19 +59,21 @@ const getAccountIdFromAssumedRoleUser = (assumedRoleUser?: AssumedRoleUser) => {
const resolveRegion = async (
_region: string | Provider<string> | undefined,
_parentRegion: string | Provider<string> | undefined,
credentialProviderLogger?: Logger
credentialProviderLogger?: Logger,
loaderConfig: Parameters<typeof stsRegionDefaultResolver>[0] = {}
): Promise<string> => {
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;
const stsDefaultRegion = await stsRegionDefaultResolver(loaderConfig)();

credentialProviderLogger?.debug?.(
"@aws-sdk/client-sts::resolveRegion",
"accepting first of:",
`${region} (provider)`,
`${parentRegion} (parent client)`,
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
`${region} (credential provider clientConfig)`,
`${parentRegion} (contextual client)`,
`${stsDefaultRegion} (STS default: AWS_REGION, profile region, or us-east-1)`
);
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
return region ?? parentRegion ?? stsDefaultRegion;
};

/**
Expand All @@ -98,7 +99,11 @@ export const getDefaultRoleAssumer = (
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
credentialProviderLogger,
{
logger,
profile,
}
);
const isCompatibleRequestHandler = !isH2(requestHandler);

Expand Down Expand Up @@ -161,7 +166,11 @@ export const getDefaultRoleAssumerWithWebIdentity = (
const resolvedRegion = await resolveRegion(
region,
stsOptions?.parentClientConfig?.region,
credentialProviderLogger
credentialProviderLogger,
{
logger,
profile,
}
);
const isCompatibleRequestHandler = !isH2(requestHandler);

Expand Down
156 changes: 109 additions & 47 deletions packages/credential-provider-ini/src/fromIni.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
import { STS } from "@aws-sdk/client-sts";
import { HttpRequest, HttpResponse } from "@smithy/protocol-http";
import { SourceProfileInit } from "@smithy/shared-ini-file-loader";
import { externalDataInterceptor } from "@smithy/shared-ini-file-loader";
import type { NodeHttpHandlerOptions, ParsedIniData } from "@smithy/types";
import { homedir } from "node:os";
import { join } from "node:path";
import { PassThrough } from "node:stream";
import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, test as it } from "vitest";

import { fromIni } from "./fromIni";

let iniProfileData: ParsedIniData = null as any;
vi.mock("@smithy/shared-ini-file-loader", async () => {
const actual: any = await vi.importActual("@smithy/shared-ini-file-loader");
const pkg = {
...actual,
async loadSsoSessionData() {
return Object.entries(iniProfileData)
.filter(([key]) => key.startsWith("sso-session."))
.reduce(
(acc, [key, value]) => ({
...acc,
[key.split("sso-session.")[1]]: value,
}),
{}
);
},
async parseKnownFiles(init: SourceProfileInit): Promise<ParsedIniData> {
return iniProfileData;
},
async getSSOTokenFromFile() {
return {
accessToken: "mock_sso_token",
expiresAt: "3000-01-01T00:00:00.000Z",
};
},
};
return {
...pkg,
default: pkg,
};
});

function setIniProfileData(data: ParsedIniData) {
iniProfileData = data;
let buffer = "";
for (const profile in data) {
if (profile.startsWith("sso-session.")) {
buffer += `[sso-session ${profile.split("sso-session.")[1]}]\n`;
} else {
buffer += `[profile ${profile}]\n`;
}
for (const [k, v] of Object.entries(data[profile])) {
buffer += `${k} = ${v}\n`;
}
buffer += "\n";
}
const dir = join(homedir(), ".aws");
externalDataInterceptor.interceptFile(join(dir, "config"), buffer);
}

class MockNodeHttpHandler {
static create(instanceOrOptions?: any) {
Expand All @@ -46,6 +36,7 @@ class MockNodeHttpHandler {
}
return new MockNodeHttpHandler();
}

async handle(request: HttpRequest) {
const body = new PassThrough({});

Expand Down Expand Up @@ -125,7 +116,9 @@ class MockNodeHttpHandler {
}),
};
}

updateHttpClientConfig(key: keyof NodeHttpHandlerOptions, value: NodeHttpHandlerOptions[typeof key]): void {}

httpHandlerConfigs(): NodeHttpHandlerOptions {
return null as any;
}
Expand All @@ -136,22 +129,20 @@ describe("fromIni region search order", () => {
process.env.AWS_PROFILE = "default";
iniProfileData = {
default: {
region: "us-west-2",
output: "json",
region: "us-stsar-1",
role_arn: "ROLE_ARN",
role_session_name: "ROLE_SESSION_NAME",
external_id: "EXTERNAL_ID",
source_profile: "assume",
},
assume: {
region: "us-stsar-1",
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
},
};
iniProfileData.assume = {
region: "us-stsar-1",
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
};
Object.assign(iniProfileData.default, {
region: "us-stsar-1",
role_arn: "ROLE_ARN",
role_session_name: "ROLE_SESSION_NAME",
external_id: "EXTERNAL_ID",
source_profile: "assume",
});
setIniProfileData(iniProfileData);
});

afterEach(() => {
Expand Down Expand Up @@ -201,6 +192,7 @@ describe("fromIni region search order", () => {

it("should use 3rd priority for the caller client", async () => {
delete iniProfileData.default.region;
setIniProfileData(iniProfileData);

const sts = new STS({
requestHandler: new MockNodeHttpHandler(),
Expand All @@ -221,8 +213,78 @@ describe("fromIni region search order", () => {
});
});

it("should use 4th priority for the default partition's default region", async () => {
delete iniProfileData.default.region;
it("should use 4th priority for the config file region", async () => {
const credentialsData = await fromIni({
clientConfig: {
requestHandler: new MockNodeHttpHandler(),
},
})();

const sts = new STS({
requestHandler: new MockNodeHttpHandler(),
credentials: credentialsData,
});

await sts.getCallerIdentity({});
const credentials = await sts.config.credentials();
expect(credentials).toMatchObject({
accessKeyId: "STS_AR_ACCESS_KEY_ID",
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
sessionToken: "STS_AR_SESSION_TOKEN_us-stsar-1",
});
});

it("should use 5th priority for the AWS_REGION value", async () => {
process.env.AWS_REGION = "ap-northeast-1";
iniProfileData = {
default: {
role_arn: "ROLE_ARN",
role_session_name: "ROLE_SESSION_NAME",
external_id: "EXTERNAL_ID",
source_profile: "assume",
},
assume: {
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
},
};
setIniProfileData(iniProfileData);

const credentialsData = await fromIni({
clientConfig: {
requestHandler: new MockNodeHttpHandler(),
},
})();

const sts = new STS({
requestHandler: new MockNodeHttpHandler(),
credentials: credentialsData,
});

await sts.getCallerIdentity({});
const credentials = await sts.config.credentials();
expect(credentials).toMatchObject({
accessKeyId: "STS_AR_ACCESS_KEY_ID",
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
sessionToken: "STS_AR_SESSION_TOKEN_ap-northeast-1",
});
});

it("should use 6th priority for the default partition's default region", async () => {
delete process.env.AWS_REGION;
iniProfileData = {
default: {
role_arn: "ROLE_ARN",
role_session_name: "ROLE_SESSION_NAME",
external_id: "EXTERNAL_ID",
source_profile: "assume",
},
assume: {
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
},
};
setIniProfileData(iniProfileData);

const credentialsData = await fromIni({
clientConfig: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe(resolveSsoCredentials.name, () => {
secretAccessKey: "mockSecretAccessKey",
};
const requestHandler = vi.fn();
const logger = vi.fn();
const logger: any = vi.fn();

vi.mocked(fromSSO).mockReturnValue(() => Promise.resolve(mockCreds));

Expand Down
2 changes: 1 addition & 1 deletion packages/credential-provider-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"build:types": "tsc -p tsconfig.types.json",
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"test": "yarn g:vitest run",
"test": "yarn g:vitest run --reporter verbose",
"test:watch": "yarn g:vitest watch",
"test:integration": "yarn g:vitest run -c vitest.config.integ.mts",
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.mts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ describe(defaultProvider.name, () => {
};

const credentials = () => {
throw new CredentialsProviderError("test", true);
throw new CredentialsProviderError("test", {
tryNextLink: true,
});
};

const finalCredentials = () => {
Expand Down
Loading
Loading