From ff539bf5e0ee9332b3f26cd7a50a68318d481afa Mon Sep 17 00:00:00 2001 From: bajajnehaa Date: Thu, 18 Dec 2025 13:58:11 +0000 Subject: [PATCH 1/3] feat(storage): add support for bucket encryption enforcement config --- google/cloud/storage/bucket_encryption.h | 28 +++++++++++++++++ google/cloud/storage/bucket_metadata.cc | 4 +-- google/cloud/storage/bucket_metadata_test.cc | 30 +++++++++++++++++-- .../internal/bucket_metadata_parser.cc | 21 +++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/google/cloud/storage/bucket_encryption.h b/google/cloud/storage/bucket_encryption.h index 2e26815bd963c..b0c7afc310645 100644 --- a/google/cloud/storage/bucket_encryption.h +++ b/google/cloud/storage/bucket_encryption.h @@ -23,6 +23,31 @@ namespace cloud { namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +/** + * Google Managed Encryption (GMEK) enforcement config of a bucket. + */ +struct GoogleManagedEncryptionEnforcementConfig { + std::string restriction_mode; + std::chrono::system_clock::time_point effective_time; +}; + +inline bool operator==(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) == + std::tie(rhs.restriction_mode, rhs.effective_time); + } + +inline bool operator<(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) < + std::tie(rhs.restriction_mode, rhs.effective_time); + } + +inline bool operator!=(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + + } + /** * Describes the default customer managed encryption key for a bucket. * @@ -37,6 +62,9 @@ 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, diff --git a/google/cloud/storage/bucket_metadata.cc b/google/cloud/storage/bucket_metadata.cc index f10c02faa01f2..56eb8089703e6 100644 --- a/google/cloud/storage/bucket_metadata.cc +++ b/google/cloud/storage/bucket_metadata.cc @@ -150,8 +150,8 @@ 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(); diff --git a/google/cloud/storage/bucket_metadata_test.cc b/google/cloud/storage/bucket_metadata_test.cc index 23f307f3a988c..f267065d2c204 100644 --- a/google/cloud/storage/bucket_metadata_test.cc +++ b/google/cloud/storage/bucket_metadata_test.cc @@ -107,7 +107,19 @@ BucketMetadata CreateBucketMetadataForTest() { "etag": "AYX=" }], "encryption": { - "defaultKmsKeyName": "projects/test-project-name/locations/us-central1/keyRings/test-keyring-name/cryptoKeys/test-key-name" + "defaultKmsKeyName": "projects/test-project-name/locations/us-central1/keyRings/test-keyring-name/cryptoKeys/test-key-name", + "googleManagedEncryptionEnforcementConfig": { + "restriction_mode": "FULLY_RESTRICTED", + "effective_time": "2025-12-18T18:13:15Z" + }, + "customerManagedEncryptionEnforcementConfig": { + "restriction_mode": "NOT_RESTRICTED", + "effective_time": "2025-12-18T18:13:15Z" + }, + "customerSuppliedEncryptionEnforcementConfig": { + "restriction_mode": "NOT_RESTRICTED", + "effective_time": "2025-12-18T18:13:15Z" + } }, "etag": "XYZ=", "hierarchicalNamespace": { @@ -224,6 +236,10 @@ TEST(BucketMetadataTest, Parse) { "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/test-key-name", actual.encryption().default_kms_key_name); + EXPECT_EQ("FULLY_RESTRICTED", actual.encryption().googleManagedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customerManagedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customerSuppliedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ("2025-12-18T18:13:15Z", actual.encryption().customerSuppliedEncryptionEnforcementConfig.effective_time); EXPECT_EQ("XYZ=", actual.etag()); // hierarchicalNamespace ASSERT_TRUE(actual.has_hierarchical_namespace()); @@ -493,6 +509,11 @@ TEST(BucketMetadataTest, ToJsonString) { "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/test-key-name", actual["encryption"].value("defaultKmsKeyName", "")); + nlohmann::json expected_encryption_enforcement_config{ + {"googleManagedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "FULLY_RESTRICTED"}}, + {"customerManagedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "NOT_RESTRICTED"}}, + {"customerSuppliedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "NOT_RESTRICTED"}}}; + EXPECT_EQ(expected_encryption_enforcement_config, actual["encryption"]); // hierarchical_namespace() ASSERT_EQ(1, actual.count("hierarchicalNamespace")); @@ -849,8 +870,13 @@ TEST(BucketMetadataTest, SetEncryption) { std::string fake_key_name = "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/another-test-key-name"; - copy.set_encryption(BucketEncryption{fake_key_name}); + std::string fake_restriction_mode = "FULLY_RESTRICTED"; + + copy.set_encryption(BucketEncryption{fake_key_name, fake_restriction_mode}); EXPECT_EQ(fake_key_name, copy.encryption().default_kms_key_name); + EXPECT_EQ(fake_restriction_mode, copy.encryption().googleManagedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ(fake_restriction_mode, copy.encryption().customerManagedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ(fake_restriction_mode, copy.encryption().customerSuppliedEncryptionEnforcementConfig.restriction_mode); EXPECT_NE(expected, copy); } diff --git a/google/cloud/storage/internal/bucket_metadata_parser.cc b/google/cloud/storage/internal/bucket_metadata_parser.cc index 18924bd133a27..d64116cb35b6a 100644 --- a/google/cloud/storage/internal/bucket_metadata_parser.cc +++ b/google/cloud/storage/internal/bucket_metadata_parser.cc @@ -160,11 +160,32 @@ Status ParseEncryption(BucketMetadata& meta, nlohmann::json const& json) { if (json.contains("encryption")) { BucketEncryption e; e.default_kms_key_name = json["encryption"].value("defaultKmsKeyName", ""); + e.google_managed_encryption_enforcement_config = ParseGoogleManagedEncryptionEnforcementConfig(json["encryption"]); + e.customer_managed_encryption_enforcement_config = ParseCustomerManagedEncryptionEnforcementConfig(json["encryption"]); + e.customer_supplied_encryption_enforcement_config = ParseCustomerSuppliedEncryptionEnforcementConfig(json["encryption"]); meta.set_encryption(std::move(e)); } return Status{}; } +StatusOr ParseGoogleManagedEncryptionEnforcementConfig(nlohmann::json const& json) { + auto restriction_mode = json["restriction_mode"]; + auto effective_time = internal::ParseTimestampField(json, "effective_time"); + return GoogleManagedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +} + +StatusOr ParseCustomerManagedEncryptionEnforcementConfig(nlohmann::json const& json) { + auto restriction_mode = json["restriction_mode"]; + auto effective_time = internal::ParseTimestampField(json, "effective_time"); + return CustomerManagedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +} + +StatusOr ParseCustomerSuppliedEncryptionEnforcementConfig(nlohmann:json const& json) { + auto restriction_mode = json["restriction_mode"]; + auto effective_time = internal::ParseTimestampField(json, "effective_time"); + return CustomerSuppliedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +} + Status ParseHierarchicalNamespace(BucketMetadata& meta, nlohmann::json const& json) { auto const i = json.find("hierarchicalNamespace"); From bf6f53495244be6ca416c44130b886f5727c1b6b Mon Sep 17 00:00:00 2001 From: bajajnehaa Date: Tue, 23 Dec 2025 11:03:30 +0000 Subject: [PATCH 2/3] feat(storage): add support for bucket encryption enforcement config --- ci/cloudbuild/builds/lib/integration.sh | 2 +- google/cloud/storage/bucket_encryption.h | 182 ++++++++++++++++-- .../storage/bucket_encryption_feature_test.cc | 145 ++++++++++++++ google/cloud/storage/bucket_metadata.cc | 45 ++++- google/cloud/storage/bucket_metadata_test.cc | 20 +- .../internal/bucket_metadata_parser.cc | 97 +++++++--- .../internal/grpc/bucket_request_parser.cc | 76 +++++++- .../grpc/bucket_request_parser_test.cc | 72 ++++++- .../storage/storage_client_unit_tests.bzl | 1 + 9 files changed, 584 insertions(+), 56 deletions(-) create mode 100644 google/cloud/storage/bucket_encryption_feature_test.cc diff --git a/ci/cloudbuild/builds/lib/integration.sh b/ci/cloudbuild/builds/lib/integration.sh index d6fba90073441..3506c661c860c 100644 --- a/ci/cloudbuild/builds/lib/integration.sh +++ b/ci/cloudbuild/builds/lib/integration.sh @@ -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 diff --git a/google/cloud/storage/bucket_encryption.h b/google/cloud/storage/bucket_encryption.h index b0c7afc310645..0d576189fd78f 100644 --- a/google/cloud/storage/bucket_encryption.h +++ b/google/cloud/storage/bucket_encryption.h @@ -16,6 +16,12 @@ #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 +#include +#include +#include +#include #include namespace google { @@ -31,22 +37,144 @@ struct GoogleManagedEncryptionEnforcementConfig { std::chrono::system_clock::time_point effective_time; }; +///@{ +/// @name Comparison operators For GoogleManagedEncryptionEnforcementConfig inline bool operator==(GoogleManagedEncryptionEnforcementConfig const& lhs, GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) == - std::tie(rhs.restriction_mode, rhs.effective_time); - } + return std::tie(lhs.restriction_mode, lhs.effective_time) == + std::tie(rhs.restriction_mode, rhs.effective_time); +} inline bool operator<(GoogleManagedEncryptionEnforcementConfig const& lhs, GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) < - std::tie(rhs.restriction_mode, rhs.effective_time); - } + return std::tie(lhs.restriction_mode, lhs.effective_time) < + std::tie(rhs.restriction_mode, rhs.effective_time); +} inline bool operator!=(GoogleManagedEncryptionEnforcementConfig const& lhs, GoogleManagedEncryptionEnforcementConfig const& rhs) { - - } + return std::rel_ops::operator!=(lhs, rhs); +} + +inline bool operator>(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>(lhs, rhs); +} + +inline bool operator<=(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator<=(lhs, rhs); +} + +inline bool operator>=(GoogleManagedEncryptionEnforcementConfig const& lhs, + GoogleManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>=(lhs, rhs); +} +///@} + +inline std::ostream& operator<<( + std::ostream& os, GoogleManagedEncryptionEnforcementConfig const& rhs) { + return os << "GoogleManagedEncryptionEnforcementConfig={restriction_mode=" + << rhs.restriction_mode << ", effective_time=" + << google::cloud::internal::FormatRfc3339(rhs.effective_time) + << "}"; +} + +struct CustomerManagedEncryptionEnforcementConfig { + std::string restriction_mode; + std::chrono::system_clock::time_point effective_time; +}; + +///@{ +/// @name Comparison operators For CustomerManagedEncryptionEnforcementConfig +inline bool operator==(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) == + std::tie(rhs.restriction_mode, rhs.effective_time); +} + +inline bool operator<(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) < + std::tie(rhs.restriction_mode, rhs.effective_time); +} + +inline bool operator!=(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator!=(lhs, rhs); +} + +inline bool operator>(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>(lhs, rhs); +} + +inline bool operator<=(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator<=(lhs, rhs); +} + +inline bool operator>=(CustomerManagedEncryptionEnforcementConfig const& lhs, + CustomerManagedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>=(lhs, rhs); +} +///@} + +inline std::ostream& operator<<( + std::ostream& os, CustomerManagedEncryptionEnforcementConfig const& rhs) { + return os << "CustomerManagedEncryptionEnforcementConfig={restriction_mode=" + << rhs.restriction_mode << ", effective_time=" + << google::cloud::internal::FormatRfc3339(rhs.effective_time) + << "}"; +} + +struct CustomerSuppliedEncryptionEnforcementConfig { + std::string restriction_mode; + std::chrono::system_clock::time_point effective_time; +}; + +///@{ +/// @name Comparison operators For CustomerSuppliedEncryptionEnforcementConfig +inline bool operator==(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) == + std::tie(rhs.restriction_mode, rhs.effective_time); +} + +inline bool operator<(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::tie(lhs.restriction_mode, lhs.effective_time) < + std::tie(rhs.restriction_mode, rhs.effective_time); +} + +inline bool operator!=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator!=(lhs, rhs); +} + +inline bool operator>(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>(lhs, rhs); +} + +inline bool operator<=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator<=(lhs, rhs); +} + +inline bool operator>=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, + CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return std::rel_ops::operator>=(lhs, rhs); +} +///@} + +inline std::ostream& operator<<( + std::ostream& os, CustomerSuppliedEncryptionEnforcementConfig const& rhs) { + return os << "CustomerSuppliedEncryptionEnforcementConfig={restriction_mode=" + << rhs.restriction_mode << ", effective_time=" + << google::cloud::internal::FormatRfc3339(rhs.effective_time) + << "}"; +} /** * Describes the default customer managed encryption key for a bucket. @@ -62,19 +190,36 @@ inline bool operator!=(GoogleManagedEncryptionEnforcementConfig const& lhs, */ 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; + 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, @@ -97,6 +242,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 diff --git a/google/cloud/storage/bucket_encryption_feature_test.cc b/google/cloud/storage/bucket_encryption_feature_test.cc new file mode 100644 index 0000000000000..b33714ebb1bde --- /dev/null +++ b/google/cloud/storage/bucket_encryption_feature_test.cc @@ -0,0 +1,145 @@ +// Copyright 2024 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 +#include + +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 diff --git a/google/cloud/storage/bucket_metadata.cc b/google/cloud/storage/bucket_metadata.cc index 56eb8089703e6..17b353fd40f6b 100644 --- a/google/cloud/storage/bucket_metadata.cc +++ b/google/cloud/storage/bucket_metadata.cc @@ -362,9 +362,48 @@ 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); + if (!v.google_managed_encryption_enforcement_config.restriction_mode + .empty()) { + builder.AddSubPatch( + "googleManagedEncryptionEnforcementConfig", + internal::PatchBuilder() + .SetStringField("restrictionMode", + v.google_managed_encryption_enforcement_config + .restriction_mode) + .SetStringField("effectiveTime", + google::cloud::internal::FormatRfc3339( + v.google_managed_encryption_enforcement_config + .effective_time))); + } + if (!v.customer_managed_encryption_enforcement_config.restriction_mode + .empty()) { + builder.AddSubPatch( + "customerManagedEncryptionEnforcementConfig", + internal::PatchBuilder() + .SetStringField("restrictionMode", + v.customer_managed_encryption_enforcement_config + .restriction_mode) + .SetStringField("effectiveTime", + google::cloud::internal::FormatRfc3339( + v.customer_managed_encryption_enforcement_config + .effective_time))); + } + if (!v.customer_supplied_encryption_enforcement_config.restriction_mode + .empty()) { + builder.AddSubPatch( + "customerSuppliedEncryptionEnforcementConfig", + internal::PatchBuilder() + .SetStringField("restrictionMode", + v.customer_supplied_encryption_enforcement_config + .restriction_mode) + .SetStringField("effectiveTime", + google::cloud::internal::FormatRfc3339( + v.customer_supplied_encryption_enforcement_config + .effective_time))); + } + impl_.AddSubPatch("encryption", std::move(builder)); return *this; } diff --git a/google/cloud/storage/bucket_metadata_test.cc b/google/cloud/storage/bucket_metadata_test.cc index f267065d2c204..55c7e2e5cd704 100644 --- a/google/cloud/storage/bucket_metadata_test.cc +++ b/google/cloud/storage/bucket_metadata_test.cc @@ -236,10 +236,14 @@ TEST(BucketMetadataTest, Parse) { "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/test-key-name", actual.encryption().default_kms_key_name); - EXPECT_EQ("FULLY_RESTRICTED", actual.encryption().googleManagedEncryptionEnforcementConfig.restriction_mode); - EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customerManagedEncryptionEnforcementConfig.restriction_mode); - EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customerSuppliedEncryptionEnforcementConfig.restriction_mode); - EXPECT_EQ("2025-12-18T18:13:15Z", actual.encryption().customerSuppliedEncryptionEnforcementConfig.effective_time); + EXPECT_EQ("FULLY_RESTRICTED", actual.encryption().google_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customer_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customer_supplied_encryption_enforcement_config.restriction_mode); + EXPECT_EQ("2025-12-18T18:13:15Z", + google::cloud::internal::FormatRfc3339( + actual.encryption() + .customer_supplied_encryption_enforcement_config + .effective_time)); EXPECT_EQ("XYZ=", actual.etag()); // hierarchicalNamespace ASSERT_TRUE(actual.has_hierarchical_namespace()); @@ -872,11 +876,11 @@ TEST(BucketMetadataTest, SetEncryption) { "test-keyring-name/cryptoKeys/another-test-key-name"; std::string fake_restriction_mode = "FULLY_RESTRICTED"; - copy.set_encryption(BucketEncryption{fake_key_name, fake_restriction_mode}); + copy.set_encryption(BucketEncryption{fake_key_name, {fake_restriction_mode}, {fake_restriction_mode}, {fake_restriction_mode}}); EXPECT_EQ(fake_key_name, copy.encryption().default_kms_key_name); - EXPECT_EQ(fake_restriction_mode, copy.encryption().googleManagedEncryptionEnforcementConfig.restriction_mode); - EXPECT_EQ(fake_restriction_mode, copy.encryption().customerManagedEncryptionEnforcementConfig.restriction_mode); - EXPECT_EQ(fake_restriction_mode, copy.encryption().customerSuppliedEncryptionEnforcementConfig.restriction_mode); + EXPECT_EQ(fake_restriction_mode, copy.encryption().google_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ(fake_restriction_mode, copy.encryption().customer_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ(fake_restriction_mode, copy.encryption().customer_supplied_encryption_enforcement_config.restriction_mode); EXPECT_NE(expected, copy); } diff --git a/google/cloud/storage/internal/bucket_metadata_parser.cc b/google/cloud/storage/internal/bucket_metadata_parser.cc index d64116cb35b6a..f61690dc0187e 100644 --- a/google/cloud/storage/internal/bucket_metadata_parser.cc +++ b/google/cloud/storage/internal/bucket_metadata_parser.cc @@ -17,6 +17,7 @@ #include "google/cloud/storage/internal/lifecycle_rule_parser.h" #include "google/cloud/storage/internal/metadata_parser.h" #include "google/cloud/storage/internal/object_access_control_parser.h" +#include "google/cloud/internal/format_time_point.h" #include "google/cloud/internal/make_status.h" #include "absl/strings/str_format.h" #include @@ -156,34 +157,59 @@ Status ParseDefaultObjectAcl(BucketMetadata& meta, nlohmann::json const& json) { return Status{}; } -Status ParseEncryption(BucketMetadata& meta, nlohmann::json const& json) { - if (json.contains("encryption")) { - BucketEncryption e; - e.default_kms_key_name = json["encryption"].value("defaultKmsKeyName", ""); - e.google_managed_encryption_enforcement_config = ParseGoogleManagedEncryptionEnforcementConfig(json["encryption"]); - e.customer_managed_encryption_enforcement_config = ParseCustomerManagedEncryptionEnforcementConfig(json["encryption"]); - e.customer_supplied_encryption_enforcement_config = ParseCustomerSuppliedEncryptionEnforcementConfig(json["encryption"]); - meta.set_encryption(std::move(e)); - } - return Status{}; +StatusOr +ParseGoogleManagedEncryptionEnforcementConfig(nlohmann::json const& json) { + auto restriction_mode = json.value("restrictionMode", ""); + auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); + if (!effective_time) return std::move(effective_time).status(); + return GoogleManagedEncryptionEnforcementConfig{std::move(restriction_mode), + *effective_time}; } -StatusOr ParseGoogleManagedEncryptionEnforcementConfig(nlohmann::json const& json) { - auto restriction_mode = json["restriction_mode"]; - auto effective_time = internal::ParseTimestampField(json, "effective_time"); - return GoogleManagedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +StatusOr +ParseCustomerManagedEncryptionEnforcementConfig(nlohmann::json const& json) { + auto restriction_mode = json.value("restrictionMode", ""); + auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); + if (!effective_time) return std::move(effective_time).status(); + return CustomerManagedEncryptionEnforcementConfig{std::move(restriction_mode), + *effective_time}; } -StatusOr ParseCustomerManagedEncryptionEnforcementConfig(nlohmann::json const& json) { - auto restriction_mode = json["restriction_mode"]; - auto effective_time = internal::ParseTimestampField(json, "effective_time"); - return CustomerManagedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +StatusOr +ParseCustomerSuppliedEncryptionEnforcementConfig(nlohmann::json const& json) { + auto restriction_mode = json.value("restrictionMode", ""); + auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); + if (!effective_time) return std::move(effective_time).status(); + return CustomerSuppliedEncryptionEnforcementConfig{ + std::move(restriction_mode), *effective_time}; } -StatusOr ParseCustomerSuppliedEncryptionEnforcementConfig(nlohmann:json const& json) { - auto restriction_mode = json["restriction_mode"]; - auto effective_time = internal::ParseTimestampField(json, "effective_time"); - return CustomerSuppliedEncryptionEnforcementConfig{*restriction_mode, *effective_time}; +Status ParseEncryption(BucketMetadata& meta, nlohmann::json const& json) { + if (json.contains("encryption")) { + BucketEncryption e; + auto const& encryption = json["encryption"]; + e.default_kms_key_name = encryption.value("defaultKmsKeyName", ""); + if (encryption.contains("googleManagedEncryptionEnforcementConfig")) { + auto config = ParseGoogleManagedEncryptionEnforcementConfig( + encryption["googleManagedEncryptionEnforcementConfig"]); + if (!config) return std::move(config).status(); + e.google_managed_encryption_enforcement_config = *std::move(config); + } + if (encryption.contains("customerManagedEncryptionEnforcementConfig")) { + auto config = ParseCustomerManagedEncryptionEnforcementConfig( + encryption["customerManagedEncryptionEnforcementConfig"]); + if (!config) return std::move(config).status(); + e.customer_managed_encryption_enforcement_config = *std::move(config); + } + if (encryption.contains("customerSuppliedEncryptionEnforcementConfig")) { + auto config = ParseCustomerSuppliedEncryptionEnforcementConfig( + encryption["customerSuppliedEncryptionEnforcementConfig"]); + if (!config) return std::move(config).status(); + e.customer_supplied_encryption_enforcement_config = *std::move(config); + } + meta.set_encryption(std::move(e)); + } + return Status{}; } Status ParseHierarchicalNamespace(BucketMetadata& meta, @@ -396,6 +422,33 @@ void ToJsonEncryption(nlohmann::json& json, BucketMetadata const& meta) { if (!meta.has_encryption()) return; nlohmann::json e; SetIfNotEmpty(e, "defaultKmsKeyName", meta.encryption().default_kms_key_name); + auto const& gmek = + meta.encryption().google_managed_encryption_enforcement_config; + if (!gmek.restriction_mode.empty()) { + nlohmann::json config; + config["restrictionMode"] = gmek.restriction_mode; + config["effectiveTime"] = + google::cloud::internal::FormatRfc3339(gmek.effective_time); + e["googleManagedEncryptionEnforcementConfig"] = std::move(config); + } + auto const& cmek = + meta.encryption().customer_managed_encryption_enforcement_config; + if (!cmek.restriction_mode.empty()) { + nlohmann::json config; + config["restrictionMode"] = cmek.restriction_mode; + config["effectiveTime"] = + google::cloud::internal::FormatRfc3339(cmek.effective_time); + e["customerManagedEncryptionEnforcementConfig"] = std::move(config); + } + auto const& csek = + meta.encryption().customer_supplied_encryption_enforcement_config; + if (!csek.restriction_mode.empty()) { + nlohmann::json config; + config["restrictionMode"] = csek.restriction_mode; + config["effectiveTime"] = + google::cloud::internal::FormatRfc3339(csek.effective_time); + e["customerSuppliedEncryptionEnforcementConfig"] = std::move(config); + } json["encryption"] = std::move(e); } diff --git a/google/cloud/storage/internal/grpc/bucket_request_parser.cc b/google/cloud/storage/internal/grpc/bucket_request_parser.cc index 0b4d19c886791..05fbad6405341 100644 --- a/google/cloud/storage/internal/grpc/bucket_request_parser.cc +++ b/google/cloud/storage/internal/grpc/bucket_request_parser.cc @@ -21,6 +21,7 @@ #include "google/cloud/storage/internal/lifecycle_rule_parser.h" #include "google/cloud/storage/internal/object_access_control_parser.h" #include "google/cloud/storage/internal/patch_builder_details.h" +#include "google/cloud/internal/format_time_point.h" #include "google/cloud/internal/time_utils.h" #include @@ -162,13 +163,53 @@ Status PatchLogging(Bucket& b, nlohmann::json const& l) { return Status{}; } +google::protobuf::Timestamp ToProtoTimestamp( + std::chrono::system_clock::time_point tp) { + auto duration = tp.time_since_epoch(); + auto seconds = std::chrono::duration_cast(duration); + auto nanos = + std::chrono::duration_cast(duration - seconds); + google::protobuf::Timestamp ts; + ts.set_seconds(seconds.count()); + ts.set_nanos(static_cast(nanos.count())); + return ts; +} + Status PatchEncryption(Bucket& b, nlohmann::json const& e) { if (e.is_null()) { b.clear_encryption(); - } else { - b.mutable_encryption()->set_default_kms_key( - e.value("defaultKmsKeyName", "")); + return Status{}; + } + auto& encryption = *b.mutable_encryption(); + if (e.contains("defaultKmsKeyName")) { + encryption.set_default_kms_key(e.value("defaultKmsKeyName", "")); } + auto patch_config = [&](char const* json_key, auto* mutable_config) { + if (e.contains(json_key)) { + auto const& c = e[json_key]; + if (c.contains("restrictionMode")) { + mutable_config->set_restriction_mode(c.value("restrictionMode", "")); + } + if (c.contains("effectiveTime")) { + auto ts = google::cloud::internal::ParseRfc3339( + c.value("effectiveTime", "")); + if (ts) { + *mutable_config->mutable_effective_time() = ToProtoTimestamp(*ts); + } + } + } + }; + + patch_config( + "googleManagedEncryptionEnforcementConfig", + encryption.mutable_google_managed_encryption_enforcement_config()); + patch_config( + "customerManagedEncryptionEnforcementConfig", + encryption.mutable_customer_managed_encryption_enforcement_config()); + patch_config( + "customerSuppliedEncryptionEnforcementConfig", + encryption.mutable_customer_supplied_encryption_enforcement_config()); + return Status{}; } @@ -289,8 +330,33 @@ void UpdateLogging(Bucket& bucket, storage::BucketMetadata const& metadata) { void UpdateEncryption(Bucket& bucket, storage::BucketMetadata const& metadata) { if (!metadata.has_encryption()) return; - bucket.mutable_encryption()->set_default_kms_key( - metadata.encryption().default_kms_key_name); + auto& encryption = *bucket.mutable_encryption(); + encryption.set_default_kms_key(metadata.encryption().default_kms_key_name); + + auto const& gmek = + metadata.encryption().google_managed_encryption_enforcement_config; + if (!gmek.restriction_mode.empty()) { + auto& config = + *encryption.mutable_google_managed_encryption_enforcement_config(); + config.set_restriction_mode(gmek.restriction_mode); + *config.mutable_effective_time() = ToProtoTimestamp(gmek.effective_time); + } + auto const& cmek = + metadata.encryption().customer_managed_encryption_enforcement_config; + if (!cmek.restriction_mode.empty()) { + auto& config = + *encryption.mutable_customer_managed_encryption_enforcement_config(); + config.set_restriction_mode(cmek.restriction_mode); + *config.mutable_effective_time() = ToProtoTimestamp(cmek.effective_time); + } + auto const& csek = + metadata.encryption().customer_supplied_encryption_enforcement_config; + if (!csek.restriction_mode.empty()) { + auto& config = + *encryption.mutable_customer_supplied_encryption_enforcement_config(); + config.set_restriction_mode(csek.restriction_mode); + *config.mutable_effective_time() = ToProtoTimestamp(csek.effective_time); + } } void UpdateBilling(Bucket& bucket, storage::BucketMetadata const& metadata) { diff --git a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc index 4a1769efad220..1fe7c784066c9 100644 --- a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc +++ b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc @@ -458,7 +458,21 @@ TEST(GrpcBucketRequestParser, PatchBucketRequestAllOptions) { log_bucket: "projects/_/buckets/test-log-bucket" log_object_prefix: "test-log-prefix" } - encryption { default_kms_key: "test-only-kms-key" } + encryption { + default_kms_key: "test-only-kms-key" + google_managed_encryption_enforcement_config { + restriction_mode: "FULLY_RESTRICTED" + effective_time { seconds: 1766175572 } + } + customer_managed_encryption_enforcement_config { + restriction_mode: "NOT_RESTRICTED" + effective_time { seconds: 1766175695 } + } + customer_supplied_encryption_enforcement_config { + restriction_mode: "FULLY_RESTRICTED" + effective_time { seconds: 1766175739 } + } + } autoclass { enabled: true } billing { requester_pays: true } retention_policy { retention_duration { seconds: 123000 } } @@ -534,7 +548,25 @@ TEST(GrpcBucketRequestParser, PatchBucketRequestAllOptions) { .SetLogging( storage::BucketLogging{"test-log-bucket", "test-log-prefix"}) .SetEncryption(storage::BucketEncryption{ - /*.default_kms_key=*/"test-only-kms-key"}) + /*.default_kms_key=*/"test-only-kms-key", + /*.google_managed_encryption_enforcement_config=*/storage:: + GoogleManagedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:19:32Z") + .value()}, + /*.customer_managed_encryption_enforcement_config=*/storage:: + CustomerManagedEncryptionEnforcementConfig{ + "NOT_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:21:35Z") + .value()}, + /*.customer_supplied_encryption_enforcement_config=*/storage:: + CustomerSuppliedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:22:19Z") + .value()}}) .SetAutoclass(storage::BucketAutoclass{true}) .SetBilling(storage::BucketBilling{/*.requester_pays=*/true}) .SetRetentionPolicy(std::chrono::seconds(123000)) @@ -713,7 +745,21 @@ TEST(GrpcBucketRequestParser, UpdateBucketRequestAllOptions) { log_bucket: "test-log-bucket" log_object_prefix: "test-log-prefix" } - encryption { default_kms_key: "test-only-kms-key" } + encryption { + default_kms_key: "test-only-kms-key" + google_managed_encryption_enforcement_config { + restriction_mode: "FULLY_RESTRICTED" + effective_time { seconds: 1766176065 } + } + customer_managed_encryption_enforcement_config { + restriction_mode: "NOT_RESTRICTED" + effective_time { seconds: 1766176105 } + } + customer_supplied_encryption_enforcement_config { + restriction_mode: "FULLY_RESTRICTED" + effective_time { seconds: 1766176151 } + } + } autoclass { enabled: true } billing { requester_pays: true } retention_policy { retention_duration { seconds: 123000 } } @@ -787,7 +833,25 @@ TEST(GrpcBucketRequestParser, UpdateBucketRequestAllOptions) { .set_logging( storage::BucketLogging{"test-log-bucket", "test-log-prefix"}) .set_encryption(storage::BucketEncryption{ - /*.default_kms_key=*/"test-only-kms-key"}) + /*.default_kms_key=*/"test-only-kms-key", + /*.google_managed_encryption_enforcement_config=*/storage:: + GoogleManagedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:27:45Z") + .value()}, + /*.customer_managed_encryption_enforcement_config=*/storage:: + CustomerManagedEncryptionEnforcementConfig{ + "NOT_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:28:25Z") + .value()}, + /*.customer_supplied_encryption_enforcement_config=*/storage:: + CustomerSuppliedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339( + "2025-12-19T20:29:11Z") + .value()}}) .set_autoclass(storage::BucketAutoclass{true}) .set_billing(storage::BucketBilling{/*.requester_pays=*/true}) .set_retention_policy(std::chrono::seconds(123000)) diff --git a/google/cloud/storage/storage_client_unit_tests.bzl b/google/cloud/storage/storage_client_unit_tests.bzl index b1a8a1bdb56e3..ac7311cf78af4 100644 --- a/google/cloud/storage/storage_client_unit_tests.bzl +++ b/google/cloud/storage/storage_client_unit_tests.bzl @@ -20,6 +20,7 @@ storage_client_unit_tests = [ "auto_finalize_test.cc", "bucket_access_control_test.cc", "bucket_cors_entry_test.cc", + "bucket_encryption_feature_test.cc", "bucket_iam_configuration_test.cc", "bucket_metadata_test.cc", "bucket_object_retention_test.cc", From 21d2f869a25dcc64e674917aa39d13a8ed3b3544 Mon Sep 17 00:00:00 2001 From: bajajnehaa Date: Tue, 23 Dec 2025 12:26:26 +0000 Subject: [PATCH 3/3] Fix ci failure, address review comments --- google/cloud/storage/bucket_encryption.h | 167 ++++++------------ .../storage/bucket_encryption_feature_test.cc | 42 ++--- google/cloud/storage/bucket_metadata.cc | 57 ++---- google/cloud/storage/bucket_metadata_test.cc | 84 ++++++--- .../internal/bucket_metadata_parser.cc | 77 +++----- .../internal/grpc/bucket_request_parser.cc | 43 ++--- .../grpc/bucket_request_parser_test.cc | 66 ++++--- .../storage/storage_client_unit_tests.bzl | 1 - 8 files changed, 225 insertions(+), 312 deletions(-) diff --git a/google/cloud/storage/bucket_encryption.h b/google/cloud/storage/bucket_encryption.h index 0d576189fd78f..90bdf89945eaa 100644 --- a/google/cloud/storage/bucket_encryption.h +++ b/google/cloud/storage/bucket_encryption.h @@ -29,152 +29,95 @@ namespace cloud { namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -/** - * Google Managed Encryption (GMEK) enforcement config of a bucket. - */ -struct GoogleManagedEncryptionEnforcementConfig { - std::string restriction_mode; - std::chrono::system_clock::time_point effective_time; -}; - -///@{ -/// @name Comparison operators For GoogleManagedEncryptionEnforcementConfig -inline bool operator==(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) == - std::tie(rhs.restriction_mode, rhs.effective_time); -} - -inline bool operator<(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) < - std::tie(rhs.restriction_mode, rhs.effective_time); -} - -inline bool operator!=(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator!=(lhs, rhs); -} - -inline bool operator>(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator>(lhs, rhs); -} - -inline bool operator<=(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator<=(lhs, rhs); -} - -inline bool operator>=(GoogleManagedEncryptionEnforcementConfig const& lhs, - GoogleManagedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator>=(lhs, rhs); -} -///@} - -inline std::ostream& operator<<( - std::ostream& os, GoogleManagedEncryptionEnforcementConfig const& rhs) { - return os << "GoogleManagedEncryptionEnforcementConfig={restriction_mode=" - << rhs.restriction_mode << ", effective_time=" - << google::cloud::internal::FormatRfc3339(rhs.effective_time) - << "}"; -} +template +struct EncryptionEnforcementConfigName; -struct CustomerManagedEncryptionEnforcementConfig { +template +struct EncryptionEnforcementConfig { std::string restriction_mode; std::chrono::system_clock::time_point effective_time; }; -///@{ -/// @name Comparison operators For CustomerManagedEncryptionEnforcementConfig -inline bool operator==(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator==(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::tie(lhs.restriction_mode, lhs.effective_time) == std::tie(rhs.restriction_mode, rhs.effective_time); } -inline bool operator<(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator<(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::tie(lhs.restriction_mode, lhs.effective_time) < std::tie(rhs.restriction_mode, rhs.effective_time); } -inline bool operator!=(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator!=(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::rel_ops::operator!=(lhs, rhs); } -inline bool operator>(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator>(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::rel_ops::operator>(lhs, rhs); } -inline bool operator<=(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator<=(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::rel_ops::operator<=(lhs, rhs); } -inline bool operator>=(CustomerManagedEncryptionEnforcementConfig const& lhs, - CustomerManagedEncryptionEnforcementConfig const& rhs) { +template +inline bool operator>=(EncryptionEnforcementConfig const& lhs, + EncryptionEnforcementConfig const& rhs) { return std::rel_ops::operator>=(lhs, rhs); } -///@} -inline std::ostream& operator<<( - std::ostream& os, CustomerManagedEncryptionEnforcementConfig const& rhs) { - return os << "CustomerManagedEncryptionEnforcementConfig={restriction_mode=" - << rhs.restriction_mode << ", effective_time=" +template +inline std::ostream& operator<<(std::ostream& os, + EncryptionEnforcementConfig const& rhs) { + return os << EncryptionEnforcementConfigName::value + << "={restriction_mode=" << rhs.restriction_mode + << ", effective_time=" << google::cloud::internal::FormatRfc3339(rhs.effective_time) << "}"; } -struct CustomerSuppliedEncryptionEnforcementConfig { - std::string restriction_mode; - std::chrono::system_clock::time_point effective_time; -}; - -///@{ -/// @name Comparison operators For CustomerSuppliedEncryptionEnforcementConfig -inline bool operator==(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) == - std::tie(rhs.restriction_mode, rhs.effective_time); -} - -inline bool operator<(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::tie(lhs.restriction_mode, lhs.effective_time) < - std::tie(rhs.restriction_mode, rhs.effective_time); -} +struct GoogleManagedEncryptionEnforcementConfigTag {}; +using GoogleManagedEncryptionEnforcementConfig = + EncryptionEnforcementConfig; -inline bool operator!=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator!=(lhs, rhs); -} +template <> +struct EncryptionEnforcementConfigName< + GoogleManagedEncryptionEnforcementConfigTag> { + static constexpr char const* value = + "GoogleManagedEncryptionEnforcementConfig"; +}; -inline bool operator>(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator>(lhs, rhs); -} +struct CustomerManagedEncryptionEnforcementConfigTag {}; +using CustomerManagedEncryptionEnforcementConfig = + EncryptionEnforcementConfig; -inline bool operator<=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator<=(lhs, rhs); -} +template <> +struct EncryptionEnforcementConfigName< + CustomerManagedEncryptionEnforcementConfigTag> { + static constexpr char const* value = + "CustomerManagedEncryptionEnforcementConfig"; +}; -inline bool operator>=(CustomerSuppliedEncryptionEnforcementConfig const& lhs, - CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return std::rel_ops::operator>=(lhs, rhs); -} -///@} +struct CustomerSuppliedEncryptionEnforcementConfigTag {}; +using CustomerSuppliedEncryptionEnforcementConfig = + EncryptionEnforcementConfig; -inline std::ostream& operator<<( - std::ostream& os, CustomerSuppliedEncryptionEnforcementConfig const& rhs) { - return os << "CustomerSuppliedEncryptionEnforcementConfig={restriction_mode=" - << rhs.restriction_mode << ", effective_time=" - << google::cloud::internal::FormatRfc3339(rhs.effective_time) - << "}"; -} +template <> +struct EncryptionEnforcementConfigName< + CustomerSuppliedEncryptionEnforcementConfigTag> { + static constexpr char const* value = + "CustomerSuppliedEncryptionEnforcementConfig"; +}; /** * Describes the default customer managed encryption key for a bucket. diff --git a/google/cloud/storage/bucket_encryption_feature_test.cc b/google/cloud/storage/bucket_encryption_feature_test.cc index b33714ebb1bde..be56b2c9289e5 100644 --- a/google/cloud/storage/bucket_encryption_feature_test.cc +++ b/google/cloud/storage/bucket_encryption_feature_test.cc @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// 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. @@ -62,15 +62,17 @@ TEST(BucketEncryptionTest, Parse) { encryption.google_managed_encryption_enforcement_config .effective_time)); - EXPECT_EQ("NOT_RESTRICTED", encryption.customer_managed_encryption_enforcement_config - .restriction_mode); + 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("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 @@ -98,18 +100,14 @@ TEST(BucketEncryptionTest, ToJson) { 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_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")); } @@ -130,12 +128,10 @@ TEST(BucketEncryptionTest, Patch) { 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"]); + EXPECT_EQ("FULLY_RESTRICTED", + e["googleManagedEncryptionEnforcementConfig"]["restrictionMode"]); + EXPECT_EQ("2025-12-18T18:13:15Z", + e["googleManagedEncryptionEnforcementConfig"]["effectiveTime"]); } } // namespace diff --git a/google/cloud/storage/bucket_metadata.cc b/google/cloud/storage/bucket_metadata.cc index 17b353fd40f6b..0d4a3ea42d4fa 100644 --- a/google/cloud/storage/bucket_metadata.cc +++ b/google/cloud/storage/bucket_metadata.cc @@ -150,8 +150,7 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) { os << "]"; if (rhs.has_encryption()) { - os << ", encryption=" - << rhs.encryption(); + os << ", encryption=" << rhs.encryption(); } os << ", etag=" << rhs.etag(); @@ -364,45 +363,23 @@ BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetEncryption( BucketEncryption const& v) { internal::PatchBuilder builder; builder.SetStringField("defaultKmsKeyName", v.default_kms_key_name); - if (!v.google_managed_encryption_enforcement_config.restriction_mode - .empty()) { - builder.AddSubPatch( - "googleManagedEncryptionEnforcementConfig", - internal::PatchBuilder() - .SetStringField("restrictionMode", - v.google_managed_encryption_enforcement_config - .restriction_mode) - .SetStringField("effectiveTime", - google::cloud::internal::FormatRfc3339( - v.google_managed_encryption_enforcement_config - .effective_time))); - } - if (!v.customer_managed_encryption_enforcement_config.restriction_mode - .empty()) { - builder.AddSubPatch( - "customerManagedEncryptionEnforcementConfig", - internal::PatchBuilder() - .SetStringField("restrictionMode", - v.customer_managed_encryption_enforcement_config - .restriction_mode) - .SetStringField("effectiveTime", - google::cloud::internal::FormatRfc3339( - v.customer_managed_encryption_enforcement_config - .effective_time))); - } - if (!v.customer_supplied_encryption_enforcement_config.restriction_mode - .empty()) { + + auto add_config_patch = [&](char const* name, auto const& config) { + if (config.restriction_mode.empty()) return; builder.AddSubPatch( - "customerSuppliedEncryptionEnforcementConfig", - internal::PatchBuilder() - .SetStringField("restrictionMode", - v.customer_supplied_encryption_enforcement_config - .restriction_mode) - .SetStringField("effectiveTime", - google::cloud::internal::FormatRfc3339( - v.customer_supplied_encryption_enforcement_config - .effective_time))); - } + 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; } diff --git a/google/cloud/storage/bucket_metadata_test.cc b/google/cloud/storage/bucket_metadata_test.cc index 55c7e2e5cd704..c2595c7e7e934 100644 --- a/google/cloud/storage/bucket_metadata_test.cc +++ b/google/cloud/storage/bucket_metadata_test.cc @@ -109,16 +109,16 @@ BucketMetadata CreateBucketMetadataForTest() { "encryption": { "defaultKmsKeyName": "projects/test-project-name/locations/us-central1/keyRings/test-keyring-name/cryptoKeys/test-key-name", "googleManagedEncryptionEnforcementConfig": { - "restriction_mode": "FULLY_RESTRICTED", - "effective_time": "2025-12-18T18:13:15Z" + "restrictionMode": "FULLY_RESTRICTED", + "effectiveTime": "2025-12-18T18:13:15Z" }, "customerManagedEncryptionEnforcementConfig": { - "restriction_mode": "NOT_RESTRICTED", - "effective_time": "2025-12-18T18:13:15Z" + "restrictionMode": "NOT_RESTRICTED", + "effectiveTime": "2025-12-18T18:13:15Z" }, "customerSuppliedEncryptionEnforcementConfig": { - "restriction_mode": "NOT_RESTRICTED", - "effective_time": "2025-12-18T18:13:15Z" + "restrictionMode": "NOT_RESTRICTED", + "effectiveTime": "2025-12-18T18:13:15Z" } }, "etag": "XYZ=", @@ -236,14 +236,22 @@ TEST(BucketMetadataTest, Parse) { "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/test-key-name", actual.encryption().default_kms_key_name); - EXPECT_EQ("FULLY_RESTRICTED", actual.encryption().google_managed_encryption_enforcement_config.restriction_mode); - EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customer_managed_encryption_enforcement_config.restriction_mode); - EXPECT_EQ("NOT_RESTRICTED", actual.encryption().customer_supplied_encryption_enforcement_config.restriction_mode); - EXPECT_EQ("2025-12-18T18:13:15Z", - google::cloud::internal::FormatRfc3339( - actual.encryption() - .customer_supplied_encryption_enforcement_config - .effective_time)); + EXPECT_EQ("FULLY_RESTRICTED", + actual.encryption() + .google_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ( + "NOT_RESTRICTED", + actual.encryption() + .customer_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ( + "NOT_RESTRICTED", + actual.encryption() + .customer_supplied_encryption_enforcement_config.restriction_mode); + EXPECT_EQ( + "2025-12-18T18:13:15Z", + google::cloud::internal::FormatRfc3339( + actual.encryption() + .customer_supplied_encryption_enforcement_config.effective_time)); EXPECT_EQ("XYZ=", actual.etag()); // hierarchicalNamespace ASSERT_TRUE(actual.has_hierarchical_namespace()); @@ -509,15 +517,32 @@ TEST(BucketMetadataTest, ToJsonString) { // encryption() ASSERT_EQ(1U, actual.count("encryption")); + auto const& encryption = actual["encryption"]; EXPECT_EQ( "projects/test-project-name/locations/us-central1/keyRings/" "test-keyring-name/cryptoKeys/test-key-name", - actual["encryption"].value("defaultKmsKeyName", "")); - nlohmann::json expected_encryption_enforcement_config{ - {"googleManagedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "FULLY_RESTRICTED"}}, - {"customerManagedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "NOT_RESTRICTED"}}, - {"customerSuppliedEncryptionEnforcementConfig", nlohmann::json{"restriction_mode", "NOT_RESTRICTED"}}}; - EXPECT_EQ(expected_encryption_enforcement_config, actual["encryption"]); + encryption.value("defaultKmsKeyName", "")); + + EXPECT_EQ("FULLY_RESTRICTED", + encryption["googleManagedEncryptionEnforcementConfig"].value( + "restrictionMode", "")); + EXPECT_EQ("2025-12-18T18:13:15Z", + encryption["googleManagedEncryptionEnforcementConfig"].value( + "effectiveTime", "")); + + EXPECT_EQ("NOT_RESTRICTED", + encryption["customerManagedEncryptionEnforcementConfig"].value( + "restrictionMode", "")); + EXPECT_EQ("2025-12-18T18:13:15Z", + encryption["customerManagedEncryptionEnforcementConfig"].value( + "effectiveTime", "")); + + EXPECT_EQ("NOT_RESTRICTED", + encryption["customerSuppliedEncryptionEnforcementConfig"].value( + "restrictionMode", "")); + EXPECT_EQ("2025-12-18T18:13:15Z", + encryption["customerSuppliedEncryptionEnforcementConfig"].value( + "effectiveTime", "")); // hierarchical_namespace() ASSERT_EQ(1, actual.count("hierarchicalNamespace")); @@ -876,11 +901,22 @@ TEST(BucketMetadataTest, SetEncryption) { "test-keyring-name/cryptoKeys/another-test-key-name"; std::string fake_restriction_mode = "FULLY_RESTRICTED"; - copy.set_encryption(BucketEncryption{fake_key_name, {fake_restriction_mode}, {fake_restriction_mode}, {fake_restriction_mode}}); + copy.set_encryption(BucketEncryption{fake_key_name, + {fake_restriction_mode}, + {fake_restriction_mode}, + {fake_restriction_mode}}); EXPECT_EQ(fake_key_name, copy.encryption().default_kms_key_name); - EXPECT_EQ(fake_restriction_mode, copy.encryption().google_managed_encryption_enforcement_config.restriction_mode); - EXPECT_EQ(fake_restriction_mode, copy.encryption().customer_managed_encryption_enforcement_config.restriction_mode); - EXPECT_EQ(fake_restriction_mode, copy.encryption().customer_supplied_encryption_enforcement_config.restriction_mode); + EXPECT_EQ(fake_restriction_mode, + copy.encryption() + .google_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ( + fake_restriction_mode, + copy.encryption() + .customer_managed_encryption_enforcement_config.restriction_mode); + EXPECT_EQ( + fake_restriction_mode, + copy.encryption() + .customer_supplied_encryption_enforcement_config.restriction_mode); EXPECT_NE(expected, copy); } diff --git a/google/cloud/storage/internal/bucket_metadata_parser.cc b/google/cloud/storage/internal/bucket_metadata_parser.cc index f61690dc0187e..1c138bb0c3d45 100644 --- a/google/cloud/storage/internal/bucket_metadata_parser.cc +++ b/google/cloud/storage/internal/bucket_metadata_parser.cc @@ -157,31 +157,13 @@ Status ParseDefaultObjectAcl(BucketMetadata& meta, nlohmann::json const& json) { return Status{}; } -StatusOr -ParseGoogleManagedEncryptionEnforcementConfig(nlohmann::json const& json) { - auto restriction_mode = json.value("restrictionMode", ""); - auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); - if (!effective_time) return std::move(effective_time).status(); - return GoogleManagedEncryptionEnforcementConfig{std::move(restriction_mode), - *effective_time}; -} - -StatusOr -ParseCustomerManagedEncryptionEnforcementConfig(nlohmann::json const& json) { - auto restriction_mode = json.value("restrictionMode", ""); - auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); - if (!effective_time) return std::move(effective_time).status(); - return CustomerManagedEncryptionEnforcementConfig{std::move(restriction_mode), - *effective_time}; -} - -StatusOr -ParseCustomerSuppliedEncryptionEnforcementConfig(nlohmann::json const& json) { +template +StatusOr ParseEncryptionEnforcementConfig( + nlohmann::json const& json) { auto restriction_mode = json.value("restrictionMode", ""); auto effective_time = internal::ParseTimestampField(json, "effectiveTime"); if (!effective_time) return std::move(effective_time).status(); - return CustomerSuppliedEncryptionEnforcementConfig{ - std::move(restriction_mode), *effective_time}; + return ConfigType{std::move(restriction_mode), *effective_time}; } Status ParseEncryption(BucketMetadata& meta, nlohmann::json const& json) { @@ -190,19 +172,22 @@ Status ParseEncryption(BucketMetadata& meta, nlohmann::json const& json) { auto const& encryption = json["encryption"]; e.default_kms_key_name = encryption.value("defaultKmsKeyName", ""); if (encryption.contains("googleManagedEncryptionEnforcementConfig")) { - auto config = ParseGoogleManagedEncryptionEnforcementConfig( + auto config = ParseEncryptionEnforcementConfig< + GoogleManagedEncryptionEnforcementConfig>( encryption["googleManagedEncryptionEnforcementConfig"]); if (!config) return std::move(config).status(); e.google_managed_encryption_enforcement_config = *std::move(config); } if (encryption.contains("customerManagedEncryptionEnforcementConfig")) { - auto config = ParseCustomerManagedEncryptionEnforcementConfig( + auto config = ParseEncryptionEnforcementConfig< + CustomerManagedEncryptionEnforcementConfig>( encryption["customerManagedEncryptionEnforcementConfig"]); if (!config) return std::move(config).status(); e.customer_managed_encryption_enforcement_config = *std::move(config); } if (encryption.contains("customerSuppliedEncryptionEnforcementConfig")) { - auto config = ParseCustomerSuppliedEncryptionEnforcementConfig( + auto config = ParseEncryptionEnforcementConfig< + CustomerSuppliedEncryptionEnforcementConfig>( encryption["customerSuppliedEncryptionEnforcementConfig"]); if (!config) return std::move(config).status(); e.customer_supplied_encryption_enforcement_config = *std::move(config); @@ -422,33 +407,25 @@ void ToJsonEncryption(nlohmann::json& json, BucketMetadata const& meta) { if (!meta.has_encryption()) return; nlohmann::json e; SetIfNotEmpty(e, "defaultKmsKeyName", meta.encryption().default_kms_key_name); - auto const& gmek = - meta.encryption().google_managed_encryption_enforcement_config; - if (!gmek.restriction_mode.empty()) { - nlohmann::json config; - config["restrictionMode"] = gmek.restriction_mode; - config["effectiveTime"] = - google::cloud::internal::FormatRfc3339(gmek.effective_time); - e["googleManagedEncryptionEnforcementConfig"] = std::move(config); - } - auto const& cmek = - meta.encryption().customer_managed_encryption_enforcement_config; - if (!cmek.restriction_mode.empty()) { - nlohmann::json config; - config["restrictionMode"] = cmek.restriction_mode; - config["effectiveTime"] = - google::cloud::internal::FormatRfc3339(cmek.effective_time); - e["customerManagedEncryptionEnforcementConfig"] = std::move(config); - } - auto const& csek = - meta.encryption().customer_supplied_encryption_enforcement_config; - if (!csek.restriction_mode.empty()) { + + auto to_json_config = [&](char const* name, auto const& config_source) { + if (config_source.restriction_mode.empty()) return; nlohmann::json config; - config["restrictionMode"] = csek.restriction_mode; + config["restrictionMode"] = config_source.restriction_mode; config["effectiveTime"] = - google::cloud::internal::FormatRfc3339(csek.effective_time); - e["customerSuppliedEncryptionEnforcementConfig"] = std::move(config); - } + google::cloud::internal::FormatRfc3339(config_source.effective_time); + e[name] = std::move(config); + }; + to_json_config( + "googleManagedEncryptionEnforcementConfig", + meta.encryption().google_managed_encryption_enforcement_config); + to_json_config( + "customerManagedEncryptionEnforcementConfig", + meta.encryption().customer_managed_encryption_enforcement_config); + to_json_config( + "customerSuppliedEncryptionEnforcementConfig", + meta.encryption().customer_supplied_encryption_enforcement_config); + json["encryption"] = std::move(e); } diff --git a/google/cloud/storage/internal/grpc/bucket_request_parser.cc b/google/cloud/storage/internal/grpc/bucket_request_parser.cc index 05fbad6405341..703125edd9c02 100644 --- a/google/cloud/storage/internal/grpc/bucket_request_parser.cc +++ b/google/cloud/storage/internal/grpc/bucket_request_parser.cc @@ -191,8 +191,8 @@ Status PatchEncryption(Bucket& b, nlohmann::json const& e) { mutable_config->set_restriction_mode(c.value("restrictionMode", "")); } if (c.contains("effectiveTime")) { - auto ts = google::cloud::internal::ParseRfc3339( - c.value("effectiveTime", "")); + auto ts = + google::cloud::internal::ParseRfc3339(c.value("effectiveTime", "")); if (ts) { *mutable_config->mutable_effective_time() = ToProtoTimestamp(*ts); } @@ -333,30 +333,21 @@ void UpdateEncryption(Bucket& bucket, storage::BucketMetadata const& metadata) { auto& encryption = *bucket.mutable_encryption(); encryption.set_default_kms_key(metadata.encryption().default_kms_key_name); - auto const& gmek = - metadata.encryption().google_managed_encryption_enforcement_config; - if (!gmek.restriction_mode.empty()) { - auto& config = - *encryption.mutable_google_managed_encryption_enforcement_config(); - config.set_restriction_mode(gmek.restriction_mode); - *config.mutable_effective_time() = ToProtoTimestamp(gmek.effective_time); - } - auto const& cmek = - metadata.encryption().customer_managed_encryption_enforcement_config; - if (!cmek.restriction_mode.empty()) { - auto& config = - *encryption.mutable_customer_managed_encryption_enforcement_config(); - config.set_restriction_mode(cmek.restriction_mode); - *config.mutable_effective_time() = ToProtoTimestamp(cmek.effective_time); - } - auto const& csek = - metadata.encryption().customer_supplied_encryption_enforcement_config; - if (!csek.restriction_mode.empty()) { - auto& config = - *encryption.mutable_customer_supplied_encryption_enforcement_config(); - config.set_restriction_mode(csek.restriction_mode); - *config.mutable_effective_time() = ToProtoTimestamp(csek.effective_time); - } + auto update_config = [&](auto const& source, auto* dest) { + if (source.restriction_mode.empty()) return; + dest->set_restriction_mode(source.restriction_mode); + *dest->mutable_effective_time() = ToProtoTimestamp(source.effective_time); + }; + + update_config( + metadata.encryption().google_managed_encryption_enforcement_config, + encryption.mutable_google_managed_encryption_enforcement_config()); + update_config( + metadata.encryption().customer_managed_encryption_enforcement_config, + encryption.mutable_customer_managed_encryption_enforcement_config()); + update_config( + metadata.encryption().customer_supplied_encryption_enforcement_config, + encryption.mutable_customer_supplied_encryption_enforcement_config()); } void UpdateBilling(Bucket& bucket, storage::BucketMetadata const& metadata) { diff --git a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc index 1fe7c784066c9..0612e8f647b22 100644 --- a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc +++ b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc @@ -549,24 +549,21 @@ TEST(GrpcBucketRequestParser, PatchBucketRequestAllOptions) { storage::BucketLogging{"test-log-bucket", "test-log-prefix"}) .SetEncryption(storage::BucketEncryption{ /*.default_kms_key=*/"test-only-kms-key", - /*.google_managed_encryption_enforcement_config=*/storage:: - GoogleManagedEncryptionEnforcementConfig{ - "FULLY_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:19:32Z") - .value()}, - /*.customer_managed_encryption_enforcement_config=*/storage:: - CustomerManagedEncryptionEnforcementConfig{ - "NOT_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:21:35Z") - .value()}, - /*.customer_supplied_encryption_enforcement_config=*/storage:: - CustomerSuppliedEncryptionEnforcementConfig{ - "FULLY_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:22:19Z") - .value()}}) + /*.google_managed_encryption_enforcement_config=*/ + storage::GoogleManagedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:19:32Z") + .value()}, + /*.customer_managed_encryption_enforcement_config=*/ + storage::CustomerManagedEncryptionEnforcementConfig{ + "NOT_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:21:35Z") + .value()}, + /*.customer_supplied_encryption_enforcement_config=*/ + storage::CustomerSuppliedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:22:19Z") + .value()}}) .SetAutoclass(storage::BucketAutoclass{true}) .SetBilling(storage::BucketBilling{/*.requester_pays=*/true}) .SetRetentionPolicy(std::chrono::seconds(123000)) @@ -834,24 +831,21 @@ TEST(GrpcBucketRequestParser, UpdateBucketRequestAllOptions) { storage::BucketLogging{"test-log-bucket", "test-log-prefix"}) .set_encryption(storage::BucketEncryption{ /*.default_kms_key=*/"test-only-kms-key", - /*.google_managed_encryption_enforcement_config=*/storage:: - GoogleManagedEncryptionEnforcementConfig{ - "FULLY_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:27:45Z") - .value()}, - /*.customer_managed_encryption_enforcement_config=*/storage:: - CustomerManagedEncryptionEnforcementConfig{ - "NOT_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:28:25Z") - .value()}, - /*.customer_supplied_encryption_enforcement_config=*/storage:: - CustomerSuppliedEncryptionEnforcementConfig{ - "FULLY_RESTRICTED", - google::cloud::internal::ParseRfc3339( - "2025-12-19T20:29:11Z") - .value()}}) + /*.google_managed_encryption_enforcement_config=*/ + storage::GoogleManagedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:27:45Z") + .value()}, + /*.customer_managed_encryption_enforcement_config=*/ + storage::CustomerManagedEncryptionEnforcementConfig{ + "NOT_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:28:25Z") + .value()}, + /*.customer_supplied_encryption_enforcement_config=*/ + storage::CustomerSuppliedEncryptionEnforcementConfig{ + "FULLY_RESTRICTED", + google::cloud::internal::ParseRfc3339("2025-12-19T20:29:11Z") + .value()}}) .set_autoclass(storage::BucketAutoclass{true}) .set_billing(storage::BucketBilling{/*.requester_pays=*/true}) .set_retention_policy(std::chrono::seconds(123000)) diff --git a/google/cloud/storage/storage_client_unit_tests.bzl b/google/cloud/storage/storage_client_unit_tests.bzl index ac7311cf78af4..b1a8a1bdb56e3 100644 --- a/google/cloud/storage/storage_client_unit_tests.bzl +++ b/google/cloud/storage/storage_client_unit_tests.bzl @@ -20,7 +20,6 @@ storage_client_unit_tests = [ "auto_finalize_test.cc", "bucket_access_control_test.cc", "bucket_cors_entry_test.cc", - "bucket_encryption_feature_test.cc", "bucket_iam_configuration_test.cc", "bucket_metadata_test.cc", "bucket_object_retention_test.cc",