Skip to content

Commit a241c97

Browse files
committed
wire smithy client to use account id resolved from identity for endpoint paramters
1 parent 76081b2 commit a241c97

File tree

10 files changed

+628
-77
lines changed

10 files changed

+628
-77
lines changed

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

+37-4
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
#include <aws/core/utils/FutureOutcome.h>
2121
#include <aws/core/utils/Outcome.h>
2222
#include <aws/core/utils/threading/Executor.h>
23-
#include <aws/core/utils/threading/SameThreadExecutor.h>
2423
#include <aws/core/http/HttpResponse.h>
2524
#include <aws/core/http/HttpClientFactory.h>
2625
#include <smithy/identity/signer/built-in/SignerProperties.h>
27-
#include <smithy/identity/auth/built-in/SigV4AuthSchemeOption.h>
2826
#include <smithy/client/AwsLegacyClient.h>
2927

3028
namespace smithy {
@@ -204,16 +202,24 @@ namespace client
204202
false/*retryable*/);
205203
}
206204

207-
SigningOutcome SignHttpRequest(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const override
205+
SigningOutcome SignHttpRequest(std::shared_ptr<HttpRequest> httpRequest, const AwsSmithyClientAsyncRequestContext& ctx) const override
208206
{
209-
return AwsClientRequestSigning<AuthSchemesVariantT>::SignRequest(httpRequest, targetAuthSchemeOption, m_authSchemes);
207+
return AwsClientRequestSigning<AuthSchemesVariantT>::SignRequest(httpRequest, ctx, m_authSchemes);
210208
}
211209

212210
bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const override
213211
{
214212
return AwsClientRequestSigning<AuthSchemesVariantT>::AdjustClockSkew(outcome, authSchemeOption, m_authSchemes);
215213
}
216214

215+
IdentityOutcome ResolveIdentity(const AwsSmithyClientAsyncRequestContext& ctx) const override {
216+
return AwsClientRequestSigning<AuthSchemesVariantT>::ResolveIdentity(ctx, m_authSchemes);
217+
}
218+
219+
GetContextEndpointParametersOutcome GetContextEndpointParameters(const AwsSmithyClientAsyncRequestContext& ctx) const override {
220+
return GetContextEndpointParametersImpl(ctx);
221+
}
222+
217223
ResponseT MakeRequestDeserialize(Aws::AmazonWebServiceRequest const * const request,
218224
const char* requestName,
219225
Aws::Http::HttpMethod method,
@@ -354,6 +360,33 @@ namespace client
354360
SerializerT,
355361
ResponseT,
356362
ErrorMarshallerT>>;
363+
364+
/**
365+
* SFINAE implementation for refreshing client context params enabled if the client configuration
366+
* type has a AccountId member. If there is a corresponding member we want to use that in the endpoint
367+
* calculation and set it as a client context parameter.
368+
*/
369+
template <typename T, typename = void>
370+
struct HasAccountId : std::false_type {};
371+
372+
template<typename T>
373+
struct HasAccountId<T, decltype(void(std::declval<T>().accountId))> : std::true_type {};
374+
375+
template<typename ConfigT = ServiceClientConfigurationT, typename std::enable_if<HasAccountId<ConfigT>::value, int>::type = 0>
376+
GetContextEndpointParametersOutcome GetContextEndpointParametersImpl(const AwsSmithyClientAsyncRequestContext& ctx) const {
377+
Aws::Vector<Aws::Endpoint::EndpointParameter> endpointParameters;
378+
const auto resolvedAccountId = ctx.m_awsIdentity->accountId();
379+
if (resolvedAccountId.has_value() && !resolvedAccountId.value().empty() && m_clientConfiguration.accountId.empty()) {
380+
endpointParameters.emplace_back("AccountId", resolvedAccountId.value(), Aws::Endpoint::EndpointParameter::ParameterOrigin::OPERATION_CONTEXT);
381+
}
382+
return endpointParameters;
383+
}
384+
385+
template<typename ConfigT = ServiceClientConfigurationT, typename std::enable_if<!HasAccountId<ConfigT>::value, int>::type = 0>
386+
GetContextEndpointParametersOutcome GetContextEndpointParametersImpl(const AwsSmithyClientAsyncRequestContext& ctx) const {
387+
AWS_UNREFERENCED_PARAM(ctx);
388+
return Aws::Vector<Aws::Endpoint::EndpointParameter>{};
389+
}
357390
};
358391

359392
} // namespace client

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

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace smithy
7070
ResponseHandlerFunc m_responseHandler;
7171
std::shared_ptr<Aws::Utils::Threading::Executor> m_pExecutor;
7272
std::shared_ptr<interceptor::InterceptorContext> m_interceptorContext;
73+
std::shared_ptr<smithy::AwsIdentity> m_awsIdentity;
7374
};
7475
} // namespace client
7576
} // namespace smithy

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <aws/core/utils/FutureOutcome.h>
1919
#include <aws/core/utils/memory/stl/AWSMap.h>
2020
#include <aws/core/utils/Outcome.h>
21+
#include <aws/core/NoResult.h>
2122
#include <aws/core/http/HttpClientFactory.h>
2223
#include <aws/core/client/AWSErrorMarshaller.h>
2324
#include <aws/core/AmazonWebServiceResult.h>
@@ -84,6 +85,8 @@ namespace client
8485
using SelectAuthSchemeOptionOutcome = Aws::Utils::Outcome<AuthSchemeOption, AWSCoreError>;
8586
using ResolveEndpointOutcome = Aws::Utils::Outcome<Aws::Endpoint::AWSEndpoint, AWSCoreError>;
8687
using StreamOutcome = Aws::Utils::Outcome<Aws::AmazonWebServiceResult<Aws::Utils::Stream::ResponseStream>, AWSCoreError >;
88+
using IdentityOutcome = Aws::Utils::Outcome<std::shared_ptr<smithy::AwsIdentity>, AWSCoreError>;
89+
using GetContextEndpointParametersOutcome = Aws::Utils::Outcome<Aws::Vector<Aws::Endpoint::EndpointParameter>, AWSCoreError>;
8790

