Skip to content

Commit cf40f73

Browse files
authored
Multi auth selection (Client level) (#3608)
* Add support for client level auth resolution with preferences * Updated dynamoDB client * Remove unneccesary includes from GenericAuthSchemeResolver * Fix hasSigV4aAuth function for including the corresponding header * Update AuthSchemeResolverBase.h from UnorderedMap to Array * Fix StringUtils namespace invocation in ClientConfiguration * restore deleted AuthSchemeResolvers
1 parent 94304da commit cf40f73

File tree

15 files changed

+377
-77
lines changed

15 files changed

+377
-77
lines changed

generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClient.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
#include <aws/dynamodb/DynamoDB_EXPORTS.h>
1212
#include <smithy/client/AwsSmithyClient.h>
1313
#include <smithy/client/serializer/JsonOutcomeSerializer.h>
14+
#include <smithy/identity/auth/built-in/GenericAuthSchemeResolver.h>
1415
#include <smithy/identity/auth/built-in/SigV4AuthScheme.h>
15-
#include <smithy/identity/auth/built-in/SigV4AuthSchemeResolver.h>
1616

1717
namespace Aws {
1818
namespace DynamoDB {
@@ -38,7 +38,7 @@ AWS_DYNAMODB_API extern const char SERVICE_NAME[];
3838
class AWS_DYNAMODB_API DynamoDBClient
3939
: Aws::Client::ClientWithAsyncTemplateMethods<DynamoDBClient>,
4040
smithy::client::AwsSmithyClientT<Aws::DynamoDB::SERVICE_NAME, Aws::DynamoDB::DynamoDBClientConfiguration,
41-
smithy::SigV4AuthSchemeResolver<>, Aws::Crt::Variant<smithy::SigV4AuthScheme>,
41+
smithy::AuthSchemeResolverBase<>, Aws::Crt::Variant<smithy::SigV4AuthScheme>,
4242
DynamoDBEndpointProviderBase, smithy::client::JsonOutcomeSerializer, smithy::client::JsonOutcome,
4343
Aws::Client::DynamoDBErrorMarshaller> {
4444
public:

generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClient.cpp

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ DynamoDBClient::DynamoDBClient(const DynamoDB::DynamoDBClientConfiguration& clie
104104
: AwsSmithyClientT(clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
105105
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG),
106106
endpointProvider ? endpointProvider : Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
107-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
107+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
108+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
108109
{
109110
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
110111
smithy::SigV4AuthScheme{GetServiceName(), clientConfiguration.region}},
@@ -116,7 +117,8 @@ DynamoDBClient::DynamoDBClient(const AWSCredentials& credentials, std::shared_pt
116117
clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
117118
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG),
118119
endpointProvider ? endpointProvider : Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
119-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
120+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
121+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
120122
{
121123
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
122124
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::SimpleAwsCredentialIdentityResolver>(ALLOCATION_TAG, credentials),
@@ -130,37 +132,50 @@ DynamoDBClient::DynamoDBClient(const std::shared_ptr<AWSCredentialsProvider>& cr
130132
clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
131133
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG),
132134
endpointProvider ? endpointProvider : Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
133-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
134-
{{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
135-
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::AwsCredentialsProviderIdentityResolver>(ALLOCATION_TAG, credentialsProvider),
136-
GetServiceName(), clientConfiguration.region}}}) {}
135+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
136+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
137+
{
138+
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
139+
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::AwsCredentialsProviderIdentityResolver>(ALLOCATION_TAG, credentialsProvider),
140+
GetServiceName(), clientConfiguration.region}},
141+
}) {}
137142

138143
/* Legacy constructors due deprecation */
139144
DynamoDBClient::DynamoDBClient(const Client::ClientConfiguration& clientConfiguration)
140145
: AwsSmithyClientT(clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
141146
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG), Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
142-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
143-
{{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
144-
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::DefaultAwsCredentialIdentityResolver>(ALLOCATION_TAG),
145-
GetServiceName(), clientConfiguration.region}}}) {}
147+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
148+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
149+
{
150+
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
151+
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::DefaultAwsCredentialIdentityResolver>(ALLOCATION_TAG),
152+
GetServiceName(), clientConfiguration.region}},
153+
}) {}
146154

