Skip to content

Commit 1761e41

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Add ProtoTypeMaskRegistry, ProtoTypeMask, and FieldPath classes. The ProtoTypeMaskRegistry 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 ddcece1 commit 1761e41

10 files changed

Lines changed: 1130 additions & 0 deletions

checker/internal/BUILD

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,87 @@ cc_test(
310310
"@com_google_absl//absl/types:optional",
311311
],
312312
)
313+
314+
cc_library(
315+
name = "field_path",
316+
srcs = ["field_path.cc"],
317+
hdrs = ["field_path.h"],
318+
deps = [
319+
"@com_google_absl//absl/strings",
320+
"@com_google_absl//absl/strings:str_format",
321+
"@com_google_absl//absl/strings:string_view",
322+
"@com_google_absl//absl/types:span",
323+
],
324+
)
325+
326+
cc_test(
327+
name = "field_path_test",
328+
srcs = ["field_path_test.cc"],
329+
deps = [
330+
":field_path",
331+
"//internal:testing",
332+
"@com_google_absl//absl/strings",
333+
],
334+
)
335+
336+
cc_library(
337+
name = "proto_type_mask",
338+
srcs = ["proto_type_mask.cc"],
339+
hdrs = ["proto_type_mask.h"],
340+
deps = [
341+
":field_path",
342+
"@com_google_absl//absl/container:btree",
343+
"@com_google_absl//absl/strings",
344+
],
345+
)
346+
347+
cc_test(
348+
name = "proto_type_mask_test",
349+
srcs = ["proto_type_mask_test.cc"],
350+
deps = [
351+
":field_path",
352+
":proto_type_mask",
353+
"//internal:testing",
354+
"@com_google_absl//absl/strings",
355+
],
356+
)
357+
358+
cc_library(
359+
name = "proto_type_mask_registry",
360+
srcs = ["proto_type_mask_registry.cc"],
361+
hdrs = ["proto_type_mask_registry.h"],
362+
deps = [
363+
":field_path",
364+
":proto_type_mask",
365+
"//common:type",
366+
"//internal:status_macros",
367+
"@com_google_absl//absl/base:nullability",
368+
"@com_google_absl//absl/container:btree",
369+
"@com_google_absl//absl/container:flat_hash_map",
370+
"@com_google_absl//absl/container:flat_hash_set",
371+
"@com_google_absl//absl/status",
372+
"@com_google_absl//absl/status:statusor",
373+
"@com_google_absl//absl/strings",
374+
"@com_google_absl//absl/strings:string_view",
375+
"@com_google_absl//absl/types:optional",
376+
"@com_google_absl//absl/types:span",
377+
"@com_google_protobuf//:protobuf",
378+
],
379+
)
380+
381+
cc_test(
382+
name = "proto_type_mask_registry_test",
383+
srcs = ["proto_type_mask_registry_test.cc"],
384+
deps = [
385+
":proto_type_mask",
386+
":proto_type_mask_registry",
387+
"//internal:testing",
388+
"//internal:testing_descriptor_pool",
389+
"@com_google_absl//absl/container:flat_hash_map",
390+
"@com_google_absl//absl/container:flat_hash_set",
391+
"@com_google_absl//absl/status",
392+
"@com_google_absl//absl/status:status_matchers",
393+
"@com_google_absl//absl/strings",
394+
"@com_google_absl//absl/strings:string_view",
395+
],
396+
)

checker/internal/field_path.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 "checker/internal/field_path.h"
16+
17+
#include <string>
18+
19+
#include "absl/strings/str_join.h"
20+
#include "absl/strings/substitute.h"
21+
22+
namespace cel::checker_internal {
23+
24+
std::string FieldPath::DebugString() const {
25+
return absl::Substitute(
26+
"FieldPath { field path: '$0', field selection: {'$1'} }", path_,
27+
absl::StrJoin(field_selection_, "', '"));
28+
}
29+
30+
} // namespace cel::checker_internal

checker/internal/field_path.h

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

checker/internal/proto_type_mask.h

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

0 commit comments

Comments
 (0)