Skip to content

Commit 6acaff3

Browse files
feat(storage): Support bucket encryption config (#33452)
1 parent 67687e9 commit 6acaff3

11 files changed

Lines changed: 537 additions & 10 deletions

google-cloud-storage/acceptance/storage/bucket_encryption_test.rb

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,23 @@
2525
ENV["GCLOUD_TEST_STORAGE_KMS_KEY_2"] ||
2626
"projects/#{storage.project_id}/locations/#{bucket_location}/keyRings/ruby-test/cryptoKeys/ruby-test-key-2"
2727
}
28+
29+
let(:customer_managed_config) do
30+
{ restriction_mode: "NotRestricted" }
31+
end
32+
33+
let(:customer_supplied_config) do
34+
{ restriction_mode: "FullyRestricted" }
35+
end
36+
37+
let(:google_managed_config) do
38+
{ restriction_mode: "FullyRestricted" }
39+
end
40+
2841
let :bucket do
29-
b = safe_gcs_execute { storage.create_bucket(bucket_name, location: bucket_location) }
42+
b = safe_gcs_execute { storage.bucket(bucket_name) || storage.create_bucket(bucket_name, location: bucket_location) }
3043
b.default_kms_key = kms_key
44+
b.customer_managed_encryption_enforcement_config = customer_managed_config
3145
b
3246
end
3347

@@ -71,4 +85,45 @@
7185
_(bucket.default_kms_key).must_be :nil?
7286
end
7387
end
88+
89+
describe "Encryption Enforcement Config" do
90+
let(:google_managed_config_complete) do
91+
{google_managed_encryption_enforcement_config: { restriction_mode: "FullyRestricted" } }
92+
end
93+
it "knows its encryption enforcement config" do
94+
_(bucket.customer_managed_encryption_enforcement_config).wont_be :nil?
95+
_(bucket.customer_managed_encryption_enforcement_config.restriction_mode).must_equal "NotRestricted"
96+
bucket.reload!
97+
_(bucket.customer_managed_encryption_enforcement_config).wont_be :nil?
98+
_(bucket.customer_managed_encryption_enforcement_config.restriction_mode).must_equal "NotRestricted"
99+
end
100+
101+
it "updates encryption enforcement configs" do
102+
_(bucket.customer_supplied_encryption_enforcement_config).must_be :nil?
103+
104+
bucket.customer_supplied_encryption_enforcement_config = customer_supplied_config
105+
_(bucket.customer_supplied_encryption_enforcement_config.restriction_mode).must_equal "FullyRestricted"
106+
bucket.update do |b|
107+
b.google_managed_encryption_enforcement_config = google_managed_config
108+
end
109+
_(bucket.google_managed_encryption_enforcement_config.restriction_mode).must_equal "FullyRestricted"
110+
111+
bucket.reload!
112+
_(bucket.customer_supplied_encryption_enforcement_config.restriction_mode).must_equal "FullyRestricted"
113+
_(bucket.google_managed_encryption_enforcement_config.restriction_mode).must_equal "FullyRestricted"
114+
end
115+
116+
it "deletes all encryption enforcement configs" do
117+
bucket.update do |b|
118+
b.customer_managed_encryption_enforcement_config = nil
119+
b.customer_supplied_encryption_enforcement_config = nil
120+
b.google_managed_encryption_enforcement_config = nil
121+
end
122+
# Removed all encryption enforcement configs without removing default_kms_key
123+
_(bucket.customer_managed_encryption_enforcement_config).must_be :nil?
124+
_(bucket.customer_supplied_encryption_enforcement_config).must_be :nil?
125+
_(bucket.google_managed_encryption_enforcement_config).must_be :nil?
126+
_(bucket.default_kms_key).must_equal kms_key
127+
end
128+
end
74129
end

google-cloud-storage/acceptance/storage/file_encryption_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
let(:bucket_location) { "us-central1" }
2323

