Skip to content

Commit

Permalink
more placeholder changes
Browse files Browse the repository at this point in the history
  • Loading branch information
sbera87 committed Sep 5, 2024
1 parent 932b302 commit 0ccab92
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,52 @@
#pragma once

#include <smithy/identity/resolver/AwsIdentityResolverBase.h>

#include <smithy/identity/identity/AwsBearerTokenIdentity.h>
#include <aws/core/auth/bearer-token-provider/SSOBearerTokenProvider.h>
#include <aws/core/auth/bearer-token-provider/AWSBearerTokenProviderBase.h>


namespace smithy {

static const char SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] = "SSOBearerTokenProvider";
static const char BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] = "BearerTokenProvider";

class AwsBearerTokenIdentityResolver : public IdentityResolverBase<AwsBearerTokenIdentity> {
public:
using IdentityT = AwsBearerTokenIdentity;
virtual ~AwsBearerTokenIdentityResolver() = default;

ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override = 0;
explicit AwsBearerTokenIdentityResolver(const Aws::Vector<std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase> >& providerChain) : m_providerChainLegacy{providerChain}
{

}

ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override
{
for (auto&& bearerTokenProvider : m_providerChainLegacy)
{
if(!bearerTokenProvider) {
AWS_LOGSTREAM_FATAL(BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG,
"Unexpected nullptr in DefaultBearerTokenProviderChain::m_providerChain");
return Aws::Client::AWSError(Aws::Client::CoreErrors::INVALID_PARAMETER_VALUE, "", "Unexpected nullptr in BearerTokenProviderChain::m_providerChain", false);
}
auto bearerToken = bearerTokenProvider->GetAWSBearerToken();
if(!bearerToken.IsExpiredOrEmpty())
{
return ResolveIdentityFutureOutcome(std::move( Aws::MakeUnique<AwsBearerTokenIdentity>( BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG, bearerToken.GetToken(), bearerToken.GetExpiration())));
}
}
}
protected:
Aws::Vector<std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase> > m_providerChainLegacy;
};

class DefaultAwsBearerTokenIdentityResolver : public AwsBearerTokenIdentityResolver {
public:
using IdentityT = AwsBearerTokenIdentity;
virtual ~DefaultAwsBearerTokenIdentityResolver() = default;

DefaultAwsBearerTokenIdentityResolver() : AwsBearerTokenIdentityResolver({Aws::MakeShared<Aws::Auth::SSOBearerTokenProvider>(SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG)}){};

};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#pragma once

#include <smithy/identity/signer/AwsSignerBase.h>
#include <smithy/identity/identity/AwsCredentialIdentityBase.h>
#include <smithy/identity/identity/AwsBearerTokenIdentityBase.h>
#include <aws/core/auth/AWSCredentials.h>
#include <aws/crt/auth/Credentials.h>

Expand All @@ -18,202 +18,39 @@
#include <mutex>

