Skip to content

Commit 0b2ea09

Browse files
Merge master into feature/sdkv3 (#6576)
## Automatic merge failed - Resolve conflicts and push to this PR branch. - **Do not squash-merge** this PR. Use the "Create a merge commit" option to do a regular merge. ## Command line hint To perform the merge from the command line, you could do something like the following (where "origin" is the name of the remote in your local git repo): ``` git stash git fetch --all git checkout origin/feature/sdkv3 git merge origin/master git commit git push origin HEAD:refs/heads/autoMerge/feature/sdkv3 ``` Co-authored-by: Hweinstock <[email protected]> Co-authored-by: hkobew <[email protected]>
1 parent 5e4dc15 commit 0b2ea09

File tree

7 files changed

+18444
-13278
lines changed

7 files changed

+18444
-13278
lines changed

docs/faq-credentials.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ Issue [aws-toolkit-vscode#3667](https://github.com/aws/aws-toolkit-vscode/issues
1313
2. Attempt to sign in again with AWS Builder ID
1414
3. If sign is is successful you can remove the old folder: `rm -rf ~/.aws/sso-OLD`
1515
1. Or revert the change: `mv ~/.aws/sso-OLD ~/.aws/sso`
16+
17+
### AWS Shared Credentials File
18+
19+
When authenticating with IAM credentials, the profile name, access key, and secret key will be stored on disk at a default location of `~/.aws/credentials` on Linux and MacOS, and `%USERPROFILE%\.aws\credentials` on Windows machines. The toolkit also supports editting this file manually, with the format specified [here](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html#file-format-creds). The credentials files also supports [role assumption](https://docs.aws.amazon.com/sdkref/latest/guide/access-assume-role.html) and [MFA](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa.html). Note that this credentials file is shared between all local AWS development tools. For more information, see the full documentation [here](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html).

package-lock.json

Lines changed: 18316 additions & 13250 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@
505505
"@aws-sdk/client-lambda": "^3.637.0",
506506
"@aws-sdk/client-sso": "^3.342.0",
507507
"@aws-sdk/client-sso-oidc": "^3.574.0",
508-
"@aws-sdk/credential-provider-ini": "3.46.0",
508+
"@aws-sdk/credential-provider-env": "3.696.0",
509509
"@aws-sdk/credential-provider-process": "3.37.0",
510510
"@aws-sdk/credential-provider-sso": "^3.345.0",
511511
"@aws-sdk/property-provider": "3.46.0",

packages/core/src/auth/providers/ec2CredentialsProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { Credentials } from '@aws-sdk/types'
7-
import { fromInstanceMetadata } from '@aws-sdk/credential-provider-imds'
7+
import { fromInstanceMetadata } from '@smithy/credential-provider-imds'
88
import { DefaultEc2MetadataClient } from '../../shared/clients/ec2MetadataClient'
99
import { Ec2MetadataClient } from '../../shared/clients/ec2MetadataClient'
1010
import { getLogger } from '../../shared/logger/logger'

packages/core/src/auth/providers/ecsCredentialsProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { Credentials, CredentialProvider } from '@aws-sdk/types'
7-
import { fromContainerMetadata } from '@aws-sdk/credential-provider-imds'
7+
import { fromContainerMetadata } from '@smithy/credential-provider-imds'
88
import { EnvironmentVariables } from '../../shared/environmentVariables'
99
import { CredentialType } from '../../shared/telemetry/telemetry.gen'
1010
import { getStringHash } from '../../shared/utilities/textUtilities'

packages/core/src/auth/providers/sharedCredentialsProvider.ts

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
*/
55

66
import * as AWS from '@aws-sdk/types'
7-
import { AssumeRoleParams, fromIni } from '@aws-sdk/credential-provider-ini'
87
import { fromProcess } from '@aws-sdk/credential-provider-process'
9-
import { ParsedIniData, SharedConfigFiles } from '@smithy/shared-ini-file-loader'
8+
import { ParsedIniData } from '@smithy/types'
109
import { chain } from '@aws-sdk/property-provider'
11-
import { fromInstanceMetadata, fromContainerMetadata } from '@aws-sdk/credential-provider-imds'
10+
import { fromInstanceMetadata, fromContainerMetadata } from '@smithy/credential-provider-imds'
1211
import { fromEnv } from '@aws-sdk/credential-provider-env'
1312
import { getLogger } from '../../shared/logger/logger'
1413
import { getStringHash } from '../../shared/utilities/textUtilities'
@@ -29,9 +28,10 @@ import {
2928
Profile,
3029
Section,
3130
} from '../credentials/sharedCredentials'
32-
import { SectionName, SharedCredentialsKeys } from '../credentials/types'
31+
import { CredentialsData, SectionName, SharedCredentialsKeys } from '../credentials/types'
3332
import { SsoProfile, hasScopes, scopesSsoAccountAccess } from '../connection'
3433
import { builderIdStartUrl } from '../sso/constants'
34+
import { ToolkitError } from '../../shared/errors'
3535

3636
const credentialSources = {
3737
ECS_CONTAINER: 'EcsContainer',
@@ -378,18 +378,6 @@ export class SharedCredentialsProvider implements CredentialsProvider {
378378
}
379379

380380
private makeSharedIniFileCredentialsProvider(loadedCreds?: ParsedIniData): AWS.CredentialProvider {
381-
const assumeRole = async (credentials: AWS.Credentials, params: AssumeRoleParams) => {
382-
const region = this.getDefaultRegion() ?? 'us-east-1'
383-
const stsClient = new DefaultStsClient(region, credentials)
384-
const response = await stsClient.assumeRole(params)
385-
return {
386-
accessKeyId: response.Credentials!.AccessKeyId!,
387-
secretAccessKey: response.Credentials!.SecretAccessKey!,
388-
sessionToken: response.Credentials?.SessionToken,
389-
expiration: response.Credentials?.Expiration,
390-
}
391-
}
392-
393381
// Our credentials logic merges profiles from the credentials and config files but SDK v3 does not
394382
// This can cause odd behavior where the Toolkit can switch to a profile but not authenticate with it
395383
// So the workaround is to do give the SDK the merged profiles directly
@@ -399,15 +387,50 @@ export class SharedCredentialsProvider implements CredentialsProvider {
399387
(k) => this.getProfile(k)
400388
)
401389

402-
return fromIni({
403-
profile: this.profileName,
404-
mfaCodeProvider: async (mfaSerial) => await getMfaTokenFromUser(mfaSerial, this.profileName),
405-
roleAssumer: assumeRole,
406-
loadedConfig: Promise.resolve({
407-
credentialsFile: loadedCreds ?? profiles,
408-
configFile: {},
409-
} as SharedConfigFiles),
410-
})
390+
return async () => {
391+
const iniData = loadedCreds ?? profiles
392+
const profile: CredentialsData = iniData[this.profileName]
393+
if (!profile) {
394+
throw new ToolkitError(`auth: Profile ${this.profileName} not found`)
395+
}
396+
// No role to assume, return static credentials.
397+
if (!profile.role_arn) {
398+
return {
399+
accessKeyId: profile.aws_access_key_id!,
400+
secretAccessKey: profile.aws_secret_access_key!,
401+
sessionToken: profile.aws_session_token,
402+
}
403+
}
404+
if (!profile.source_profile || !iniData[profile.source_profile]) {
405+
throw new ToolkitError(
406+
`auth: Profile ${this.profileName} is missing source_profile for role assumption`
407+
)
408+
}
409+
// Use source profile to assume IAM role based on role ARN provided.
410+
const sourceProfile = iniData[profile.source_profile!]
411+
const stsClient = new DefaultStsClient(this.getDefaultRegion() ?? 'us-east-1', {
412+
accessKeyId: sourceProfile.aws_access_key_id!,
413+
secretAccessKey: sourceProfile.aws_secret_access_key!,
414+
})
415+
// Prompt for MFA Token if needed.
416+
const assumeRoleReq = {
417+
RoleArn: profile.role_arn,
418+
RoleSessionName: 'AssumeRoleSession',
419+
...(profile.mfa_serial
420+
? {
421+
SerialNumber: profile.mfa_serial,
422+
TokenCode: await getMfaTokenFromUser(profile.mfa_serial, this.profileName),
423+
}
424+
: {}),
425+
}
426+
const assumeRoleRsp = await stsClient.assumeRole(assumeRoleReq)
427+
return {
428+
accessKeyId: assumeRoleRsp.Credentials!.AccessKeyId!,
429+
secretAccessKey: assumeRoleRsp.Credentials!.SecretAccessKey!,
430+
sessionToken: assumeRoleRsp.Credentials?.SessionToken,
431+
expiration: assumeRoleRsp.Credentials?.Expiration,
432+
}
433+
}
411434
}
412435

413436
private makeSourcedCredentialsProvider(): AWS.CredentialProvider {

packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import { SsoClient } from '../../../auth/sso/clients'
1313
import { stub } from '../../utilities/stubber'
1414
import { SsoAccessTokenProvider } from '../../../auth/sso/ssoAccessTokenProvider'
1515
import { createTestSections } from '../testUtil'
16+
import { DefaultStsClient } from '../../../shared/clients/stsClient'
17+
import { oneDay } from '../../../shared/datetime'
18+
import { getTestWindow } from '../../shared/vscode/window'
1619

1720
const missingPropertiesFragment = 'missing properties'
1821

@@ -450,6 +453,76 @@ describe('SharedCredentialsProvider', async function () {
450453
})
451454
})
452455
})
456+
457+
describe('makeSharedIniFileCredentialsProvider', function () {
458+
let defaultSection: string
459+
460+
before(function () {
461+
defaultSection = `[profile default]
462+
aws_access_key_id = x
463+
aws_secret_access_key = y`
464+
})
465+
466+
beforeEach(function () {
467+
sandbox.stub(DefaultStsClient.prototype, 'assumeRole').callsFake(async (request) => {
468+
assert.strictEqual(request.RoleArn, 'testarn')
469+
if (request.SerialNumber) {
470+
assert.strictEqual(request.SerialNumber, 'mfaSerialToken')
471+
assert.strictEqual(request.TokenCode, 'mfaToken')
472+
}
473+
return {
474+
Credentials: {
475+
AccessKeyId: 'id',
476+
SecretAccessKey: 'secret',
477+
SessionToken: 'token',
478+
Expiration: new Date(Date.now() + oneDay),
479+
},
480+
}
481+
})
482+
})
483+
484+
it('assumes role given in ini data', async function () {
485+
const sections = await createTestSections(`
486+
${defaultSection}
487+
[profile assume]
488+
source_profile = default
489+
role_arn = testarn
490+
`)
491+
492+
const sut = new SharedCredentialsProvider('assume', sections)
493+
const creds = await sut.getCredentials()
494+
assert.strictEqual(creds.accessKeyId, 'id')
495+
assert.strictEqual(creds.secretAccessKey, 'secret')
496+
assert.strictEqual(creds.sessionToken, 'token')
497+
})
498+
499+
it('assumes role with mfa token', async function () {
500+
const sections = await createTestSections(`
501+
${defaultSection}
502+
[profile assume]
503+
source_profile = default
504+
role_arn = testarn
505+
mfa_serial= mfaSerialToken
506+
`)
507+
const sut = new SharedCredentialsProvider('assume', sections)
508+
509+
getTestWindow().onDidShowInputBox((inputBox) => {
510+
inputBox.acceptValue('mfaToken')
511+
})
512+
513+
const creds = await sut.getCredentials()
514+
assert.strictEqual(creds.accessKeyId, 'id')
515+
assert.strictEqual(creds.secretAccessKey, 'secret')
516+
assert.strictEqual(creds.sessionToken, 'token')
517+
})
518+
519+
it('does not assume role when no roleArn is present', async function () {
520+
const sut = new SharedCredentialsProvider('default', await createTestSections(defaultSection))
521+
const creds = await sut.getCredentials()
522+
assert.strictEqual(creds.accessKeyId, 'x')
523+
assert.strictEqual(creds.secretAccessKey, 'y')
524+
})
525+
})
453526
})
454527

455528
function assertSubstringsInText(text: string | undefined, ...substrings: string[]) {

0 commit comments

Comments
 (0)