Skip to content

Commit 3c1d254

Browse files
authored
feat(storage): add support for bucket encryption enforcement config (#15844)
1 parent 6b6d490 commit 3c1d254

11 files changed

+559
-26
lines changed

ci/cloudbuild/builds/lib/integration.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ source module ci/lib/io.sh
3131
export PATH="${HOME}/.local/bin:${PATH}"
3232
python3 -m pip uninstall -y --quiet googleapis-storage-testbench
3333
python3 -m pip install --upgrade --user --quiet --disable-pip-version-check \
34-
"git+https://github.com/googleapis/storage-testbench@v0.59.0"
34+
"git+https://github.com/googleapis/storage-testbench@v0.60.0"
3535

3636
# Some of the tests will need a valid roots.pem file.
3737
rm -f /dev/shm/roots.pem

google/cloud/storage/bucket_encryption.h

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,109 @@
1616
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_ENCRYPTION_H
1717

1818
#include "google/cloud/storage/version.h"
19+
#include "google/cloud/internal/format_time_point.h"
20+
#include <chrono>
21+
#include <iosfwd>
22+
#include <iostream>
23+
#include <string>
24+
#include <tuple>
1925
#include <utility>
2026

2127
namespace google {
2228
namespace cloud {
2329
namespace storage {
2430
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
2531

32+
template <typename Tag>
33+
struct EncryptionEnforcementConfigName;
34+
35+
template <typename Tag>
36+
struct EncryptionEnforcementConfig {
37+
std::string restriction_mode;
38+
std::chrono::system_clock::time_point effective_time;
39+
};
40+
41+
template <typename Tag>
42+
inline bool operator==(EncryptionEnforcementConfig<Tag> const& lhs,
43+
EncryptionEnforcementConfig<Tag> const& rhs) {
44+
return std::tie(lhs.restriction_mode, lhs.effective_time) ==
45+
std::tie(rhs.restriction_mode, rhs.effective_time);
46+
}
47+
48+
template <typename Tag>
49+
inline bool operator<(EncryptionEnforcementConfig<Tag> const& lhs,
50+
EncryptionEnforcementConfig<Tag> const& rhs) {
51+
return std::tie(lhs.restriction_mode, lhs.effective_time) <
52+
std::tie(rhs.restriction_mode, rhs.effective_time);
53+
}
54+
55+
template <typename Tag>
56+
inline bool operator!=(EncryptionEnforcementConfig<Tag> const& lhs,
57+
EncryptionEnforcementConfig<Tag> const& rhs) {
58+
return std::rel_ops::operator!=(lhs, rhs);
59+
}
60+
61+
template <typename Tag>
62+
inline bool operator>(EncryptionEnforcementConfig<Tag> const& lhs,
63+
EncryptionEnforcementConfig<Tag> const& rhs) {
64+
return std::rel_ops::operator>(lhs, rhs);
65+
}
66+
67+
template <typename Tag>
68+
inline bool operator<=(EncryptionEnforcementConfig<Tag> const& lhs,
69+
EncryptionEnforcementConfig<Tag> const& rhs) {
70+
return std::rel_ops::operator<=(lhs, rhs);
71+
}
72+
73+
template <typename Tag>
74+
inline bool operator>=(EncryptionEnforcementConfig<Tag> const& lhs,
75+
EncryptionEnforcementConfig<Tag> const& rhs) {
76+
return std::rel_ops::operator>=(lhs, rhs);
77+
}
78+
79+
template <typename Tag>
80+
inline std::ostream& operator<<(std::ostream& os,
81+
EncryptionEnforcementConfig<Tag> const& rhs) {
82+
return os << EncryptionEnforcementConfigName<Tag>::kValue
83+
<< "={restriction_mode=" << rhs.restriction_mode
84+
<< ", effective_time="
85+
<< google::cloud::internal::FormatRfc3339(rhs.effective_time)
86+
<< "}";
87+
}
88+
89+
struct GoogleManagedEncryptionEnforcementConfigTag {};
90+
using GoogleManagedEncryptionEnforcementConfig =
91+
EncryptionEnforcementConfig<GoogleManagedEncryptionEnforcementConfigTag>;
92+
93+
template <>
94+
struct EncryptionEnforcementConfigName<
95+
GoogleManagedEncryptionEnforcementConfigTag> {
96+
static constexpr char const* kValue =
97+
"GoogleManagedEncryptionEnforcementConfig";
98+
};
99+
100+
struct CustomerManagedEncryptionEnforcementConfigTag {};
101+
using CustomerManagedEncryptionEnforcementConfig =
102+
EncryptionEnforcementConfig<CustomerManagedEncryptionEnforcementConfigTag>;
103+
104+
template <>
105+
struct EncryptionEnforcementConfigName<
106+
CustomerManagedEncryptionEnforcementConfigTag> {
107+
static constexpr char const* kValue =
108+
"CustomerManagedEncryptionEnforcementConfig";
109+
};
110+
111+
struct CustomerSuppliedEncryptionEnforcementConfigTag {};
112+
using CustomerSuppliedEncryptionEnforcementConfig =
113+
EncryptionEnforcementConfig<CustomerSuppliedEncryptionEnforcementConfigTag>;
114+
115+
template <>
116+
struct EncryptionEnforcementConfigName<
117+
CustomerSuppliedEncryptionEnforcementConfigTag> {
118+
static constexpr char const* kValue =
119+
"CustomerSuppliedEncryptionEnforcementConfig";
120+
};
121+
26122
/**
27123
* Describes the default customer managed encryption key for a bucket.
28124
*
@@ -36,17 +132,49 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
36132
* Service.
37133
*/
38134
struct BucketEncryption {
135+
BucketEncryption() = default;
136+
explicit BucketEncryption(std::string default_kms_key_name)
137+
: default_kms_key_name(std::move(default_kms_key_name)) {}
138+
BucketEncryption(std::string default_kms_key_name,
139+
GoogleManagedEncryptionEnforcementConfig gmek,
140+
CustomerManagedEncryptionEnforcementConfig cmek,
141+
CustomerSuppliedEncryptionEnforcementConfig csek)
142+
: default_kms_key_name(std::move(default_kms_key_name)),
143+
google_managed_encryption_enforcement_config(std::move(gmek)),
144+
customer_managed_encryption_enforcement_config(std::move(cmek)),
145+
customer_supplied_encryption_enforcement_config(std::move(csek)) {}
146+
39147
std::string default_kms_key_name;
148+
GoogleManagedEncryptionEnforcementConfig
149+
google_managed_encryption_enforcement_config;
150+
CustomerManagedEncryptionEnforcementConfig
151+
customer_managed_encryption_enforcement_config;
152+
CustomerSuppliedEncryptionEnforcementConfig
153+
customer_supplied_encryption_enforcement_config;
40154
};
41155

42156
inline bool operator==(BucketEncryption const& lhs,
43157
BucketEncryption const& rhs) {
44-
return lhs.default_kms_key_name == rhs.default_kms_key_name;
158+
return std::tie(lhs.default_kms_key_name,
159+
lhs.google_managed_encryption_enforcement_config,
160+
lhs.customer_managed_encryption_enforcement_config,
161+
lhs.customer_supplied_encryption_enforcement_config) ==
162+
std::tie(rhs.default_kms_key_name,
163+
rhs.google_managed_encryption_enforcement_config,
164+
rhs.customer_managed_encryption_enforcement_config,
165+
rhs.customer_supplied_encryption_enforcement_config);
45166
}
46167

47168
inline bool operator<(BucketEncryption const& lhs,
48169
BucketEncryption const& rhs) {
49-
return lhs.default_kms_key_name < rhs.default_kms_key_name;
170+
return std::tie(lhs.default_kms_key_name,
171+
lhs.google_managed_encryption_enforcement_config,
172+
lhs.customer_managed_encryption_enforcement_config,
173+
lhs.customer_supplied_encryption_enforcement_config) <
174+
std::tie(rhs.default_kms_key_name,
175+
rhs.google_managed_encryption_enforcement_config,
176+
rhs.customer_managed_encryption_enforcement_config,
177+
rhs.customer_supplied_encryption_enforcement_config);
50178
}
51179

52180
inline bool operator!=(BucketEncryption const& lhs,
@@ -69,6 +197,17 @@ inline bool operator>=(BucketEncryption const& lhs,
69197
return std::rel_ops::operator>=(lhs, rhs);
70198
}
71199

200+
inline std::ostream& operator<<(std::ostream& os, BucketEncryption const& rhs) {
201+
os << "BucketEncryption={default_kms_key_name=" << rhs.default_kms_key_name;
202+
os << ", google_managed_encryption_enforcement_config="
203+
<< rhs.google_managed_encryption_enforcement_config;
204+
os << ", customer_managed_encryption_enforcement_config="
205+
<< rhs.customer_managed_encryption_enforcement_config;
206+
os << ", customer_supplied_encryption_enforcement_config="
207+
<< rhs.customer_supplied_encryption_enforcement_config;
208+
return os << "}";
209+
}
210+
72211
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
73212
} // namespace storage
74213
} // namespace cloud
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/storage/bucket_metadata.h"
16+
#include "google/cloud/storage/internal/bucket_metadata_parser.h"
17+
#include "google/cloud/internal/format_time_point.h"
18+
#include "google/cloud/testing_util/status_matchers.h"
19+
#include <gmock/gmock.h>
20+
#include <nlohmann/json.hpp>
21+
22+
namespace google {
23+
namespace cloud {
24+
namespace storage {
25+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
26+
namespace {
27+
28+
TEST(BucketEncryptionTest, Parse) {
29+
std::string text = R"""({
30+
"encryption": {
31+
"defaultKmsKeyName": "projects/test-project-name/locations/us-central1/keyRings/test-keyring-name/cryptoKeys/test-key-name",
32+
"googleManagedEncryptionEnforcementConfig": {
33+
"restrictionMode": "FullyRestricted",
34+
"effectiveTime": "2025-12-18T18:13:15Z"
35+
},
36+
"customerManagedEncryptionEnforcementConfig": {
37+
"restrictionMode": "NotRestricted",
38+
"effectiveTime": "2025-12-18T18:13:15Z"
39+
},
40+
"customerSuppliedEncryptionEnforcementConfig": {
41+
"restrictionMode": "NotRestricted",
42+
"effectiveTime": "2025-12-18T18:13:15Z"
43+
}
44+
}
45+
})""";
46+
47+
auto actual = internal::BucketMetadataParser::FromString(text);
48+
ASSERT_STATUS_OK(actual);
49+
50+
ASSERT_TRUE(actual->has_encryption());
51+
auto const& encryption = actual->encryption();
52+
EXPECT_EQ(
53+
"projects/test-project-name/locations/us-central1/keyRings/"
54+
"test-keyring-name/cryptoKeys/test-key-name",
55+
encryption.default_kms_key_name);
56+
57+
EXPECT_EQ(
58+
"FullyRestricted",
59+
encryption.google_managed_encryption_enforcement_config.restriction_mode);
60+
EXPECT_EQ("2025-12-18T18:13:15Z",
61+
google::cloud::internal::FormatRfc3339(
62+
encryption.google_managed_encryption_enforcement_config
63+
.effective_time));
64+
65+
EXPECT_EQ("NotRestricted",
66+
encryption.customer_managed_encryption_enforcement_config
67+
.restriction_mode);
68+
EXPECT_EQ("2025-12-18T18:13:15Z",
69+
google::cloud::internal::FormatRfc3339(
70+
encryption.customer_managed_encryption_enforcement_config
71+
.effective_time));
72+
73+
EXPECT_EQ("NotRestricted",
74+
encryption.customer_supplied_encryption_enforcement_config
75+
.restriction_mode);
76+
EXPECT_EQ("2025-12-18T18:13:15Z",
77+
google::cloud::internal::FormatRfc3339(
78+
encryption.customer_supplied_encryption_enforcement_config
79+
.effective_time));
80+
}
81+
82+
TEST(BucketEncryptionTest, ToJson) {
83+
BucketMetadata meta;
84+
BucketEncryption encryption;
85+
encryption.default_kms_key_name = "test-key";
86+
encryption.google_managed_encryption_enforcement_config.restriction_mode =
87+
"FullyRestricted";
88+
encryption.google_managed_encryption_enforcement_config.effective_time =
89+
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();
90+
encryption.customer_managed_encryption_enforcement_config.restriction_mode =
91+
"NotRestricted";
92+
encryption.customer_managed_encryption_enforcement_config.effective_time =
93+
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();
94+
95+
meta.set_encryption(encryption);
96+
97+
auto json_string = internal::BucketMetadataToJsonString(meta);
98+
auto json = nlohmann::json::parse(json_string);
99+
100+
ASSERT_TRUE(json.contains("encryption"));
101+
auto e = json["encryption"];
102+
EXPECT_EQ("test-key", e["defaultKmsKeyName"]);
103+
EXPECT_EQ("FullyRestricted",
104+
e["googleManagedEncryptionEnforcementConfig"]["restrictionMode"]);
105+
EXPECT_EQ("2025-12-18T18:13:15Z",
106+
e["googleManagedEncryptionEnforcementConfig"]["effectiveTime"]);
107+
EXPECT_EQ("NotRestricted",
108+
e["customerManagedEncryptionEnforcementConfig"]["restrictionMode"]);
109+
EXPECT_EQ("2025-12-18T18:13:15Z",
110+
e["customerManagedEncryptionEnforcementConfig"]["effectiveTime"]);
111+
EXPECT_FALSE(e.contains("customerSuppliedEncryptionEnforcementConfig"));
112+
}
113+
114+
TEST(BucketEncryptionTest, Patch) {
115+
BucketMetadataPatchBuilder builder;
116+
BucketEncryption encryption;
117+
encryption.default_kms_key_name = "test-key";
118+
encryption.google_managed_encryption_enforcement_config.restriction_mode =
119+
"FullyRestricted";
120+
encryption.google_managed_encryption_enforcement_config.effective_time =
121+
google::cloud::internal::ParseRfc3339("2025-12-18T18:13:15Z").value();
122+
123+
builder.SetEncryption(encryption);
124+
125+
auto patch_string = builder.BuildPatch();
126+
auto patch = nlohmann::json::parse(patch_string);
127+
128+
ASSERT_TRUE(patch.contains("encryption"));
129+
auto e = patch["encryption"];
130+
EXPECT_EQ("test-key", e["defaultKmsKeyName"]);
131+
EXPECT_EQ("FullyRestricted",
132+
e["googleManagedEncryptionEnforcementConfig"]["restrictionMode"]);
133+
EXPECT_EQ("2025-12-18T18:13:15Z",
134+
e["googleManagedEncryptionEnforcementConfig"]["effectiveTime"]);
135+
}
136+
137+
} // namespace
138+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
139+
} // namespace storage
140+
} // namespace cloud
141+
} // namespace google

google/cloud/storage/bucket_metadata.cc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) {
150150
os << "]";
151151

152152
if (rhs.has_encryption()) {
153-
os << ", encryption.default_kms_key_name="
154-
<< rhs.encryption().default_kms_key_name;
153+
os << ", encryption=" << rhs.encryption();
155154
}
156155

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

363362
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetEncryption(
364363
BucketEncryption const& v) {
365-
impl_.AddSubPatch("encryption",
366-
internal::PatchBuilder().SetStringField(
367-
"defaultKmsKeyName", v.default_kms_key_name));
364+
internal::PatchBuilder builder;
365+
builder.SetStringField("defaultKmsKeyName", v.default_kms_key_name);
366+
367+
auto add_config_patch = [&](char const* name, auto const& config) {
368+
if (config.restriction_mode.empty()) return;
369+
builder.AddSubPatch(
370+
name, internal::PatchBuilder()
371+
.SetStringField("restrictionMode", config.restriction_mode)
372+
.SetStringField("effectiveTime",
373+
google::cloud::internal::FormatRfc3339(
374+
config.effective_time)));
375+
};
376+
add_config_patch("googleManagedEncryptionEnforcementConfig",
377+
v.google_managed_encryption_enforcement_config);
378+
add_config_patch("customerManagedEncryptionEnforcementConfig",
379+
v.customer_managed_encryption_enforcement_config);
380+
add_config_patch("customerSuppliedEncryptionEnforcementConfig",
381+
v.customer_supplied_encryption_enforcement_config);
382+
383+
impl_.AddSubPatch("encryption", std::move(builder));
368384
return *this;
369385
}
370386

0 commit comments

Comments
 (0)