namespace smithy {
static const char* UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
static const char v4AsymmetricLogTag[] = "AWSAuthSymmetricV4Signer";
static const char* USER_AGENT = "user-agent";
static const char* X_AMZN_TRACE_ID = "x-amzn-trace-id";
static const char AUTHORIZATION_HEADER[] = "authorization";


/**
* A smithy SigV4 signer wrapper on top of legacy SDK SigV4 signer
* TODO: refactor into own signer using smithy design
*/
class AwsSigV4aSigner : public AwsSignerBase<AwsCredentialIdentityBase> {
class BearerTokenSigner : public AwsSignerBase<AwsBearerTokenIdentityBase> {

public:
using SigV4aAuthSchemeParameters = smithy::DefaultAuthSchemeResolverParameters;
explicit AwsSigV4aSigner(const Aws::String& serviceName, const Aws::String& region)
: m_serviceName(serviceName), m_region(region)
//using SigV4aAuthSchemeParameters = smithy::DefaultAuthSchemeResolverParameters;
explicit BearerTokenSigner()
{
}

SigningFutureOutcome sign(std::shared_ptr<HttpRequest> httpRequest, const AwsCredentialIdentityBase& identity, SigningProperties properties) override
SigningFutureOutcome sign(std::shared_ptr<HttpRequest> httpRequest, const smithy::AwsBearerTokenIdentityBase& identity, SigningProperties properties) override
{

auto signPayloadIt = properties.find("SignPayload");
bool signPayload = signPayloadIt != properties.end() ? signPayloadIt->second.get<Aws::String>() == "true" : false;

assert(httpRequest);
assert(identity.expiration().has_value());

auto &request = *httpRequest;

auto crtCredentials = Aws::MakeShared<Aws::Crt::Auth::Credentials>(v4AsymmetricLogTag,
Aws::Crt::ByteCursorFromCString(identity.accessKeyId().c_str()),
Aws::Crt::ByteCursorFromCString(identity.secretAccessKey().c_str()),
Aws::Crt::ByteCursorFromCString((*identity.sessionToken()).c_str()),
(*identity.expiration()).Seconds());

Aws::Crt::Auth::AwsSigningConfig awsSigningConfig;

bool success = createAwsSigningConfig(crtCredentials, request, awsSigningConfig, signPayload);

if(!success)
if(Aws::Http::Scheme::HTTPS != httpRequest->GetUri().GetScheme())
{
AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "Failed to get Auth configuration");

return SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to get Auth configuration", false);
// Clients MUST always use TLS (https) or equivalent transport security
// when making requests with bearer tokens.
// https://datatracker.ietf.org/doc/html/rfc6750
AWS_LOGSTREAM_ERROR(LOGGING_TAG, "HTTPS scheme must be used with a bearer token authorization");
return SigningError(Aws::Client::CoreErrors::INVALID_PARAMETER_VALUE, "", "Failed to sign the request with bearer", false);
}

std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest = request.ToCrtHttpRequest();

auto sigv4HttpRequestSigner = Aws::MakeShared<Aws::Crt::Auth::Sigv4HttpRequestSigner>(v4AsymmetricLogTag);
//This is an async call, so we need to wait till we have received an outcome
Aws::String errorMessage;
bool processed = false;
//producer function
sigv4HttpRequestSigner->SignRequest(crtHttpRequest, awsSigningConfig,
[&request, &success, &errorMessage, &processed, this](const std::shared_ptr<Aws::Crt::Http::HttpRequest>& signedCrtHttpRequest, int errorCode) {
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [&]{ return !processed; });
success = (errorCode == AWS_ERROR_SUCCESS);
if (success)
{
if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders)
{
for (size_t i = 0; i < signedCrtHttpRequest->GetHeaderCount(); i++)
{
Aws::Crt::Optional<Aws::Crt::Http::HttpHeader> httpHeader = signedCrtHttpRequest->GetHeader(i);
request.SetHeaderValue(Aws::String(reinterpret_cast<const char*>(httpHeader->name.ptr), httpHeader->name.len),
Aws::String(reinterpret_cast<const char*>(httpHeader->value.ptr), httpHeader->value.len));
}
}
else if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams)
{
Aws::Http::URI newPath(reinterpret_cast<const char*>(signedCrtHttpRequest->GetPath()->ptr));
request.GetUri().SetQueryString(newPath.GetQueryString());
}
else
{
errorMessage = "No action to take when signature type is neither \"HttpRequestViaHeaders\" nor \"HttpRequestViaQueryParams\"";
AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, errorMessage);
success = false;
}
}
else
{
Aws::OStringStream errStream;
errStream << "Encountered internal error during signing process with AWS signature version 4 (Asymmetric):" << aws_error_str(errorCode);
errorMessage = errStream.str();
AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, errorMessage);
}

processed = true;
m_cv.notify_all();
}
);

//consumer
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [&]{ return processed; });

}
httpRequest->SetHeaderValue(AUTHORIZATION_HEADER, "Bearer " + identity.token());


return success? SigningFutureOutcome(std::move(httpRequest)) : SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to sign the request with sigv4", false);
return SigningFutureOutcome(std::move(httpRequest));
}



virtual ~AwsSigV4aSigner() {};
virtual ~BearerTokenSigner() {};
protected:

