Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/cloudbuild/builds/lib/integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ source module ci/lib/io.sh
export PATH="${HOME}/.local/bin:${PATH}"
python3 -m pip uninstall -y --quiet googleapis-storage-testbench
python3 -m pip install --upgrade --user --quiet --disable-pip-version-check \
"git+https://github.com/googleapis/storage-testbench@v0.59.0"
"git+https://github.com/googleapis/storage-testbench@v0.60.0"

# Some of the tests will need a valid roots.pem file.
rm -f /dev/shm/roots.pem
Expand Down
131 changes: 129 additions & 2 deletions google/cloud/storage/bucket_encryption.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,109 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_ENCRYPTION_H

#include "google/cloud/storage/version.h"
#include "google/cloud/internal/format_time_point.h"
#include <chrono>
#include <iosfwd>
#include <iostream>
#include <string>
#include <tuple>
#include <utility>

namespace google {
namespace cloud {
namespace storage {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

template <typename Tag>
struct EncryptionEnforcementConfigName;

template <typename Tag>
struct EncryptionEnforcementConfig {
std::string restriction_mode;
std::chrono::system_clock::time_point effective_time;
};

template <typename Tag>
inline bool operator==(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::tie(lhs.restriction_mode, lhs.effective_time) ==
std::tie(rhs.restriction_mode, rhs.effective_time);
}

template <typename Tag>
inline bool operator<(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::tie(lhs.restriction_mode, lhs.effective_time) <
std::tie(rhs.restriction_mode, rhs.effective_time);
}

template <typename Tag>
inline bool operator!=(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::rel_ops::operator!=(lhs, rhs);
}

template <typename Tag>
inline bool operator>(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::rel_ops::operator>(lhs, rhs);
}

template <typename Tag>
inline bool operator<=(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::rel_ops::operator<=(lhs, rhs);
}

template <typename Tag>
inline bool operator>=(EncryptionEnforcementConfig<Tag> const& lhs,
EncryptionEnforcementConfig<Tag> const& rhs) {
return std::rel_ops::operator>=(lhs, rhs);
}

template <typename Tag>
inline std::ostream& operator<<(std::ostream& os,
EncryptionEnforcementConfig<Tag> const& rhs) {
return os << EncryptionEnforcementConfigName<Tag>::value
<< "={restriction_mode=" << rhs.restriction_mode
<< ", effective_time="
<< google::cloud::internal::FormatRfc3339(rhs.effective_time)
<< "}";
}

struct GoogleManagedEncryptionEnforcementConfigTag {};
using GoogleManagedEncryptionEnforcementConfig =
EncryptionEnforcementConfig<GoogleManagedEncryptionEnforcementConfigTag>;

template <>
struct EncryptionEnforcementConfigName<
GoogleManagedEncryptionEnforcementConfigTag> {
static constexpr char const* value =
"GoogleManagedEncryptionEnforcementConfig";
};

struct CustomerManagedEncryptionEnforcementConfigTag {};
using CustomerManagedEncryptionEnforcementConfig =
EncryptionEnforcementConfig<CustomerManagedEncryptionEnforcementConfigTag>;

template <>
struct EncryptionEnforcementConfigName<
CustomerManagedEncryptionEnforcementConfigTag> {
static constexpr char const* value =
"CustomerManagedEncryptionEnforcementConfig";
};

struct CustomerSuppliedEncryptionEnforcementConfigTag {};
using CustomerSuppliedEncryptionEnforcementConfig =
EncryptionEnforcementConfig<CustomerSuppliedEncryptionEnforcementConfigTag>;

template <>
struct EncryptionEnforcementConfigName<
CustomerSuppliedEncryptionEnforcementConfigTag> {
static constexpr char const* value =
"CustomerSuppliedEncryptionEnforcementConfig";
};

/**
* Describes the default customer managed encryption key for a bucket.
*
Expand All @@ -37,16 +133,36 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
*/
struct BucketEncryption {
std::string default_kms_key_name;
GoogleManagedEncryptionEnforcementConfig
google_managed_encryption_enforcement_config;
CustomerManagedEncryptionEnforcementConfig
customer_managed_encryption_enforcement_config;
CustomerSuppliedEncryptionEnforcementConfig
customer_supplied_encryption_enforcement_config;
};

inline bool operator==(BucketEncryption const& lhs,
BucketEncryption const& rhs) {
return lhs.default_kms_key_name == rhs.default_kms_key_name;
return std::tie(lhs.default_kms_key_name,
lhs.google_managed_encryption_enforcement_config,
lhs.customer_managed_encryption_enforcement_config,
lhs.customer_supplied_encryption_enforcement_config) ==
std::tie(rhs.default_kms_key_name,
rhs.google_managed_encryption_enforcement_config,
rhs.customer_managed_encryption_enforcement_config,
rhs.customer_supplied_encryption_enforcement_config);
}

inline bool operator<(BucketEncryption const& lhs,
BucketEncryption const& rhs) {
return lhs.default_kms_key_name < rhs.default_kms_key_name;
return std::tie(lhs.default_kms_key_name,
lhs.google_managed_encryption_enforcement_config,
lhs.customer_managed_encryption_enforcement_config,
lhs.customer_supplied_encryption_enforcement_config) <
std::tie(rhs.default_kms_key_name,
rhs.google_managed_encryption_enforcement_config,
rhs.customer_managed_encryption_enforcement_config,
rhs.customer_supplied_encryption_enforcement_config);
}

inline bool operator!=(BucketEncryption const& lhs,
Expand All @@ -69,6 +185,17 @@ inline bool operator>=(BucketEncryption const& lhs,
return std::rel_ops::operator>=(lhs, rhs);
}

inline std::ostream& operator<<(std::ostream& os, BucketEncryption const& rhs) {
os << "BucketEncryption={default_kms_key_name=" << rhs.default_kms_key_name;
os << ", google_managed_encryption_enforcement_config="
<< rhs.google_managed_encryption_enforcement_config;
os << ", customer_managed_encryption_enforcement_config="
<< rhs.customer_managed_encryption_enforcement_config;
os << ", customer_supplied_encryption_enforcement_config="
<< rhs.customer_supplied_encryption_enforcement_config;
return os << "}";
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace storage
} // namespace cloud
Expand Down
141 changes: 141 additions & 0 deletions google/cloud/storage/bucket_encryption_feature_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2025 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
//
// https://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/bucket_metadata.h"
#include "google/cloud/storage/internal/bucket_metadata_parser.h"
#include "google/cloud/internal/format_time_point.h"
#include "google/cloud/testing_util/status_matchers.h"
#include <gmock/gmock.h>
#include <nlohmann/json.hpp>

namespace google {
namespace cloud {
namespace storage {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace {

TEST(BucketEncryptionTest, Parse) {
std::string text = R"""({
"encryption": {
"defaultKmsKeyName": "projects/test-project-name/locations/us-central1/keyRings/test-keyring-name/cryptoKeys/test-key-name",
"googleManagedEncryptionEnforcementConfig": {
"restrictionMode": "FULLY_RESTRICTED",
"effectiveTime": "2025-12-18T18:13:15Z"
},
"customerManagedEncryptionEnforcementConfig": {
"restrictionMode": "NOT_RESTRICTED",
"effectiveTime": "2025-12-18T18:13:15Z"
},
"customerSuppliedEncryptionEnforcementConfig": {
"restrictionMode": "NOT_RESTRICTED",
"effectiveTime": "2025-12-18T18:13:15Z"
}
}
})""";

auto actual = internal::BucketMetadataParser::FromString(text);
ASSERT_STATUS_OK(actual);

ASSERT_TRUE(actual->has_encryption());
auto const& encryption = actual->encryption();
EXPECT_EQ(
"projects/test-project-name/locations/us-central1/keyRings/"
"test-keyring-name/cryptoKeys/test-key-name",
encryption.default_kms_key_name);

EXPECT_EQ(
"FULLY_RESTRICTED",
encryption.google_managed_encryption_enforcement_config.restriction_mode);
EXPECT_EQ("2025-12-18T18:13:15Z",
google::cloud::internal::FormatRfc3339(
encryption.google_managed_encryption_enforcement_config
.effective_time));

EXPECT_EQ("NOT_RESTRICTED",
encryption.customer_managed_encryption_enforcement_config
.restriction_mode);
EXPECT_EQ("2025-12-18T18:13:15Z",
google::cloud::internal::FormatRfc3339(
encryption.customer_managed_encryption_enforcement_config
.effective_time));

EXPECT_EQ("NOT_RESTRICTED",
encryption.customer_supplied_encryption_enforcement_config
.restriction_mode);
EXPECT_EQ("2025-12-18T18:13:15Z",
google::cloud::internal::FormatRfc3339(
encryption.customer_supplied_encryption_enforcement_config
.effective_time));
}

TEST(BucketEncryptionTest, ToJson) {
BucketMetadata meta;
BucketEncryption encryption;
encryption.default_kms_key_name = "test-key";
encryption.google_managed_encryption_enforcement_config.restriction_mode =
"FULLY_RESTRICTED";
encryption.google_managed_encryption_enforcement_config.effective_time =
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();
encryption.customer_managed_encryption_enforcement_config.restriction_mode =
"NOT_RESTRICTED";
encryption.customer_managed_encryption_enforcement_config.effective_time =
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();

meta.set_encryption(encryption);

auto json_string = internal::BucketMetadataToJsonString(meta);
auto json = nlohmann::json::parse(json_string);

ASSERT_TRUE(json.contains("encryption"));
auto e = json["encryption"];
EXPECT_EQ("test-key", e["defaultKmsKeyName"]);
EXPECT_EQ("FULLY_RESTRICTED",
e["googleManagedEncryptionEnforcementConfig"]["restrictionMode"]);
EXPECT_EQ("2025-12-18T18:13:15Z",
e["googleManagedEncryptionEnforcementConfig"]["effectiveTime"]);
EXPECT_EQ("NOT_RESTRICTED",
e["customerManagedEncryptionEnforcementConfig"]["restrictionMode"]);
EXPECT_EQ("2025-12-18T18:13:15Z",
e["customerManagedEncryptionEnforcementConfig"]["effectiveTime"]);
EXPECT_FALSE(e.contains("customerSuppliedEncryptionEnforcementConfig"));
}

TEST(BucketEncryptionTest, Patch) {
BucketMetadataPatchBuilder builder;
BucketEncryption encryption;
encryption.default_kms_key_name = "test-key";
encryption.google_managed_encryption_enforcement_config.restriction_mode =
"FULLY_RESTRICTED";
encryption.google_managed_encryption_enforcement_config.effective_time =
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();

builder.SetEncryption(encryption);

auto patch_string = builder.BuildPatch();
auto patch = nlohmann::json::parse(patch_string);

ASSERT_TRUE(patch.contains("encryption"));
auto e = patch["encryption"];
EXPECT_EQ("test-key", e["defaultKmsKeyName"]);
EXPECT_EQ("FULLY_RESTRICTED",
e["googleManagedEncryptionEnforcementConfig"]["restrictionMode"]);
EXPECT_EQ("2025-12-18T18:13:15Z",
e["googleManagedEncryptionEnforcementConfig"]["effectiveTime"]);
}

} // namespace
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace storage
} // namespace cloud
} // namespace google
26 changes: 21 additions & 5 deletions google/cloud/storage/bucket_metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) {
os << "]";

if (rhs.has_encryption()) {
os << ", encryption.default_kms_key_name="
<< rhs.encryption().default_kms_key_name;
os << ", encryption=" << rhs.encryption();
}

os << ", etag=" << rhs.etag();
Expand Down Expand Up @@ -362,9 +361,26 @@ BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetDefaultAcl() {

BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetEncryption(
BucketEncryption const& v) {
impl_.AddSubPatch("encryption",
internal::PatchBuilder().SetStringField(
"defaultKmsKeyName", v.default_kms_key_name));
internal::PatchBuilder builder;
builder.SetStringField("defaultKmsKeyName", v.default_kms_key_name);

auto add_config_patch = [&](char const* name, auto const& config) {
if (config.restriction_mode.empty()) return;
builder.AddSubPatch(
name, internal::PatchBuilder()
.SetStringField("restrictionMode", config.restriction_mode)
.SetStringField("effectiveTime",
google::cloud::internal::FormatRfc3339(
config.effective_time)));
};
add_config_patch("googleManagedEncryptionEnforcementConfig",
v.google_managed_encryption_enforcement_config);
add_config_patch("customerManagedEncryptionEnforcementConfig",
v.customer_managed_encryption_enforcement_config);
add_config_patch("customerSuppliedEncryptionEnforcementConfig",
v.customer_supplied_encryption_enforcement_config);

impl_.AddSubPatch("encryption", std::move(builder));
return *this;
}

Expand Down
Loading