Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions checker/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,81 @@ cc_test(
"@com_google_protobuf//:protobuf",
],
)

cc_library(
name = "field_path",
hdrs = ["field_path.h"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:span",
],
)

cc_test(
name = "field_path_test",
srcs = ["field_path_test.cc"],
deps = [
":field_path",
"//internal:testing",
],
)

cc_library(
name = "proto_type_mask",
hdrs = ["proto_type_mask.h"],
deps = [
":field_path",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/strings",
],
)

cc_test(
name = "proto_type_mask_test",
srcs = ["proto_type_mask_test.cc"],
deps = [
":field_path",
":proto_type_mask",
"//internal:testing",
],
)

cc_library(
name = "proto_type_mask_registry",
srcs = ["proto_type_mask_registry.cc"],
hdrs = ["proto_type_mask_registry.h"],
deps = [
":field_path",
":proto_type_mask",
"//common:type",
"//internal:status_macros",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_protobuf//:protobuf",
],
)

cc_test(
name = "proto_type_mask_registry_test",
srcs = ["proto_type_mask_registry_test.cc"],
deps = [
":proto_type_mask",
":proto_type_mask_registry",
"//internal:testing",
"//internal:testing_descriptor_pool",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:status_matchers",
"@com_google_absl//absl/strings:string_view",
],
)
73 changes: 73 additions & 0 deletions checker/internal/field_path.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2025 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.

#ifndef THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
#define THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_

#include <string>
#include <utility>
#include <vector>

#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"

namespace cel::checker_internal {

// Represents a single path within a FieldMask.
class FieldPath {
public:
explicit FieldPath(std::string path)
: path_(std::move(path)),
field_selection_(absl::StrSplit(path_, kPathDelimiter)) {}

absl::string_view GetPath() const { return path_; }

absl::Span<const std::string> GetFieldSelection() const {
return field_selection_;
}

// Returns the first field name in the path.
std::string GetFieldName() const { return field_selection_.front(); }

std::string DebugString() const {
return absl::Substitute(
"FieldPath { field path: '$0', field selection: {'$1'} }", path_,
absl::StrJoin(field_selection_, "', '"));
}

private:
static inline constexpr char kPathDelimiter = '.';

// The input path. For example: "f.b.d".
std::string path_;
// The list of nested field names in the path. For example: {"f", "b", "d"}.
std::vector<std::string> field_selection_;
};

inline bool operator==(const FieldPath& lhs, const FieldPath& rhs) {
return lhs.GetFieldSelection() == rhs.GetFieldSelection();
}

// Compares the field selections in the field paths.
// This is only intended as an arbitrary ordering for a set.
inline bool operator<(const FieldPath& lhs, const FieldPath& rhs) {
return lhs.GetFieldSelection() < rhs.GetFieldSelection();
}

} // namespace cel::checker_internal

#endif // THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
78 changes: 78 additions & 0 deletions checker/internal/field_path_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2025 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.

#include "checker/internal/field_path.h"

#include "internal/testing.h"

namespace cel::checker_internal {
namespace {

using ::testing::ElementsAre;

TEST(FieldPathTest, EmptyPathReturnsEmptyString) {
FieldPath field_path("");
EXPECT_EQ(field_path.GetPath(), "");
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre(""));
EXPECT_EQ(field_path.GetFieldName(), "");
}

TEST(FieldPathTest, DelimiterPathReturnsEmptyStrings) {
FieldPath field_path(".");
EXPECT_EQ(field_path.GetPath(), ".");
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre("", ""));
EXPECT_EQ(field_path.GetFieldName(), "");
}

TEST(FieldPathTest, FieldPathReturnsFields) {
FieldPath field_path("resource.name.other_field");
EXPECT_EQ(field_path.GetPath(), "resource.name.other_field");
EXPECT_THAT(field_path.GetFieldSelection(),
ElementsAre("resource", "name", "other_field"));
EXPECT_EQ(field_path.GetFieldName(), "resource");
}

TEST(FieldPathTest, DebugStringPrintsFieldSelection) {
FieldPath field_path("resource.name");
EXPECT_EQ(field_path.DebugString(),
"FieldPath { field path: 'resource.name', field selection: "
"{'resource', 'name'} }");
}

TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsTrue) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.name");
EXPECT_TRUE(field_path_1 == field_path_2);
}

TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsFalse) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.type");
EXPECT_FALSE(field_path_1 == field_path_2);
}

TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsTrue) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.type");
EXPECT_TRUE(field_path_1 < field_path_2);
}

TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsFalse) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.name");
EXPECT_FALSE(field_path_1 < field_path_2);
}

} // namespace
} // namespace cel::checker_internal
74 changes: 74 additions & 0 deletions checker/internal/proto_type_mask.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2025 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.

#ifndef THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_
#define THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_

#include <set>
#include <string>
#include <utility>
#include <vector>

#include "absl/container/btree_set.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "checker/internal/field_path.h"

namespace cel::checker_internal {

// Represents the fraction of a protobuf type's object graph that should be
// visible within CEL expressions.
class ProtoTypeMask {
public:
explicit ProtoTypeMask(std::string type_name,
const std::set<std::string>& field_paths)
: type_name_(std::move(type_name)) {
for (const std::string& field_path : field_paths) {
field_paths_.insert(FieldPath(field_path));
}
};

absl::string_view GetTypeName() const { return type_name_; }

const absl::btree_set<FieldPath>& GetFieldPaths() const {
return field_paths_;
}

std::string DebugString() const {
// Represent each FieldPath by its path because it is easiest to read.
std::vector<std::string> paths;
paths.reserve(field_paths_.size());
for (const FieldPath& field_path : field_paths_) {
paths.emplace_back(field_path.GetPath());
}
return absl::Substitute(
"ProtoTypeMask { type name: '$0', field paths: { '$1' } }",
type_name_, absl::StrJoin(paths, "', '"));
}

private:
// A type's full name. For example: "google.rpc.context.AttributeContext".
std::string type_name_;
// A representation of a FieldMask, which is a set of field paths.
// A FieldMask contains one or more paths which contain identifier characters
// that have been dot delimited, e.g. resource.name, request.auth.claims.
// For each path, all descendent fields after the last element in the path are
// visible. An empty set means all fields are hidden.
absl::btree_set<FieldPath> field_paths_;
};

} // namespace cel::checker_internal

#endif // THIRD_PARTY_CEL_CPP_CHECKER_PROTO_TYPE_MASK_H_
Loading