1#include <userver/s3api/authenticators/utils.hpp>
8#include <boost/algorithm/string.hpp>
10#include <userver/clients/http/request.hpp>
11#include <userver/crypto/hash.hpp>
12#include <userver/utils/datetime.hpp>
14USERVER_NAMESPACE_BEGIN
18std::string HttpMethodToString(
const clients::http::
HttpMethod http_method) {
19 std::string http_method_string;
21 switch (http_method) {
22 case clients::http::HttpMethod::kDelete:
23 http_method_string =
"DELETE";
25 case clients::http::HttpMethod::kGet:
26 http_method_string =
"GET";
28 case clients::http::HttpMethod::kHead:
29 http_method_string =
"HEAD";
31 case clients::http::HttpMethod::kPost:
32 http_method_string =
"POST";
34 case clients::http::HttpMethod::kPut:
35 http_method_string =
"PUT";
37 case clients::http::HttpMethod::kPatch:
38 http_method_string =
"PATCH";
41 throw std::runtime_error(
"Unknown http method");
44 return http_method_string;
47std::string RemoveExcessiveSpaces(std::string value) {
49 std::replace(value.begin(), value.end(),
'\n',
' ');
50 unique_copy(value.begin(), value.end(), back_inserter(result), [](
char a,
char b) {
return a ==
' ' && b ==
' '; });
54std::string MakeHeaderDate() {
55 static constexpr int kHeaderDateLength{64};
57 std::string header_date(kHeaderDateLength,
'\0');
61 gmtime_r(&time, &ptm);
62 auto result_len = std::strftime(&header_date.front(), header_date.size(),
"%a, %d %b %Y %T %z", &ptm);
65 header_date.resize(result_len);
67 std::string kErrorMessage{
"MakeHeaderDate strftime fail [exceed maxsize]"};
68 throw std::runtime_error{kErrorMessage};
74std::string MakeHeaderContentMd5(
const std::string& data) {
75 return crypto::hash::weak::Md5(data, crypto::hash::OutputEncoding::kBase64);
78std::string MakeStringToSign(
80 const std::string& header_date,
81 const std::optional<std::string>& header_content_md5
83 std::ostringstream signature;
85 signature << HttpMethodToString(request.method) <<
'\n';
89 if (header_content_md5) {
90 signature << *header_content_md5;
98 static const std::string kContentType{
"Content-Type"};
100 const auto it = request.headers.find(kContentType);
102 if (it != request.headers.cend()) {
103 signature << it->second;
110 signature << header_date <<
'\n';
114 std::vector<std::pair<std::string, std::string>> canonical_headers;
115 canonical_headers.reserve(request.headers.size());
118 request.headers.begin(),
119 request.headers.end(),
120 std::back_inserter(canonical_headers),
121 [](
const auto& header) {
122 static const std::string kAmzHeader =
"x-amz-";
123 auto header_start = header.first.substr(0, kAmzHeader.size());
124 boost::to_lower(header_start);
125 return header_start == kAmzHeader;
128 std::for_each(canonical_headers.begin(), canonical_headers.end(), [](
auto& header) {
129 boost::to_lower(header.first);
131 std::sort(canonical_headers.begin(), canonical_headers.end(), [](
const auto& header1,
const auto& header2) {
132 return header1.first < header2.first;
135 for (
const auto& [header, value] : canonical_headers) {
136 signature << header <<
':' << RemoveExcessiveSpaces(value) <<
'\n';
143 if (!request.bucket.empty()) {
144 signature <<
'/' + request.bucket;
147 auto actual_subresources = std::set<std::string>{
164 { signature <<
'/' + request.req.substr(0, request.req.find(
'?')); }
166 if (
auto pos = request.req.find(
'?'); pos != std::string::npos) {
167 std::vector<std::string> subresources_strings;
168 auto query = request.req.substr(pos + 1);
169 boost::split(subresources_strings, query, [](
char c) {
return c ==
'&'; });
171 std::map<std::string, std::optional<std::string>> subresources;
172 for (
auto&& subresource : subresources_strings) {
173 std::optional<std::string> parameter_value = std::nullopt;
175 if (
auto eq_pos = subresource.find(
'='); eq_pos != std::string::npos) {
176 parameter_value.emplace(subresource.substr(eq_pos + 1));
177 subresource.resize(eq_pos);
180 if (actual_subresources.count(subresource) != 0) {
181 subresources.emplace(std::move(subresource), std::move(parameter_value));
185 bool is_first =
true;
186 for (
auto& [subresource, value] : subresources) {
187 signature << (is_first ?
"?" :
"&");
190 signature << fmt::format(
"{}={}", subresource, *value);
192 signature << subresource;
198 return signature.str();
201std::string MakeSignature(
const std::string& string_to_sign,
const Secret& secret_key) {
202 return crypto::hash::HmacSha1(secret_key.GetUnderlying(), string_to_sign, crypto::hash::OutputEncoding::kBase64);
206MakeHeaderAuthorization(
const std::string& string_to_sign,
const std::string& access_key,
const Secret& secret_key) {
207 return "AWS " + access_key +
":" + MakeSignature(string_to_sign, secret_key);