2424
let :bucket do
25-
safe_gcs_execute {storage.create_bucket bucket_name, location: bucket_location }
25+
safe_gcs_execute { storage.bucket(bucket_name) || storage.create_bucket(bucket_name, location: bucket_location) }
2626
end
2727

2828
let(:file_path) { "acceptance/data/abc.txt" }

google-cloud-storage/lib/google/cloud/storage/bucket.rb

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,171 @@ def default_kms_key= new_default_kms_key
716716
default_kms_key_name: new_default_kms_key
717717
patch_gapi! :encryption
718718
end
719+
##
720+
# The bucket's encryption configuration for customer-managed encryption keys.
721+
# This configuration defines the
722+
# default encryption behavior for the bucket and its files, and it can be used to enforce encryption requirements for the bucket.
723+
# For more information, see [Bucket encryption](https://docs.cloud.google.com/storage/docs/encryption/).
724+
# @return [Google::Apis::StorageV1::Bucket::Encryption::CustomerManagedEncryptionEnforcementConfig, nil] The bucket's encryption configuration, or `nil` if no encryption configuration has been set.
725+
# @example
726+
# require "google/cloud/storage"
727+
# #
728+
# storage = Google::Cloud::Storage.new
729+
# bucket = storage.bucket "my-bucket"
730+
# bucket.customer_managed_encryption_enforcement_config
731+
# ==> #<Google::Apis::StorageV1::Bucket::Encryption::CustomerManagedEncryptionEnforcementConfig:0x00007f3b1c102e90 @restriction_mode="NotRestricted">
732+
# The value for `restriction_mode` can be either "NotRestricted" or "FullyRestricted"
733+
734+
def customer_managed_encryption_enforcement_config
735+
@gapi.encryption&.customer_managed_encryption_enforcement_config
736+
end
737+
##
738+
# Sets the customer-managed encryption enforcement configuration for the bucket.
739+
#
740+
# @param new_customer_managed_encryption_enforcement_config [Hash, nil]
741+
# The configuration hash for encryption enforcement.
742+
# * `:restriction_mode` (String) - Can be "NotRestricted" or "FullyRestricted".
743+
# Pass `nil` to clear the current configuration.
744+
#
745+
# @example Enforcing Customer-Managed Encryption
746+
# require "google/cloud/storage"
747+
#
748+
# storage = Google::Cloud::Storage.new
749+
# bucket = storage.bucket "my-bucket"
750+
#
751+
# # Set restriction mode to FullyRestricted
752+
# restricted_config = { restriction_mode: "FullyRestricted" }
753+
# bucket.customer_managed_encryption_enforcement_config = restricted_config
754+
#
755+
# @example Setting via Request Object (Google API Client)
756+
# require "google/apis/storage_v1"
757+
#
758+
# enforcement_config = { restriction_mode: "FullyRestricted" }
759+
#
760+
# request_obj = Google::Apis::StorageV1::Bucket::Encryption.new(
761+
# customer_managed_encryption_enforcement_config: enforcement_config
762+
# )
763+
# bucket.customer_managed_encryption_enforcement_config = request_obj
764+
#
765+
# @return [Hash, Google::Apis::StorageV1::Bucket::Encryption] The updated configuration.
766+
# @raise [Google::Cloud::Error] If the update fails due to permissions or invalid arguments.
767+
def customer_managed_encryption_enforcement_config= new_customer_managed_encryption_enforcement_config
768+
@gapi.encryption ||= API::Bucket::Encryption.new
769+
@gapi.encryption.customer_managed_encryption_enforcement_config =
770+
new_customer_managed_encryption_enforcement_config || {}
771+
patch_gapi! :encryption
772+
end
773+
774+
##
775+
# The bucket's encryption configuration for customer-supplied encryption keys.
776+
# For more information, see [Bucket encryption](https://docs.cloud.google.com/storage/docs/encryption/).
777+
# @return [Google::Apis::StorageV1::Bucket::Encryption::CustomerSuppliedEncryptionEnforcementConfig, nil]
778+
# The bucket's encryption configuration, or `nil` if no encryption configuration has been set.
779+
# @example
780+
# require "google/cloud/storage"
781+
#
782+
# storage = Google::Cloud::Storage.new
783+
# bucket = storage.bucket "my-bucket"
784+
#
785+
# bucket.customer_supplied_encryption_enforcement_config
786+
# ==> #<Google::Apis::StorageV1::Bucket::Encryption::CustomerSuppliedEncryptionEnforcementConfig:0x00007f3b1c102e90 @restriction_mode="NotRestricted">
787+
# The value for `restriction_mode` can be either "NotRestricted" or "FullyRestricted".
788+
789+
def customer_supplied_encryption_enforcement_config
790+
@gapi.encryption&.customer_supplied_encryption_enforcement_config
791+
end
792+
793+
##
794+
# Sets the bucket's encryption configuration for customer-supplied encryption that will be used to protect files.
795+
# @param new_customer_supplied_encryption_enforcement_config [Hash, nil]
796+
# The configuration hash for encryption enforcement.
797+
# * `:restriction_mode` (String) - Can be "NotRestricted" or "FullyRestricted".
798+
# Pass `nil` to clear the current configuration.
799+
# @example
800+
# require "google/cloud/storage"
801+
#
802+
# storage = Google::Cloud::Storage.new
803+
# bucket = storage.bucket "my-bucket"
804+
# restricted_config = { restriction_mode: "FullyRestricted" }
805+
# bucket.customer_supplied_encryption_enforcement_config = restricted_config
806+
#
807+
# @example Setting via Request Object (Google API Client)
808+
# require "google/apis/storage_v1"
809+
#
810+
# enforcement_config = { restriction_mode: "FullyRestricted" }
811+
#
812+
# request_obj = Google::Apis::StorageV1::Bucket::Encryption.new(
813+
# customer_supplied_encryption_enforcement_config: enforcement_config
814+
# )
815+
# bucket.customer_supplied_encryption_enforcement_config = request_obj
816+
#
817+
# @return [Hash, Google::Apis::StorageV1::Bucket::Encryption] The updated configuration.
818+
# @raise [Google::Cloud::Error] If the update fails due to permissions or invalid arguments.
819+
820+
def customer_supplied_encryption_enforcement_config= new_customer_supplied_encryption_enforcement_config
821+
@gapi.encryption ||= API::Bucket::Encryption.new
822+
@gapi.encryption.customer_supplied_encryption_enforcement_config =
823+
new_customer_supplied_encryption_enforcement_config || {}
824+
patch_gapi! :encryption
825+
end
826+
827+
##
828+
# The bucket's encryption configuration for google-managed encryption keys.
829+
# This configuration defines the
830+
# default encryption behavior for the bucket and its files, and it can be used to enforce encryption
831+
# requirements for the bucket.
832+
# For more information, see [Bucket encryption](https://docs.cloud.google.com/storage/docs/encryption/).
833+
# @return [Google::Apis::StorageV1::Bucket::Encryption::GoogleManagedEncryptionEnforcementConfig, nil]
834+
# The bucket's encryption configuration, or `nil` if no encryption configuration has been set.
835+
# @example
836+
# require "google/cloud/storage"
837+
#
838+
# storage = Google::Cloud::Storage.new
839+
# bucket = storage.bucket "my-bucket"
840+
# bucket.google_managed_encryption_enforcement_config
841+
# ==> #<Google::Apis::StorageV1::Bucket::Encryption::GoogleManagedEncryptionEnforcementConfig:0x00007f3b1c102e90 @restriction_mode="NotRestricted">
842+
# The value for `restriction_mode` can be either "NotRestricted" or "FullyRestricted".
843+
844+
def google_managed_encryption_enforcement_config
845+
@gapi.encryption&.google_managed_encryption_enforcement_config
846+
end
847+
848+
##
849+
# Sets the google-managed encryption enforcement configuration for the bucket.
850+
#
851+
# @param new_google_managed_encryption_enforcement_config [Hash, nil]
852+
# The configuration hash for encryption enforcement.
853+
# * `:restriction_mode` (String) - Can be "NotRestricted" or "FullyRestricted".
854+
# Pass `nil` to clear the current configuration.
855+
#
856+
# @example Enforcing Customer-Managed Encryption
857+
# require "google/cloud/storage"
858+
#
859+
# storage = Google::Cloud::Storage.new
860+
# bucket = storage.bucket "my-bucket"
861+
# # Set restriction mode to FullyRestricted
862+
# restricted_config = { restriction_mode: "FullyRestricted" }
863+
# bucket.google_managed_encryption_enforcement_config = restricted_config
864+
#
865+
# @example Setting via Request Object (Google API Client)
866+
# require "google/apis/storage_v1"
867+
#
868+
# enforcement_config = { restriction_mode: "FullyRestricted" }
869+
#
870+
# request_obj = Google::Apis::StorageV1::Bucket::Encryption.new(
871+
# google_managed_encryption_enforcement_config: enforcement_config
872+
# )
873+
# bucket.google_managed_encryption_enforcement_config = request_obj
874+
#
875+
# @return [Hash, Google::Apis::StorageV1::Bucket::Encryption] The updated configuration.
876+
# @raise [Google::Cloud::Error] If the update fails due to permissions or invalid arguments.
877+
878+
def google_managed_encryption_enforcement_config= new_google_managed_encryption_enforcement_config
879+
@gapi.encryption ||= API::Bucket::Encryption.new
880+
@gapi.encryption.google_managed_encryption_enforcement_config =
881+
new_google_managed_encryption_enforcement_config || {}
882+
patch_gapi! :encryption
883+
end
719884

