Skip to content

Commit dcf6595

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Add ProtoTypeMaskProvider, ProtoTypeMask, and FieldPath classes. The ProtoTypeMaskProvider has functions that can be used to validate the input field masks and to check whether a field is visible.
PiperOrigin-RevId: 818745535
1 parent 38f6e94 commit dcf6595

File tree

8 files changed

+1107
-0
lines changed

8 files changed

+1107
-0
lines changed

checker/internal/BUILD

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,81 @@ cc_test(
271271
"@com_google_protobuf//:protobuf",
272272
],
273273
)
274+
275+
cc_library(
276+
name = "field_path",
277+
hdrs = ["field_path.h"],
278+
deps = [
279+
"@com_google_absl//absl/strings",
280+
"@com_google_absl//absl/strings:str_format",
281+
"@com_google_absl//absl/strings:string_view",
282+
],
283+
)
284+
285+
cc_test(
286+
name = "field_path_test",
287+
srcs = ["field_path_test.cc"],
288+
deps = [
289+
":field_path",
290+
"//internal:testing",
291+
"@com_google_absl//absl/strings",
292+
],
293+
)
294+
295+
cc_library(
296+
name = "proto_type_mask",
297+
hdrs = ["proto_type_mask.h"],
298+
deps = [
299+
":field_path",
300+
"@com_google_absl//absl/strings",
301+
],
302+
)
303+
304+
cc_test(
305+
name = "proto_type_mask_test",
306+
srcs = ["proto_type_mask_test.cc"],
307+
deps = [
308+
":field_path",
309+
":proto_type_mask",
310+
"//internal:testing",
311+
"@com_google_absl//absl/strings",
312+
],
313+
)
314+
315+
cc_library(
316+
name = "proto_type_mask_provider",
317+
srcs = ["proto_type_mask_provider.cc"],
318+
hdrs = ["proto_type_mask_provider.h"],
319+
deps = [
320+
":field_path",
321+
":proto_type_mask",
322+
"//common:type",
323+
"//internal:status_macros",
324+
"//util/gtl:iterator_adaptors",
325+
"@com_google_absl//absl/container:flat_hash_map",
326+
"@com_google_absl//absl/memory",
327+
"@com_google_absl//absl/status",
328+
"@com_google_absl//absl/status:statusor",
329+
"@com_google_absl//absl/strings",
330+
"@com_google_absl//absl/strings:str_format",
331+
"@com_google_absl//absl/strings:string_view",
332+
"@com_google_absl//absl/types:optional",
333+
"@com_google_protobuf//:protobuf",
334+
],
335+
)
336+
337+
cc_test(
338+
name = "proto_type_mask_provider_test",
339+
srcs = ["proto_type_mask_provider_test.cc"],
340+
deps = [
341+
":proto_type_mask",
342+
":proto_type_mask_provider",
343+
"//internal:testing",
344+
"//internal:testing_descriptor_pool",
345+
"@com_google_absl//absl/container:flat_hash_map",
346+
"@com_google_absl//absl/status",
347+
"@com_google_absl//absl/status:status_matchers",
348+
"@com_google_absl//absl/strings",
349+
"@com_google_absl//absl/strings:string_view",
350+
],
351+
)

