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_light.hpp>
14USERVER_NAMESPACE_BEGIN
18std::string HttpMethodToString(
const clients::http::
HttpMethod http_method) {
19 std::string http_method_string;
21 switch (http_method) {
23 http_method_string =
"DELETE";
26 http_method_string =
"GET";
29 http_method_string =
"HEAD";
32 http_method_string =
"POST";
35 http_method_string =
"PUT";
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 ==
' '; });
56std::string MakeHeaderContentMd5(
const std::string& data) {
60std::string MakeStringToSign(
62 const std::string& header_date,
63 const std::optional<std::string>& header_content_md5
65 std::ostringstream signature;
67 signature << HttpMethodToString(request.method) <<
'\n';
71 if (header_content_md5) {
72 signature << *header_content_md5;
80 static const std::string kContentType{
"Content-Type"};
82 const auto it = request.headers
.find(kContentType
);
84 if (it != request.headers
.cend()) {
85 signature << it->second;
92 signature << header_date <<
'\n';
96 std::vector<std::pair<std::string, std::string>> canonical_headers;
97 canonical_headers.reserve(request.headers
.size());
102 std::back_inserter(canonical_headers),
103 [](
const auto& header) {
104 static const std::string kAmzHeader =
"x-amz-";
105 auto header_start = header.first.substr(0, kAmzHeader.size());
106 boost::to_lower(header_start);
107 return header_start == kAmzHeader;
110 std::for_each(canonical_headers.begin(), canonical_headers.end(), [](
auto& header) {
111 boost::to_lower(header.first);
113 std::sort(canonical_headers.begin(), canonical_headers.end(), [](
const auto& header1,
const auto& header2) {
114 return header1.first < header2.first;
117 for (
const auto& [header, value] : canonical_headers) {
118 signature << header <<
':' << RemoveExcessiveSpaces(value) <<
'\n';
125 if (!request.bucket.empty()) {
126 signature <<
'/' + request.bucket;
129 auto actual_subresources = std::set<std::string>{
146 { signature <<
'/' + request.req.substr(0, request.req.find(
'?')); }
148 if (
auto pos = request.req.find(
'?'); pos != std::string::npos) {
149 std::vector<std::string> subresources_strings;
150 auto query = request.req.substr(pos + 1);
151 boost::split(subresources_strings, query, [](
char c) {
return c ==
'&'; });
153 std::map<std::string, std::optional<std::string>> subresources;
154 for (
auto&& subresource : subresources_strings) {
155 std::optional<std::string> parameter_value = std::nullopt;
157 if (
auto eq_pos = subresource.find(
'='); eq_pos != std::string::npos) {
158 parameter_value.emplace(subresource.substr(eq_pos + 1));
159 subresource.resize(eq_pos);
162 if (actual_subresources.count(subresource) != 0) {
163 subresources.emplace(std::move(subresource), std::move(parameter_value));
167 bool is_first =
true;
168 for (
auto& [subresource, value] : subresources) {
169 signature << (is_first ?
"?" :
"&");
172 signature << fmt::format(
"{}={}", subresource, *value);
174 signature << subresource;
180 return signature.str();
183std::string MakeSignature(
const std::string& string_to_sign,
const Secret& secret_key) {
188MakeHeaderAuthorization(
const std::string& string_to_sign,
const std::string& access_key,
const Secret& secret_key) {
189 return "AWS " + access_key +
":" + MakeSignature(string_to_sign, secret_key);