720885
##
721886
# The period of time (in seconds) that files in the bucket must be
@@ -1373,6 +1538,7 @@ def update if_metageneration_match: nil, if_metageneration_not_match: nil
13731538
updater.check_for_changed_labels!
13741539
updater.check_for_mutable_cors!
13751540
updater.check_for_mutable_lifecycle!
1541+
updater.check_for_encryption_enforcement_config!
13761542
return if updater.updates.empty?
13771543
update_gapi! updater.updates,
13781544
if_metageneration_match: if_metageneration_match,
@@ -3386,6 +3552,26 @@ def check_for_mutable_lifecycle!
33863552
patch_gapi! :lifecycle
33873553
end
33883554

3555+
def check_for_encryption_enforcement_config!
3556+
return unless @gapi.encryption
3557+
3558+
[
3559+
:google_managed_encryption_enforcement_config,
3560+
:customer_managed_encryption_enforcement_config,
3561+
:customer_supplied_encryption_enforcement_config
3562+
].each do |attr|
3563+
config = @gapi.encryption.send(attr)
3564+
next unless config
3565+
unless config.respond_to?(:to_h)
3566+
raise ArgumentError, "Encryption config for #{attr} must be a Hash or valid Config object"
3567+
end
3568+
clean_config = config.to_h
3569+
clean_config.delete :effective_time
3570+
clean_config.delete "effective_time"
3571+
@gapi.encryption.send "#{attr}=", clean_config
3572+
end
3573+
end
3574+
33893575
protected
33903576