8891
/* primary constructor */
8992
AwsSmithyClientBase(Aws::UniquePtr<Aws::Client::ClientConfiguration>&& clientConfig,
@@ -199,8 +202,10 @@ namespace client
199202

200203
virtual ResolveEndpointOutcome ResolveEndpoint(const Aws::Endpoint::EndpointParameters& endpointParameters, EndpointUpdateCallback&& epCallback) const = 0;
201204
virtual SelectAuthSchemeOptionOutcome SelectAuthSchemeOption(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
202-
virtual SigningOutcome SignHttpRequest(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const = 0;
205+
virtual SigningOutcome SignHttpRequest(std::shared_ptr<HttpRequest> httpRequest, const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
203206
virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0;
207+
virtual IdentityOutcome ResolveIdentity(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
208+
virtual GetContextEndpointParametersOutcome GetContextEndpointParameters(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
204209

205210
/* AwsSmithyClientT class binds its config reference to this pointer, so don't remove const and don't re-allocate it.
206211
* This is done to avoid duplication of config object between this base and actual service template classes.

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

+93-45
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,42 @@ namespace smithy
3838
using SigningError = Aws::Client::AWSError<Aws::Client::CoreErrors>;
3939
using SigningOutcome = Aws::Utils::FutureOutcome<std::shared_ptr<HttpRequest>, SigningError>;
4040
using HttpResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
41+
using IdentityOutcome = Aws::Utils::Outcome<std::shared_ptr<smithy::AwsIdentity>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
4142

42-
static SigningOutcome SignRequest(std::shared_ptr<HttpRequest> HTTPRequest, const AuthSchemeOption& authSchemeOption,
43-
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes)
43+
static IdentityOutcome ResolveIdentity(const client::AwsSmithyClientAsyncRequestContext& ctx,
44+
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes)
4445
{
46+
auto authSchemeIt = authSchemes.find(ctx.m_authSchemeOption.schemeId);
47+
if (authSchemeIt == authSchemes.end())
48+
{
49+
assert(!"Auth scheme has not been found for a given auth option!");
50+
return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE,
51+
"",
52+
"Requested AuthSchemeOption was not found within client Auth Schemes",
53+
false/*retryable*/));
54+
}
4555

46-
auto authSchemeIt = authSchemes.find(authSchemeOption.schemeId);
56+
const AuthSchemesVariantT& authScheme = authSchemeIt->second;
57+
IdentityVisitor visitor(ctx);
58+
AuthSchemesVariantT authSchemesVariantCopy(authScheme); // TODO: allow const visiting
59+
authSchemesVariantCopy.Visit(visitor);
60+
61+
if (!visitor.result)
62+
{
63+
return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE,
64+
"",
65+
"Failed to sign with an unknown error",
66+
false/*retryable*/));
67+
}
68+
69+
return std::move(*visitor.result);
70+
}
71+
72+
static SigningOutcome SignRequest(std::shared_ptr<HttpRequest> HTTPRequest,
73+
const client::AwsSmithyClientAsyncRequestContext& ctx,
74+
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes)
75+
{
76+
auto authSchemeIt = authSchemes.find(ctx.m_authSchemeOption.schemeId);
4777
if (authSchemeIt == authSchemes.end())
4878
{
4979
assert(!"Auth scheme has not been found for a given auth option!");
@@ -55,8 +85,9 @@ namespace smithy
5585

5686
const AuthSchemesVariantT& authScheme = authSchemeIt->second;
5787

58-
return SignWithAuthScheme(std::move(HTTPRequest), authScheme, authSchemeOption);
88+
return SignWithAuthScheme(std::move(HTTPRequest), authScheme, ctx);
5989
}
90+
6091
static SigningOutcome PreSignRequest(std::shared_ptr<HttpRequest> httpRequest,
6192
const AuthSchemeOption& authSchemeOption,
6293
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes,
@@ -113,59 +144,74 @@ namespace smithy
113144

114145

115146
protected:
147+
struct IdentityVisitor
148+
{
149+
IdentityVisitor(const client::AwsSmithyClientAsyncRequestContext& ctx): m_requestContext(ctx)
150+
{
151+
}
152+
153+
const client::AwsSmithyClientAsyncRequestContext& m_requestContext;
154+
Aws::Crt::Optional<IdentityOutcome> result;
155+
156+
template <typename AuthSchemeAlternativeT>
157+
void operator()(AuthSchemeAlternativeT& authScheme)
158+
{
159+
using IdentityT = typename std::remove_reference<decltype(authScheme)>::type::IdentityT;
160+
using IdentityResolver = IdentityResolverBase<IdentityT>;
161+
162+
std::shared_ptr<IdentityResolver> identityResolver = authScheme.identityResolver();
163+
if (!identityResolver)
164+
{
165+
result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE,
166+
"",
167+
"Auth scheme provided a nullptr identityResolver",
168+
false/*retryable*/));
169+
return;
170+
}
171+
172+
//relay service params in additional properties which will be relevant in credential resolution
173+
// example: bucket Name
174+
Aws::UnorderedMap<Aws::String, Aws::Crt::Variant<Aws::String, bool>> additionalIdentityProperties;
175+
const auto& serviceSpecificParameters = m_requestContext.m_pRequest->GetServiceSpecificParameters();
176+
if(serviceSpecificParameters)
177+
{
178+
for(const auto& propPair : serviceSpecificParameters->parameterMap)
179+
{
180+
additionalIdentityProperties.emplace(propPair.first,Aws::Crt::Variant<Aws::String, bool>{propPair.second} );
181+
}
182+
}
183+
184+
auto identityResult = identityResolver->getIdentity(m_requestContext.m_authSchemeOption.identityProperties(), additionalIdentityProperties);
185+
if (!identityResult.IsSuccess())
186+
{
187+
result.emplace(identityResult.GetError());
188+
return;
189+
}
190+
result.emplace(std::move(identityResult.GetResultWithOwnership()));
191+
}
192+
};
193+
116194
struct SignerVisitor
117195
{
118-
SignerVisitor(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemeOption& targetAuthSchemeOption)
119-
: m_httpRequest(std::move(httpRequest)), m_targetAuthSchemeOption(targetAuthSchemeOption)
196+
SignerVisitor(std::shared_ptr<HttpRequest> httpRequest, const client::AwsSmithyClientAsyncRequestContext& ctx)
197+
: m_httpRequest(std::move(httpRequest)), m_requestContext(ctx)
120198
{
121199
}
122200

123201
const std::shared_ptr<HttpRequest> m_httpRequest;
124-
const AuthSchemeOption& m_targetAuthSchemeOption;
202+
const client::AwsSmithyClientAsyncRequestContext& m_requestContext;
125203

126204
Aws::Crt::Optional<SigningOutcome> result;
127205

128206
template <typename AuthSchemeAlternativeT>
129207
void operator()(AuthSchemeAlternativeT& authScheme)
130208
{
131209
// Auth Scheme Variant alternative contains the requested auth option
132-
assert(strcmp(authScheme.schemeId, m_targetAuthSchemeOption.schemeId) == 0);
210+
assert(strcmp(authScheme.schemeId, m_requestContext.m_authSchemeOption.schemeId) == 0);
133211

134212
using IdentityT = typename std::remove_reference<decltype(authScheme)>::type::IdentityT;
135-
using IdentityResolver = IdentityResolverBase<IdentityT>;
136213
using Signer = AwsSignerBase<IdentityT>;
137214

138-
std::shared_ptr<IdentityResolver> identityResolver = authScheme.identityResolver();
139-
if (!identityResolver)
140-
{
141-
result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE,
142-
"",
143-
"Auth scheme provided a nullptr identityResolver",
144-
false/*retryable*/));
145-
return;
146-
}
147-
148-
//relay service params in additional properties which will be relevant in credential resolution
149-
// example: bucket Name
150-
Aws::UnorderedMap<Aws::String, Aws::Crt::Variant<Aws::String, bool>> additionalIdentityProperties;
151-
const auto& serviceSpecificParameters = m_httpRequest->GetServiceSpecificParameters();
152-
if(serviceSpecificParameters)
153-
{
154-
for(const auto& propPair : serviceSpecificParameters->parameterMap)
155-
{
156-
additionalIdentityProperties.emplace(propPair.first,Aws::Crt::Variant<Aws::String, bool>{propPair.second} );
157-
}
158-
}
159-
160-
auto identityResult = identityResolver->getIdentity(m_targetAuthSchemeOption.identityProperties(), additionalIdentityProperties);
161-
162-
if (!identityResult.IsSuccess())
163-
{
164-
result.emplace(identityResult.GetError());
165-
return;
166-
}
167-
auto identity = std::move(identityResult.GetResultWithOwnership());
168-
169215
std::shared_ptr<Signer> signer = authScheme.signer();
170216
if (!signer)
171217
{
@@ -176,7 +222,9 @@ namespace smithy
176222
return;
177223
}
178224

