Skip to content

Commit cfa135e

Browse files
committed
feat(storage): add support for bucket ip filter
1 parent 81c1d01 commit cfa135e

16 files changed

Lines changed: 751 additions & 0 deletions
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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_ip_filter.h"
16+
#include "google/cloud/internal/ios_flags_saver.h"
17+
#include <iostream>
18+
19+
namespace google {
20+
namespace cloud {
21+
namespace storage {
22+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
23+
24+
bool operator==(BucketIpFilterPublicNetworkSource const& lhs,
25+
BucketIpFilterPublicNetworkSource const& rhs) {
26+
return lhs.allowed_ip_cidr_ranges == rhs.allowed_ip_cidr_ranges;
27+
}
28+
29+
bool operator!=(BucketIpFilterPublicNetworkSource const& lhs,
30+
BucketIpFilterPublicNetworkSource const& rhs) {
31+
return !(lhs == rhs);
32+
}
33+
34+
std::ostream& operator<<(std::ostream& os,
35+
BucketIpFilterPublicNetworkSource const& rhs) {
36+
os << "BucketIpFilterPublicNetworkSource={allowed_ip_cidr_ranges=[";
37+
char const* sep = "";
38+
for (auto const& r : rhs.allowed_ip_cidr_ranges) {
39+
os << sep << r;
40+
sep = ", ";
41+
}
42+
return os << "]}";
43+
}
44+
45+
bool operator==(BucketIpFilterVpcNetworkSource const& lhs,
46+
BucketIpFilterVpcNetworkSource const& rhs) {
47+
return std::tie(lhs.network, lhs.allowed_ip_cidr_ranges) ==
48+
std::tie(rhs.network, rhs.allowed_ip_cidr_ranges);
49+
}
50+
51+
bool operator!=(BucketIpFilterVpcNetworkSource const& lhs,
52+
BucketIpFilterVpcNetworkSource const& rhs) {
53+
return !(lhs == rhs);
54+
}
55+
56+
std::ostream& operator<<(std::ostream& os,
57+
BucketIpFilterVpcNetworkSource const& rhs) {
58+
os << "BucketIpFilterVpcNetworkSource={network=" << rhs.network
59+
<< ", allowed_ip_cidr_ranges=[";
60+
char const* sep = "";
61+
for (auto const& r : rhs.allowed_ip_cidr_ranges) {
62+
os << sep << r;
63+
sep = ", ";
64+
}
65+
return os << "]}";
66+
}
67+
68+
bool operator==(BucketIpFilter const& lhs, BucketIpFilter const& rhs) {
69+
return std::tie(lhs.allow_all_service_agent_access, lhs.allow_cross_org_vpcs,
70+
lhs.mode, lhs.public_network_source,
71+
lhs.vpc_network_sources) ==
72+
std::tie(rhs.allow_all_service_agent_access, rhs.allow_cross_org_vpcs,
73+
rhs.mode, rhs.public_network_source, rhs.vpc_network_sources);
74+
}
75+
76+
bool operator!=(BucketIpFilter const& lhs, BucketIpFilter const& rhs) {
77+
return !(lhs == rhs);
78+
}
79+
80+
std::ostream& operator<<(std::ostream& os, BucketIpFilter const& rhs) {
81+
google::cloud::internal::IosFlagsSaver save_format(os);
82+
os << "BucketIpFilter={";
83+
os << "mode=" << rhs.mode.value_or("") << ", ";
84+
if (rhs.allow_all_service_agent_access) {
85+
os << "allow_all_service_agent_access=" << std::boolalpha
86+
<< *rhs.allow_all_service_agent_access << ", ";
87+
}
88+
if (rhs.allow_cross_org_vpcs) {
89+
os << "allow_cross_org_vpcs=" << std::boolalpha << *rhs.allow_cross_org_vpcs
90+
<< ", ";
91+
}
92+
if (rhs.public_network_source) {
93+
os << "public_network_source=" << *rhs.public_network_source << ", ";
94+
}
95+
if (rhs.vpc_network_sources) {
96+
os << "vpc_network_sources=[";
97+
char const* sep = "";
98+
for (auto const& r : *rhs.vpc_network_sources) {
99+
os << sep << r;
100+
sep = ", ";
101+
}
102+
os << "]";
103+
}
104+
return os << "}";
105+
}
106+
107+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
108+
} // namespace storage
109+
} // namespace cloud
110+
} // namespace google
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_IP_FILTER_H
16+
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_IP_FILTER_H
17+
18+
#include "google/cloud/storage/version.h"
19+
#include "absl/types/optional.h"
20+
#include <iosfwd>
21+
#include <string>
22+
#include <vector>
23+
24+
namespace google {
25+
namespace cloud {
26+
namespace storage {
27+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
28+
29+
/**
30+
* Defines a public network source for the bucket IP filter.
31+
*/
32+
struct BucketIpFilterPublicNetworkSource {
33+
std::vector<std::string> allowed_ip_cidr_ranges;
34+
};
35+
36+
bool operator==(BucketIpFilterPublicNetworkSource const& lhs,
37+
BucketIpFilterPublicNetworkSource const& rhs);
38+
bool operator!=(BucketIpFilterPublicNetworkSource const& lhs,
39+
BucketIpFilterPublicNetworkSource const& rhs);
40+
41+
std::ostream& operator<<(std::ostream& os,
42+
BucketIpFilterPublicNetworkSource const& rhs);
43+
44+
/**
45+
* Defines a VPC network source for the bucket IP filter.
46+
*/
47+
struct BucketIpFilterVpcNetworkSource {
48+
std::string network;
49+
std::vector<std::string> allowed_ip_cidr_ranges;
50+
};
51+
52+
bool operator==(BucketIpFilterVpcNetworkSource const& lhs,
53+
BucketIpFilterVpcNetworkSource const& rhs);
54+
bool operator!=(BucketIpFilterVpcNetworkSource const& lhs,
55+
BucketIpFilterVpcNetworkSource const& rhs);
56+
57+
std::ostream& operator<<(std::ostream& os,
58+
BucketIpFilterVpcNetworkSource const& rhs);
59+
60+
/**
61+
* The IP filtering configuration for a Bucket.
62+
*/
63+
struct BucketIpFilter {
64+
absl::optional<bool> allow_all_service_agent_access;
65+
absl::optional<bool> allow_cross_org_vpcs;
66+
absl::optional<std::string> mode;
67+
absl::optional<BucketIpFilterPublicNetworkSource> public_network_source;
68+
absl::optional<std::vector<BucketIpFilterVpcNetworkSource>>
69+
vpc_network_sources;
70+
};
71+
72+
bool operator==(BucketIpFilter const& lhs, BucketIpFilter const& rhs);
73+
bool operator!=(BucketIpFilter const& lhs, BucketIpFilter const& rhs);
74+
75+
std::ostream& operator<<(std::ostream& os, BucketIpFilter const& rhs);
76+
77+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
78+
} // namespace storage
79+
} // namespace cloud
80+
} // namespace google
81+
82+
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_IP_FILTER_H
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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_ip_filter.h"
16+
#include "google/cloud/testing_util/is_proto_equal.h"
17+
#include <gmock/gmock.h>
18+
19+
namespace google {
20+
namespace cloud {
21+
namespace storage {
22+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
23+
namespace {
24+
25+
TEST(BucketIpFilterTest, PublicNetworkSource) {
26+
BucketIpFilterPublicNetworkSource source;
27+
source.allowed_ip_cidr_ranges.push_back("1.2.3.4/32");
28+
source.allowed_ip_cidr_ranges.push_back("5.6.7.8/32");
29+
30+
BucketIpFilterPublicNetworkSource copy = source;
31+
EXPECT_EQ(source, copy);
32+
33+
copy.allowed_ip_cidr_ranges.pop_back();
34+
EXPECT_NE(source, copy);
35+
}
36+
37+
TEST(BucketIpFilterTest, VpcNetworkSource) {
38+
BucketIpFilterVpcNetworkSource source;
39+
source.network = "projects/p/global/networks/n";
40+
source.allowed_ip_cidr_ranges.push_back("1.2.3.4/32");
41+
source.allowed_ip_cidr_ranges.push_back("5.6.7.8/32");
42+
43+
BucketIpFilterVpcNetworkSource copy = source;
44+
EXPECT_EQ(source, copy);
45+
46+
copy.network = "changed";
47+
EXPECT_NE(source, copy);
48+
}
49+
50+
TEST(BucketIpFilterTest, IpFilter) {
51+
BucketIpFilter filter;
52+
filter.mode = "Enabled";
53+
filter.allow_all_service_agent_access = true;
54+
filter.allow_cross_org_vpcs = true;
55+
filter.public_network_source =
56+
BucketIpFilterPublicNetworkSource{{"1.2.3.4/32"}};
57+
filter.vpc_network_sources =
58+
absl::make_optional<std::vector<BucketIpFilterVpcNetworkSource>>(
59+
{BucketIpFilterVpcNetworkSource{"projects/p/global/networks/n",
60+
{"5.6.7.8/32"}},
61+
BucketIpFilterVpcNetworkSource{"projects/p/global/networks/m",
62+
{"9.0.1.2/32"}}});
63+
64+
BucketIpFilter copy = filter;
65+
EXPECT_EQ(filter, copy);
66+
67+
copy.mode = "Disabled";
68+
EXPECT_NE(filter, copy);
69+
}
70+
71+
} // namespace
72+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
73+
} // namespace storage
74+
} // namespace cloud
75+
} // namespace google