33913577
##

google-cloud-storage/samples/Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,7 @@ group :test do
3636
gem "minitest-hooks", "~> 1.5"
3737
gem "rake"
3838
end
39+
# The following gems have been removed from ruby core and are required for testing.
40+
gem "ostruct"
41+
gem "cgi"
42+
gem "irb"

google-cloud-storage/samples/acceptance/buckets_test.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,20 @@
3737
require_relative "../storage_get_bucket_class_and_location"
3838
require_relative "../storage_get_bucket_metadata"
3939
require_relative "../storage_get_default_event_based_hold"
40+
require_relative "../storage_get_bucket_encryption_enforcement_config"
4041
require_relative "../storage_get_public_access_prevention"
4142
require_relative "../storage_get_requester_pays_status"
4243
require_relative "../storage_get_retention_policy"
4344
require_relative "../storage_get_uniform_bucket_level_access"
4445
require_relative "../storage_list_buckets"
4546
require_relative "../storage_list_buckets_with_partial_success"
4647
require_relative "../storage_lock_retention_policy"
48+
require_relative "../storage_update_bucket_encryption_enforcement_config"
4749
require_relative "../storage_remove_bucket_label"
4850
require_relative "../storage_remove_cors_configuration"
4951
require_relative "../storage_remove_retention_policy"
5052
require_relative "../storage_set_bucket_default_kms_key"
53+
require_relative "../storage_set_bucket_encryption_enforcement_config"
5154
require_relative "../storage_set_object_retention_policy"
5255
require_relative "../storage_set_public_access_prevention_enforced"
5356
require_relative "../storage_set_public_access_prevention_inherited"
@@ -169,6 +172,44 @@
169172
end
170173
end
171174