147155
DynamoDBClient::DynamoDBClient(const AWSCredentials& credentials, const Client::ClientConfiguration& clientConfiguration)
148-
: AwsSmithyClientT(clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
149-
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG), Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
150-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
151-
{{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
152-
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::SimpleAwsCredentialIdentityResolver>(ALLOCATION_TAG, credentials),
153-
GetServiceName(), clientConfiguration.region}}}) {}
156+
: AwsSmithyClientT(
157+
clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
158+
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG), Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
159+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
160+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
161+
{
162+
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
163+
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::SimpleAwsCredentialIdentityResolver>(ALLOCATION_TAG, credentials),
164+
GetServiceName(), clientConfiguration.region}},
165+
}) {}
154166

155167
DynamoDBClient::DynamoDBClient(const std::shared_ptr<AWSCredentialsProvider>& credentialsProvider,
156168
const Client::ClientConfiguration& clientConfiguration)
157169
: AwsSmithyClientT(
158170
clientConfiguration, GetServiceName(), "DynamoDB", Aws::Http::CreateHttpClient(clientConfiguration),
159171
Aws::MakeShared<DynamoDBErrorMarshaller>(ALLOCATION_TAG), Aws::MakeShared<DynamoDBEndpointProvider>(ALLOCATION_TAG),
160-
Aws::MakeShared<smithy::SigV4AuthSchemeResolver<>>(ALLOCATION_TAG),
161-
{{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
162-
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::AwsCredentialsProviderIdentityResolver>(ALLOCATION_TAG, credentialsProvider),
163-
GetServiceName(), clientConfiguration.region}}}) {}
172+
Aws::MakeShared<smithy::GenericAuthSchemeResolver<>>(
173+
ALLOCATION_TAG, Aws::Vector<smithy::AuthSchemeOption>({smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption})),
174+
{
175+
{smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption.schemeId,
176+
smithy::SigV4AuthScheme{Aws::MakeShared<smithy::AwsCredentialsProviderIdentityResolver>(ALLOCATION_TAG, credentialsProvider),
177+
GetServiceName(), clientConfiguration.region}},
178+
}) {}
164179
/* End of legacy constructors due deprecation */
165180

166181
DynamoDBClient::~DynamoDBClient() { ShutdownSdkClient(this, -1); }

src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,12 @@ namespace Aws
580580
std::chrono::milliseconds retrieveCredentialsFutureTimeout = std::chrono::seconds(10);
581581
} loginCredentialProviderConfig;
582582
} credentialProviderConfig;
583+
584+
/**
585+
* Authentication scheme preferences in order of preference.
586+
* First available auth scheme will be used for each operation.
587+
*/
588+
Aws::Vector<Aws::String> authPreferences;
583589
};
584590