179-
result.emplace(signer->sign(m_httpRequest, *identity, m_targetAuthSchemeOption.signerProperties()));
225+
result.emplace(signer->sign(m_httpRequest,
226+
*static_cast<IdentityT*>(m_requestContext.m_awsIdentity.get()),
227+
m_requestContext.m_authSchemeOption.signerProperties()));
180228
}
181229
};
182230

@@ -236,11 +284,11 @@ namespace smithy
236284
}
237285
};
238286

239-
static
240-
SigningOutcome SignWithAuthScheme(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemesVariantT& authSchemesVariant,
241-
const AuthSchemeOption& targetAuthSchemeOption)
287+
static SigningOutcome SignWithAuthScheme(std::shared_ptr<HttpRequest> httpRequest,
288+
const AuthSchemesVariantT& authSchemesVariant,
289+
const client::AwsSmithyClientAsyncRequestContext& ctx)
242290
{
243-
SignerVisitor visitor(httpRequest, targetAuthSchemeOption);
291+
SignerVisitor visitor(httpRequest, ctx);
244292
AuthSchemesVariantT authSchemesVariantCopy(authSchemesVariant); // TODO: allow const visiting
245293
authSchemesVariantCopy.Visit(visitor);
246294

src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsCredentialIdentityImpl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ namespace smithy {
2525
}
2626

2727
inline Aws::Crt::Optional<Aws::String> AwsCredentialIdentity::accountId() const {
28-
return m_sessionToken;
28+
return m_accountId;
2929
}
3030
}

