Skip to content

Commit fdbec33

Browse files
authored
fix(storage): add samples for bucket encryption encoforcement config (#16045)
1 parent 2bb5233 commit fdbec33

File tree

8 files changed

+264
-44
lines changed

8 files changed

+264
-44
lines changed

google/cloud/storage/bucket_metadata.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -362,16 +362,16 @@ BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetDefaultAcl() {
362362
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetEncryption(
363363
BucketEncryption const& v) {
364364
internal::PatchBuilder builder;
365-
builder.SetStringField("defaultKmsKeyName", v.default_kms_key_name);
365+
if (v.default_kms_key_name.empty()) {
366+
builder.RemoveField("defaultKmsKeyName");
367+
} else {
368+
builder.SetStringField("defaultKmsKeyName", v.default_kms_key_name);
369+
}
366370

367371
auto add_config_patch = [&](char const* name, auto const& config) {
368372
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)));
373+
builder.AddSubPatch(name, internal::PatchBuilder().SetStringField(
374+
"restrictionMode", config.restriction_mode));
375375
};
376376
add_config_patch("googleManagedEncryptionEnforcementConfig",
377377
v.google_managed_encryption_enforcement_config);

google/cloud/storage/bucket_metadata_test.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -526,23 +526,14 @@ TEST(BucketMetadataTest, ToJsonString) {
526526
EXPECT_EQ("FullyRestricted",
527527
encryption["googleManagedEncryptionEnforcementConfig"].value(
528528
"restrictionMode", ""));
529-
EXPECT_EQ("2025-12-18T18:13:15Z",
530-
encryption["googleManagedEncryptionEnforcementConfig"].value(
531-
"effectiveTime", ""));
532529

533530
EXPECT_EQ("NotRestricted",
534531
encryption["customerManagedEncryptionEnforcementConfig"].value(
535532
"restrictionMode", ""));
536-
EXPECT_EQ("2025-12-18T18:13:15Z",
537-
encryption["customerManagedEncryptionEnforcementConfig"].value(
538-
"effectiveTime", ""));
539533

540534
EXPECT_EQ("NotRestricted",
541535
encryption["customerSuppliedEncryptionEnforcementConfig"].value(
542536
"restrictionMode", ""));
543-
EXPECT_EQ("2025-12-18T18:13:15Z",
544-
encryption["customerSuppliedEncryptionEnforcementConfig"].value(
545-
"effectiveTime", ""));
546537

547538
// hierarchical_namespace()
548539
ASSERT_EQ(1, actual.count("hierarchicalNamespace"));

google/cloud/storage/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ set(storage_examples
3636
storage_bucket_autoclass_samples.cc
3737
storage_bucket_cors_samples.cc
3838
storage_bucket_default_kms_key_samples.cc
39+
storage_bucket_encryption_enforcement_samples.cc
3940
storage_bucket_iam_samples.cc
4041
storage_bucket_object_retention_samples.cc
4142
storage_bucket_requester_pays_samples.cc
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// Copyright 2026 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/client.h"
16+
#include "google/cloud/storage/examples/storage_examples_common.h"
17+
#include "google/cloud/internal/format_time_point.h"
18+
#include "google/cloud/internal/getenv.h"
19+
#include <iostream>
20+
#include <thread>
21+
#include <vector>
22+
23+
namespace {
24+
25+
void GetBucketEncryptionEnforcementConfig(
26+
google::cloud::storage::Client client,
27+
std::vector<std::string> const& argv) {
28+
//! [get bucket encryption enforcement config] [START
29+
//! storage_get_bucket_encryption_enforcement_config]
30+
namespace gcs = ::google::cloud::storage;
31+
using ::google::cloud::StatusOr;
32+
[](gcs::Client client, std::string const& bucket_name) {
33+
StatusOr<gcs::BucketMetadata> metadata =
34+
client.GetBucketMetadata(bucket_name);
35+
if (!metadata) throw std::move(metadata).status();
36+
37+
std::cout << "Bucket Name: " << metadata->name() << "\n";
38+
39+
if (!metadata->has_encryption()) {
40+
std::cout << " GMEK Enforcement: NOT SET (Default)\n"
41+
<< " CMEK Enforcement: NOT SET (Default)\n"
42+
<< " CSEK Enforcement: NOT SET (Default)\n";
43+
return;
44+
}
45+
46+
auto const& encryption = metadata->encryption();
47+
48+
auto format_config = [](auto const& config) {
49+
if (config.restriction_mode.empty())
50+
return std::string("NOT SET (Default)");
51+
return "Mode: " + config.restriction_mode + ", Effective Time: " +
52+
google::cloud::internal::FormatRfc3339(config.effective_time);
53+
};
54+
55+
std::cout << " GMEK Enforcement: "
56+
<< format_config(
57+
encryption.google_managed_encryption_enforcement_config)
58+
<< "\n"
59+
<< " CMEK Enforcement: "
60+
<< format_config(
61+
encryption.customer_managed_encryption_enforcement_config)
62+
<< "\n"
63+
<< " CSEK Enforcement: "
64+
<< format_config(
65+
encryption.customer_supplied_encryption_enforcement_config)
66+
<< "\n";
67+
}
68+
//! [get bucket encryption enforcement config] [END
69+
//! storage_get_bucket_encryption_enforcement_config]
70+
(std::move(client), argv.at(0));
71+
}
72+
73+
void SetBucketEncryptionEnforcementConfig(
74+
google::cloud::storage::Client client,
75+
std::vector<std::string> const& argv) {
76+
//! [set bucket encryption enforcement config] [START
77+
//! storage_set_bucket_encryption_enforcement_config]
78+
namespace gcs = ::google::cloud::storage;
79+
using ::google::cloud::StatusOr;
80+
[](gcs::Client client, std::string const& project_id,
81+
std::string const& bucket_name) {
82+
auto create_bucket = [&](std::string const& name,
83+
gcs::BucketEncryption encryption) {
84+
StatusOr<gcs::BucketMetadata> bucket = client.CreateBucketForProject(
85+
name, project_id, gcs::BucketMetadata().set_encryption(encryption));
86+
if (!bucket) throw std::move(bucket).status();
87+
return bucket;
88+
};
89+
90+
// Example 1: Enforce GMEK Only
91+
gcs::BucketEncryption gmek_encryption;
92+
gmek_encryption.google_managed_encryption_enforcement_config
93+
.restriction_mode = "NotRestricted";
94+
gmek_encryption.customer_managed_encryption_enforcement_config
95+
.restriction_mode = "FullyRestricted";
96+
gmek_encryption.customer_supplied_encryption_enforcement_config
97+
.restriction_mode = "FullyRestricted";
98+
std::cout << "Bucket "
99+
<< create_bucket("g-" + bucket_name, gmek_encryption)->name()
100+
<< " created with GMEK-only enforcement policy.\n";
101+
102+
// In GCS, a single project cannot create or delete buckets more often than
103+
// once every two seconds. We pause to avoid rate limiting.
104+
std::this_thread::sleep_for(std::chrono::seconds(2));
105+
106+
// Example 2: Enforce CMEK Only
107+
gcs::BucketEncryption cmek_encryption;
108+
cmek_encryption.google_managed_encryption_enforcement_config
109+
.restriction_mode = "FullyRestricted";
110+
cmek_encryption.customer_managed_encryption_enforcement_config
111+
.restriction_mode = "NotRestricted";
112+
cmek_encryption.customer_supplied_encryption_enforcement_config
113+
.restriction_mode = "FullyRestricted";
114+
std::cout << "Bucket "
115+
<< create_bucket("c-" + bucket_name, cmek_encryption)->name()
116+
<< " created with CMEK-only enforcement policy.\n";
117+
118+
// In GCS, a single project cannot create or delete buckets more often than
119+
// once every two seconds. We pause to avoid rate limiting.
120+
std::this_thread::sleep_for(std::chrono::seconds(2));
121+
122+
// Example 3: Restrict CSEK (Ransomware Protection)
123+
gcs::BucketEncryption csek_encryption;
124+
csek_encryption.customer_supplied_encryption_enforcement_config
125+
.restriction_mode = "FullyRestricted";
126+
std::cout << "Bucket "
127+
<< create_bucket("rc-" + bucket_name, csek_encryption)->name()
128+
<< " created with a policy to restrict CSEK.\n";
129+
}
130+
//! [set bucket encryption enforcement config] [END
131+
//! storage_set_bucket_encryption_enforcement_config]
132+
(std::move(client), argv.at(0), argv.at(1));
133+
}
134+
135+
void UpdateBucketEncryptionEnforcementConfig(
136+
google::cloud::storage::Client client,
137+
std::vector<std::string> const& argv) {
138+
//! [update bucket encryption enforcement config] [START
139+
//! storage_update_bucket_encryption_enforcement_config]
140+
namespace gcs = ::google::cloud::storage;
141+
using ::google::cloud::StatusOr;
142+
[](gcs::Client client, std::string const& bucket_name) {
143+
StatusOr<gcs::BucketMetadata> original =
144+
client.GetBucketMetadata(bucket_name);
145+
146+
gcs::BucketMetadata updated_metadata = *original;
147+
gcs::BucketEncryption encryption;
148+
if (original->has_encryption()) {
149+
encryption = original->encryption();
150+
}
151+
152+
// 1. Update a specific type (e.g., change GMEK to FullyRestricted)
153+
encryption.google_managed_encryption_enforcement_config.restriction_mode =
154+
"FullyRestricted";
155+
// 2. Remove a specific type (e.g., remove CMEK enforcement)
156+
encryption.customer_managed_encryption_enforcement_config.restriction_mode =
157+
"NotRestricted";
158+
// For the update, need to specify all three configs, so keeping this same
159+
// as before
160+
encryption.customer_supplied_encryption_enforcement_config
161+
.restriction_mode = "FullyRestricted";
162+
163+
updated_metadata.set_encryption(encryption);
164+
165+
StatusOr<gcs::BucketMetadata> updated =
166+
client.PatchBucket(bucket_name, *original, updated_metadata);
167+
if (!updated) throw std::move(updated).status();
168+
169+
std::cout << "Encryption enforcement policy updated for bucket "
170+
<< updated->name() << "\n"
171+
<< "GMEK is now fully restricted, and CMEK enforcement has been "
172+
"removed.\n";
173+
}
174+
//! [update bucket encryption enforcement config] [END
175+
//! storage_update_bucket_encryption_enforcement_config]
176+
(std::move(client), argv.at(0));
177+
}
178+
179+
void RunAll(std::vector<std::string> const& argv) {
180+
namespace examples = ::google::cloud::storage::examples;
181+
namespace gcs = ::google::cloud::storage;
182+
183+
if (!argv.empty()) throw examples::Usage{"auto"};
184+
examples::CheckEnvironmentVariablesAreSet({"GOOGLE_CLOUD_PROJECT"});
185+
auto const project_id =
186+
google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value();
187+
auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
188+
// Shorten bucket name to allow for prefixes without exceeding max length
189+
auto const bucket_name =
190+
examples::MakeRandomBucketName(generator).substr(0, 50);
191+
auto client = gcs::Client(examples::CreateBucketOptions());
192+
193+
auto constexpr kBucketPeriod = std::chrono::seconds(2);
194+
195+
// Clean up any potentially leaked buckets from a previous run before creating
196+
// them.
197+
(void)examples::RemoveBucketAndContents(client, "g-" + bucket_name);
198+
if (!examples::UsingEmulator()) std::this_thread::sleep_for(kBucketPeriod);
199+
(void)examples::RemoveBucketAndContents(client, "c-" + bucket_name);
200+
if (!examples::UsingEmulator()) std::this_thread::sleep_for(kBucketPeriod);
201+
(void)examples::RemoveBucketAndContents(client, "rc-" + bucket_name);
202+
if (!examples::UsingEmulator()) std::this_thread::sleep_for(kBucketPeriod);
203+
204+
std::cout << "\nRunning the SetBucketEncryptionEnforcementConfig() example"
205+
<< std::endl;
206+
SetBucketEncryptionEnforcementConfig(client, {project_id, bucket_name});
207+
208+
std::cout
209+
<< "\nRunning the GetBucketEncryptionEnforcementConfig() example [1]"
210+
<< std::endl;
211+
GetBucketEncryptionEnforcementConfig(client, {"c-" + bucket_name});
212+
213+
std::cout << "\nRunning the UpdateBucketEncryptionEnforcementConfig() example"
214+
<< std::endl;
215+
UpdateBucketEncryptionEnforcementConfig(client, {"c-" + bucket_name});
216+
217+
std::cout
218+
<< "\nRunning the GetBucketEncryptionEnforcementConfig() example [2]"
219+
<< std::endl;
220+
GetBucketEncryptionEnforcementConfig(client, {"c-" + bucket_name});
221+
222+
// In GCS a single project cannot create or delete buckets more often than
223+
// once every two seconds. We will pause until that time before deleting the
224+
// buckets.
225+
auto pause = std::chrono::steady_clock::now() + kBucketPeriod;
226+
if (!examples::UsingEmulator()) std::this_thread::sleep_until(pause);
227+
228+
(void)examples::RemoveBucketAndContents(client, "g-" + bucket_name);
229+
if (!examples::UsingEmulator()) std::this_thread::sleep_for(kBucketPeriod);
230+
(void)examples::RemoveBucketAndContents(client, "c-" + bucket_name);
231+
if (!examples::UsingEmulator()) std::this_thread::sleep_for(kBucketPeriod);
232+
(void)examples::RemoveBucketAndContents(client, "rc-" + bucket_name);
233+
}
234+
235+
} // namespace
236+
237+
int main(int argc, char* argv[]) {
238+
namespace examples = ::google::cloud::storage::examples;
239+
auto make_entry = [](std::string const& name,
240+
std::vector<std::string> arg_names,
241+
examples::ClientCommand const& cmd) {
242+
return examples::CreateCommandEntry(name, std::move(arg_names), cmd);
243+
};
244+
examples::Example example({
245+
make_entry("get-bucket-encryption-enforcement-config", {"<bucket-name>"},
246+
GetBucketEncryptionEnforcementConfig),
247+
make_entry("set-bucket-encryption-enforcement-config",
248+
{"<project-id>", "<bucket-name>"},
249+
SetBucketEncryptionEnforcementConfig),
250+
make_entry("update-bucket-encryption-enforcement-config",
251+
{"<bucket-name>"}, UpdateBucketEncryptionEnforcementConfig),
252+
{"auto", RunAll},
253+
});
254+
return example.Run(argc, argv);
255+
}

google/cloud/storage/examples/storage_examples.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ storage_examples = [
2121
"storage_bucket_autoclass_samples.cc",
2222
"storage_bucket_cors_samples.cc",
2323
"storage_bucket_default_kms_key_samples.cc",
24+
"storage_bucket_encryption_enforcement_samples.cc",
2425
"storage_bucket_iam_samples.cc",
2526
"storage_bucket_object_retention_samples.cc",
2627
"storage_bucket_requester_pays_samples.cc",

google/cloud/storage/internal/bucket_metadata_parser.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,6 @@ void ToJsonEncryption(nlohmann::json& json, BucketMetadata const& meta) {
412412
if (config_source.restriction_mode.empty()) return;
413413
nlohmann::json config;
414414
config["restrictionMode"] = config_source.restriction_mode;
415-
config["effectiveTime"] =
416-
google::cloud::internal::FormatRfc3339(config_source.effective_time);
417415
e[name] = std::move(config);
418416
};
419417
to_json_config(

google/cloud/storage/internal/grpc/bucket_request_parser.cc

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,6 @@ Status PatchLogging(Bucket& b, nlohmann::json const& l) {
163163
return Status{};
164164
}
165165

166-
google::protobuf::Timestamp ToProtoTimestamp(
167-
std::chrono::system_clock::time_point tp) {
168-
auto duration = tp.time_since_epoch();
169-
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
170-
auto nanos =
171-
std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds);
172-
google::protobuf::Timestamp ts;
173-
ts.set_seconds(seconds.count());
174-
ts.set_nanos(static_cast<std::int32_t>(nanos.count()));
175-
return ts;
176-
}
177-
178166
Status PatchEncryption(Bucket& b, nlohmann::json const& e) {
179167
if (e.is_null()) {
180168
b.clear_encryption();
@@ -190,13 +178,6 @@ Status PatchEncryption(Bucket& b, nlohmann::json const& e) {
190178
if (c.contains("restrictionMode")) {
191179
mutable_config->set_restriction_mode(c.value("restrictionMode", ""));
192180
}
193-
if (c.contains("effectiveTime")) {
194-
auto ts =
195-
google::cloud::internal::ParseRfc3339(c.value("effectiveTime", ""));
196-
if (ts) {
197-
*mutable_config->mutable_effective_time() = ToProtoTimestamp(*ts);
198-
}
199-
}
200181
}
201182
};
202183

@@ -336,7 +317,6 @@ void UpdateEncryption(Bucket& bucket, storage::BucketMetadata const& metadata) {
336317
auto update_config = [&](auto const& source, auto* dest) {
337318
if (source.restriction_mode.empty()) return;
338319
dest->set_restriction_mode(source.restriction_mode);
339-
*dest->mutable_effective_time() = ToProtoTimestamp(source.effective_time);
340320
};
341321

342322
update_config(

0 commit comments

Comments
 (0)