585591
/**

src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ namespace client
163163
identityParams.serviceName = m_serviceName;
164164
identityParams.operation = ctx.m_requestName;
165165
identityParams.region = m_clientConfiguration.region;
166+
identityParams.authPreferences = m_clientConfiguration.authPreferences;
166167

167168
if (ctx.m_pRequest) {
168169
// refactor once auth scheme resolver will use it's own rule set

src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeResolverBase.h

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,23 @@
88
#include <smithy/identity/signer/AwsSignerBase.h>
99

1010
#include <aws/crt/Variant.h>
11-
#include <aws/core/utils/memory/stl/AWSMap.h>
12-
11+
#include <aws/core/utils/memory/stl/AWSVector.h>
1312

1413
namespace smithy {
14+
15+
static const char SIGV4_PREFERENCE[] = "sigv4";
16+
static const char SIGV4A_PREFERENCE[] = "sigv4a";
17+
static const char BEARER_PREFERENCE[] = "bearer";
18+
static const char NO_AUTH_PREFERENCE[] = "noauth";
19+
20+
// Global map from auth scheme name (trimmed ID) to full ID for case insensitive lookup
21+
static const Aws::Array<std::pair<const char*, const char*>, 4> AUTH_SCHEME_NAME_TO_ID = {
22+
std::make_pair(SIGV4_PREFERENCE, "aws.auth#sigv4"),
23+
std::make_pair(SIGV4A_PREFERENCE, "aws.auth#sigv4a"),
24+
std::make_pair(BEARER_PREFERENCE, "smithy.api#HTTPBearerAuth"),
25+
std::make_pair(NO_AUTH_PREFERENCE, "smithy.api#noAuth")
26+
};
27+
1528
/**
1629
* A base interface for code-generated interfaces for passing in the data required for determining the
1730
* authentication scheme. By default, this only includes the operation name.
@@ -22,6 +35,7 @@ class DefaultAuthSchemeResolverParameters
2235
Aws::String serviceName;
2336
Aws::String operation;
2437
Aws::Crt::Optional<Aws::String> region;
38+
Aws::Vector<Aws::String> authPreferences;
2539

2640
Aws::UnorderedMap<Aws::String, Aws::Crt::Variant<Aws::String,
2741
bool,
@@ -43,7 +57,31 @@ class AuthSchemeResolverBase
4357
using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT;
4458

4559
virtual ~AuthSchemeResolverBase() = default;
46-
// AuthScheme Resolver returns a list of AuthSchemeOptions for some reason, according to the SRA...
47-
virtual Aws::Vector<AuthSchemeOption> resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) = 0;
60+
61+
virtual Aws::Vector<AuthSchemeOption> resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) {
62+
auto options = resolveAuthSchemeImpl(identityProperties);
63+
return filterByPreferences(options, identityProperties.authPreferences);
64+
}
65+
66+
protected:
67+
virtual Aws::Vector<AuthSchemeOption> resolveAuthSchemeImpl(const ServiceAuthSchemeParameters& identityProperties) = 0;
68+
69+
virtual Aws::Vector<AuthSchemeOption> filterByPreferences(const Aws::Vector<AuthSchemeOption>& options,
70+
const Aws::Vector<Aws::String>& preferences) {
71+
if (preferences.empty()) return options;
72+
73+
Aws::Vector<AuthSchemeOption> filtered;
74+
for (const auto& pref : preferences) {
75+
auto prefSchemeIt = find_if(AUTH_SCHEME_NAME_TO_ID.begin(), AUTH_SCHEME_NAME_TO_ID.end(), [&](const std::pair<const char*, const char*> &pair) { return Aws::Utils::StringUtils::ToLower(pref.c_str()) == pair.first; });
76+
if (prefSchemeIt == AUTH_SCHEME_NAME_TO_ID.end()) continue;
77+
for (const auto& option : options) {
78+
if (strcmp(option.schemeId, prefSchemeIt->second) == 0) {
79+
filtered.push_back(option);
80+
break;
81+
}
82+
}
83+
}
84+
return filtered.empty() ? options : filtered;
85+
}
4886
};
4987
}

src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/BearerTokenAuthScheme.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <smithy/identity/signer/built-in/BearerTokenSigner.h>
1212
namespace smithy
1313
{
14+
constexpr char BEARER[] = "smithy.api#HTTPBearerAuth";
15+
1416
class BearerTokenAuthScheme : public AuthScheme<AwsBearerTokenIdentityBase>
1517
{
1618
public:
@@ -22,7 +24,7 @@ class BearerTokenAuthScheme : public AuthScheme<AwsBearerTokenIdentityBase>
2224
explicit BearerTokenAuthScheme(
2325
std::shared_ptr<AwsCredentialIdentityResolverT> identityResolver,
2426
const Aws::String &serviceName, const Aws::String &region)
25-
: AuthScheme("smithy.api#HTTPBearerAuth"),
27+
: AuthScheme(BEARER),
2628
m_identityResolver{identityResolver},
2729
m_signer{Aws::MakeShared<smithy::BearerTokenSigner>(
2830
"BearerTokenAuthScheme", serviceName, region)}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
#pragma once
6+
7+
#include <smithy/identity/auth/AuthSchemeResolverBase.h>
8+
9+
10+
namespace smithy {
11+
template<typename ServiceAuthSchemeParametersT = DefaultAuthSchemeResolverParameters>
12+
class GenericAuthSchemeResolver : public AuthSchemeResolverBase<ServiceAuthSchemeParametersT>
13+
{
14+
public:
15+
using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT;
16+
GenericAuthSchemeResolver(const Aws::Vector<AuthSchemeOption>& allowedAuth) : m_allowedAuth(allowedAuth) {}
17+
GenericAuthSchemeResolver() = default;
18+
virtual ~GenericAuthSchemeResolver() = default;
19+
20+
private:
21+
Aws::Vector<AuthSchemeOption> m_allowedAuth;
22+
23+
protected:
24+
Aws::Vector<AuthSchemeOption> resolveAuthSchemeImpl(const ServiceAuthSchemeParameters& identityProperties) override
25+
{
26+
AWS_UNREFERENCED_PARAM(identityProperties);
27+
return m_allowedAuth;
28+
}
29+
};
30+
}

src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4MultiAuthResolver.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@
1212
#include <smithy/identity/auth/built-in/SigV4aAuthSchemeOption.h>
1313
#include <smithy/identity/auth/built-in/SigV4AuthScheme.h>
1414

15+
/**
16+
* This is an auth scheme resolver prioritizing endpoints2.0 auth resolution
17+
* used for s3 which supersedes the model based auth resolution we do in GenericAuthResolver.
18+
*/
19+
1520
namespace smithy {
1621
template<typename EndPointProviderType, typename ClientConfigType,typename ServiceAuthSchemeParametersT = DefaultAuthSchemeResolverParameters>
1722
class SigV4MultiAuthSchemeResolver : public AuthSchemeResolverBase<ServiceAuthSchemeParametersT, ClientConfigType>
1823
{
19-
private:
2024
public:
25+
SigV4MultiAuthSchemeResolver(const Aws::Vector<AuthSchemeOption>& allowedAuth) : m_allowedAuth(allowedAuth) {}
26+
SigV4MultiAuthSchemeResolver() = default;
2127

2228
void Init(const ClientConfigType& config) override{
2329
m_endpointProviderForAuth = Aws::MakeShared<EndPointProviderType>("SigV4MultiAuthSchemeResolver");
@@ -27,8 +33,12 @@ namespace smithy {
2733
using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT;
2834
virtual ~SigV4MultiAuthSchemeResolver() = default;
2935

30-
Aws::Vector<AuthSchemeOption> resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) override
36+
Aws::Vector<AuthSchemeOption> resolveAuthSchemeImpl(const ServiceAuthSchemeParameters& identityProperties) override
3137
{
38+
if (!m_allowedAuth.empty()) {
39+
// This code path will not currently be taken, but we should enable a way for the user to disable preference for endpoints2.0 resolution and use the preference list
40+
return m_allowedAuth;
41+
}
3242
//pack endpoint params from identityProperties
3343
Aws::Endpoint::EndpointParameters epParams;
3444

@@ -66,6 +76,10 @@ namespace smithy {
6676

6777
return {SigV4AuthSchemeOption::sigV4AuthSchemeOption};
6878
}
79+
80+
private:
81+
Aws::Vector<AuthSchemeOption> m_allowedAuth;
82+
6983
protected:
7084
std::shared_ptr<EndPointProviderType> m_endpointProviderForAuth;
7185
};

src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ Aws::String calculateRegion() {
190190
return "";
191191
}
192192

193+
194+
Aws::Vector<Aws::String> calculateAuthPreferences() {
195+
// Automatically determine the AWS region from environment variables, configuration file and EC2 metadata.
196+
Aws::Vector<Aws::String> res;
197+
auto prefs = Aws::Environment::GetEnv("AWS_AUTH_SCHEME_PREFERENCE");
198+
Aws::Vector<Aws::String> prefsList = Aws::Utils::StringUtils::Split(prefs, ',');
199+
for (auto& pref : prefsList) {
200+
res.push_back(Aws::Utils::StringUtils::Trim(pref.c_str()));
201+
}
202+
return res;
203+
}
204+
193205
void setLegacyClientConfigurationParameters(ClientConfiguration& clientConfig)
194206
{
195207
clientConfig.scheme = Aws::Http::Scheme::HTTPS;
@@ -253,6 +265,7 @@ void setLegacyClientConfigurationParameters(ClientConfiguration& clientConfig)
253265

254266
clientConfig.region = calculateRegion();
255267
clientConfig.credentialProviderConfig.region = clientConfig.region;
268+
clientConfig.authPreferences = calculateAuthPreferences();
256269

257270
// Set the endpoint to interact with EC2 instance's metadata service
258271
Aws::String ec2MetadataServiceEndpoint = Aws::Environment::GetEnv("AWS_EC2_METADATA_SERVICE_ENDPOINT");

0 commit comments

Comments
 (0)