bool createAwsSigningConfig(
std::shared_ptr<Aws::Crt::Auth::Credentials>& crtCredentials,
const Aws::Http::HttpRequest& request,
Aws::Crt::Auth::AwsSigningConfig& awsSigningConfig,
bool signBody) const
{
awsSigningConfig.SetSigningAlgorithm(static_cast<Aws::Crt::Auth::SigningAlgorithm>(Aws::Auth::AWSSigningAlgorithm::ASYMMETRIC_SIGV4));
awsSigningConfig.SetSignatureType(m_signatureType);
awsSigningConfig.SetRegion(m_region.c_str());
awsSigningConfig.SetService(m_region.c_str());
awsSigningConfig.SetSigningTimepoint(GetSigningTimestamp().UnderlyingTimestamp());
awsSigningConfig.SetUseDoubleUriEncode(m_urlEscape);
awsSigningConfig.SetShouldNormalizeUriPath(true);
awsSigningConfig.SetOmitSessionToken(false);
awsSigningConfig.SetShouldSignHeaderUserData(reinterpret_cast<void*>(const_cast<Aws::Set<Aws::String>*>(&m_unsignedHeaders)));
awsSigningConfig.SetShouldSignHeaderCallback([](const Aws::Crt::ByteCursor *name, void *user_data) {
Aws::Set<Aws::String>* unsignedHeaders = static_cast<Aws::Set<Aws::String>*>(user_data);
Aws::String headerKey(reinterpret_cast<const char*>(name->ptr), name->len);
return unsignedHeaders->find(Aws::Utils::StringUtils::ToLower(headerKey.c_str())) == unsignedHeaders->cend();
});
if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders)
{
Aws::String payloadHash(UNSIGNED_PAYLOAD);
if(signBody || request.GetUri().GetScheme() != Aws::Http::Scheme::HTTPS)
{
if (!request.GetContentBody())
{
AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty.");
payloadHash = EMPTY_STRING_SHA256;
}
else
{
// The hash will be calculated from the payload during signing.
payloadHash = {};
}
}
else
{
AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody
<< " http scheme=" << Aws::Http::SchemeMapper::ToString(request.GetUri().GetScheme()));
}
awsSigningConfig.SetSignedBodyValue(payloadHash.c_str());
awsSigningConfig.SetSignedBodyHeader(m_includeSha256HashHeader ? Aws::Crt::Auth::SignedBodyHeaderType::XAmzContentSha256 : Aws::Crt::Auth::SignedBodyHeaderType::None);
}
else if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams)
{
if (ServiceRequireUnsignedPayload(m_serviceName))
{
awsSigningConfig.SetSignedBodyValue(UNSIGNED_PAYLOAD);
}
else
{
awsSigningConfig.SetSignedBodyValue(EMPTY_STRING_SHA256);
}
}
else
{
AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "The signature type should be either \"HttpRequestViaHeaders\" or \"HttpRequestViaQueryParams\"");
return false;
}
awsSigningConfig.SetExpirationInSeconds(static_cast<uint64_t>(m_expirationTimeInSeconds));
awsSigningConfig.SetCredentials(crtCredentials);
return true;
}


bool ServiceRequireUnsignedPayload(const Aws::String& serviceName) const
{
// S3 uses a magic string (instead of the empty string) for its body hash for presigned URLs as outlined here:
// https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
// this is true for PUT, POST, GET, DELETE and HEAD operations.
// However, other services (for example RDS) implement the specification as outlined here:
// https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
// which states that body-less requests should use the empty-string SHA256 hash.
return "s3" == serviceName || "s3-object-lambda" == serviceName;
}

Aws::String m_serviceName;
Aws::String m_region;
//params that can be exposed later
long long m_expirationTimeInSeconds{0};
const bool m_includeSha256HashHeader{true};
const bool m_urlEscape{true};
const Aws::Set<Aws::String> m_unsignedHeaders{USER_AGENT,X_AMZN_TRACE_ID};
const Aws::Crt::Auth::SignatureType m_signatureType{Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams};
std::condition_variable m_cv;
std::mutex m_mutex;
};
}

0 comments on commit 0ccab92

Please sign in to comment.