Skip to content

Commit

Permalink
presigner support
Browse files Browse the repository at this point in the history
  • Loading branch information
sbera87 committed Jan 12, 2025
1 parent 24104d4 commit 19b7945
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 81 deletions.
8 changes: 4 additions & 4 deletions generated/src/aws-cpp-sdk-dsql/source/DSQLClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,23 +450,23 @@ Aws::Utils::Outcome<String, DSQLError> DSQLClient::GenerateDBConnectAuthToken(co
{
if (hostname.empty() || region.empty())
{
return AWSError<CoreErrors>{CoreErrors::INVALID_PARAMETER_VALUE, "InavlidParameterValue", "all argments must be non-empty", false};
return Aws::Client::AWSError<CoreErrors>{CoreErrors::INVALID_PARAMETER_VALUE, "InavlidParameterValue", "all argments must be non-empty", false};
}
URI uri(hostname);
uri.AddQueryStringParameter("Action", "DbConnect");
auto url = GeneratePresignedUrl(uri, Aws::Http::HttpMethod::HTTP_GET, region.c_str(), GetServiceName(), expiresIn);
auto url = GeneratePresignedUrl(uri, Aws::Http::HttpMethod::HTTP_GET, region.c_str(), GetServiceName(), expiresIn, {}, nullptr);
Aws::Utils::StringUtils::Replace(url, "http://", "");
return url;
}
Aws::Utils::Outcome<String, DSQLError> DSQLClient::GenerateDBConnectAdminAuthToken(const Aws::String& hostname, const Aws::String& region, long long expiresIn)
{
if (hostname.empty() || region.empty())
{
return AWSError<CoreErrors>{CoreErrors::INVALID_PARAMETER_VALUE, "InavlidParameterValue", "all argments must be non-empty", false};
return Aws::Client::AWSError<CoreErrors>{CoreErrors::INVALID_PARAMETER_VALUE, "InavlidParameterValue", "all argments must be non-empty", false};
}
URI uri(hostname);
uri.AddQueryStringParameter("Action", "DbConnectAdmin");
auto url = GeneratePresignedUrl(uri, Aws::Http::HttpMethod::HTTP_GET, region.c_str(), GetServiceName(), expiresIn);
auto url = GeneratePresignedUrl(uri, Aws::Http::HttpMethod::HTTP_GET, region.c_str(), GetServiceName(), expiresIn, {}, nullptr);
Aws::Utils::StringUtils::Replace(url, "http://", "");
return url;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ namespace Aws
*/
bool PresignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, long long expirationInSeconds = 0) const override;

/**
* Takes a request and signs the URI based on request, region, servicename, expiration and credentials.
* The URI can then be used in a normal HTTP call until expiration.
* Uses AWS Auth V4 signing method with SHA256 HMAC algorithm.
* expirationInSeconds defaults to 0 which provides a URI good for 7 days.
* Using m_region by default if parameter region is nullptr.
* Using m_serviceName by default if parameter serviceName is nullptr.
*/
bool PresignRequest(Aws::Http::HttpRequest& request, const Aws::Auth::AWSCredentials& creds, const char* region, const char* serviceName, long long expirationInSeconds = 0) const;


virtual Aws::Auth::AWSCredentials GetCredentials(const std::shared_ptr<Aws::Http::ServiceSpecificParameters> &serviceSpecificParameters) const;

Aws::String GetServiceName() const { return m_serviceName; }
Expand Down
29 changes: 29 additions & 0 deletions src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include <smithy/identity/auth/AuthSchemeResolverBase.h>
#include <smithy/identity/identity/AwsIdentity.h>
#include <smithy/tracing/TelemetryProvider.h>
#include <aws/core/http/HttpClientFactory.h>


namespace smithy {
namespace client
Expand Down Expand Up @@ -128,6 +130,33 @@ namespace client
return m_serializer->Deserialize(std::move(httpResponseOutcome), GetServiceClientName(), requestName);
}


Aws::String GeneratePresignedUrl(const Aws::Http::URI& uri,
Aws::Http::HttpMethod method,
const Aws::String& region,
const Aws::String& serviceName,
long long expirationInSeconds,
const Aws::Http::HeaderValueCollection& customizedHeaders,
const std::shared_ptr<Aws::Http::ServiceSpecificParameters> serviceSpecificParameters) const
{
std::shared_ptr<HttpRequest> request = CreateHttpRequest(uri, method, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
request->SetServiceSpecificParameters(serviceSpecificParameters);
for (const auto& it: customizedHeaders)
{
request->SetHeaderValue(it.first.c_str(), it.second);
}
AwsSmithyClientAsyncRequestContext ctx;
auto authSchemeOptionOutcome = SelectAuthSchemeOption( ctx);
auto authSchemeOption = std::move(authSchemeOptionOutcome.GetResultWithOwnership());
if (AwsClientRequestSigning<AuthSchemesVariantT>::PreSignRequest(request, authSchemeOption, m_authSchemes, region, serviceName, expirationInSeconds).IsSuccess())
{
return request->GetURIString();
}
return {};
}



protected:

ServiceClientConfigurationT& m_clientConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace smithy

Aws::String m_invocationId;
Aws::Http::HttpMethod m_method;
const Aws::AmazonWebServiceRequest* m_pRequest; // optional
const Aws::AmazonWebServiceRequest* m_pRequest{nullptr}; // optional

RequestInfo m_requestInfo;
Aws::String m_requestName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,37 @@ namespace smithy
return SignWithAuthScheme(std::move(HTTPRequest), authScheme, authSchemeOption);
}

static SigningOutcome PreSignRequest(std::shared_ptr<HttpRequest> httpRequest,
const AuthSchemeOption& authSchemeOption,
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes,
const Aws::String& region,
const Aws::String& serviceName,
long long expirationTimeInSeconds)
{

auto authSchemeIt = authSchemes.find(authSchemeOption.schemeId);
if (authSchemeIt == authSchemes.end())
{
assert(!"Auth scheme has not been found for a given auth option!");
return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE,
"",
"Requested AuthSchemeOption was not found within client Auth Schemes",
false/*retryable*/));
}

