<commit_before>// Copyright 2018 Google LLC
//
// 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 "google/cloud/storage/internal/signed_url_requests.h"
#include "google/cloud/internal/format_time_point.h"
#include "google/cloud/storage/internal/curl_handle.h"
#include "google/cloud/storage/internal/sha256_hash.h"
#include <algorithm>
#include <cctype>
#include <sstream>

namespace google {
namespace cloud {
namespace storage {
inline namespace STORAGE_CLIENT_NS {
namespace internal {

void SignUrlRequestCommon::SetOption(AddExtensionHeaderOption const& o) {
  if (!o.has_value()) {
    return;
  }
  auto kv = o.value();
  // Normalize the header, they are not case sensitive.
  std::transform(kv.first.begin(), kv.first.end(), kv.first.begin(),
                 [](unsigned char c) { return std::tolower(c); });
  auto res = extension_headers_.insert(kv);
  if (!res.second) {
    // The element already exists, we need to append:
    res.first->second.push_back(',');
    res.first->second.append(kv.second);
  }
}

std::vector<std::string> SignUrlRequestCommon::ObjectNameParts() const {
  std::vector<std::string> parts;
  std::istringstream is(object_name());
  std::string part;
  do {
    std::getline(is, part, '/');
    parts.push_back(part);
    is.tellg();
  } while (is);
  return parts;
}

std::vector<std::string> V4SignUrlRequest::ObjectNameParts() const {
  return common_request_.ObjectNameParts();
}

std::chrono::system_clock::time_point
V2SignUrlRequest::DefaultExpirationTime() {
  return std::chrono::system_clock::now() + std::chrono::hours(7 * 24);
}

std::string V2SignUrlRequest::StringToSign() const {
  std::ostringstream os;

  os << verb() << "\n"
     << md5_hash_value_ << "\n"
     << content_type_ << "\n"
     << expiration_time_as_seconds().count() << "\n";

  for (auto const& kv : common_request_.extension_headers()) {
    os << kv.first << ":" << kv.second << "\n";
  }

  CurlHandle curl;
  os << '/' << bucket_name();
  if (!object_name().empty()) {
    os << '/' << curl.MakeEscapedString(object_name()).get();
  }
  char const* sep = "?";
  if (!sub_resource().empty()) {
    os << sep << curl.MakeEscapedString(sub_resource()).get();
    sep = "&";
  }
  for (auto const& kv : common_request_.query_parameters()) {
    os << sep << curl.MakeEscapedString(kv.first).get() << "="
       << curl.MakeEscapedString(kv.second).get();
    sep = "&";
  }

  return std::move(os).str();
}

std::ostream& operator<<(std::ostream& os, V2SignUrlRequest const& r) {
  return os << "SingUrlRequest={" << r.StringToSign() << "}";
}

namespace {
std::string QueryStringFromParameters(
    CurlHandle& curl,
    std::multimap<std::string, std::string> const& parameters) {
  std::string result;
  char const* sep = "";
  for (auto const& qp : parameters) {
    result += sep;
    result += curl.MakeEscapedString(qp.first).get();
    result += '=';
    result += curl.MakeEscapedString(qp.second).get();
    sep = "&";
  }
  return result;
}

std::string TrimHeaderValue(std::string const& value) {
  std::string tmp = value;
  // Heasder values need to be normalized for spaces, whitespaces and tabs
  std::replace_if(tmp.begin(), tmp.end(),
                  [](char c) { return std::isspace(c); }, ' ');
  tmp.erase(0, tmp.find_first_not_of(' '));
  tmp = tmp.substr(0, tmp.find_last_not_of(' ') + 1);
  auto end = std::unique(tmp.begin(), tmp.end(),
                         [](char a, char b) { return a == ' ' && b == ' '; });
  tmp.erase(end, tmp.end());
  return tmp;
}
}  // namespace

void V4SignUrlRequest::AddMissingRequiredHeaders() {
  auto const& headers = common_request_.extension_headers();
  if (headers.find("host") == headers.end()) {
    SetOption(AddExtensionHeader("host", "storage.googleapis.com"));
  }
}

std::string V4SignUrlRequest::CanonicalQueryString(
    std::string const& client_id) const {
  CurlHandle curl;
  // Query parameters.
  auto parameters = AllQueryParameters(client_id);
  return QueryStringFromParameters(curl, parameters);
}

std::string V4SignUrlRequest::CanonicalRequest(
    std::string const& client_id) const {
  std::ostringstream os;

  os << verb() << "\n";
  CurlHandle curl;
  os << '/' << bucket_name();
  for (auto& part : ObjectNameParts()) {
    os << '/' << curl.MakeEscapedString(part).get();
  }
  if (!sub_resource().empty()) {
    os << '?' << curl.MakeEscapedString(sub_resource()).get();
  }
  os << "\n";

  // Query parameters.
  auto parameters = AllQueryParameters(client_id);
  os << QueryStringFromParameters(curl, parameters) << "\n";

  // Headers
  for (auto&& kv : common_request_.extension_headers()) {
    os << kv.first << ":" << TrimHeaderValue(kv.second) << "\n";
  }
  os << "\n" << SignedHeaders() << "\n" << PayloadHashValue();

  return std::move(os).str();
}

std::string V4SignUrlRequest::StringToSign(std::string const& client_id) const {
  return "GOOG4-RSA-SHA256\n" +
         google::cloud::internal::FormatV4SignedUrlTimestamp(timestamp_) +
         "\n" + Scope() + "\n" + CanonicalRequestHash(client_id);
}

std::chrono::system_clock::time_point V4SignUrlRequest::DefaultTimestamp() {
  return std::chrono::system_clock::now();
}

std::chrono::seconds V4SignUrlRequest::DefaultExpires() {
  return std::chrono::seconds(7 * std::chrono::hours(24));
}

std::string V4SignUrlRequest::CanonicalRequestHash(
    std::string const& client_id) const {
  return HexEncode(Sha256Hash(CanonicalRequest(client_id)));
}

std::string V4SignUrlRequest::Scope() const {
  return google::cloud::internal::FormatV4SignedUrlScope(timestamp_) +
         "/auto/storage/goog4_request";
}

std::multimap<std::string, std::string><commit_msg>fix: avoid repeating default V4 expiration definition (#3452)<commit_after>// Copyright 2018 Google LLC
//
// 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 "google/cloud/storage/internal/signed_url_requests.h"
#include "google/cloud/internal/format_time_point.h"
#include "google/cloud/storage/internal/curl_handle.h"
#include "google/cloud/storage/internal/sha256_hash.h"
#include <algorithm>
#include <cctype>
#include <sstream>

namespace google {
namespace cloud {
namespace storage {
inline namespace STORAGE_CLIENT_NS {
namespace internal {

void SignUrlRequestCommon::SetOption(AddExtensionHeaderOption const& o) {
  if (!o.has_value()) {
    return;
  }
  auto kv = o.value();
  // Normalize the header, they are not case sensitive.
  std::transform(kv.first.begin(), kv.first.end(), kv.first.begin(),
                 [](unsigned char c) { return std::tolower(c); });
  auto res = extension_headers_.insert(kv);
  if (!res.second) {
    // The element already exists, we need to append:
    res.first->second.push_back(',');
    res.first->second.append(kv.second);
  }
}

std::vector<std::string> SignUrlRequestCommon::ObjectNameParts() const {
  std::vector<std::string> parts;
  std::istringstream is(object_name());
  std::string part;
  do {
    std::getline(is, part, '/');
    parts.push_back(part);
    is.tellg();
  } while (is);
  return parts;
}

std::vector<std::string> V4SignUrlRequest::ObjectNameParts() const {
  return common_request_.ObjectNameParts();
}

namespace {
std::chrono::hours DefaultV4SignedUrlExpiration() {
  auto constexpr kHoursInDay = 24;
  auto constexpr kDaysInWeek = 7;
  return std::chrono::hours{kDaysInWeek * kHoursInDay};
}
}  // namespace

std::chrono::system_clock::time_point
V2SignUrlRequest::DefaultExpirationTime() {
  return std::chrono::system_clock::now() + DefaultV4SignedUrlExpiration();
}

std::string V2SignUrlRequest::StringToSign() const {
  std::ostringstream os;

  os << verb() << "\n"
     << md5_hash_value_ << "\n"
     << content_type_ << "\n"
     << expiration_time_as_seconds().count() << "\n";

  for (auto const& kv : common_request_.extension_headers()) {
    os << kv.first << ":" << kv.second << "\n";
  }

  CurlHandle curl;
  os << '/' << bucket_name();
  if (!object_name().empty()) {
    os << '/' << curl.MakeEscapedString(object_name()).get();
  }
  char const* sep = "?";
  if (!sub_resource().empty()) {
    os << sep << curl.MakeEscapedString(sub_resource()).get();
    sep = "&";
  }
  for (auto const& kv : common_request_.query_parameters()) {
    os << sep << curl.MakeEscapedString(kv.first).get() << "="
       << curl.MakeEscapedString(kv.second).get();
    sep = "&";
  }

  return std::move(os).str();
}

std::ostream& operator<<(std::ostream& os, V2SignUrlRequest const& r) {
  return os << "SingUrlRequest={" << r.StringToSign() << "}";
}

namespace {
std::string QueryStringFromParameters(
    CurlHandle& curl,
    std::multimap<std::string, std::string> const& parameters) {
  std::string result;
  char const* sep = "";
  for (auto const& qp : parameters) {
    result += sep;
    result += curl.MakeEscapedString(qp.first).get();
    result += '=';
    result += curl.MakeEscapedString(qp.second).get();
    sep = "&";
  }
  return result;
}

std::string TrimHeaderValue(std::string const& value) {
  std::string tmp = value;
  // Heasder values need to be normalized for spaces, whitespaces and tabs
  std::replace_if(tmp.begin(), tmp.end(),
                  [](char c) { return std::isspace(c); }, ' ');
  tmp.erase(0, tmp.find_first_not_of(' '));
  tmp = tmp.substr(0, tmp.find_last_not_of(' ') + 1);
  auto end = std::unique(tmp.begin(), tmp.end(),
                         [](char a, char b) { return a == ' ' && b == ' '; });
  tmp.erase(end, tmp.end());
  return tmp;
}
}  // namespace

void V4SignUrlRequest::AddMissingRequiredHeaders() {
  auto const& headers = common_request_.extension_headers();
  if (headers.find("host") == headers.end()) {
    SetOption(AddExtensionHeader("host", "storage.googleapis.com"));
  }
}

std::string V4SignUrlRequest::CanonicalQueryString(
    std::string const& client_id) const {
  CurlHandle curl;
  // Query parameters.
  auto parameters = AllQueryParameters(client_id);
  return QueryStringFromParameters(curl, parameters);
}

std::string V4SignUrlRequest::CanonicalRequest(
    std::string const& client_id) const {
  std::ostringstream os;

  os << verb() << "\n";
  CurlHandle curl;
  os << '/' << bucket_name();
  for (auto& part : ObjectNameParts()) {
    os << '/' << curl.MakeEscapedString(part).get();
  }
  if (!sub_resource().empty()) {
    os << '?' << curl.MakeEscapedString(sub_resource()).get();
  }
  os << "\n";

  // Query parameters.
  auto parameters = AllQueryParameters(client_id);
  os << QueryStringFromParameters(curl, parameters) << "\n";

  // Headers
  for (auto&& kv : common_request_.extension_headers()) {
    os << kv.first << ":" << TrimHeaderValue(kv.second) << "\n";
  }
  os << "\n" << SignedHeaders() << "\n" << PayloadHashValue();

  return std::move(os).str();
}

std::string V4SignUrlRequest::StringToSign(std::string const& client_id) const {
  return "GOOG4-RSA-SHA256\n" +
         google::cloud::internal::FormatV4SignedUrlTimestamp(timestamp_) +
         "\n" + Scope() + "\n" + CanonicalRequestHash(client_id);
}

std::chrono::system_clock::time_point V4SignUrlRequest::DefaultTimestamp() {
  return std::chrono::system_clock::now();
}

std::chrono::seconds V4SignUrlRequest::DefaultExpires() {
  return DefaultV4SignedUrlExpiration();
}

std::string V4SignUrlRequest::CanonicalRequestHash(
    std::string const& client_id) const {
  return HexEncode(Sha256Hash(CanonicalRequest(client_id)));
}

std::string V4SignUrlRequest::Scope() const {
  return google::cloud::internal::FormatV4SignedUrlScope(timestamp_) +
         "/auto/storage/goog4_request";
}

std::multimap<std::string, std::string>
{'fraction_non_alphanumeric': 0.16724960254372018, 'fraction_numerical': 0.007233704292527822, 'mean_word_length': 4.231185031185031, 'pattern_counts': {'":': 4, '<': 199, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4818493', 'n_tokens_mistral': 4136, 'n_tokens_neox': 4041, 'n_words': 881}