Skip to content

Commit 8897fa7

Browse files
authored
Update SDK to respect Profile property set in the client config when resolving credentials (#3753)
1 parent 26828bb commit 8897fa7

15 files changed

+288
-798
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"core": {
3+
"updateMinimum": true,
4+
"type": "Patch",
5+
"changeLogMessages": [
6+
"Update SDK to respect `Profile` property set in the client config when resolving credentials."
7+
]
8+
}
9+
}

sdk/src/Core/Amazon.Runtime/Credentials/AnonymousIdentityResolver.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,22 @@ public class AnonymousIdentityResolver : IIdentityResolver<AnonymousAWSCredentia
2626
{
2727
private readonly AnonymousAWSCredentials _credentials = new();
2828

29-
BaseIdentity IIdentityResolver.ResolveIdentity() => _credentials;
29+
BaseIdentity IIdentityResolver.ResolveIdentity(IClientConfig clientConfig)
30+
=> _credentials;
3031

3132
/// <summary>
3233
/// Resolves the identity by returning an instance of <see cref="AnonymousAWSCredentials"/>.
3334
/// </summary>
34-
public AnonymousAWSCredentials ResolveIdentity() => _credentials;
35+
public AnonymousAWSCredentials ResolveIdentity(IClientConfig clientConfig)
36+
=> _credentials;
3537

36-
Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(CancellationToken cancellationToken)
38+
Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken)
3739
=> Task.FromResult<BaseIdentity>(_credentials);
3840

3941
/// <summary>
4042
/// Resolves the identity by returning an instance of <see cref="AnonymousAWSCredentials"/>.
4143
/// </summary>
42-
public Task<AnonymousAWSCredentials> ResolveIdentityAsync(CancellationToken cancellationToken = default)
44+
public Task<AnonymousAWSCredentials> ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken = default)
4345
=> Task.FromResult(_credentials);
4446
}
4547
}

sdk/src/Core/Amazon.Runtime/Credentials/DefaultAWSCredentialsIdentityResolver.cs

+49-31
Original file line numberDiff line numberDiff line change
@@ -53,38 +53,69 @@ public DefaultAWSCredentialsIdentityResolver()
5353
#endif
5454
() => new EnvironmentVariablesAWSCredentials(), // Look for credentials set in environment vars.
5555
() => AssumeRoleWithWebIdentityCredentials.FromEnvironmentVariables(),
56-
() => GetAWSCredentials(_credentialProfileChain),
56+
() => GetProfileCredentials(_credentialProfileChain),
5757
() => ContainerEC2CredentialsWrapper(), // either get ECS / EKS credentials or instance profile credentials
5858
};
5959
}
6060

61+
#region Helper methods for migration from FallbackCredentialsFactory
62+
6163
/// <summary>
6264
/// Search the environment for configured AWS credentials. The search includes
6365
/// environment variables, "default" AWS credentials profiles and EC2 instance metadata.
6466
/// </summary>
67+
/// <param name="clientConfig">
68+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
69+
/// </param>
6570
/// <returns>AWSCredentials that can be used when creating AWS service clients.</returns>
66-
public static AWSCredentials GetCredentials()
71+
public static AWSCredentials GetCredentials(IClientConfig clientConfig = null)
6772
{
68-
return _defaultInstance.Value.ResolveIdentity();
73+
return _defaultInstance.Value.ResolveIdentity(clientConfig);
6974
}
7075

7176
/// <summary>
7277
/// Search the environment for configured AWS credentials. The search includes
7378
/// environment variables, "default" AWS credentials profiles and EC2 instance metadata.
7479
/// </summary>
80+
/// <param name="clientConfig">
81+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
82+
/// </param>
7583
/// <returns>AWSCredentials that can be used when creating AWS service clients.</returns>
76-
public static Task<AWSCredentials> GetCredentialsAsync()
84+
public static Task<AWSCredentials> GetCredentialsAsync(IClientConfig clientConfig = null)
7785
{
78-
return _defaultInstance.Value.ResolveIdentityAsync();
86+
return _defaultInstance.Value.ResolveIdentityAsync(clientConfig);
7987
}
8088

