Skip to content

Commit

Permalink
feat(credential-providers): pass caller client options to fromTempora…
Browse files Browse the repository at this point in the history
…ryCredentials inner STSClient
  • Loading branch information
kuhe committed Jan 22, 2025
1 parent 84d6c18 commit 2ffc611
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 316 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ CHANGELOG.md
**/*.hbs
**/*/report.md
clients/*/src/endpoint/ruleset.ts
packages/nested-clients/src/submodules/*/endpoint/ruleset.ts
**/*.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const resolveRegion = async (
*/
export const getDefaultRoleAssumer = (
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
STSClient: new (options: STSClientConfig) => STSClient
): RoleAssumer => {
let stsClient: STSClient;
let closureSourceCreds: AwsCredentialIdentity;
Expand All @@ -101,7 +101,8 @@ export const getDefaultRoleAssumer = (
);
const isCompatibleRequestHandler = !isH2(requestHandler);

stsClient = new stsClientCtor({
stsClient = new STSClient({
profile: stsOptions?.parentClientConfig?.profile,
// A hack to make sts client uses the credential in current closure.
credentialDefaultProvider: () => async () => closureSourceCreds,
region: resolvedRegion,
Expand Down Expand Up @@ -143,7 +144,7 @@ export type RoleAssumerWithWebIdentity = (
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
STSClient: new (options: STSClientConfig) => STSClient
): RoleAssumerWithWebIdentity => {
let stsClient: STSClient;
return async (params) => {
Expand All @@ -161,7 +162,8 @@ export const getDefaultRoleAssumerWithWebIdentity = (
);
const isCompatibleRequestHandler = !isH2(requestHandler);

stsClient = new stsClientCtor({
stsClient = new STSClient({
profile: stsOptions?.parentClientConfig?.profile,
region: resolvedRegion,
requestHandler: isCompatibleRequestHandler ? (requestHandler as any) : undefined,
logger: logger as any,
Expand Down
1 change: 1 addition & 0 deletions packages/credential-providers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@aws-sdk/credential-provider-web-identity": "*",
"@aws-sdk/nested-clients": "*",
"@aws-sdk/types": "*",
"@smithy/core": "^3.0.0",
"@smithy/credential-provider-imds": "^4.0.0",
"@smithy/property-provider": "^4.0.0",
"@smithy/types": "^4.0.0",
Expand Down
80 changes: 73 additions & 7 deletions packages/credential-providers/src/fromTemporaryCredentials.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import type {
CredentialProviderOptions,
RuntimeConfigAwsCredentialIdentityProvider,
} from "@aws-sdk/types";
import { normalizeProvider } from "@smithy/core";
import { CredentialsProviderError } from "@smithy/property-provider";
import { AwsCredentialIdentity, AwsCredentialIdentityProvider, Pluggable } from "@smithy/types";
import { AwsCredentialIdentity, AwsCredentialIdentityProvider, Logger, Pluggable, RequestHandler } from "@smithy/types";

export interface FromTemporaryCredentialsOptions extends CredentialProviderOptions {
params: Omit<AssumeRoleCommandInput, "RoleSessionName"> & { RoleSessionName?: string };
masterCredentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
clientConfig?: STSClientConfig;
logger?: Logger;
clientPlugins?: Pluggable<any, any>[];
mfaCodeProvider?: (mfaSerial: string) => Promise<string>;
}

const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";

export const fromTemporaryCredentials = (
options: FromTemporaryCredentialsOptions,
credentialDefaultProvider?: () => AwsCredentialIdentityProvider
Expand All @@ -41,15 +45,59 @@ export const fromTemporaryCredentials = (
if (!stsClient) {
const defaultCredentialsOrError =
typeof credentialDefaultProvider === "function" ? credentialDefaultProvider() : undefined;

const { callerClientConfig } = awsIdentityProperties;

const credentialSources = [
options.masterCredentials,
options.clientConfig?.credentials,
callerClientConfig?.credentials,
callerClientConfig?.credentialDefaultProvider?.(),
defaultCredentialsOrError,
];
let credentialSource = "STS client default credentials";
if (credentialSources[0]) {
credentialSource = "options.masterCredentials";
} else if (credentialSources[1]) {
credentialSource = "options.clientConfig.credentials";
} else if (credentialSources[2]) {
credentialSource = "caller client's credentials";
} else if (credentialSources[3]) {
credentialSource = "caller client's credentialDefaultProvider";
}

const regionSources = [options.clientConfig?.region, callerClientConfig?.region, ASSUME_ROLE_DEFAULT_REGION];
let regionSource = "default partition's default region";
if (regionSources[0]) {
regionSource = "options.clientConfig.region";
} else if (regionSources[1]) {
regionSource = "caller client's region";
}

const requestHandlerSources = [
filterRequestHandler(options.clientConfig?.requestHandler),
filterRequestHandler(callerClientConfig?.requestHandler),
];
let requestHandlerSource = "STS default requestHandler";
if (requestHandlerSources[0]) {
requestHandlerSource = "options.clientConfig.requestHandler";
} else if (requestHandlerSources[1]) {
requestHandlerSource = "caller client's requestHandler";
}

options.logger?.debug?.(
`@aws-sdk/credential-providers - fromTemporaryCredentials STS client init with ` +
`${regionSource}=${await normalizeProvider(
coalesce(regionSources)
)()}, ${credentialSource}, ${requestHandlerSource}.`
);

stsClient = new STSClient({
...options.clientConfig,
credentials:
options.masterCredentials ??
options.clientConfig?.credentials ??
callerClientConfig?.credentialDefaultProvider?.() ??
defaultCredentialsOrError,
credentials: coalesce(credentialSources),
logger: options.logger,
profile: options.clientConfig?.profile ?? callerClientConfig?.profile,
region: coalesce(regionSources),
requestHandler: coalesce(requestHandlerSources),
});
}
if (options.clientPlugins) {
Expand All @@ -73,3 +121,21 @@ export const fromTemporaryCredentials = (
};
};
};

/**
* @internal
*/
const filterRequestHandler = (requestHandler: STSClientConfig["requestHandler"]): undefined | typeof requestHandler => {
return (requestHandler as RequestHandler<any, any>)?.metadata?.handlerProtocol === "h2" ? undefined : requestHandler;
};

/**
* @internal
*/
const coalesce = (...args: any) => {
for (const item of args) {
if (item !== undefined) {
return item;
}
}
};
149 changes: 25 additions & 124 deletions packages/nested-clients/src/submodules/sso-oidc/endpoint/ruleset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,128 +6,29 @@ import { RuleSetObject } from "@smithy/types";
or see "smithy.rules#endpointRuleSet"
in codegen/sdk-codegen/aws-models/sso-oidc.json */

const u = "required",
v = "fn",
w = "argv",
x = "ref";
const a = true,
b = "isSet",
c = "booleanEquals",
d = "error",
e = "endpoint",
f = "tree",
g = "PartitionResult",
h = "getAttr",
i = { [u]: false, type: "String" },
j = { [u]: true, default: false, type: "Boolean" },
k = { [x]: "Endpoint" },
l = { [v]: c, [w]: [{ [x]: "UseFIPS" }, true] },
m = { [v]: c, [w]: [{ [x]: "UseDualStack" }, true] },
n = {},
o = { [v]: h, [w]: [{ [x]: g }, "supportsFIPS"] },
p = { [x]: g },
q = { [v]: c, [w]: [true, { [v]: h, [w]: [p, "supportsDualStack"] }] },
r = [l],
s = [m],
t = [{ [x]: "Region" }];
const _data = {
version: "1.0",
parameters: { Region: i, UseDualStack: j, UseFIPS: j, Endpoint: i },
rules: [
{
conditions: [{ [v]: b, [w]: [k] }],
rules: [
{ conditions: r, error: "Invalid Configuration: FIPS and custom endpoint are not supported", type: d },
{ conditions: s, error: "Invalid Configuration: Dualstack and custom endpoint are not supported", type: d },
{ endpoint: { url: k, properties: n, headers: n }, type: e },
],
type: f,
},
{
conditions: [{ [v]: b, [w]: t }],
rules: [
{
conditions: [{ [v]: "aws.partition", [w]: t, assign: g }],
rules: [
{
conditions: [l, m],
rules: [
{
conditions: [{ [v]: c, [w]: [a, o] }, q],
rules: [
{
endpoint: {
url: "https://oidc-fips.{Region}.{PartitionResult#dualStackDnsSuffix}",
properties: n,
headers: n,
},
type: e,
},
],
type: f,
},
{ error: "FIPS and DualStack are enabled, but this partition does not support one or both", type: d },
],
type: f,
},
{
conditions: r,
rules: [
{
conditions: [{ [v]: c, [w]: [o, a] }],
rules: [
{
conditions: [{ [v]: "stringEquals", [w]: [{ [v]: h, [w]: [p, "name"] }, "aws-us-gov"] }],
endpoint: { url: "https://oidc.{Region}.amazonaws.com", properties: n, headers: n },
type: e,
},
{
endpoint: {
url: "https://oidc-fips.{Region}.{PartitionResult#dnsSuffix}",
properties: n,
headers: n,
},
type: e,
},
],
type: f,
},
{ error: "FIPS is enabled but this partition does not support FIPS", type: d },
],
type: f,
},
{
conditions: s,
rules: [
{
conditions: [q],
rules: [
{
endpoint: {
url: "https://oidc.{Region}.{PartitionResult#dualStackDnsSuffix}",
properties: n,
headers: n,
},
type: e,
},
],
type: f,
},
{ error: "DualStack is enabled but this partition does not support DualStack", type: d },
],
type: f,
},
{
endpoint: { url: "https://oidc.{Region}.{PartitionResult#dnsSuffix}", properties: n, headers: n },
type: e,
},
],
type: f,
},
],
type: f,
},
{ error: "Invalid Configuration: Missing Region", type: d },
],
};
const u="required",
v="fn",
w="argv",
x="ref";
const a=true,
b="isSet",
c="booleanEquals",
d="error",
e="endpoint",
f="tree",
g="PartitionResult",
h="getAttr",
i={[u]:false,"type":"String"},
j={[u]:true,"default":false,"type":"Boolean"},
k={[x]:"Endpoint"},
l={[v]:c,[w]:[{[x]:"UseFIPS"},true]},
m={[v]:c,[w]:[{[x]:"UseDualStack"},true]},
n={},
o={[v]:h,[w]:[{[x]:g},"supportsFIPS"]},
p={[x]:g},
q={[v]:c,[w]:[true,{[v]:h,[w]:[p,"supportsDualStack"]}]},
r=[l],
s=[m],
t=[{[x]:"Region"}];
const _data={version:"1.0",parameters:{Region:i,UseDualStack:j,UseFIPS:j,Endpoint:i},rules:[{conditions:[{[v]:b,[w]:[k]}],rules:[{conditions:r,error:"Invalid Configuration: FIPS and custom endpoint are not supported",type:d},{conditions:s,error:"Invalid Configuration: Dualstack and custom endpoint are not supported",type:d},{endpoint:{url:k,properties:n,headers:n},type:e}],type:f},{conditions:[{[v]:b,[w]:t}],rules:[{conditions:[{[v]:"aws.partition",[w]:t,assign:g}],rules:[{conditions:[l,m],rules:[{conditions:[{[v]:c,[w]:[a,o]},q],rules:[{endpoint:{url:"https://oidc-fips.{Region}.{PartitionResult#dualStackDnsSuffix}",properties:n,headers:n},type:e}],type:f},{error:"FIPS and DualStack are enabled, but this partition does not support one or both",type:d}],type:f},{conditions:r,rules:[{conditions:[{[v]:c,[w]:[o,a]}],rules:[{conditions:[{[v]:"stringEquals",[w]:[{[v]:h,[w]:[p,"name"]},"aws-us-gov"]}],endpoint:{url:"https://oidc.{Region}.amazonaws.com",properties:n,headers:n},type:e},{endpoint:{url:"https://oidc-fips.{Region}.{PartitionResult#dnsSuffix}",properties:n,headers:n},type:e}],type:f},{error:"FIPS is enabled but this partition does not support FIPS",type:d}],type:f},{conditions:s,rules:[{conditions:[q],rules:[{endpoint:{url:"https://oidc.{Region}.{PartitionResult#dualStackDnsSuffix}",properties:n,headers:n},type:e}],type:f},{error:"DualStack is enabled but this partition does not support DualStack",type:d}],type:f},{endpoint:{url:"https://oidc.{Region}.{PartitionResult#dnsSuffix}",properties:n,headers:n},type:e}],type:f}],type:f},{error:"Invalid Configuration: Missing Region",type:d}]};
export const ruleSet: RuleSetObject = _data;
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const resolveRegion = async (
*/
export const getDefaultRoleAssumer = (
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
STSClient: new (options: STSClientConfig) => STSClient
): RoleAssumer => {
let stsClient: STSClient;
let closureSourceCreds: AwsCredentialIdentity;
Expand All @@ -104,7 +104,8 @@ export const getDefaultRoleAssumer = (
);
const isCompatibleRequestHandler = !isH2(requestHandler);

stsClient = new stsClientCtor({
stsClient = new STSClient({
profile: stsOptions?.parentClientConfig?.profile,
// A hack to make sts client uses the credential in current closure.
credentialDefaultProvider: () => async () => closureSourceCreds,
region: resolvedRegion,
Expand Down Expand Up @@ -146,7 +147,7 @@ export type RoleAssumerWithWebIdentity = (
*/
export const getDefaultRoleAssumerWithWebIdentity = (
stsOptions: STSRoleAssumerOptions,
stsClientCtor: new (options: STSClientConfig) => STSClient
STSClient: new (options: STSClientConfig) => STSClient
): RoleAssumerWithWebIdentity => {
let stsClient: STSClient;
return async (params) => {
Expand All @@ -164,7 +165,8 @@ export const getDefaultRoleAssumerWithWebIdentity = (
);
const isCompatibleRequestHandler = !isH2(requestHandler);

stsClient = new stsClientCtor({
stsClient = new STSClient({
profile: stsOptions?.parentClientConfig?.profile,
region: resolvedRegion,
requestHandler: isCompatibleRequestHandler ? (requestHandler as any) : undefined,
logger: logger as any,
Expand Down
Loading

0 comments on commit 2ffc611

Please sign in to comment.