175+
describe "storage_bucket_encryption_enforcement_config" do
176+
bucket_name = random_bucket_name
177+
178+
it "gets, sets and updates bucket encryption enforcement config" do
179+
# creates bucket with encryption enforcement config
180+
expected = "Created bucket #{bucket_name} with Encryption Enforcement Config.\n"
181+
182+
retry_resource_exhaustion do
183+
assert_output expected do
184+
set_bucket_encryption_enforcement_config bucket_name: bucket_name
185+
end
186+
end
187+
188+
# get encryption enforcement config
189+
expected = "Encryption Enforcement Config for bucket #{bucket_name}:\n" \
190+
"Customer-managed encryption enforcement config restriction mode: NotRestricted\n" \
191+
"Customer-supplied encryption enforcement config restriction mode: FullyRestricted\n" \
192+
"Google-managed encryption enforcement config restriction mode: FullyRestricted\n"
193+
retry_resource_exhaustion do
194+
assert_output expected do
195+
get_bucket_encryption_enforcement_config bucket_name: bucket_name
196+
end
197+
end
198+
199+
# update encryption enforcement config
200+
expected = "Updated google_managed_config to NotRestricted for bucket #{bucket_name}.\n"
201+
202+
retry_resource_exhaustion do
203+
assert_output expected do
204+
update_bucket_encryption_enforcement_config bucket_name: bucket_name
205+
end
206+
end
207+
208+
refute_nil storage_client.bucket bucket_name
209+
end
210+
delete_bucket_helper bucket_name
211+
end
212+
172213
describe "storage_create_bucket_with_object_retention" do
173214
it "creates a bucket with object retention enabled." do
174215
bucket_name = random_bucket_name
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
# http://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+
# [START storage_get_bucket_encryption_enforcement_config]
16+
def get_bucket_encryption_enforcement_config bucket_name:
17+
# The ID to give your GCS bucket
18+
# bucket_name = "your-unique-bucket-name"
19+
20+
require "google/cloud/storage"
21+
22+
storage = Google::Cloud::Storage.new
23+
bucket = storage.bucket bucket_name
24+
puts "Encryption Enforcement Config for bucket #{bucket.name}:"
25+
puts "Customer-managed encryption enforcement config restriction mode: " \
26+
"#{bucket.customer_managed_encryption_enforcement_config&.restriction_mode}"
27+
puts "Customer-supplied encryption enforcement config restriction mode: " \
28+
"#{bucket.customer_supplied_encryption_enforcement_config&.restriction_mode}"
29+
puts "Google-managed encryption enforcement config restriction mode: " \
30+
"#{bucket.google_managed_encryption_enforcement_config&.restriction_mode}"
31+
end
32+
# [END storage_get_bucket_encryption_enforcement_config]
33+
34+
if $PROGRAM_NAME == __FILE__
35+
get_bucket_encryption_enforcement_config bucket_name: ARGV.shift
36+
end

0 commit comments

Comments
 (0)