mirror of https://github.com/grpc/grpc.git
parent
850f0c7c86
commit
bedaeb5265
20 changed files with 680 additions and 0 deletions
@ -0,0 +1,208 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2020 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/security/credentials/external/aws_request_signer.h" |
||||||
|
|
||||||
|
#include "absl/strings/ascii.h" |
||||||
|
#include "absl/strings/escaping.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
#include "absl/strings/str_join.h" |
||||||
|
#include "absl/strings/str_split.h" |
||||||
|
#include "absl/time/clock.h" |
||||||
|
#include "absl/time/time.h" |
||||||
|
|
||||||
|
#include <openssl/hmac.h> |
||||||
|
#include <openssl/sha.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
const char kAlgorithm[] = "AWS4-HMAC-SHA256"; |
||||||
|
const char kDateFormat[] = "%a, %d %b %E4Y %H:%M:%S %Z"; |
||||||
|
const char kXAmzDateFormat[] = "%Y%m%dT%H%M%SZ"; |
||||||
|
|
||||||
|
void SHA256(const std::string& str, unsigned char out[SHA256_DIGEST_LENGTH]) { |
||||||
|
SHA256_CTX sha256; |
||||||
|
SHA256_Init(&sha256); |
||||||
|
SHA256_Update(&sha256, str.c_str(), str.size()); |
||||||
|
SHA256_Final(out, &sha256); |
||||||
|
} |
||||||
|
|
||||||
|
std::string SHA256Hex(const std::string& str) { |
||||||
|
unsigned char hash[SHA256_DIGEST_LENGTH]; |
||||||
|
SHA256(str, hash); |
||||||
|
std::string hash_str(reinterpret_cast<char const*>(hash), |
||||||
|
SHA256_DIGEST_LENGTH); |
||||||
|
return absl::BytesToHexString(hash_str); |
||||||
|
} |
||||||
|
|
||||||
|
std::string HMAC(const std::string& key, const std::string& msg) { |
||||||
|
unsigned int len; |
||||||
|
unsigned char digest[EVP_MAX_MD_SIZE]; |
||||||
|
HMAC(EVP_sha256(), key.c_str(), key.length(), |
||||||
|
(const unsigned char*)msg.c_str(), msg.length(), digest, &len); |
||||||
|
return std::string(digest, digest + len); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AwsRequestSigner::AwsRequestSigner( |
||||||
|
std::string access_key_id, std::string secret_access_key, std::string token, |
||||||
|
std::string method, std::string url, std::string region, |
||||||
|
std::string request_payload, |
||||||
|
std::map<std::string, std::string> additional_headers, grpc_error** error) |
||||||
|
: access_key_id_(std::move(access_key_id)), |
||||||
|
secret_access_key_(std::move(secret_access_key)), |
||||||
|
token_(std::move(token)), |
||||||
|
method_(std::move(method)), |
||||||
|
region_(std::move(region)), |
||||||
|
request_payload_(std::move(request_payload)), |
||||||
|
additional_headers_(std::move(additional_headers)) { |
||||||
|
auto amz_date_it = additional_headers_.find("x-amz-date"); |
||||||
|
auto date_it = additional_headers_.find("date"); |
||||||
|
if (amz_date_it != additional_headers_.end() && |
||||||
|
date_it != additional_headers_.end()) { |
||||||
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||||
|
"Only one of {date, x-amz-date} can be specified, not both."); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (amz_date_it != additional_headers_.end()) { |
||||||
|
static_request_date_ = amz_date_it->second; |
||||||
|
} else if (date_it != additional_headers_.end()) { |
||||||
|
absl::Time request_date; |
||||||
|
std::string err_str; |
||||||
|
if (!absl::ParseTime(kDateFormat, date_it->second, &request_date, |
||||||
|
&err_str)) { |
||||||
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(err_str.c_str()); |
||||||
|
return; |
||||||
|
} |
||||||
|
static_request_date_ = |
||||||
|
absl::FormatTime(kXAmzDateFormat, request_date, absl::UTCTimeZone()); |
||||||
|
} |
||||||
|
url_ = grpc_uri_parse(url, false); |
||||||
|
if (url_ == nullptr) { |
||||||
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid Aws request url."); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
AwsRequestSigner::~AwsRequestSigner() { grpc_uri_destroy(url_); } |
||||||
|
|
||||||
|
std::map<std::string, std::string> AwsRequestSigner::GetSignedRequestHeaders() { |
||||||
|
std::string request_date_full; |
||||||
|
if (!static_request_date_.empty()) { |
||||||
|
if (!request_headers_.empty()) { |
||||||
|
return request_headers_; |
||||||
|
} |
||||||
|
request_date_full = static_request_date_; |
||||||
|
} else { |
||||||
|
absl::Time request_date = absl::Now(); |
||||||
|
request_date_full = |
||||||
|
absl::FormatTime(kXAmzDateFormat, request_date, absl::UTCTimeZone()); |
||||||
|
} |
||||||
|
std::string request_date_short = request_date_full.substr(0, 8); |
||||||
|
// TASK 1: Create a canonical request for Signature Version 4
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||||
|
std::vector<absl::string_view> canonical_request_vector; |
||||||
|
// 1. HTTPRequestMethod
|
||||||
|
canonical_request_vector.emplace_back(method_); |
||||||
|
canonical_request_vector.emplace_back("\n"); |
||||||
|
// 2. CanonicalURI
|
||||||
|
|
||||||
|
canonical_request_vector.emplace_back(*url_->path == '\0' ? "/" : url_->path); |
||||||
|
canonical_request_vector.emplace_back("\n"); |
||||||
|
// 3. CanonicalQueryString
|
||||||
|
canonical_request_vector.emplace_back(url_->query); |
||||||
|
canonical_request_vector.emplace_back("\n"); |
||||||
|
// 4. CanonicalHeaders
|
||||||
|
if (request_headers_.empty()) { |
||||||
|
request_headers_.insert({"host", url_->authority}); |
||||||
|
if (!token_.empty()) { |
||||||
|
request_headers_.insert({"x-amz-security-token", token_}); |
||||||
|
} |
||||||
|
for (const auto& header : additional_headers_) { |
||||||
|
request_headers_.insert( |
||||||
|
{absl::AsciiStrToLower(header.first), header.second}); |
||||||
|
} |
||||||
|
} |
||||||
|
if (additional_headers_.find("date") == additional_headers_.end()) { |
||||||
|
request_headers_["x-amz-date"] = request_date_full; |
||||||
|
} |
||||||
|
std::vector<absl::string_view> canonical_headers_vector; |
||||||
|
for (const auto& header : request_headers_) { |
||||||
|
canonical_headers_vector.emplace_back(header.first); |
||||||
|
canonical_headers_vector.emplace_back(":"); |
||||||
|
canonical_headers_vector.emplace_back(header.second); |
||||||
|
canonical_headers_vector.emplace_back("\n"); |
||||||
|
} |
||||||
|
std::string canonical_headers = absl::StrJoin(canonical_headers_vector, ""); |
||||||
|
canonical_request_vector.emplace_back(canonical_headers); |
||||||
|
canonical_request_vector.emplace_back("\n"); |
||||||
|
// 5. SignedHeaders
|
||||||
|
std::vector<absl::string_view> signed_headers_vector; |
||||||
|
for (const auto& header : request_headers_) { |
||||||
|
signed_headers_vector.emplace_back(header.first); |
||||||
|
} |
||||||
|
std::string signed_headers = absl::StrJoin(signed_headers_vector, ";"); |
||||||
|
canonical_request_vector.emplace_back(signed_headers); |
||||||
|
canonical_request_vector.emplace_back("\n"); |
||||||
|
// 6. RequestPayload
|
||||||
|
std::string hashed_request_payload = SHA256Hex(request_payload_); |
||||||
|
canonical_request_vector.emplace_back(hashed_request_payload); |
||||||
|
std::string canonical_request = absl::StrJoin(canonical_request_vector, ""); |
||||||
|
// TASK 2: Create a string to sign for Signature Version 4
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||||
|
std::vector<absl::string_view> string_to_sign_vector; |
||||||
|
// 1. Algorithm
|
||||||
|
string_to_sign_vector.emplace_back("AWS4-HMAC-SHA256"); |
||||||
|
string_to_sign_vector.emplace_back("\n"); |
||||||
|
// 2. RequestDateTime
|
||||||
|
string_to_sign_vector.emplace_back(request_date_full); |
||||||
|
string_to_sign_vector.emplace_back("\n"); |
||||||
|
// 3. CredentialScope
|
||||||
|
std::pair<absl::string_view, absl::string_view> host_parts = |
||||||
|
absl::StrSplit(url_->authority, absl::MaxSplits('.', 1)); |
||||||
|
std::string service_name(host_parts.first); |
||||||
|
std::string credential_scope = absl::StrFormat( |
||||||
|
"%s/%s/%s/aws4_request", request_date_short, region_, service_name); |
||||||
|
string_to_sign_vector.emplace_back(credential_scope); |
||||||
|
string_to_sign_vector.emplace_back("\n"); |
||||||
|
// 4. HashedCanonicalRequest
|
||||||
|
std::string hashed_canonical_request = SHA256Hex(canonical_request); |
||||||
|
string_to_sign_vector.emplace_back(hashed_canonical_request); |
||||||
|
std::string string_to_sign = absl::StrJoin(string_to_sign_vector, ""); |
||||||
|
// TASK 3: Task 3: Calculate the signature for AWS Signature Version 4
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
||||||
|
// 1. Derive your signing key.
|
||||||
|
std::string date = HMAC("AWS4" + secret_access_key_, request_date_short); |
||||||
|
std::string region = HMAC(date, region_); |
||||||
|
std::string service = HMAC(region, service_name); |
||||||
|
std::string signing = HMAC(service, "aws4_request"); |
||||||
|
// 2. Calculate the signature.
|
||||||
|
std::string signature_str = HMAC(signing, string_to_sign); |
||||||
|
std::string signature = absl::BytesToHexString(signature_str); |
||||||
|
// TASK 4: Add the signature to the HTTP request
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
|
||||||
|
std::string authorization_header = absl::StrFormat( |
||||||
|
"%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", kAlgorithm, |
||||||
|
access_key_id_, credential_scope, signed_headers, signature); |
||||||
|
request_headers_["Authorization"] = authorization_header; |
||||||
|
return request_headers_; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
@ -0,0 +1,73 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2020 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_AWS_REQUEST_SIGNER_H |
||||||
|
#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_AWS_REQUEST_SIGNER_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "src/core/lib/iomgr/error.h" |
||||||
|
#include "src/core/lib/uri/uri_parser.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
// Implements an AWS API request signer based on the AWS Signature Version 4
|
||||||
|
// signing process.
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
||||||
|
// To retrieve the subject token in AwsExternalAccountCredentials, we need to
|
||||||
|
// sign an AWS request server and use the signed request as the subject token.
|
||||||
|
// This class is a utility to sign an AWS request.
|
||||||
|
class AwsRequestSigner { |
||||||
|
public: |
||||||
|
// Construct a signer with the necessary information to sign a request.
|
||||||
|
// `access_key_id`, `secret_access_key` and `token` are the AWS credentials
|
||||||
|
// required for signing. `method` and `url` are the HTTP method and url of the
|
||||||
|
// request. `region` is the region of the AWS environment. `request_payload`
|
||||||
|
// is the payload of the HTTP request. `additional_headers` are additional
|
||||||
|
// headers to be inject into the request.
|
||||||
|
AwsRequestSigner(std::string access_key_id, std::string secret_access_key, |
||||||
|
std::string token, std::string method, std::string url, |
||||||
|
std::string region, std::string request_payload, |
||||||
|
std::map<std::string, std::string> additional_headers, |
||||||
|
grpc_error** error); |
||||||
|
~AwsRequestSigner(); |
||||||
|
|
||||||
|
// This method triggers the signing process then returns the headers of the
|
||||||
|
// signed request as a map. In case there is an error, the input `error`
|
||||||
|
// parameter will be updated and an empty map will be returned if there is
|
||||||
|
// error.
|
||||||
|
std::map<std::string, std::string> GetSignedRequestHeaders(); |
||||||
|
|
||||||
|
private: |
||||||
|
std::string access_key_id_; |
||||||
|
std::string secret_access_key_; |
||||||
|
std::string token_; |
||||||
|
std::string method_; |
||||||
|
grpc_uri* url_ = nullptr; |
||||||
|
std::string region_; |
||||||
|
std::string request_payload_; |
||||||
|
std::map<std::string, std::string> additional_headers_; |
||||||
|
|
||||||
|
std::string static_request_date_; |
||||||
|
std::map<std::string, std::string> request_headers_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_LIB_SECURITY_CREDENTIALS_EXTERNAL_AWS_REQUEST_SIGNER_H
|
@ -0,0 +1,283 @@ |
|||||||
|
//
|
||||||
|
// Copyright 2020 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "src/core/lib/security/credentials/external/aws_request_signer.h" |
||||||
|
|
||||||
|
#include <gmock/gmock.h> |
||||||
|
#include <grpc/grpc_security.h> |
||||||
|
|
||||||
|
#include "test/core/util/test_config.h" |
||||||
|
|
||||||
|
namespace testing { |
||||||
|
|
||||||
|
namespace { |
||||||
|
// Test cases of Aws endpoints that the aws-sourced credentials will depend
|
||||||
|
// on.
|
||||||
|
const char* kAmzTestAccessKeyId = "ASIARD4OQDT6A77FR3CL"; |
||||||
|
const char* kAmzTestSecretAccessKey = |
||||||
|
"Y8AfSaucF37G4PpvfguKZ3/l7Id4uocLXxX0+VTx"; |
||||||
|
const char* kAmzTestToken = |
||||||
|
"IQoJb3JpZ2luX2VjEIz//////////wEaCXVzLWVhc3QtMiJGMEQCIH7MHX/Oy/" |
||||||
|
"OB8OlLQa9GrqU1B914+iMikqWQW7vPCKlgAiA/" |
||||||
|
"Lsv8Jcafn14owfxXn95FURZNKaaphj0ykpmS+Ki+" |
||||||
|
"CSq0AwhlEAAaDDA3NzA3MTM5MTk5NiIMx9sAeP1ovlMTMKLjKpEDwuJQg41/" |
||||||
|
"QUKx0laTZYjPlQvjwSqS3OB9P1KAXPWSLkliVMMqaHqelvMF/WO/" |
||||||
|
"glv3KwuTfQsavRNs3v5pcSEm4SPO3l7mCs7KrQUHwGP0neZhIKxEXy+Ls//1C/" |
||||||
|
"Bqt53NL+LSbaGv6RPHaX82laz2qElphg95aVLdYgIFY6JWV5fzyjgnhz0DQmy62/" |
||||||
|
"Vi8pNcM2/" |
||||||
|
"VnxeCQ8CC8dRDSt52ry2v+nc77vstuI9xV5k8mPtnaPoJDRANh0bjwY5Sdwkbp+" |
||||||
|
"mGRUJBAQRlNgHUJusefXQgVKBCiyJY4w3Csd8Bgj9IyDV+" |
||||||
|
"Azuy1jQqfFZWgP68LSz5bURyIjlWDQunO82stZ0BgplKKAa/" |
||||||
|
"KJHBPCp8Qi6i99uy7qh76FQAqgVTsnDuU6fGpHDcsDSGoCls2HgZjZFPeOj8mmRhFk1Xqvkb" |
||||||
|
"juz8V1cJk54d3gIJvQt8gD2D6yJQZecnuGWd5K2e2HohvCc8Fc9kBl1300nUJPV+k4tr/" |
||||||
|
"A5R/0QfEKOZL1/" |
||||||
|
"k5lf1g9CREnrM8LVkGxCgdYMxLQow1uTL+QU67AHRRSp5PhhGX4Rek+" |
||||||
|
"01vdYSnJCMaPhSEgcLqDlQkhk6MPsyT91QMXcWmyO+cAZwUPwnRamFepuP4K8k2KVXs/" |
||||||
|
"LIJHLELwAZ0ekyaS7CptgOqS7uaSTFG3U+vzFZLEnGvWQ7y9IPNQZ+" |
||||||
|
"Dffgh4p3vF4J68y9049sI6Sr5d5wbKkcbm8hdCDHZcv4lnqohquPirLiFQ3q7B17V9krMPu3" |
||||||
|
"mz1cg4Ekgcrn/" |
||||||
|
"E09NTsxAqD8NcZ7C7ECom9r+" |
||||||
|
"X3zkDOxaajW6hu3Az8hGlyylDaMiFfRbBJpTIlxp7jfa7CxikNgNtEKLH9iCzvuSg2vhA=="; |
||||||
|
const char* kAmzTestDate = "20200811T065522Z"; |
||||||
|
|
||||||
|
// Test cases derived from the Aws signature v4 test suite.
|
||||||
|
// https://github.com/boto/botocore/tree/master/tests/unit/auth/aws4_testsuite
|
||||||
|
const char* kBotoTestAccessKeyId = "AKIDEXAMPLE"; |
||||||
|
const char* kBotoTestSecretAccessKey = |
||||||
|
"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"; |
||||||
|
const char* kBotoTestToken = ""; |
||||||
|
const char* kBotoTestDate = "Mon, 09 Sep 2011 23:36:00 GMT"; |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// AWS official example from the developer doc.
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
||||||
|
TEST(GrpcAwsRequestSignerTest, AWSOfficialExample) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
"AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "", "GET", |
||||||
|
"https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08", |
||||||
|
"us-east-1", "", |
||||||
|
{{"content-type", "application/x-www-form-urlencoded; charset=utf-8"}, |
||||||
|
{"x-amz-date", "20150830T123600Z"}}, |
||||||
|
&error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, " |
||||||
|
"SignedHeaders=content-type;host;x-amz-date, " |
||||||
|
"Signature=" |
||||||
|
"5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, GetDescribeRegions) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kAmzTestAccessKeyId, kAmzTestSecretAccessKey, kAmzTestToken, "GET", |
||||||
|
"https://" |
||||||
|
"ec2.us-east-2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15", |
||||||
|
"us-east-2", "", {{"x-amz-date", kAmzTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ( |
||||||
|
signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=ASIARD4OQDT6A77FR3CL/20200811/us-east-2/ec2/aws4_request, " |
||||||
|
"SignedHeaders=host;x-amz-date;x-amz-security-token, " |
||||||
|
"Signature=" |
||||||
|
"631ea80cddfaa545fdadb120dc92c9f18166e38a5c47b50fab9fce476e022855"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, PostGetCallerIdentity) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kAmzTestAccessKeyId, kAmzTestSecretAccessKey, kAmzTestToken, "POST", |
||||||
|
"https://" |
||||||
|
"sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", |
||||||
|
"us-east-2", "", {{"x-amz-date", kAmzTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ( |
||||||
|
signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=ASIARD4OQDT6A77FR3CL/20200811/us-east-2/sts/aws4_request, " |
||||||
|
"SignedHeaders=host;x-amz-date;x-amz-security-token, " |
||||||
|
"Signature=" |
||||||
|
"73452984e4a880ffdc5c392355733ec3f5ba310d5e0609a89244440cadfe7a7a"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, PostGetCallerIdentityNoToken) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kAmzTestAccessKeyId, kAmzTestSecretAccessKey, "", "POST", |
||||||
|
"https://" |
||||||
|
"sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", |
||||||
|
"us-east-2", "", {{"x-amz-date", kAmzTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ( |
||||||
|
signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=ASIARD4OQDT6A77FR3CL/20200811/us-east-2/sts/aws4_request, " |
||||||
|
"SignedHeaders=host;x-amz-date, " |
||||||
|
"Signature=" |
||||||
|
"d095ba304919cd0d5570ba8a3787884ee78b860f268ed040ba23831d55536d56"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, GetHost) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer(kBotoTestAccessKeyId, |
||||||
|
kBotoTestSecretAccessKey, kBotoTestToken, |
||||||
|
"GET", "https://host.foo.com", "us-east-1", |
||||||
|
"", {{"date", kBotoTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host, " |
||||||
|
"Signature=" |
||||||
|
"b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, GetHostDuplicateQueryParam) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "GET", |
||||||
|
"https://host.foo.com/?foo=Zoo&foo=aha", "us-east-1", "", |
||||||
|
{{"date", kBotoTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host, " |
||||||
|
"Signature=" |
||||||
|
"be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, PostWithUpperCaseHeaderKey) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "POST", |
||||||
|
"https://host.foo.com/", "us-east-1", "", |
||||||
|
{{"date", kBotoTestDate}, {"ZOO", "zoobar"}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host;zoo, " |
||||||
|
"Signature=" |
||||||
|
"b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, PostWithUpperCaseHeaderValue) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "POST", |
||||||
|
"https://host.foo.com/", "us-east-1", "", |
||||||
|
{{"date", kBotoTestDate}, {"zoo", "ZOOBAR"}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host;zoo, " |
||||||
|
"Signature=" |
||||||
|
"273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, SignPostWithHeader) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "POST", |
||||||
|
"https://host.foo.com/", "us-east-1", "", |
||||||
|
{{"date", kBotoTestDate}, {"p", "phfft"}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host;p, " |
||||||
|
"Signature=" |
||||||
|
"debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, PostWithBodyNoCustomHeaders) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "POST", |
||||||
|
"https://host.foo.com/", "us-east-1", "foo=bar", |
||||||
|
{{"date", kBotoTestDate}, |
||||||
|
{"Content-Type", "application/x-www-form-urlencoded"}}, |
||||||
|
&error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=content-type;date;host, " |
||||||
|
"Signature=" |
||||||
|
"5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, SignPostWithQueryString) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
kBotoTestAccessKeyId, kBotoTestSecretAccessKey, kBotoTestToken, "POST", |
||||||
|
"https://host.foo.com/?foo=bar", "us-east-1", "", |
||||||
|
{{"date", kBotoTestDate}}, &error); |
||||||
|
EXPECT_EQ(error, GRPC_ERROR_NONE); |
||||||
|
EXPECT_EQ(signer.GetSignedRequestHeaders()["Authorization"], |
||||||
|
"AWS4-HMAC-SHA256 " |
||||||
|
"Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, " |
||||||
|
"SignedHeaders=date;host, " |
||||||
|
"Signature=" |
||||||
|
"b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, InvalidUrl) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer("access_key_id", "secret_access_key", |
||||||
|
"token", "POST", "invalid_url", |
||||||
|
"us-east-1", "", {}, &error); |
||||||
|
grpc_slice expected_error_description = |
||||||
|
grpc_slice_from_static_string("Invalid Aws request url."); |
||||||
|
grpc_slice actual_error_description; |
||||||
|
GPR_ASSERT(grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, |
||||||
|
&actual_error_description)); |
||||||
|
EXPECT_TRUE(grpc_slice_cmp(expected_error_description, |
||||||
|
actual_error_description) == 0); |
||||||
|
GRPC_ERROR_UNREF(error); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(GrpcAwsRequestSignerTest, DuplicateRequestDate) { |
||||||
|
grpc_error* error = GRPC_ERROR_NONE; |
||||||
|
grpc_core::AwsRequestSigner signer( |
||||||
|
"access_key_id", "secret_access_key", "token", "POST", "invalid_url", |
||||||
|
"us-east-1", "", {{"date", kBotoTestDate}, {"x-amz-date", kAmzTestDate}}, |
||||||
|
&error); |
||||||
|
grpc_slice expected_error_description = grpc_slice_from_static_string( |
||||||
|
"Only one of {date, x-amz-date} can be specified, not both."); |
||||||
|
grpc_slice actual_error_description; |
||||||
|
GPR_ASSERT(grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, |
||||||
|
&actual_error_description)); |
||||||
|
EXPECT_TRUE(grpc_slice_cmp(expected_error_description, |
||||||
|
actual_error_description) == 0); |
||||||
|
GRPC_ERROR_UNREF(error); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
grpc::testing::TestEnvironment env(argc, argv); |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
grpc_init(); |
||||||
|
int ret = RUN_ALL_TESTS(); |
||||||
|
grpc_shutdown(); |
||||||
|
return ret; |
||||||
|
} |
Loading…
Reference in new issue