81-
BaseIdentity IIdentityResolver.ResolveIdentity()
89+
#endregion
90+
91+
BaseIdentity IIdentityResolver.ResolveIdentity(IClientConfig clientConfig)
92+
=> ResolveIdentity(clientConfig);
93+
94+
public AWSCredentials ResolveIdentity(IClientConfig clientConfig)
8295
{
83-
return ResolveIdentity();
96+
var profile = clientConfig?.Profile;
97+
if (profile != null)
98+
{
99+
var source = new CredentialProfileStoreChain(profile.Location);
100+
if (source.TryGetProfile(profile.Name, out CredentialProfile storedProfile))
101+
{
102+
return storedProfile.GetAWSCredentials(source, true);
103+
}
104+
105+
throw new AmazonClientException($"Unable to find the \"{profile.Name}\" profile specified in the client configuration.");
106+
}
107+
108+
return InternalGetCredentials();
84109
}
85110

111+
Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken) =>
112+
Task.FromResult<BaseIdentity>(ResolveIdentity(clientConfig));
113+
114+
public Task<AWSCredentials> ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken = default) =>
115+
Task.FromResult(ResolveIdentity(clientConfig));
116+
86117
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We need to catch all exceptions to be able to move the the next generator.")]
87-
public AWSCredentials ResolveIdentity()
118+
private AWSCredentials InternalGetCredentials()
88119
{
89120
var hasEnvironmentChanged = false;
90121

@@ -95,7 +126,7 @@ public AWSCredentials ResolveIdentity()
95126
{
96127
hasEnvironmentChanged = _lastKnownEnvironmentState.HasEnvironmentChanged();
97128
if (!hasEnvironmentChanged)
98-
{
129+
{
99130
return _cachedCredentials;
100131
}
101132
}
@@ -155,37 +186,24 @@ public AWSCredentials ResolveIdentity()
155186
return _cachedCredentials;
156187
}
157188

158-
using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
189+
using var writer = new StringWriter(CultureInfo.InvariantCulture);
190+
writer.WriteLine("Failed to resolve AWS credentials. The credential providers used to search for credentials returned the following errors:");
191+
writer.WriteLine();
192+
for (int i = 0; i < errors.Count; i++)
159193
{
160-
writer.WriteLine("Failed to resolve AWS credentials. The credential providers used to search for credentials returned the following errors:");
161-
writer.WriteLine();
162-
for (int i = 0; i < errors.Count; i++)
163-
{
164-
Exception e = errors[i];
165-
writer.WriteLine("Exception {0} of {1}: {2}", i + 1, errors.Count, e.Message);
166-
}
167-
168-
throw new AmazonClientException(writer.ToString());
194+
Exception e = errors[i];
195+
writer.WriteLine("Exception {0} of {1}: {2}", i + 1, errors.Count, e.Message);
169196
}
197+
198+
throw new AmazonClientException(writer.ToString());
170199
}
171200
finally
172201
{
173202
_cachedCredentialsLock.ExitWriteLock();
174203
}
175204
}
176205

177-
async Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(CancellationToken cancellationToken)
178-
{
179-
var identity = await ResolveIdentityAsync(cancellationToken).ConfigureAwait(false);
180-
return identity;
181-
}
182-
183-
public async Task<AWSCredentials> ResolveIdentityAsync(CancellationToken cancellationToken = default)
184-
{
185-
return await Task.Run(() => ResolveIdentity(), cancellationToken).ConfigureAwait(false);
186-
}
187-
188-
private static AWSCredentials GetAWSCredentials(ICredentialProfileSource source)
206+
private static AWSCredentials GetProfileCredentials(ICredentialProfileSource source)
189207
{
190208
var profileName = GetProfileName();
191209
if (source.TryGetProfile(profileName, out CredentialProfile profile))

sdk/src/Core/Amazon.Runtime/Credentials/DefaultAWSTokenIdentityResolver.cs

+6-8
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ public class DefaultAWSTokenIdentityResolver : IIdentityResolver<AWSToken>
2929
public DefaultAWSTokenIdentityResolver()
3030
=> _tokenProvider = new AWSTokenProviderChain(new ProfileTokenProvider());
3131

32-
BaseIdentity IIdentityResolver.ResolveIdentity()
33-
{
34-
return ResolveIdentity();
35-
}
32+
BaseIdentity IIdentityResolver.ResolveIdentity(IClientConfig clientConfig)
33+
=> ResolveIdentity(clientConfig: null);
3634

3735
/// <inheritdoc/>
38-
public AWSToken ResolveIdentity()
36+
public AWSToken ResolveIdentity(IClientConfig clientConfig)
3937
{
4038
#if NETFRAMEWORK
4139
if (_tokenProvider.TryResolveToken(out var token))
@@ -56,14 +54,14 @@ public AWSToken ResolveIdentity()
5654
#endif
5755
}
5856

59-
async Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(CancellationToken cancellationToken)
57+
async Task<BaseIdentity> IIdentityResolver.ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken)
6058
{
61-
var identity = await ResolveIdentityAsync(cancellationToken).ConfigureAwait(false);
59+
var identity = await ResolveIdentityAsync(clientConfig, cancellationToken).ConfigureAwait(false);
6260
return identity;
6361
}
6462

