Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2026 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.

require_relative "../storage_helper"
describe Google::Cloud::Storage::Bucket, :storage do
let(:ip_filter_disabled) do
{
mode: "Disabled",
public_network_source: {
allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"]
}
}
end

let(:ip_filter_disabled_update) do
{
mode: "Disabled",
public_network_source: {
allowed_ip_cidr_ranges: ["8.8.8.8/32"]
}
}
end
let(:bucket_name) { "#{$bucket_names.first}-ip-filter" }
let :bucket do
storage.bucket(bucket_name) ||
storage.create_bucket(bucket_name, ip_filter: ip_filter_disabled)
end

after(:all) do
safe_gcs_execute { bucket.delete }
end

it "creates, gets, updates, and deletes a bucket with ip_filter" do
# Create a bucket with ip_filter
puts "*************************Bucket Name**************************"
puts bucket.ip_filter
puts "*************************Bucket Name**************************"

# bucket = storage.bucket(bucket_name) ||
# safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled}

# _(bucket.ip_filter).wont_be_nil
_(bucket.ip_filter.mode).must_equal "Disabled"
_(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"]

# Get the bucket and verify ip_filter
bucket = storage.bucket bucket_name
_(bucket.ip_filter).wont_be_nil
_(bucket.ip_filter.mode).must_equal "Disabled"
_(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"]

# Update the ip_filter
safe_gcs_execute do
bucket.update do |b|
b.ip_filter = ip_filter_disabled_update
end
end

_(bucket.ip_filter.mode).must_equal "Disabled"
_(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["8.8.8.8/32"]

# Disable ip_filter
safe_gcs_execute do
bucket.ip_filter = {
mode: "Disabled",
public_network_source: {
allowed_ip_cidr_ranges: []
}
}
end

_(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_be_nil

end
end
76 changes: 71 additions & 5 deletions google-cloud-storage/lib/google/cloud/storage/bucket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1115,11 +1115,7 @@ def uniform_bucket_level_access?
end

##
# Sets whether uniform bucket-level access is enabled for this bucket. When this is enabled, access to the
# bucket will be configured through IAM, and legacy ACL policies will not work. When it is first enabled,
# {#uniform_bucket_level_access_locked_at} will be set by the API automatically. The uniform bucket-level access
# can then be disabled until the time specified, after which it will become immutable and calls to change it
# will fail. If uniform bucket-level access is enabled, calls to access legacy ACL information will fail.
# Sets whether uniform bucket-level access is enabled for this bucket.
#
# Before enabling uniform bucket-level access please review [uniform bucket-level
# access](https://cloud.google.com/storage/docs/uniform-bucket-level-access).
Expand Down Expand Up @@ -1576,6 +1572,76 @@ def delete if_metageneration_match: nil, if_metageneration_not_match: nil
user_project: user_project
end

##
# The bucket's IP filter configuration.
# This value can be modified by calling {#ip_filter=}.
#
# @return [Google::Apis::StorageV1::Bucket::IpFilter, nil] The bucket's IP filter configuration,
# or `nil` if not configured.
#
# @example
# require "google/cloud/storage"
#
# storage = Google::Cloud::Storage.new
#
# bucket = storage.bucket "my-bucket"
# ip_filter = bucket.ip_filter
# if ip_filter
# puts "Mode: #{ip_filter.mode}"
# puts "Public CIDR: #{ip_filter.public_network_source&.allowed_ip_cidr_ranges}"
# end
#
def ip_filter
@gapi.ip_filter
end

##
# Sets the value for IP filter in the bucket. This value can
# be queried by calling {#ip_filter}.
#
# @param [Google::Apis::StorageV1::Bucket::IpFilter, Hash] new_ip_filter The bucket's new IP filter.
# Acceptable Hash structure:
# - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled"
# - :public_network_source - [Hash] The public network source configuration:
# - :allowed_ip_cidr_ranges - [Array<String>] Array of IP CIDR ranges allowed for public access.
# - :vpc_network_sources - [Array<Hash>] The VPC network sources configuration:
# - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME".
# - :allowed_ip_cidr_ranges - [Array<String>] Array of IP CIDR ranges allowed for VPC access.
# - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access.
# - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access.
#
# @example Enable IP filter with Hash:
# require "google/cloud/storage"
#
# storage = Google::Cloud::Storage.new
#
# bucket = storage.bucket "my-bucket"
# bucket.ip_filter = {
# mode: "Enabled",
# allow_all_service_agent_access: true,
# public_network_source: {
# allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"]
# }
# }
#
# @example Clear/delete IP filter:
# require "google/cloud/storage"
#
# storage = Google::Cloud::Storage.new
#
# bucket = storage.bucket "my-bucket"
# bucket.ip_filter = {
# mode: "Disabled",
# public_network_source: {
# allowed_ip_cidr_ranges: []
# }
# }
#
def ip_filter= new_ip_filter
@gapi.ip_filter = new_ip_filter || {}
patch_gapi! :ip_filter
end

##
# Retrieves a list of files matching the criteria.
#
Expand Down
20 changes: 18 additions & 2 deletions google-cloud-storage/lib/google/cloud/storage/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,18 @@ def bucket bucket_name,
# @param [Boolean] enable_object_retention
# When set to true, object retention is enabled for this bucket.
#
# @param [Hash] ip_filter The bucket's IP filter configuration.
# Acceptable values are:
# - {Google::Apis::StorageV1::Bucket::IpFilter} object
# - Hash that can be converted to a {Google::Apis::StorageV1::Bucket::IpFilter} object
# - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled"
# - :public_network_source - [Hash] The public network source configuration:
# - :allowed_ip_cidr_ranges - [Array<String>] Array of IP CIDR ranges allowed for public access.
# - :vpc_network_sources - [Array<Hash>] The VPC network sources configuration:
# - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME".
# - :allowed_ip_cidr_ranges - [Array<String>] Array of IP CIDR ranges allowed for VPC access.
# - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access.
# - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access.
# @yield [bucket] a block for configuring the bucket before it is
# created
# @yieldparam [Bucket] bucket the bucket object to be configured
Expand Down Expand Up @@ -477,12 +489,15 @@ def create_bucket bucket_name,
user_project: nil,
autoclass_enabled: false,
enable_object_retention: nil,
hierarchical_namespace: nil
hierarchical_namespace: nil,
ip_filter: nil

params = {
name: bucket_name,
location: location,
custom_placement_config: custom_placement_config,
hierarchical_namespace: hierarchical_namespace
hierarchical_namespace: hierarchical_namespace,
ip_filter: ip_filter
}.delete_if { |_, v| v.nil? }
new_bucket = Google::Apis::StorageV1::Bucket.new(**params)
storage_class = storage_class_for storage_class
Expand All @@ -496,6 +511,7 @@ def create_bucket bucket_name,
b.versioning = versioning unless versioning.nil?
b.requester_pays = requester_pays unless requester_pays.nil?
b.hierarchical_namespace = hierarchical_namespace unless hierarchical_namespace.nil?
b.ip_filter = ip_filter unless ip_filter.nil?
end
yield updater if block_given?
updater.check_for_changed_labels!
Expand Down
68 changes: 68 additions & 0 deletions google-cloud-storage/samples/acceptance/buckets_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
require_relative "../storage_create_bucket_class_location"
require_relative "../storage_create_bucket_dual_region"
require_relative "../storage_create_bucket_hierarchical_namespace"
require_relative "../storage_create_bucket_with_ip_filter"
require_relative "../storage_get_bucket_ip_filter"
require_relative "../storage_disable_ip_filtering"
require_relative "../storage_delete_bucket_ip_filter"
require_relative "../storage_enable_bucket_ip_filter"
require_relative "../storage_list_bucket_ip_filters"
require_relative "../storage_create_bucket_with_object_retention"
require_relative "../storage_define_bucket_website_configuration"
require_relative "../storage_delete_bucket"
Expand Down Expand Up @@ -172,6 +178,68 @@
end
end

describe "storage_bucket_ip_filter" do
let(:bucket_name) { random_bucket_name }

after :all do
delete_bucket_helper bucket_name
end

it "creates, updates, gets, lists, and removes IP filter config" do
# Creates IP filter enabled bucket
expected = "Created bucket #{bucket_name} with IP filter.\n"
retry_resource_exhaustion do
assert_output expected do
create_bucket_with_ip_filter bucket_name: bucket_name
end
end

# Disables IP filter of an existing bucket
expected = "Disabled IP filtering for bucket #{bucket_name}.\n"
retry_resource_exhaustion do
assert_output expected do
disable_ip_filtering bucket_name: bucket_name
end
end

# Gets IP filter of an existing bucket
expected = "Bucket #{bucket_name} has IP filter mode: Disabled.\n" \
"Allowed public network CIDR ranges: 8.8.8.8/32.\n"
retry_resource_exhaustion do
assert_output expected do
get_bucket_ip_filter bucket_name: bucket_name
end
end

# Lists IP filter allowed ranges
expected = "IP filter mode: Disabled\n" \
"Allowed range: 8.8.8.8/32\n"
retry_resource_exhaustion do
assert_output expected do
list_bucket_ip_filters bucket_name: bucket_name
end
end

# Deletes IP filter of an existing bucket
expected = "Deleted IP filter for bucket #{bucket_name}.\n"
retry_resource_exhaustion do
assert_output expected do
delete_bucket_ip_filter bucket_name: bucket_name
end
end

# Enables IP filter of an existing bucket (SKIPPED)
skip "SKIPPED : If Ip filter is enabled, we cannot access the bucket"
expected = "Enabled IP filter for bucket #{bucket_name}.\n"
retry_resource_exhaustion do
assert_output expected do
enable_bucket_ip_filter bucket_name: bucket_name
end
end
end

end

describe "storage_bucket_encryption_enforcement_config" do
bucket_name = random_bucket_name

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2026 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
#
# http://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.

# [START storage_create_bucket_with_ip_filter]
def create_bucket_with_ip_filter bucket_name:
# The ID to give your GCS bucket
# bucket_name = "your-unique-bucket-name"

require "google/cloud/storage"

storage = Google::Cloud::Storage.new
ip_filter = {
mode: "Disabled",
public_network_source: {
allowed_ip_cidr_ranges: [
"0.0.0.0/0", "::/0"
]
}
}

bucket = storage.create_bucket bucket_name do |b|
b.ip_filter = ip_filter
b.uniform_bucket_level_access = true
end
puts "Created bucket #{bucket_name} with IP filter."
end
# [END storage_create_bucket_with_ip_filter]

if $PROGRAM_NAME == __FILE__
create_bucket_with_ip_filter bucket_name: ARGV.shift
end
41 changes: 41 additions & 0 deletions google-cloud-storage/samples/storage_delete_bucket_ip_filter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2026 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
#
# http://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.

# [START storage_delete_ip_filtering_rules]
def delete_bucket_ip_filter bucket_name:
# The ID of your GCS bucket
# bucket_name = "your-unique-bucket-name"

require "google/cloud/storage"

storage = Google::Cloud::Storage.new
bucket = storage.bucket bucket_name

# Clear IP filter configuration by setting it to empty/disabled
bucket.update do |b|
b.ip_filter = {
mode: "Disabled",
public_network_source: {
allowed_ip_cidr_ranges: []
}
}
end

puts "Deleted IP filter for bucket #{bucket_name}."
end
# [END storage_delete_ip_filtering_rules]

if $PROGRAM_NAME == __FILE__
delete_bucket_ip_filter bucket_name: ARGV.shift
end
Loading
Loading