/
resolveAssumeRoleCredentials.ts
140 lines (121 loc) · 4.25 KB
/
resolveAssumeRoleCredentials.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { CredentialsProviderError } from "@smithy/property-provider";
import { getProfileName } from "@smithy/shared-ini-file-loader";
import { AwsCredentialIdentity, ParsedIniData, Profile } from "@smithy/types";
import { FromIniInit } from "./fromIni";
import { resolveCredentialSource } from "./resolveCredentialSource";
import { resolveProfileData } from "./resolveProfileData";
/**
* @internal
*
* @see http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property
* TODO update the above to link to V3 docs
*/
export interface AssumeRoleParams {
/**
* The identifier of the role to be assumed.
*/
RoleArn: string;
/**
* A name for the assumed role session.
*/
RoleSessionName: string;
/**
* A unique identifier that is used by third parties when assuming roles in
* their customers' accounts.
*/
ExternalId?: string;
/**
* The identification number of the MFA device that is associated with the
* user who is making the `AssumeRole` call.
*/
SerialNumber?: string;
/**
* The value provided by the MFA device.
*/
TokenCode?: string;
/**
* The duration, in seconds, of the role session.
*/
DurationSeconds?: number;
}
interface AssumeRoleWithSourceProfile extends Profile {
role_arn: string;
source_profile: string;
}
interface AssumeRoleWithProviderProfile extends Profile {
role_arn: string;
credential_source: string;
}
/**
* @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));
const isAssumeRoleWithSourceProfile = (arg: any): arg is AssumeRoleWithSourceProfile =>
typeof arg.source_profile === "string" && typeof arg.credential_source === "undefined";
const isAssumeRoleWithProviderProfile = (arg: any): arg is AssumeRoleWithProviderProfile =>
typeof arg.credential_source === "string" && typeof arg.source_profile === "undefined";
/**
* @internal
*/
export const resolveAssumeRoleCredentials = async (
profileName: string,
profiles: ParsedIniData,
options: FromIniInit,
visitedProfiles: Record<string, true> = {}
) => {
options.logger?.debug("@aws-sdk/credential-provider-ini", "resolveAssumeRoleCredentials (STS)");
const data = profiles[profileName];
if (!options.roleAssumer) {
// @ts-ignore Cannot find module '@aws-sdk/client-sts'
const { getDefaultRoleAssumer } = await import("@aws-sdk/client-sts");
options.roleAssumer = getDefaultRoleAssumer(
{
...options.clientConfig,
credentialProviderLogger: options.logger,
parentClientConfig: options?.parentClientConfig,
},
options.clientPlugins
);
}
const { source_profile } = data;
if (source_profile && source_profile in visitedProfiles) {
throw new CredentialsProviderError(
`Detected a cycle attempting to resolve credentials for profile` +
` ${getProfileName(options)}. Profiles visited: ` +
Object.keys(visitedProfiles).join(", "),
false
);
}
const sourceCredsProvider: Promise<AwsCredentialIdentity> = source_profile
? resolveProfileData(source_profile, profiles, options, {
...visitedProfiles,
[source_profile]: true,
})
: (await resolveCredentialSource(data.credential_source!, profileName)(options))();
const params: AssumeRoleParams = {
RoleArn: data.role_arn!,
RoleSessionName: data.role_session_name || `aws-sdk-js-${Date.now()}`,
ExternalId: data.external_id,
DurationSeconds: parseInt(data.duration_seconds || "3600", 10),
};
const { mfa_serial } = data;
if (mfa_serial) {
if (!options.mfaCodeProvider) {
throw new CredentialsProviderError(
`Profile ${profileName} requires multi-factor authentication, but no MFA code callback was provided.`,
false
);
}
params.SerialNumber = mfa_serial;
params.TokenCode = await options.mfaCodeProvider(mfa_serial);
}
const sourceCreds = await sourceCredsProvider;
return options.roleAssumer!(sourceCreds, params);
};