6563
/// <inheritdoc/>
66-
public async Task<AWSToken> ResolveIdentityAsync(CancellationToken cancellationToken = default)
64+
public async Task<AWSToken> ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken = default)
6765
{
6866
var tokenResponse = await _tokenProvider.TryResolveTokenAsync(cancellationToken).ConfigureAwait(false);
6967
if (tokenResponse.Success)

sdk/src/Core/Amazon.Runtime/Credentials/Internal/DefaultIdentityResolverConfiguration.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public IIdentityResolver GetIdentityResolver<T>() where T : BaseIdentity
4444
public static T ResolveDefaultIdentity<T>() where T : BaseIdentity
4545
{
4646
var identityResolver = Instance.GetIdentityResolver<T>();
47-
return identityResolver.ResolveIdentity() as T;
47+
return identityResolver.ResolveIdentity(clientConfig: null) as T;
4848
}
4949
}
5050
}

sdk/src/Core/Amazon.Runtime/IClientConfig.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ public partial interface IClientConfig
5353
string ServiceId { get; }
5454

5555
/// <summary>
56-
/// Specifies the profile to be used. When this is set on the ClientConfig and that config is passed to
57-
/// the service client constructor the sdk will try to find the credentials associated with the Profile.Name property
58-
/// If set, this will override AWS_PROFILE and AWSConfigs.ProfileName.
56+
/// Specifies the profile to be used.
57+
/// When this is set on the config passed to the service client constructor the SDK will try to find the
58+
/// credentials associated with the <see cref="Profile.Name"/> property.
59+
///
60+
/// <para />
61+
/// If set, this will override <c>AWS_PROFILE</c> and <c>AWSConfigs.ProfileName</c>.
5962
/// </summary>
6063
Profile Profile { get; }
6164

sdk/src/Core/Amazon.Runtime/Identity/IIdentityResolver.cs

+22-4
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,22 @@ public interface IIdentityResolver
3434
/// Loads the customer's identity for this resolver.
3535
/// If the identity cannot be resolved an <c>AmazonClientException</c> will be thrown.
3636
/// </summary>
37-
BaseIdentity ResolveIdentity();
37+
/// <param name="clientConfig">
38+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
39+
/// </param>
40+
BaseIdentity ResolveIdentity(IClientConfig clientConfig);
3841

3942
/// <summary>
4043
/// Loads the customer's identity for this resolver.
4144
/// If the identity cannot be resolved an <c>AmazonClientException</c> will be thrown.
4245
/// </summary>
43-
Task<BaseIdentity> ResolveIdentityAsync(CancellationToken cancellationToken = default);
46+
/// <param name="clientConfig">
47+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
48+
/// </param>
49+
/// <param name="cancellationToken">
50+
/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
51+
/// </param>
52+
Task<BaseIdentity> ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken = default);
4453
}
4554

4655
/// <summary>
@@ -59,12 +68,21 @@ public interface IIdentityResolver<T> : IIdentityResolver where T : BaseIdentity
5968
/// Loads the customer's identity for this resolver.
6069
/// If the identity cannot be resolved an <c>AmazonClientException</c> will be thrown.
6170
/// </summary>
62-
new T ResolveIdentity();
71+
/// <param name="clientConfig">
72+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
73+
/// </param>
74+
new T ResolveIdentity(IClientConfig clientConfig);
6375