google/cloud/storage/bucket_metadata.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ bool operator==(BucketMetadata const& lhs, BucketMetadata const& rhs) {
9696
&& lhs.etag_ == rhs.etag_ //
9797
&& lhs.hierarchical_namespace_ == rhs.hierarchical_namespace_ //
9898
&& lhs.iam_configuration_ == rhs.iam_configuration_ //
99+
&& lhs.ip_filter_ == rhs.ip_filter_ //
99100
&& lhs.id_ == rhs.id_ //
100101
&& lhs.kind_ == rhs.kind_ //
101102
&& lhs.labels_ == rhs.labels_ //
@@ -164,6 +165,10 @@ std::ostream& operator<<(std::ostream& os, BucketMetadata const& rhs) {
164165
os << ", iam_configuration=" << rhs.iam_configuration();
165166
}
166167

168+
if (rhs.has_ip_filter()) {
169+
os << ", ip_filter=" << rhs.ip_filter();
170+
}
171+
167172
os << ", id=" << rhs.id() << ", kind=" << rhs.kind();
168173

169174
for (auto const& kv : rhs.labels_) {
@@ -402,6 +407,51 @@ BucketMetadataPatchBuilder::ResetIamConfiguration() {
402407
return *this;
403408
}
404409

410+
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::SetIpFilter(
411+
BucketIpFilter const& v) {
412+
internal::PatchBuilder ip_filter;
413+
if (v.mode.has_value()) {
414+
ip_filter.SetStringField("mode", *v.mode);
415+
}
416+
if (v.allow_all_service_agent_access.has_value()) {
417+
ip_filter.SetBoolField("allowAllServiceAgentAccess",
418+
*v.allow_all_service_agent_access);
419+
}
420+
if (v.allow_cross_org_vpcs.has_value()) {
421+
ip_filter.SetBoolField("allowCrossOrgVpcs", *v.allow_cross_org_vpcs);
422+
}
423+
if (v.public_network_source.has_value()) {
424+
internal::PatchBuilder public_network_source;
425+
auto array = nlohmann::json::array();
426+
for (auto const& r : v.public_network_source->allowed_ip_cidr_ranges) {
427+
array.emplace_back(r);
428+
}
429+
public_network_source.SetArrayField("allowedIpCidrRanges", array.dump());
430+
ip_filter.AddSubPatch("publicNetworkSource", public_network_source);
431+
}
432+
if (v.vpc_network_sources.has_value()) {
433+
auto array = nlohmann::json::array();
434+
for (auto const& r : *v.vpc_network_sources) {
435+
nlohmann::json vpc_network_source;
436+
vpc_network_source["network"] = r.network;
437+
auto allowed_ips = nlohmann::json::array();
438+
for (auto const& ip : r.allowed_ip_cidr_ranges) {
439+
allowed_ips.emplace_back(ip);
440+
}
441+
vpc_network_source["allowedIpCidrRanges"] = allowed_ips;
442+
array.emplace_back(vpc_network_source);
443+
}
444+
ip_filter.SetArrayField("vpcNetworkSources", array.dump());
445+
}
446+
impl_.AddSubPatch("ipFilter", ip_filter);
447+
return *this;
448+
}
449+
450+
BucketMetadataPatchBuilder& BucketMetadataPatchBuilder::ResetIpFilter() {
451+
impl_.RemoveField("ipFilter");
452+
return *this;
453+
}
454+
405455
BucketMetadataPatchBuilder&
406456
BucketMetadataPatchBuilder::SetHierarchicalNamespace(
407457
BucketHierarchicalNamespace const& v) {

google/cloud/storage/bucket_metadata.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "google/cloud/storage/bucket_encryption.h"
2424
#include "google/cloud/storage/bucket_hierarchical_namespace.h"
2525
#include "google/cloud/storage/bucket_iam_configuration.h"
26+
#include "google/cloud/storage/bucket_ip_filter.h"
2627
#include "google/cloud/storage/bucket_lifecycle.h"
2728
#include "google/cloud/storage/bucket_logging.h"
2829
#include "google/cloud/storage/bucket_object_retention.h"
@@ -269,6 +270,23 @@ class BucketMetadata {
269270
}
270271
///@}
271272

273+
/// @name Accessors and modifiers for the IP filter configuration.
274+
///@{
275+
bool has_ip_filter() const { return ip_filter_.has_value(); }
276+
BucketIpFilter const& ip_filter() const { return *ip_filter_; }
277+
absl::optional<BucketIpFilter> const& ip_filter_as_optional() const {
278+
return ip_filter_;
279+
}
280+
BucketMetadata& set_ip_filter(BucketIpFilter v) {
281+
ip_filter_ = std::move(v);
282+
return *this;
283+
}
284+
BucketMetadata& reset_ip_filter() {
285+
ip_filter_.reset();
286+
return *this;
287+
}
288+
///@}
289+
272290
/// Return the bucket id.
273291
std::string const& id() const { return id_; }
274292

@@ -664,6 +682,7 @@ class BucketMetadata {
664682
std::string etag_;
665683
absl::optional<BucketHierarchicalNamespace> hierarchical_namespace_;
666684
absl::optional<BucketIamConfiguration> iam_configuration_;
685+
absl::optional<BucketIpFilter> ip_filter_;
667686
std::string id_;
668687
std::string kind_;
669688
std::map<std::string, std::string> labels_;
@@ -742,6 +761,9 @@ class BucketMetadataPatchBuilder {
742761
BucketIamConfiguration const& v);
743762
BucketMetadataPatchBuilder& ResetIamConfiguration();
744763

764+
BucketMetadataPatchBuilder& SetIpFilter(BucketIpFilter const& v);
765+
BucketMetadataPatchBuilder& ResetIpFilter();
766+
745767
/// Sets a new hierarchical namespace configuration.
746768
BucketMetadataPatchBuilder& SetHierarchicalNamespace(
747769
BucketHierarchicalNamespace const& v);

0 commit comments

Comments
 (0)