userver: /data/code/userver/libraries/s3api/src/s3api/authenticators/signature_v4_test.cpp Source File
Loading...
Searching...
No Matches
signature_v4_test.cpp
1#include <userver/s3api/authenticators/signature_v4.hpp>
2
3#include <userver/http/common_headers.hpp>
4#include <userver/s3api/models/request.hpp>
5#include <userver/utils/mock_now.hpp>
6
7#include <userver/utest/utest.hpp>
8
9USERVER_NAMESPACE_BEGIN
10
11namespace s3api::authenticators {
12
13namespace {
14
15// Test vectors from
16// https://docs.aws.amazon.com/AmazonS3/latest/developerguide/sig-v4-header-based-auth.html
17constexpr std::string_view kAccessKey = "AKIAIOSFODNN7EXAMPLE";
18constexpr std::string_view kSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
19constexpr std::string_view kRegion = "us-east-1";
20constexpr std::string_view kBucket = "examplebucket";
21constexpr std::string_view kVirtualHost = "examplebucket.s3.amazonaws.com";
22
23constexpr std::string_view kEmptyPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
24
25// Fri, 24 May 2013 00:00:00 GMT
26constexpr time_t kMockedNowEpoch = 1369353600;
27
28SignatureV4 MakeAuthenticator() {
29 return SignatureV4{std::string{kAccessKey}, Secret{std::string{kSecretKey}}, std::string{kRegion}};
30}
31
32Request MakeRequest(clients::http::HttpMethod method, std::string req) {
33 utils::datetime::MockNowSet(std::chrono::system_clock::from_time_t(kMockedNowEpoch));
34
35 Request request;
36 request.method = method;
37 request.bucket = kBucket;
38 request.req = std::move(req);
39 request.headers[USERVER_NAMESPACE::http::headers::kHost] = std::string{kVirtualHost};
40 return request;
41}
42
43} // namespace
44
45TEST(S3ApiSignatureV4, AuthGetObject) {
46 auto request = MakeRequest(clients::http::HttpMethod::kGet, "test.txt");
47 request.headers[USERVER_NAMESPACE::http::headers::kRange] = "bytes=0-9";
48
49 const auto headers = MakeAuthenticator().Auth(request);
50
51 EXPECT_EQ(headers.at("X-Amz-Date"), "20130524T000000Z");
52 EXPECT_EQ(headers.at("X-Amz-Content-Sha256"), kEmptyPayloadHash);
53 EXPECT_EQ(
54 headers.at("Authorization"),
55 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
56 "SignedHeaders=host;range;x-amz-content-sha256;x-amz-date, "
57 "Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41"
58 );
59}
60
61TEST(S3ApiSignatureV4, AuthPutObject) {
62 auto request = MakeRequest(clients::http::HttpMethod::kPut, "test$file.text");
63 request.body = "Welcome to Amazon S3.";
64 request.headers[USERVER_NAMESPACE::http::headers::kDate] = "Fri, 24 May 2013 00:00:00 GMT";
65 request.headers[std::string_view{"x-amz-storage-class"}] = "REDUCED_REDUNDANCY";
66
67 const auto headers = MakeAuthenticator().Auth(request);
68
69 EXPECT_EQ(headers.at("X-Amz-Content-Sha256"), "44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072");
70 EXPECT_EQ(
71 headers.at("Authorization"),
72 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
73 "SignedHeaders=date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class, "
74 "Signature=98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd"
75 );
76}
77
78TEST(S3ApiSignatureV4, AuthQueryParameterWithoutValue) {
79 const auto request = MakeRequest(clients::http::HttpMethod::kGet, "?lifecycle");
80
81 const auto headers = MakeAuthenticator().Auth(request);
82
83 EXPECT_EQ(
84 headers.at("Authorization"),
85 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
86 "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
87 "Signature=fea454ca298b7da1c68078a5d1bdbfbbe0d65c699e0f91ac7a200a0136783543"
88 );
89}
90
91TEST(S3ApiSignatureV4, AuthQueryParametersSorted) {
92 const auto request = MakeRequest(clients::http::HttpMethod::kGet, "?prefix=J&max-keys=2");
93
94 const auto headers = MakeAuthenticator().Auth(request);
95
96 EXPECT_EQ(
97 headers.at("Authorization"),
98 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
99 "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
100 "Signature=34b48302e7b5fa45bde8084f4b7868a86f0a534bc59db6670ed5711ef69dc6f7"
101 );
102}
103
104TEST(S3ApiSignatureV4, AuthPathStyleAddressing) {
105 auto request = MakeRequest(clients::http::HttpMethod::kGet, "test.txt");
106 // the bucket is not a subdomain, so it becomes a part of the canonical URI
107 request.headers[USERVER_NAMESPACE::http::headers::kHost] = "s3.amazonaws.com";
108
109 const auto headers = MakeAuthenticator().Auth(request);
110
111 EXPECT_EQ(
112 headers.at("Authorization"),
113 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
114 "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
115 "Signature=0fcb291c4b47980ad34dd9a29532ceae67b48e45de3d6054873b430740567ec2"
116 );
117}
118
119TEST(S3ApiSignatureV4, AuthEncodedPathAndQuery) {
120 // the path is encoded the same way api_methods do: http::EncodeS3Key + http::MakeQuery
121 const auto request = MakeRequest(clients::http::HttpMethod::kGet, "my%20folder/my%20file.txt?versionId=abc%20123");
122
123 const auto headers = MakeAuthenticator().Auth(request);
124
125 EXPECT_EQ(
126 headers.at("Authorization"),
127 "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, "
128 "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
129 "Signature=d6e900c34937a984ac3eeff28c985f58b4cbfa4ecfdb216af808afdef4145933"
130 );
131}
132
133TEST(S3ApiSignatureV4, AuthRequiresHostHeader) {
134 utils::datetime::MockNowSet(std::chrono::system_clock::from_time_t(kMockedNowEpoch));
135
136 Request request;
137 request.method = clients::http::HttpMethod::kGet;
138 request.bucket = kBucket;
139 request.req = "test.txt";
140
141 EXPECT_THROW(MakeAuthenticator().Auth(request), std::runtime_error);
142 EXPECT_THROW(MakeAuthenticator().Sign(request, kMockedNowEpoch + 60), std::runtime_error);
143}
144
145TEST(S3ApiSignatureV4, SignPresignedUrl) {
146 const auto request = MakeRequest(clients::http::HttpMethod::kGet, "test.txt");
147
148 const auto params = MakeAuthenticator().Sign(request, kMockedNowEpoch + 86400);
149
150 EXPECT_EQ(params.at("X-Amz-Algorithm"), "AWS4-HMAC-SHA256");
151 EXPECT_EQ(params.at("X-Amz-Credential"), "AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request");
152 EXPECT_EQ(params.at("X-Amz-Date"), "20130524T000000Z");
153 EXPECT_EQ(params.at("X-Amz-Expires"), "86400");
154 EXPECT_EQ(params.at("X-Amz-SignedHeaders"), "host");
155 EXPECT_EQ(params.at("X-Amz-Signature"), "aeeed9bbccd4d02ee5c0109b86d86835f995330da4c265957d157751f604d404");
156}
157
158} // namespace s3api::authenticators
159
160USERVER_NAMESPACE_END