const AuthSchemesVariantT& authScheme = authSchemeIt->second;

PreSignerVisitor visitor(httpRequest, authSchemeOption, region, serviceName, expirationTimeInSeconds);
AuthSchemesVariantT authSchemesVariantCopy(authScheme);
authSchemesVariantCopy.Visit(visitor);

if (!visitor.result) {
return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, "", "Failed to sign with an unknown error",
false /*retryable*/));
}
return std::move(*visitor.result);
}

static bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption,
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes)
{
Expand Down Expand Up @@ -124,6 +155,63 @@ namespace smithy
}
};

//for presigning, region and expiration can be passed in runtime
struct PreSignerVisitor {
explicit PreSignerVisitor(std::shared_ptr<HttpRequest> httpRequest,
const AuthSchemeOption& targetAuthSchemeOption,
const Aws::String& region,
const Aws::String& serviceName,
long long expirationTimeInSeconds)
: m_httpRequest(std::move(httpRequest)),
m_targetAuthSchemeOption(targetAuthSchemeOption) ,
m_region(region),
m_serviceName(serviceName),
m_expirationTimeInSeconds(expirationTimeInSeconds)
{}

const std::shared_ptr<HttpRequest> m_httpRequest;
const AuthSchemeOption& m_targetAuthSchemeOption;
const Aws::String& m_region;
const Aws::String& m_serviceName;
const long long m_expirationTimeInSeconds;

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

template <typename AuthSchemeAlternativeT>
void operator()(AuthSchemeAlternativeT& authScheme) {
// Auth Scheme Variant alternative contains the requested auth option
assert(strcmp(authScheme.schemeId, m_targetAuthSchemeOption.schemeId) == 0);

using IdentityT = typename std::remove_reference<decltype(authScheme)>::type::IdentityT;
using IdentityResolver = IdentityResolverBase<IdentityT>;
using Signer = AwsSignerBase<IdentityT>;

std::shared_ptr<IdentityResolver> identityResolver = authScheme.identityResolver();
if (!identityResolver) {
result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, "",
"Auth scheme provided a nullptr identityResolver", false /*retryable*/));
return;
}

auto identityResult =
identityResolver->getIdentity(m_targetAuthSchemeOption.identityProperties(), m_targetAuthSchemeOption.identityProperties());

if (!identityResult.IsSuccess()) {
result.emplace(identityResult.GetError());
return;
}
auto identity = std::move(identityResult.GetResultWithOwnership());

std::shared_ptr<Signer> signer = authScheme.signer();
if (!signer) {
result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, "", "Auth scheme provided a nullptr signer",
false /*retryable*/));
return;
}
result.emplace(signer->presign(m_httpRequest, *identity, m_targetAuthSchemeOption.signerProperties(), m_region, m_serviceName, m_expirationTimeInSeconds));
}
};

static SigningOutcome SignWithAuthScheme(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemesVariantT& authSchemesVariant,
const AuthSchemeOption& targetAuthSchemeOption) {
SignerVisitor visitor(httpRequest, targetAuthSchemeOption);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ namespace smithy {

// signer may copy the original httpRequest or create a new one
virtual SigningFutureOutcome sign(std::shared_ptr<HttpRequest> httpRequest, const IdentityT& identity, SigningProperties properties) = 0;
virtual SigningFutureOutcome presign(std::shared_ptr<HttpRequest> httpRequest, const IdentityT& identity, SigningProperties properties, const Aws::String& region, const Aws::String& serviceName, long long expirationTimeInSeconds) = 0;

virtual ~AwsSignerBase() {};
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ class BearerTokenSigner : public AwsSignerBase<AwsBearerTokenIdentityBase>
return SigningFutureOutcome(std::move(httpRequest));
}

SigningFutureOutcome presign(std::shared_ptr<HttpRequest> , const IdentityT& , SigningProperties , const Aws::String& , const Aws::String& , long long ) override
{
return SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, "", "presign not supported",
false /*retryable*/);
}


virtual ~BearerTokenSigner(){};

protected:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ namespace smithy {
return SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to sign the request with sigv4", false);
}

SigningFutureOutcome presign(std::shared_ptr<HttpRequest> httpRequest, const AwsCredentialIdentityBase& identity, SigningProperties properties, const Aws::String& region, const Aws::String& serviceName, long long expirationTimeInSeconds) override
{
AWS_UNREFERENCED_PARAM(properties);
const auto legacyCreds = [&identity]() -> Aws::Auth::AWSCredentials {
if(identity.sessionToken().has_value() && identity.expiration().has_value())
{
return {identity.accessKeyId(), identity.secretAccessKey(), *identity.sessionToken(), *identity.expiration()};
}
if(identity.sessionToken().has_value())
{
return {identity.accessKeyId(), identity.secretAccessKey(), *identity.sessionToken()};
}
return {identity.accessKeyId(), identity.secretAccessKey()};
}();
auto result = legacySigner.PresignRequest(*httpRequest, legacyCreds, region.c_str(), serviceName.c_str(), expirationTimeInSeconds);

return (result ? SigningFutureOutcome(std::move(httpRequest)) :
SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, "", "presign failed",
false /*retryable*/));
}


virtual ~AwsSigV4Signer() {};
protected:
Aws::String m_serviceName;
Expand Down
Loading

0 comments on commit 19b7945

Please sign in to comment.