6476
/// <summary>
6577
/// Loads the customer's identity for this resolver.
6678
/// If the identity cannot be resolved an <c>AmazonClientException</c> will be thrown.
6779
/// </summary>
68-
new Task<T> ResolveIdentityAsync(CancellationToken cancellationToken = default);
80+
/// <param name="clientConfig">
81+
/// Optional config object that can be used to specify a different profile programatically (via the <see cref="Profile"/> property).
82+
/// </param>
83+
/// <param name="cancellationToken">
84+
/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
85+
/// </param>
86+
new Task<T> ResolveIdentityAsync(IClientConfig clientConfig, CancellationToken cancellationToken = default);
6987
}
7088
}

sdk/src/Core/Amazon.Runtime/Pipeline/Handlers/BaseAuthResolverHandler.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ protected void PreInvoke(IExecutionContext executionContext)
103103
}
104104

105105
var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
106-
executionContext.RequestContext.Identity = identityResolver.ResolveIdentity();
106+
executionContext.RequestContext.Identity = identityResolver.ResolveIdentity(clientConfig);
107107

108108
if (executionContext.RequestContext.Identity != null)
109109
{
@@ -183,7 +183,7 @@ protected async Task PreInvokeAsync(IExecutionContext executionContext)
183183

184184
var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
185185
executionContext.RequestContext.Identity = await identityResolver
186-
.ResolveIdentityAsync(cancellationToken)
186+
.ResolveIdentityAsync(clientConfig, cancellationToken)
187187
.ConfigureAwait(false);
188188

189189
if (executionContext.RequestContext.Identity != null)

sdk/src/Core/Profile.cs

+27-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.IO;
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
using Amazon.Runtime;
417
using System.Text;
518

619
namespace Amazon
720
{
821
/// <summary>
9-
/// Represents a profile in the configuration file. For example in ~/.aws/config
22+
/// Represents a profile in the configuration file. For example in ~/.aws/config:
23+
/// <code>
1024
/// [profile foo]
1125
/// name = value
26+
///
1227
/// Profile profile = new Profile("foo");
13-
/// When this is set on the ClientConfig and that config is passed to
14-
/// the service client constructor the sdk will try to find the credentials associated with the Profile.Name property
15-
/// If set, this will override AWS_PROFILE and AWSConfigs.ProfileName.
28+
/// </code>
29+
///
30+
/// When this is set on the <see cref="IClientConfig"/> and that config is passed to
31+
/// the service client constructor the SDK will try to find the credentials associated with the <see cref="Name"/> property.
32+
///
33+
/// <para />
34+
///
35+
/// If set, this will override <c>AWS_PROFILE</c> and <c>AWSConfigs.ProfileName</c>.
1636
/// </summary>
1737
public class Profile
1838
{

sdk/test/Services/DSQL/UnitTests/Custom/DSQLAuthTokenGeneratorTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void Initialize()
5858
originalIdentityResolvers = field.GetValue(null) as Dictionary<Type, IIdentityResolver>;
5959

6060
var mockIdentityResolver = new Mock<IIdentityResolver>();
61-
mockIdentityResolver.Setup(i => i.ResolveIdentity()).Returns(BasicCredentials);
61+
mockIdentityResolver.Setup(i => i.ResolveIdentity(null)).Returns(BasicCredentials);
6262

6363
field.SetValue(null, new Dictionary<Type, IIdentityResolver>()
6464
{

sdk/test/Services/RDS/UnitTests/Custom/RDSAuthTokenGeneratorTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void Initialize()
5858
originalIdentityResolvers = field.GetValue(null) as Dictionary<Type, IIdentityResolver>;
5959

6060
var mockIdentityResolver = new Mock<IIdentityResolver>();
61-
mockIdentityResolver.Setup(i => i.ResolveIdentity()).Returns(BasicCredentials);
61+
mockIdentityResolver.Setup(i => i.ResolveIdentity(null)).Returns(BasicCredentials);
6262

6363
field.SetValue(null, new Dictionary<Type, IIdentityResolver>()
6464
{

sdk/test/UnitTests/AWSSDK.UnitTests.Custom.NetFramework.csproj

-6
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,4 @@ This project file should not be used as part of a release pipeline.
104104
<None Remove="Custom\obj\**" />
105105
<Compile Remove="Custom\obj\**" />
106106
</ItemGroup>
107-
108-
<ItemGroup>
109-
<None Update="Custom\Runtime\Credentials\AccountIdEndpointTests\accountid-source-testcases.json">
110-
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
111-
</None>
112-
</ItemGroup>
113107
</Project>

0 commit comments

Comments
 (0)