checker/internal/field_path.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
16+
#define THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
17+
18+
#include <string>
19+
#include <utility>
20+
#include <vector>
21+
22+
#include "absl/strings/str_format.h"
23+
#include "absl/strings/str_join.h"
24+
#include "absl/strings/str_split.h"
25+
#include "absl/strings/string_view.h"
26+
#include "absl/strings/substitute.h"
27+
28+
// Represents a single path within a FieldMask.
29+
class FieldPath {
30+
public:
31+
explicit FieldPath(std::string path) : path_(std::move(path)) {
32+
field_selection_ = absl::StrSplit(path_, kPathDelimiter);
33+
}
34+
35+
std::string GetPath() const { return path_; }
36+
37+
std::vector<std::string> GetFieldSelection() const {
38+
return field_selection_;
39+
}
40+
41+
// Returns the first field name in the path.
42+
std::string GetFieldName() const { return field_selection_.front(); }
43+
44+
template <typename Sink>
45+
friend void AbslStringify(Sink& sink, const FieldPath& field_path) {
46+
const std::string field_selection =
47+
absl::StrJoin(field_path.GetFieldSelection(), "', '");
48+
std::string output = absl::Substitute(
49+
"FieldPath { field path: '$0', field selection: {'$1'} }",
50+
field_path.GetPath(), field_selection);
51+
absl::Format(&sink, "%v", output);
52+
}
53+
54+
private:
55+
static inline constexpr char kPathDelimiter = '.';
56+
57+
// The input path. For example: "f.b.d".
58+
std::string path_;
59+
// The list of nested field names in the path. For example: {"f", "b", "d"}.
60+
std::vector<std::string> field_selection_;
61+
};
62+
63+
inline bool operator==(const FieldPath& lhs, const FieldPath& rhs) {
64+
return lhs.GetFieldSelection() == rhs.GetFieldSelection();
65+
}
66+
67+
inline bool operator<(const FieldPath& lhs, const FieldPath& rhs) {
68+
return lhs.GetFieldSelection() < rhs.GetFieldSelection();
69+
}
70+
71+
#endif // THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 "checker/internal/field_path.h"
16+
17+
#include "absl/strings/str_cat.h"
18+
#include "internal/testing.h"
19+
20+
namespace {
21+
22+
using ::testing::ElementsAre;
23+
24+
TEST(FieldPathTest, EmptyPathReturnsEmptyString) {
25+
FieldPath field_path("");
26+
EXPECT_EQ(field_path.GetPath(), "");
27+
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre(""));
28+
EXPECT_EQ(field_path.GetFieldName(), "");
29+
}
30+
31+
TEST(FieldPathTest, DelimiterPathReturnsEmptyStrings) {
32+
FieldPath field_path(".");
33+
EXPECT_EQ(field_path.GetPath(), ".");
34+
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre("", ""));
35+
EXPECT_EQ(field_path.GetFieldName(), "");
36+
}
37+
38+
TEST(FieldPathTest, FieldPathReturnsFields) {
39+
FieldPath field_path("resource.name.other_field");
40+
EXPECT_EQ(field_path.GetPath(), "resource.name.other_field");
41+
EXPECT_THAT(field_path.GetFieldSelection(),
42+
ElementsAre("resource", "name", "other_field"));
43+
EXPECT_EQ(field_path.GetFieldName(), "resource");
44+
}
45+
46+
TEST(FieldPathTest, AbslStringifyPrintsFieldSelection) {
47+
FieldPath field_path("resource.name");
48+
EXPECT_EQ(absl::StrCat(field_path),
49+
"FieldPath { field path: 'resource.name', field selection: "
50+
"{'resource', 'name'} }");
51+
}
52+
53+
TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsTrue) {
54+
FieldPath field_path_1("resource.name");
55+
FieldPath field_path_2("resource.name");
56+
EXPECT_TRUE(field_path_1 == field_path_2);
57+
}
58+
59+
TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsFalse) {
60+
FieldPath field_path_1("resource.name");
61+
FieldPath field_path_2("resource.type");
62+
EXPECT_FALSE(field_path_1 == field_path_2);
63+
}
64+
65+
TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsTrue) {
66+
FieldPath field_path_1("resource.name");
67+
FieldPath field_path_2("resource.type");
68+
EXPECT_TRUE(field_path_1 < field_path_2);
69+
}
70+
71+
TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsFalse) {
72+
FieldPath field_path_1("resource.name");
73+
FieldPath field_path_2("resource.name");
74+
EXPECT_FALSE(field_path_1 < field_path_2);
75+
}
76+
77+
} // namespace

checker/internal/proto_type_mask.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_
16+
#define THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_
17+
18+
#include <set>
19+
#include <string>
20+
#include <utility>
21+
22+
#include "absl/strings/str_join.h"
23+
#include "absl/strings/substitute.h"
24+
#include "checker/internal/field_path.h"
25+
26+
// Represents the fraction of a protobuf type's object graph that should be
27+
// visible within CEL expressions.
28+
class ProtoTypeMask {
29+
public:
30+
explicit ProtoTypeMask(std::string type_name,
31+
const std::set<std::string>& field_paths)
32+
: type_name_(std::move(type_name)) {
33+
for (const std::string& field_path : field_paths) {
34+
field_paths_.insert(FieldPath(field_path));
35+
}
36+
};
37+
38+
std::string GetTypeName() const { return type_name_; }
39+
40+
std::set<FieldPath> GetFieldPaths() const { return field_paths_; }
41+
42+
template <typename Sink>
43+
friend void AbslStringify(Sink& sink, const ProtoTypeMask& proto_type_mask) {
44+
std::string field_paths =
45+
absl::StrJoin(proto_type_mask.GetFieldPaths(), ", ");
46+
std::string output = absl::Substitute(
47+
"ProtoTypeMask { type name: '$0', field paths: { $1 } }",
48+
proto_type_mask.GetTypeName(), field_paths);
49+
absl::Format(&sink, "%v", output);
50+
}
51+
52+
private:
53+
// A type's full name. For example: "google.rpc.context.AttributeContext".
54+
std::string type_name_;
55+
// A representation of a FieldMask, which is a set of field paths.
56+
// A FieldMask contains one or more paths which contain identifier characters
57+
// that have been dot delimited, e.g. resource.name, request.auth.claims.
58+
// For each path, all descendent fields after the last element in the path are
59+
// visible. An empty set means all fields are hidden.
60+
std::set<FieldPath> field_paths_;
61+
};
62+
63+
#endif // THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_

0 commit comments

Comments
 (0)