src/aws-cpp-sdk-core/include/smithy/identity/resolver/built-in/DefaultAwsCredentialIdentityResolver.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace smithy {
5050
legacyCreds.GetAWSSecretKey(),
5151
legacyCreds.GetSessionToken().empty()? Aws::Crt::Optional<Aws::String>() : legacyCreds.GetSessionToken(),
5252
legacyCreds.GetExpiration(),
53-
legacyCreds.GetAccountId().empty()? Aws::Crt::Optional<Aws::String>() : legacyCreds.GetSessionToken());
53+
legacyCreds.GetAccountId().empty()? Aws::Crt::Optional<Aws::String>() : legacyCreds.GetAccountId());
5454

5555
return ResolveIdentityFutureOutcome(std::move(smithyCreds));
5656
}

src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp

+27-1
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,35 @@ void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* c
223223
}
224224
pRequestCtx->m_authSchemeOption = std::move(authSchemeOptionOutcome.GetResultWithOwnership());
225225
assert(pRequestCtx->m_authSchemeOption.schemeId);
226+
227+
// resolve identity
228+
auto identityOutcome = this->ResolveIdentity(*pRequestCtx);
229+
if (!identityOutcome.IsSuccess())
230+
{
231+
pExecutor->Submit([identityOutcome, responseHandler]() mutable
232+
{
233+
responseHandler(std::move(identityOutcome));
234+
});
235+
return;
236+
}
237+
pRequestCtx->m_awsIdentity = std::move(identityOutcome.GetResultWithOwnership());
238+
239+
// get endpoint params from operation context
240+
const auto contextEndpointParameters = this->GetContextEndpointParameters(*pRequestCtx);
241+
if (!contextEndpointParameters.IsSuccess())
242+
{
243+
pExecutor->Submit([contextEndpointParameters, responseHandler]() mutable
244+
{
245+
responseHandler(std::move(contextEndpointParameters.GetError()));
246+
});
247+
return;
248+
}
249+
226250
Aws::Endpoint::EndpointParameters epParams = request ? request->GetEndpointContextParams() : Aws::Endpoint::EndpointParameters();
227251
const auto authSchemeEpParams = pRequestCtx->m_authSchemeOption.endpointParameters();
228252
epParams.insert(epParams.end(), authSchemeEpParams.begin(), authSchemeEpParams.end());
253+
const auto contextParams = contextEndpointParameters.GetResult();
254+
epParams.insert(epParams.end(), contextParams.begin(), contextParams.end());
229255
auto epResolutionOutcome = this->ResolveEndpoint(std::move(epParams), std::move(endpointCallback));
230256
if (!epResolutionOutcome.IsSuccess())
231257
{
@@ -348,7 +374,7 @@ void AwsSmithyClientBase::AttemptOneRequestAsync(std::shared_ptr<AwsSmithyClient
348374
};
349375

350376
SigningOutcome signingOutcome = TracingUtils::MakeCallWithTiming<SigningOutcome>([&]() -> SigningOutcome {
351-
return this->SignHttpRequest(pRequestCtx->m_httpRequest, pRequestCtx->m_authSchemeOption);
377+
return this->SignHttpRequest(pRequestCtx->m_httpRequest, *pRequestCtx);
352378
},
353379
TracingUtils::SMITHY_CLIENT_SIGNING_METRIC,
354380
*m_clientConfig->telemetryProvider->getMeter(this->GetServiceClientName(), {}),

0 commit comments

Comments
 (0)