Refactor [String/Header/Path]matchers in xds_api.

pull/25122/head
Ashitha Santhosh 4 years ago
parent 4860738db7
commit 4ca878799b
  1. 4
      BUILD
  2. 2
      BUILD.gn
  3. 40
      CMakeLists.txt
  4. 2
      Makefile
  5. 15
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 2
      gRPC-C++.podspec
  9. 3
      gRPC-Core.podspec
  10. 2
      grpc.gemspec
  11. 1
      grpc.gyp
  12. 2
      package.xml
  13. 2
      src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
  14. 78
      src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
  15. 408
      src/core/ext/xds/xds_api.cc
  16. 84
      src/core/ext/xds/xds_api.h
  17. 4
      src/core/ext/xds/xds_certificate_provider.cc
  18. 6
      src/core/ext/xds/xds_certificate_provider.h
  19. 339
      src/core/lib/security/authorization/matchers.cc
  20. 158
      src/core/lib/security/authorization/matchers.h
  21. 6
      src/core/lib/security/credentials/xds/xds_credentials.cc
  22. 2
      src/core/lib/security/credentials/xds/xds_credentials.h
  23. 1
      src/python/grpcio/grpc_core_dependencies.py
  24. 12
      test/core/security/BUILD
  25. 218
      test/core/security/matchers_test.cc
  26. 54
      test/core/security/xds_credentials_test.cc
  27. 8
      test/cpp/end2end/xds_end2end_test.cc
  28. 2
      tools/doxygen/Doxyfile.c++.internal
  29. 2
      tools/doxygen/Doxyfile.core.internal
  30. 24
      tools/run_tests/generated/tests.json

@ -1401,6 +1401,7 @@ grpc_cc_library(
deps = [
"envoy_ads_upb",
"envoy_ads_upbdefs",
"grpc_authorization_engine",
"grpc_base",
"grpc_client_channel",
"grpc_secure",
@ -1950,13 +1951,16 @@ grpc_cc_library(
srcs = [
"src/core/lib/security/authorization/authorization_engine.cc",
"src/core/lib/security/authorization/evaluate_args.cc",
"src/core/lib/security/authorization/matchers.cc",
],
hdrs = [
"src/core/lib/security/authorization/authorization_engine.h",
"src/core/lib/security/authorization/evaluate_args.h",
"src/core/lib/security/authorization/matchers.h",
],
external_deps = [
"absl/container:flat_hash_set",
"re2",
],
language = "c++",
deps = [

@ -983,6 +983,8 @@ config("grpc_config") {
"src/core/lib/security/authorization/authorization_engine.h",
"src/core/lib/security/authorization/evaluate_args.cc",
"src/core/lib/security/authorization/evaluate_args.h",
"src/core/lib/security/authorization/matchers.cc",
"src/core/lib/security/authorization/matchers.h",
"src/core/lib/security/authorization/mock_cel/activation.h",
"src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h",
"src/core/lib/security/authorization/mock_cel/cel_expression.h",

@ -866,6 +866,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx lb_load_data_store_test)
add_dependencies(buildtests_cxx linux_system_roots_test)
add_dependencies(buildtests_cxx log_test)
add_dependencies(buildtests_cxx matchers_test)
add_dependencies(buildtests_cxx message_allocator_end2end_test)
add_dependencies(buildtests_cxx mock_test)
add_dependencies(buildtests_cxx nonblocking_test)
@ -1849,6 +1850,7 @@ add_library(grpc
src/core/lib/json/json_writer.cc
src/core/lib/security/authorization/authorization_engine.cc
src/core/lib/security/authorization/evaluate_args.cc
src/core/lib/security/authorization/matchers.cc
src/core/lib/security/context/security_context.cc
src/core/lib/security/credentials/alts/alts_credentials.cc
src/core/lib/security/credentials/alts/check_gcp_environment.cc
@ -12734,6 +12736,44 @@ target_link_libraries(log_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(matchers_test
test/core/security/matchers_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(matchers_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(matchers_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc
gpr
address_sorting
upb
)
endif()
if(gRPC_BUILD_TESTS)

@ -1438,6 +1438,7 @@ LIBGRPC_SRC = \
src/core/lib/json/json_writer.cc \
src/core/lib/security/authorization/authorization_engine.cc \
src/core/lib/security/authorization/evaluate_args.cc \
src/core/lib/security/authorization/matchers.cc \
src/core/lib/security/context/security_context.cc \
src/core/lib/security/credentials/alts/alts_credentials.cc \
src/core/lib/security/credentials/alts/check_gcp_environment.cc \
@ -2826,6 +2827,7 @@ src/core/ext/xds/xds_server_config_fetcher.cc: $(OPENSSL_DEP)
src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/authorization_engine.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/evaluate_args.cc: $(OPENSSL_DEP)
src/core/lib/security/authorization/matchers.cc: $(OPENSSL_DEP)
src/core/lib/security/context/security_context.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/alts_credentials.cc: $(OPENSSL_DEP)
src/core/lib/security/credentials/alts/check_gcp_environment.cc: $(OPENSSL_DEP)

@ -755,6 +755,7 @@ libs:
- src/core/lib/json/json_util.h
- src/core/lib/security/authorization/authorization_engine.h
- src/core/lib/security/authorization/evaluate_args.h
- src/core/lib/security/authorization/matchers.h
- src/core/lib/security/authorization/mock_cel/activation.h
- src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h
- src/core/lib/security/authorization/mock_cel/cel_expression.h
@ -1270,6 +1271,7 @@ libs:
- src/core/lib/json/json_writer.cc
- src/core/lib/security/authorization/authorization_engine.cc
- src/core/lib/security/authorization/evaluate_args.cc
- src/core/lib/security/authorization/matchers.cc
- src/core/lib/security/context/security_context.cc
- src/core/lib/security/credentials/alts/alts_credentials.cc
- src/core/lib/security/credentials/alts/check_gcp_environment.cc
@ -6694,6 +6696,19 @@ targets:
- address_sorting
- upb
uses_polling: false
- name: matchers_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/security/matchers_test.cc
deps:
- grpc_test_util
- grpc
- gpr
- address_sorting
- upb
- name: message_allocator_end2end_test
gtest: true
build: test

@ -489,6 +489,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/profiling/stap_timers.cc \
src/core/lib/security/authorization/authorization_engine.cc \
src/core/lib/security/authorization/evaluate_args.cc \
src/core/lib/security/authorization/matchers.cc \
src/core/lib/security/context/security_context.cc \
src/core/lib/security/credentials/alts/alts_credentials.cc \
src/core/lib/security/credentials/alts/check_gcp_environment.cc \

@ -456,6 +456,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\profiling\\stap_timers.cc " +
"src\\core\\lib\\security\\authorization\\authorization_engine.cc " +
"src\\core\\lib\\security\\authorization\\evaluate_args.cc " +
"src\\core\\lib\\security\\authorization\\matchers.cc " +
"src\\core\\lib\\security\\context\\security_context.cc " +
"src\\core\\lib\\security\\credentials\\alts\\alts_credentials.cc " +
"src\\core\\lib\\security\\credentials\\alts\\check_gcp_environment.cc " +

@ -602,6 +602,7 @@ Pod::Spec.new do |s|
'src/core/lib/profiling/timers.h',
'src/core/lib/security/authorization/authorization_engine.h',
'src/core/lib/security/authorization/evaluate_args.h',
'src/core/lib/security/authorization/matchers.h',
'src/core/lib/security/authorization/mock_cel/activation.h',
'src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h',
'src/core/lib/security/authorization/mock_cel/cel_expression.h',
@ -1220,6 +1221,7 @@ Pod::Spec.new do |s|
'src/core/lib/profiling/timers.h',
'src/core/lib/security/authorization/authorization_engine.h',
'src/core/lib/security/authorization/evaluate_args.h',
'src/core/lib/security/authorization/matchers.h',
'src/core/lib/security/authorization/mock_cel/activation.h',
'src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h',
'src/core/lib/security/authorization/mock_cel/cel_expression.h',

@ -1041,6 +1041,8 @@ Pod::Spec.new do |s|
'src/core/lib/security/authorization/authorization_engine.h',
'src/core/lib/security/authorization/evaluate_args.cc',
'src/core/lib/security/authorization/evaluate_args.h',
'src/core/lib/security/authorization/matchers.cc',
'src/core/lib/security/authorization/matchers.h',
'src/core/lib/security/authorization/mock_cel/activation.h',
'src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h',
'src/core/lib/security/authorization/mock_cel/cel_expression.h',
@ -1752,6 +1754,7 @@ Pod::Spec.new do |s|
'src/core/lib/profiling/timers.h',
'src/core/lib/security/authorization/authorization_engine.h',
'src/core/lib/security/authorization/evaluate_args.h',
'src/core/lib/security/authorization/matchers.h',
'src/core/lib/security/authorization/mock_cel/activation.h',
'src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h',
'src/core/lib/security/authorization/mock_cel/cel_expression.h',

@ -956,6 +956,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/security/authorization/authorization_engine.h )
s.files += %w( src/core/lib/security/authorization/evaluate_args.cc )
s.files += %w( src/core/lib/security/authorization/evaluate_args.h )
s.files += %w( src/core/lib/security/authorization/matchers.cc )
s.files += %w( src/core/lib/security/authorization/matchers.h )
s.files += %w( src/core/lib/security/authorization/mock_cel/activation.h )
s.files += %w( src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h )
s.files += %w( src/core/lib/security/authorization/mock_cel/cel_expression.h )

@ -857,6 +857,7 @@
'src/core/lib/json/json_writer.cc',
'src/core/lib/security/authorization/authorization_engine.cc',
'src/core/lib/security/authorization/evaluate_args.cc',
'src/core/lib/security/authorization/matchers.cc',
'src/core/lib/security/context/security_context.cc',
'src/core/lib/security/credentials/alts/alts_credentials.cc',
'src/core/lib/security/credentials/alts/check_gcp_environment.cc',

@ -936,6 +936,8 @@
<file baseinstalldir="/" name="src/core/lib/security/authorization/authorization_engine.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/evaluate_args.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/evaluate_args.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/matchers.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/matchers.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/mock_cel/activation.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/security/authorization/mock_cel/cel_expression.h" role="src" />

@ -533,7 +533,7 @@ grpc_error* CdsLb::UpdateXdsCertificateProvider(
? nullptr
: identity_certificate_provider_->distributor());
// Configure SAN matchers.
const std::vector<XdsApi::StringMatcher>& match_subject_alt_names =
const std::vector<StringMatcher>& match_subject_alt_names =
cluster_data.common_tls_context.combined_validation_context
.default_validation_context.match_subject_alt_names;
xds_certificate_provider_->UpdateSubjectAlternativeNameMatchers(

@ -359,28 +359,6 @@ void XdsResolver::XdsConfigSelector::MaybeAddCluster(const std::string& name) {
}
}
bool PathMatch(const absl::string_view& path,
const XdsApi::Route::Matchers::PathMatcher& path_matcher) {
switch (path_matcher.type) {
case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX:
return path_matcher.case_sensitive
? absl::StartsWith(path, path_matcher.string_matcher)
: absl::StartsWithIgnoreCase(path,
path_matcher.string_matcher);
case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH:
return path_matcher.case_sensitive
? path == path_matcher.string_matcher
: absl::EqualsIgnoreCase(path, path_matcher.string_matcher);
case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX:
// Note: Case-sensitive option will already have been set appropriately
// in path_matcher.regex_matcher when it was constructed, so no
// need to check it here.
return RE2::FullMatch(path.data(), *path_matcher.regex_matcher);
default:
return false;
}
}
absl::optional<absl::string_view> GetMetadataValue(
const std::string& target_key, grpc_metadata_batch* initial_metadata,
std::string* concatenated_value) {
@ -404,61 +382,29 @@ absl::optional<absl::string_view> GetMetadataValue(
return *concatenated_value;
}
bool HeaderMatchHelper(
const XdsApi::Route::Matchers::HeaderMatcher& header_matcher,
grpc_metadata_batch* initial_metadata) {
bool HeaderMatchHelper(const HeaderMatcher& header_matcher,
grpc_metadata_batch* initial_metadata) {
std::string concatenated_value;
absl::optional<absl::string_view> value;
// Note: If we ever allow binary headers here, we still need to
// special-case ignore "grpc-tags-bin" and "grpc-trace-bin", since
// they are not visible to the LB policy in grpc-go.
if (absl::EndsWith(header_matcher.name, "-bin") ||
header_matcher.name == "grpc-previous-rpc-attempts") {
if (absl::EndsWith(header_matcher.name(), "-bin") ||
header_matcher.name() == "grpc-previous-rpc-attempts") {
value = absl::nullopt;
} else if (header_matcher.name == "content-type") {
} else if (header_matcher.name() == "content-type") {
value = "application/grpc";
} else {
value = GetMetadataValue(header_matcher.name, initial_metadata,
value = GetMetadataValue(header_matcher.name(), initial_metadata,
&concatenated_value);
}
if (!value.has_value()) {
if (header_matcher.type ==
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT) {
return !header_matcher.present_match;
} else {
// For all other header matcher types, we need the header value to
// exist to consider matches.
return false;
}
}
switch (header_matcher.type) {
case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT:
return value.value() == header_matcher.string_matcher;
case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX:
return RE2::FullMatch(value.value().data(), *header_matcher.regex_match);
case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE:
int64_t int_value;
if (!absl::SimpleAtoi(value.value(), &int_value)) {
return false;
}
return int_value >= header_matcher.range_start &&
int_value < header_matcher.range_end;
case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX:
return absl::StartsWith(value.value(), header_matcher.string_matcher);
case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX:
return absl::EndsWith(value.value(), header_matcher.string_matcher);
default:
return false;
}
return header_matcher.Match(value);
}
bool HeadersMatch(
const std::vector<XdsApi::Route::Matchers::HeaderMatcher>& header_matchers,
grpc_metadata_batch* initial_metadata) {
bool HeadersMatch(const std::vector<HeaderMatcher>& header_matchers,
grpc_metadata_batch* initial_metadata) {
for (const auto& header_matcher : header_matchers) {
bool match = HeaderMatchHelper(header_matcher, initial_metadata);
if (header_matcher.invert_match) match = !match;
if (!match) return false;
if (!HeaderMatchHelper(header_matcher, initial_metadata)) return false;
}
return true;
}
@ -473,8 +419,8 @@ ConfigSelector::CallConfig XdsResolver::XdsConfigSelector::GetCallConfig(
GetCallConfigArgs args) {
for (const auto& entry : route_table_) {
// Path matching.
if (!PathMatch(StringViewFromSlice(*args.path),
entry.route.matchers.path_matcher)) {
if (!entry.route.matchers.path_matcher.Match(
StringViewFromSlice(*args.path))) {
continue;
}
// Header Matching.

@ -112,170 +112,14 @@ bool XdsSecurityEnabled() {
return parse_succeeded && parsed_value;
}
//
// XdsApi::Route::Matchers::PathMatcher
//
XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other)
: type(other.type), case_sensitive(other.case_sensitive) {
if (type == PathMatcherType::REGEX) {
RE2::Options options;
options.set_case_sensitive(case_sensitive);
regex_matcher =
absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
} else {
string_matcher = other.string_matcher;
}
}
XdsApi::Route::Matchers::PathMatcher& XdsApi::Route::Matchers::PathMatcher::
operator=(const PathMatcher& other) {
type = other.type;
case_sensitive = other.case_sensitive;
if (type == PathMatcherType::REGEX) {
RE2::Options options;
options.set_case_sensitive(case_sensitive);
regex_matcher =
absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
} else {
string_matcher = other.string_matcher;
}
return *this;
}
bool XdsApi::Route::Matchers::PathMatcher::operator==(
const PathMatcher& other) const {
if (type != other.type) return false;
if (case_sensitive != other.case_sensitive) return false;
if (type == PathMatcherType::REGEX) {
// Should never be null.
if (regex_matcher == nullptr || other.regex_matcher == nullptr) {
return false;
}
return regex_matcher->pattern() == other.regex_matcher->pattern();
}
return string_matcher == other.string_matcher;
}
std::string XdsApi::Route::Matchers::PathMatcher::ToString() const {
std::string path_type_string;
switch (type) {
case PathMatcherType::PATH:
path_type_string = "path match";
break;
case PathMatcherType::PREFIX:
path_type_string = "prefix match";
break;
case PathMatcherType::REGEX:
path_type_string = "regex match";
break;
default:
break;
}
return absl::StrFormat("Path %s:%s%s", path_type_string,
type == PathMatcherType::REGEX
? regex_matcher->pattern()
: string_matcher,
case_sensitive ? "" : "[case_sensitive=false]");
}
//
// XdsApi::Route::Matchers::HeaderMatcher
//
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher(
const HeaderMatcher& other)
: name(other.name), type(other.type), invert_match(other.invert_match) {
switch (type) {
case HeaderMatcherType::REGEX:
regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
break;
case HeaderMatcherType::RANGE:
range_start = other.range_start;
range_end = other.range_end;
break;
case HeaderMatcherType::PRESENT:
present_match = other.present_match;
break;
default:
string_matcher = other.string_matcher;
}
}
XdsApi::Route::Matchers::HeaderMatcher& XdsApi::Route::Matchers::HeaderMatcher::
operator=(const HeaderMatcher& other) {
name = other.name;
type = other.type;
invert_match = other.invert_match;
switch (type) {
case HeaderMatcherType::REGEX:
regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
break;
case HeaderMatcherType::RANGE:
range_start = other.range_start;
range_end = other.range_end;
break;
case HeaderMatcherType::PRESENT:
present_match = other.present_match;
break;
default:
string_matcher = other.string_matcher;
}
return *this;
}
bool XdsApi::Route::Matchers::HeaderMatcher::operator==(
const HeaderMatcher& other) const {
if (name != other.name) return false;
if (type != other.type) return false;
if (invert_match != other.invert_match) return false;
switch (type) {
case HeaderMatcherType::REGEX:
return regex_match->pattern() != other.regex_match->pattern();
case HeaderMatcherType::RANGE:
return range_start != other.range_start && range_end != other.range_end;
case HeaderMatcherType::PRESENT:
return present_match != other.present_match;
default:
return string_matcher != other.string_matcher;
}
}
std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const {
switch (type) {
case HeaderMatcherType::EXACT:
return absl::StrFormat("Header exact match:%s %s:%s",
invert_match ? " not" : "", name, string_matcher);
case HeaderMatcherType::REGEX:
return absl::StrFormat("Header regex match:%s %s:%s",
invert_match ? " not" : "", name,
regex_match->pattern());
case HeaderMatcherType::RANGE:
return absl::StrFormat("Header range match:%s %s:[%d, %d)",
invert_match ? " not" : "", name, range_start,
range_end);
case HeaderMatcherType::PRESENT:
return absl::StrFormat("Header present match:%s %s:%s",
invert_match ? " not" : "", name,
present_match ? "true" : "false");
case HeaderMatcherType::PREFIX:
return absl::StrFormat("Header prefix match:%s %s:%s",
invert_match ? " not" : "", name, string_matcher);
case HeaderMatcherType::SUFFIX:
return absl::StrFormat("Header suffix match:%s %s:%s",
invert_match ? " not" : "", name, string_matcher);
default:
return "";
}
}
//
// XdsApi::Route
//
std::string XdsApi::Route::Matchers::ToString() const {
std::vector<std::string> contents;
contents.push_back(path_matcher.ToString());
contents.push_back(
absl::StrFormat("PathMatcher{%s}", path_matcher.ToString()));
for (const HeaderMatcher& header_matcher : header_matchers) {
contents.push_back(header_matcher.ToString());
}
@ -427,102 +271,6 @@ XdsApi::RdsUpdate::VirtualHost* XdsApi::RdsUpdate::FindVirtualHostForDomain(
return target_vhost;
}
//
// XdsApi::StringMatcher
//
XdsApi::StringMatcher::StringMatcher(StringMatcherType type,
const std::string& matcher,
bool ignore_case)
: type_(type), ignore_case_(ignore_case) {
if (type_ == StringMatcherType::SAFE_REGEX) {
regex_matcher_ = absl::make_unique<RE2>(matcher);
} else {
string_matcher_ = matcher;
}
}
XdsApi::StringMatcher::StringMatcher(const StringMatcher& other)
: type_(other.type_), ignore_case_(other.ignore_case_) {
switch (type_) {
case StringMatcherType::SAFE_REGEX:
regex_matcher_ = absl::make_unique<RE2>(other.regex_matcher_->pattern());
break;
default:
string_matcher_ = other.string_matcher_;
}
}
XdsApi::StringMatcher& XdsApi::StringMatcher::operator=(
const StringMatcher& other) {
type_ = other.type_;
switch (type_) {
case StringMatcherType::SAFE_REGEX:
regex_matcher_ = absl::make_unique<RE2>(other.regex_matcher_->pattern());
break;
default:
string_matcher_ = other.string_matcher_;
}
ignore_case_ = other.ignore_case_;
return *this;
}
bool XdsApi::StringMatcher::operator==(const StringMatcher& other) const {
if (type_ != other.type_ || ignore_case_ != other.ignore_case_) return false;
switch (type_) {
case StringMatcherType::SAFE_REGEX:
return regex_matcher_->pattern() == other.regex_matcher_->pattern();
default:
return string_matcher_ == other.string_matcher_;
}
}
bool XdsApi::StringMatcher::Match(absl::string_view value) const {
switch (type_) {
case XdsApi::StringMatcher::StringMatcherType::EXACT:
return ignore_case_ ? absl::EqualsIgnoreCase(value, string_matcher_)
: value == string_matcher_;
case XdsApi::StringMatcher::StringMatcherType::PREFIX:
return ignore_case_ ? absl::StartsWithIgnoreCase(value, string_matcher_)
: absl::StartsWith(value, string_matcher_);
case XdsApi::StringMatcher::StringMatcherType::SUFFIX:
return ignore_case_ ? absl::EndsWithIgnoreCase(value, string_matcher_)
: absl::EndsWith(value, string_matcher_);
case XdsApi::StringMatcher::StringMatcherType::CONTAINS:
return ignore_case_
? absl::StrContains(absl::AsciiStrToLower(value),
absl::AsciiStrToLower(string_matcher_))
: absl::StrContains(value, string_matcher_);
case XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX:
// ignore_case_ is ignored for SAFE_REGEX
return RE2::FullMatch(std::string(value), *regex_matcher_);
default:
return false;
}
}
std::string XdsApi::StringMatcher::ToString() const {
switch (type_) {
case StringMatcherType::EXACT:
return absl::StrFormat("StringMatcher{exact=%s%s}", string_matcher_,
ignore_case_ ? ", ignore_case" : "");
case StringMatcherType::PREFIX:
return absl::StrFormat("StringMatcher{prefix=%s%s}", string_matcher_,
ignore_case_ ? ", ignore_case" : "");
case StringMatcherType::SUFFIX:
return absl::StrFormat("StringMatcher{suffix=%s%s}", string_matcher_,
ignore_case_ ? ", ignore_case" : "");
case StringMatcherType::CONTAINS:
return absl::StrFormat("StringMatcher{contains=%s%s}", string_matcher_,
ignore_case_ ? ", ignore_case" : "");
case StringMatcherType::SAFE_REGEX:
return absl::StrFormat("StringMatcher{safe_regex=%s}",
regex_matcher_->pattern());
default:
return "";
}
}
//
// XdsApi::CommonTlsContext::CertificateValidationContext
//
@ -1103,11 +851,14 @@ void MaybeLogClusterLoadAssignment(
grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
XdsApi::Route* route, bool* ignore_route) {
auto* case_sensitive = envoy_config_route_v3_RouteMatch_case_sensitive(match);
if (case_sensitive != nullptr) {
route->matchers.path_matcher.case_sensitive =
google_protobuf_BoolValue_value(case_sensitive);
}
auto* case_sensitive_ptr =
envoy_config_route_v3_RouteMatch_case_sensitive(match);
bool case_sensitive = true;
if (case_sensitive_ptr != nullptr) {
case_sensitive = google_protobuf_BoolValue_value(case_sensitive_ptr);
}
StringMatcher::Type type;
std::string match_string;
if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
absl::string_view prefix =
UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
@ -1132,9 +883,8 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
return GRPC_ERROR_NONE;
}
}
route->matchers.path_matcher.type =
XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX;
route->matchers.path_matcher.string_matcher = std::string(prefix);
type = StringMatcher::Type::PREFIX;
match_string = std::string(prefix);
} else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
absl::string_view path =
UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
@ -1167,29 +917,28 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
*ignore_route = true;
return GRPC_ERROR_NONE;
}
route->matchers.path_matcher.type =
XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH;
route->matchers.path_matcher.string_matcher = std::string(path);
type = StringMatcher::Type::EXACT;
match_string = std::string(path);
} else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
envoy_config_route_v3_RouteMatch_safe_regex(match);
GPR_ASSERT(regex_matcher != nullptr);
std::string matcher = UpbStringToStdString(
type = StringMatcher::Type::SAFE_REGEX;
match_string = UpbStringToStdString(
envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
RE2::Options options;
options.set_case_sensitive(route->matchers.path_matcher.case_sensitive);
auto regex = absl::make_unique<RE2>(std::move(matcher), options);
if (!regex->ok()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid regex string specified in path matcher.");
}
route->matchers.path_matcher.type =
XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX;
route->matchers.path_matcher.regex_matcher = std::move(regex);
} else {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid route path specifier specified.");
}
absl::StatusOr<StringMatcher> string_matcher =
StringMatcher::Create(type, match_string, case_sensitive);
if (!string_matcher.ok()) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("path matcher: ", string_matcher.status().message())
.c_str());
;
}
route->matchers.path_matcher = std::move(string_matcher.value());
return GRPC_ERROR_NONE;
}
@ -1200,64 +949,62 @@ grpc_error* RouteHeaderMatchersParse(
envoy_config_route_v3_RouteMatch_headers(match, &size);
for (size_t i = 0; i < size; ++i) {
const envoy_config_route_v3_HeaderMatcher* header = headers[i];
XdsApi::Route::Matchers::HeaderMatcher header_matcher;
header_matcher.name =
const std::string name =
UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
HeaderMatcher::Type type;
std::string match_string;
int64_t range_start = 0;
int64_t range_end = 0;
bool present_match = false;
if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT;
header_matcher.string_matcher = UpbStringToStdString(
type = HeaderMatcher::Type::EXACT;
match_string = UpbStringToStdString(
envoy_config_route_v3_HeaderMatcher_exact_match(header));
} else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
header)) {
const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
GPR_ASSERT(regex_matcher != nullptr);
const std::string matcher = UpbStringToStdString(
type = HeaderMatcher::Type::SAFE_REGEX;
match_string = UpbStringToStdString(
envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
std::unique_ptr<RE2> regex = absl::make_unique<RE2>(matcher);
if (!regex->ok()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid regex string specified in header matcher.");
}
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX;
header_matcher.regex_match = std::move(regex);
} else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE;
type = HeaderMatcher::Type::RANGE;
const envoy_type_v3_Int64Range* range_matcher =
envoy_config_route_v3_HeaderMatcher_range_match(header);
header_matcher.range_start =
envoy_type_v3_Int64Range_start(range_matcher);
header_matcher.range_end = envoy_type_v3_Int64Range_end(range_matcher);
if (header_matcher.range_end < header_matcher.range_start) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid range header matcher specifier specified: end "
"cannot be smaller than start.");
}
range_start = envoy_type_v3_Int64Range_start(range_matcher);
range_end = envoy_type_v3_Int64Range_end(range_matcher);
} else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT;
header_matcher.present_match =
envoy_config_route_v3_HeaderMatcher_present_match(header);
type = HeaderMatcher::Type::PRESENT;
present_match = envoy_config_route_v3_HeaderMatcher_present_match(header);
} else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX;
header_matcher.string_matcher = UpbStringToStdString(
type = HeaderMatcher::Type::PREFIX;
match_string = UpbStringToStdString(
envoy_config_route_v3_HeaderMatcher_prefix_match(header));
} else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
header_matcher.type =
XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX;
header_matcher.string_matcher = UpbStringToStdString(
type = HeaderMatcher::Type::SUFFIX;
match_string = UpbStringToStdString(
envoy_config_route_v3_HeaderMatcher_suffix_match(header));
} else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) {
type = HeaderMatcher::Type::CONTAINS;
match_string = UpbStringToStdString(
envoy_config_route_v3_HeaderMatcher_contains_match(header));
} else {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid route header matcher specified.");
}
header_matcher.invert_match =
bool invert_match =
envoy_config_route_v3_HeaderMatcher_invert_match(header);
route->matchers.header_matchers.emplace_back(std::move(header_matcher));
absl::StatusOr<HeaderMatcher> header_matcher =
HeaderMatcher::Create(name, type, match_string, range_start, range_end,
present_match, invert_match);
if (!header_matcher.ok()) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("header matcher: ", header_matcher.status().message())
.c_str());
}
route->matchers.header_matchers.emplace_back(
std::move(header_matcher.value()));
}
return GRPC_ERROR_NONE;
}
@ -1489,35 +1236,35 @@ grpc_error* CommonTlsContextParse(
envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
default_validation_context, &len);
for (size_t i = 0; i < len; ++i) {
XdsApi::StringMatcher::StringMatcherType type;
StringMatcher::Type type;
std::string matcher;
if (envoy_type_matcher_v3_StringMatcher_has_exact(
subject_alt_names_matchers[i])) {
type = XdsApi::StringMatcher::StringMatcherType::EXACT;
type = StringMatcher::Type::EXACT;
matcher =
UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
subject_alt_names_matchers[i]));
} else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
subject_alt_names_matchers[i])) {
type = XdsApi::StringMatcher::StringMatcherType::PREFIX;
type = StringMatcher::Type::PREFIX;
matcher =
UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
subject_alt_names_matchers[i]));
} else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
subject_alt_names_matchers[i])) {
type = XdsApi::StringMatcher::StringMatcherType::SUFFIX;
type = StringMatcher::Type::SUFFIX;
matcher =
UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
subject_alt_names_matchers[i]));
} else if (envoy_type_matcher_v3_StringMatcher_has_contains(
subject_alt_names_matchers[i])) {
type = XdsApi::StringMatcher::StringMatcherType::CONTAINS;
type = StringMatcher::Type::CONTAINS;
matcher =
UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains(
subject_alt_names_matchers[i]));
} else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
subject_alt_names_matchers[i])) {
type = XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX;
type = StringMatcher::Type::SAFE_REGEX;
auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
subject_alt_names_matchers[i]);
matcher = UpbStringToStdString(
@ -1528,20 +1275,23 @@ grpc_error* CommonTlsContextParse(
}
bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
subject_alt_names_matchers[i]);
XdsApi::StringMatcher string_matcher(type, matcher, ignore_case);
if (type == XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX) {
if (!string_matcher.regex_matcher()->ok()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Invalid regex string specified in string matcher.");
}
if (ignore_case) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"StringMatcher: ignore_case has no effect for SAFE_REGEX.");
}
absl::StatusOr<StringMatcher> string_matcher =
StringMatcher::Create(type, matcher,
/*case_sensitive=*/!ignore_case);
if (!string_matcher.ok()) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("string matcher: ",
string_matcher.status().message())
.c_str());
}
if (type == StringMatcher::Type::SAFE_REGEX && ignore_case) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"StringMatcher: ignore_case has no effect for SAFE_REGEX.");
}
common_tls_context->combined_validation_context
.default_validation_context.match_subject_alt_names.push_back(
std::move(string_matcher));
std::move(string_matcher.value()));
}
}
auto* validation_context_certificate_provider_instance =

@ -36,6 +36,7 @@
#include "src/core/ext/filters/client_channel/server_address.h"
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_client_stats.h"
#include "src/core/lib/security/authorization/matchers.h"
namespace grpc_core {
@ -69,52 +70,7 @@ class XdsApi {
struct Route {
// Matchers for this route.
struct Matchers {
struct PathMatcher {
enum class PathMatcherType {
PATH, // path stored in string_matcher field
PREFIX, // prefix stored in string_matcher field
REGEX, // regex stored in regex_matcher field
};
PathMatcherType type;
std::string string_matcher;
std::unique_ptr<RE2> regex_matcher;
bool case_sensitive = true;
PathMatcher() = default;
PathMatcher(const PathMatcher& other);
PathMatcher& operator=(const PathMatcher& other);
bool operator==(const PathMatcher& other) const;
std::string ToString() const;
};
struct HeaderMatcher {
enum class HeaderMatcherType {
EXACT, // value stored in string_matcher field
REGEX, // uses regex_match field
RANGE, // uses range_start and range_end fields
PRESENT, // uses present_match field
PREFIX, // prefix stored in string_matcher field
SUFFIX, // suffix stored in string_matcher field
};
std::string name;
HeaderMatcherType type;
int64_t range_start;
int64_t range_end;
std::string string_matcher;
std::unique_ptr<RE2> regex_match;
bool present_match;
// invert_match field may or may not exisit, so initialize it to
// false.
bool invert_match = false;
HeaderMatcher() = default;
HeaderMatcher(const HeaderMatcher& other);
HeaderMatcher& operator=(const HeaderMatcher& other);
bool operator==(const HeaderMatcher& other) const;
std::string ToString() const;
};
PathMatcher path_matcher;
StringMatcher path_matcher;
std::vector<HeaderMatcher> header_matchers;
absl::optional<uint32_t> fraction_per_million;
@ -175,42 +131,6 @@ class XdsApi {
VirtualHost* FindVirtualHostForDomain(const std::string& domain);
};
class StringMatcher {
public:
enum class StringMatcherType {
EXACT, // value stored in string_matcher_ field
PREFIX, // value stored in string_matcher_ field
SUFFIX, // value stored in string_matcher_ field
SAFE_REGEX, // pattern stored in regex_matcher_ field
CONTAINS, // value stored in string_matcher_ field
};
StringMatcher() = default;
StringMatcher(const StringMatcher& other);
StringMatcher(StringMatcherType type, const std::string& matcher,
bool ignore_case = false);
StringMatcher& operator=(const StringMatcher& other);
bool operator==(const StringMatcher& other) const;
bool Match(absl::string_view value) const;
std::string ToString() const;
StringMatcherType type() const { return type_; }
// Valid for EXACT, PREFIX, SUFFIX and CONTAINS
const std::string& string_matcher() const { return string_matcher_; }
// Valid for SAFE_REGEX
RE2* regex_matcher() const { return regex_matcher_.get(); }
private:
StringMatcherType type_ = StringMatcherType::EXACT;
std::string string_matcher_;
std::unique_ptr<RE2> regex_matcher_;
bool ignore_case_ = false;
};
struct CommonTlsContext {
struct CertificateValidationContext {
std::vector<StringMatcher> match_subject_alt_names;

@ -330,7 +330,7 @@ void XdsCertificateProvider::UpdateRequireClientCertificate(
it->second->set_require_client_certificate(require_client_certificate);
}
std::vector<XdsApi::StringMatcher> XdsCertificateProvider::GetSanMatchers(
std::vector<StringMatcher> XdsCertificateProvider::GetSanMatchers(
const std::string& cluster) {
MutexLock lock(&san_matchers_mu_);
auto it = san_matcher_map_.find(cluster);
@ -339,7 +339,7 @@ std::vector<XdsApi::StringMatcher> XdsCertificateProvider::GetSanMatchers(
}
void XdsCertificateProvider::UpdateSubjectAlternativeNameMatchers(
const std::string& cluster, std::vector<XdsApi::StringMatcher> matchers) {
const std::string& cluster, std::vector<StringMatcher> matchers) {
MutexLock lock(&san_matchers_mu_);
if (matchers.empty()) {
san_matcher_map_.erase(cluster);

@ -56,9 +56,9 @@ class XdsCertificateProvider : public grpc_tls_certificate_provider {
void UpdateRequireClientCertificate(const std::string& cert_name,
bool require_client_certificate);
std::vector<XdsApi::StringMatcher> GetSanMatchers(const std::string& cluster);
std::vector<StringMatcher> GetSanMatchers(const std::string& cluster);
void UpdateSubjectAlternativeNameMatchers(
const std::string& cluster, std::vector<XdsApi::StringMatcher> matchers);
const std::string& cluster, std::vector<StringMatcher> matchers);
grpc_arg MakeChannelArg() const;
@ -140,7 +140,7 @@ class XdsCertificateProvider : public grpc_tls_certificate_provider {
// -> HandshakeManager::Add() -> SecurityHandshaker::DoHandshake() ->
// subject_alternative_names_matchers()
Mutex san_matchers_mu_;
std::map<std::string /*cluster_name*/, std::vector<XdsApi::StringMatcher>>
std::map<std::string /*cluster_name*/, std::vector<StringMatcher>>
san_matcher_map_;
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;

@ -0,0 +1,339 @@
// Copyright 2021 gRPC authors.
//
// 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.
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/authorization/matchers.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
namespace grpc_core {
//
// StringMatcher
//
absl::StatusOr<StringMatcher> StringMatcher::Create(Type type,
const std::string& matcher,
bool case_sensitive) {
if (type == Type::SAFE_REGEX) {
RE2::Options options;
options.set_case_sensitive(case_sensitive);
auto regex_matcher = absl::make_unique<RE2>(matcher, options);
if (!regex_matcher->ok()) {
return absl::InvalidArgumentError(
"Invalid regex string specified in matcher.");
}
return StringMatcher(std::move(regex_matcher), case_sensitive);
} else {
return StringMatcher(type, matcher, case_sensitive);
}
}
StringMatcher::StringMatcher(Type type, const std::string& matcher,
bool case_sensitive)
: type_(type), string_matcher_(matcher), case_sensitive_(case_sensitive) {}
StringMatcher::StringMatcher(std::unique_ptr<RE2> regex_matcher,
bool case_sensitive)
: type_(Type::SAFE_REGEX),
regex_matcher_(std::move(regex_matcher)),
case_sensitive_(case_sensitive) {}
StringMatcher::StringMatcher(const StringMatcher& other)
: type_(other.type_), case_sensitive_(other.case_sensitive_) {
if (type_ == Type::SAFE_REGEX) {
RE2::Options options;
options.set_case_sensitive(other.case_sensitive_);
regex_matcher_ =
absl::make_unique<RE2>(other.regex_matcher_->pattern(), options);
} else {
string_matcher_ = other.string_matcher_;
}
}
StringMatcher& StringMatcher::operator=(const StringMatcher& other) {
type_ = other.type_;
if (type_ == Type::SAFE_REGEX) {
RE2::Options options;
options.set_case_sensitive(other.case_sensitive_);
regex_matcher_ =
absl::make_unique<RE2>(other.regex_matcher_->pattern(), options);
} else {
string_matcher_ = other.string_matcher_;
}
case_sensitive_ = other.case_sensitive_;
return *this;
}
StringMatcher::StringMatcher(StringMatcher&& other) noexcept
: type_(other.type_), case_sensitive_(other.case_sensitive_) {
if (type_ == Type::SAFE_REGEX) {
regex_matcher_ = std::move(other.regex_matcher_);
} else {
string_matcher_ = std::move(other.string_matcher_);
}
}
StringMatcher& StringMatcher::operator=(StringMatcher&& other) noexcept {
type_ = other.type_;
if (type_ == Type::SAFE_REGEX) {
regex_matcher_ = std::move(other.regex_matcher_);
} else {
string_matcher_ = std::move(other.string_matcher_);
}
case_sensitive_ = other.case_sensitive_;
return *this;
}
bool StringMatcher::operator==(const StringMatcher& other) const {
if (type_ != other.type_ || case_sensitive_ != other.case_sensitive_) {
return false;
}
if (type_ == Type::SAFE_REGEX) {
return regex_matcher_->pattern() == other.regex_matcher_->pattern();
} else {
return string_matcher_ == other.string_matcher_;
}
}
bool StringMatcher::Match(absl::string_view value) const {
switch (type_) {
case Type::EXACT:
return case_sensitive_ ? value == string_matcher_
: absl::EqualsIgnoreCase(value, string_matcher_);
case StringMatcher::Type::PREFIX:
return case_sensitive_
? absl::StartsWith(value, string_matcher_)
: absl::StartsWithIgnoreCase(value, string_matcher_);
case StringMatcher::Type::SUFFIX:
return case_sensitive_ ? absl::EndsWith(value, string_matcher_)
: absl::EndsWithIgnoreCase(value, string_matcher_);
case StringMatcher::Type::CONTAINS:
return case_sensitive_
? absl::StrContains(value, string_matcher_)
: absl::StrContains(absl::AsciiStrToLower(value),
absl::AsciiStrToLower(string_matcher_));
case StringMatcher::Type::SAFE_REGEX:
return RE2::FullMatch(std::string(value), *regex_matcher_);
default:
return false;
}
}
std::string StringMatcher::ToString() const {
switch (type_) {
case Type::EXACT:
return absl::StrFormat("StringMatcher{exact=%s%s}", string_matcher_,
case_sensitive_ ? "" : ", case_sensitive=false");
case Type::PREFIX:
return absl::StrFormat("StringMatcher{prefix=%s%s}", string_matcher_,
case_sensitive_ ? "" : ", case_sensitive=false");
case Type::SUFFIX:
return absl::StrFormat("StringMatcher{suffix=%s%s}", string_matcher_,
case_sensitive_ ? "" : ", case_sensitive=false");
case Type::CONTAINS:
return absl::StrFormat("StringMatcher{contains=%s%s}", string_matcher_,
case_sensitive_ ? "" : ", case_sensitive=false");
case Type::SAFE_REGEX:
return absl::StrFormat("StringMatcher{safe_regex=%s%s}",
regex_matcher_->pattern(),
case_sensitive_ ? "" : ", case_sensitive=false");
default:
return "";
}
}
//
// HeaderMatcher
//
absl::StatusOr<HeaderMatcher> HeaderMatcher::Create(
const std::string& name, Type type, const std::string& matcher,
int64_t range_start, int64_t range_end, bool present_match,
bool invert_match) {
if (static_cast<int>(type) < 5) {
// Only for EXACT, PREFIX, SUFFIX, SAFE_REGEX and CONTAINS.
absl::StatusOr<StringMatcher> string_matcher =
StringMatcher::Create(static_cast<StringMatcher::Type>(type), matcher,
/*case_sensitive=*/true);
if (!string_matcher.ok()) {
return string_matcher.status();
}
return HeaderMatcher(name, type, std::move(string_matcher.value()),
invert_match);
} else if (type == Type::RANGE) {
if (range_start > range_end) {
return absl::InvalidArgumentError(
"Invalid range specifier specified: end cannot be smaller than "
"start.");
}
return HeaderMatcher(name, range_start, range_end, invert_match);
} else {
return HeaderMatcher(name, present_match, invert_match);
}
}
HeaderMatcher::HeaderMatcher(const std::string& name, Type type,
StringMatcher string_matcher, bool invert_match)
: name_(name),
type_(type),
matcher_(std::move(string_matcher)),
invert_match_(invert_match) {}
HeaderMatcher::HeaderMatcher(const std::string& name, int64_t range_start,
int64_t range_end, bool invert_match)
: name_(name),
type_(Type::RANGE),
range_start_(range_start),
range_end_(range_end),
invert_match_(invert_match) {}
HeaderMatcher::HeaderMatcher(const std::string& name, bool present_match,
bool invert_match)
: name_(name),
type_(Type::PRESENT),
present_match_(present_match),
invert_match_(invert_match) {}
HeaderMatcher::HeaderMatcher(const HeaderMatcher& other)
: name_(other.name_),
type_(other.type_),
invert_match_(other.invert_match_) {
switch (type_) {
case Type::RANGE:
range_start_ = other.range_start_;
range_end_ = other.range_end_;
break;
case Type::PRESENT:
present_match_ = other.present_match_;
break;
default:
matcher_ = other.matcher_;
}
}
HeaderMatcher& HeaderMatcher::operator=(const HeaderMatcher& other) {
name_ = other.name_;
type_ = other.type_;
invert_match_ = other.invert_match_;
switch (type_) {
case Type::RANGE:
range_start_ = other.range_start_;
range_end_ = other.range_end_;
break;
case Type::PRESENT:
present_match_ = other.present_match_;
break;
default:
matcher_ = other.matcher_;
}
return *this;
}
HeaderMatcher::HeaderMatcher(HeaderMatcher&& other) noexcept
: name_(std::move(other.name_)),
type_(other.type_),
invert_match_(other.invert_match_) {
switch (type_) {
case Type::RANGE:
range_start_ = other.range_start_;
range_end_ = other.range_end_;
break;
case Type::PRESENT:
present_match_ = other.present_match_;
break;
default:
matcher_ = std::move(other.matcher_);
}
}
HeaderMatcher& HeaderMatcher::operator=(HeaderMatcher&& other) noexcept {
name_ = std::move(other.name_);
type_ = other.type_;
invert_match_ = other.invert_match_;
switch (type_) {
case Type::RANGE:
range_start_ = other.range_start_;
range_end_ = other.range_end_;
break;
case Type::PRESENT:
present_match_ = other.present_match_;
break;
default:
matcher_ = std::move(other.matcher_);
}
return *this;
}
bool HeaderMatcher::operator==(const HeaderMatcher& other) const {
if (name_ != other.name_) return false;
if (type_ != other.type_) return false;
if (invert_match_ != other.invert_match_) return false;
switch (type_) {
case Type::RANGE:
return range_start_ == other.range_start_ &&
range_end_ == other.range_end_;
case Type::PRESENT:
return present_match_ == other.present_match_;
default:
return matcher_ == other.matcher_;
}
}
bool HeaderMatcher::Match(
const absl::optional<absl::string_view>& value) const {
bool match;
if (type_ == Type::PRESENT) {
match = value.has_value() == present_match_;
} else if (!value.has_value()) {
// All other types fail to match if field is not present.
match = false;
} else if (type_ == Type::RANGE) {
int64_t int_value;
match = absl::SimpleAtoi(value.value(), &int_value) &&
int_value >= range_start_ && int_value < range_end_;
} else {
match = matcher_.Match(value.value());
}
return match != invert_match_;
}
std::string HeaderMatcher::ToString() const {
switch (type_) {
case Type::RANGE:
return absl::StrFormat("HeaderMatcher{%s %srange=[%d, %d]}", name_,
invert_match_ ? "not " : "", range_start_,
range_end_);
case Type::PRESENT:
return absl::StrFormat("HeaderMatcher{%s %spresent=%s}", name_,
invert_match_ ? "not " : "",
present_match_ ? "true" : "false");
case Type::EXACT:
case Type::PREFIX:
case Type::SUFFIX:
case Type::SAFE_REGEX:
case Type::CONTAINS:
return absl::StrFormat("HeaderMatcher{%s %s%s}", name_,
invert_match_ ? "not " : "", matcher_.ToString());
default:
return "";
}
}
} // namespace grpc_core

@ -0,0 +1,158 @@
// Copyright 2021 gRPC authors.
//
// 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.
#ifndef GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H
#define GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H
#include <grpc/support/port_platform.h>
#include <memory>
#include <string>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "re2/re2.h"
namespace grpc_core {
class StringMatcher {
public:
enum class Type {
EXACT, // value stored in string_matcher_ field
PREFIX, // value stored in string_matcher_ field
SUFFIX, // value stored in string_matcher_ field
SAFE_REGEX, // pattern stored in regex_matcher_ field
CONTAINS, // value stored in string_matcher_ field
};
// Creates StringMatcher instance. Returns error status on failure.
static absl::StatusOr<StringMatcher> Create(Type type,
const std::string& matcher,
bool case_sensitive = true);
StringMatcher() = default;
StringMatcher(const StringMatcher& other);
StringMatcher& operator=(const StringMatcher& other);
StringMatcher(StringMatcher&& other) noexcept;
StringMatcher& operator=(StringMatcher&& other) noexcept;
bool operator==(const StringMatcher& other) const;
bool Match(absl::string_view value) const;
std::string ToString() const;
Type type() const { return type_; }
// Valid for EXACT, PREFIX, SUFFIX and CONTAINS
const std::string& string_matcher() const { return string_matcher_; }
// Valid for SAFE_REGEX
RE2* regex_matcher() const { return regex_matcher_.get(); }
bool case_sensitive() const { return case_sensitive_; }
private:
StringMatcher(Type type, const std::string& matcher, bool case_sensitive);
StringMatcher(std::unique_ptr<RE2> regex_matcher, bool case_sensitive);
Type type_ = Type::EXACT;
std::string string_matcher_;
std::unique_ptr<RE2> regex_matcher_;
bool case_sensitive_ = true;
};
class HeaderMatcher {
public:
enum class Type {
EXACT, // value stored in StringMatcher field
PREFIX, // value stored in StringMatcher field
SUFFIX, // value stored in StringMatcher field
SAFE_REGEX, // value stored in StringMatcher field
CONTAINS, // value stored in StringMatcher field
RANGE, // uses range_start and range_end fields
PRESENT, // uses present_match field
};
// Make sure that the first five HeaderMatcher::Type enum values match up to
// the corresponding StringMatcher::Type enum values, so that it's safe to
// convert by casting when delegating to StringMatcher.
static_assert(static_cast<StringMatcher::Type>(Type::EXACT) ==
StringMatcher::Type::EXACT,
"");
static_assert(static_cast<StringMatcher::Type>(Type::PREFIX) ==
StringMatcher::Type::PREFIX,
"");
static_assert(static_cast<StringMatcher::Type>(Type::SUFFIX) ==
StringMatcher::Type::SUFFIX,
"");
static_assert(static_cast<StringMatcher::Type>(Type::SAFE_REGEX) ==
StringMatcher::Type::SAFE_REGEX,
"");
static_assert(static_cast<StringMatcher::Type>(Type::CONTAINS) ==
StringMatcher::Type::CONTAINS,
"");
// Creates HeaderMatcher instance. Returns error status on failure.
static absl::StatusOr<HeaderMatcher> Create(
const std::string& name, Type type, const std::string& matcher,
int64_t range_start = 0, int64_t range_end = 0,
bool present_match = false, bool invert_match = false);
HeaderMatcher() = default;
HeaderMatcher(const HeaderMatcher& other);
HeaderMatcher& operator=(const HeaderMatcher& other);
HeaderMatcher(HeaderMatcher&& other) noexcept;
HeaderMatcher& operator=(HeaderMatcher&& other) noexcept;
bool operator==(const HeaderMatcher& other) const;
const std::string& name() const { return name_; }
Type type() const { return type_; }
// Valid for EXACT, PREFIX, SUFFIX and CONTAINS
const std::string& string_matcher() const {
return matcher_.string_matcher();
}
// Valid for SAFE_REGEX
RE2* regex_matcher() const { return matcher_.regex_matcher(); }
bool Match(const absl::optional<absl::string_view>& value) const;
std::string ToString() const;
private:
// For StringMatcher.
HeaderMatcher(const std::string& name, Type type, StringMatcher matcher,
bool invert_match);
// For RangeMatcher.
HeaderMatcher(const std::string& name, int64_t range_start, int64_t range_end,
bool invert_match);
// For PresentMatcher.
HeaderMatcher(const std::string& name, bool present_match, bool invert_match);
std::string name_;
Type type_ = Type::EXACT;
StringMatcher matcher_;
int64_t range_start_;
int64_t range_end_;
bool present_match_;
bool invert_match_ = false;
};
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H */

@ -36,11 +36,11 @@ namespace {
bool XdsVerifySubjectAlternativeNames(
const char* const* subject_alternative_names,
size_t subject_alternative_names_size,
const std::vector<XdsApi::StringMatcher>& matchers) {
const std::vector<StringMatcher>& matchers) {
if (matchers.empty()) return true;
for (size_t i = 0; i < subject_alternative_names_size; ++i) {
for (const auto& matcher : matchers) {
if (matcher.type() == XdsApi::StringMatcher::StringMatcherType::EXACT) {
if (matcher.type() == StringMatcher::Type::EXACT) {
// For EXACT match, use DNS rules for verifying SANs
// TODO(zhenlian): Right now, the SSL layer does not save the type of
// the SAN, so we are doing a DNS style verification for all SANs when
@ -105,7 +105,7 @@ class ServerAuthCheck {
bool TestOnlyXdsVerifySubjectAlternativeNames(
const char* const* subject_alternative_names,
size_t subject_alternative_names_size,
const std::vector<XdsApi::StringMatcher>& matchers) {
const std::vector<StringMatcher>& matchers) {
return XdsVerifySubjectAlternativeNames(
subject_alternative_names, subject_alternative_names_size, matchers);
}

@ -62,7 +62,7 @@ class XdsServerCredentials final : public grpc_server_credentials {
bool TestOnlyXdsVerifySubjectAlternativeNames(
const char* const* subject_alternative_names,
size_t subject_alternative_names_size,
const std::vector<XdsApi::StringMatcher>& matchers);
const std::vector<StringMatcher>& matchers);
} // namespace grpc_core

@ -465,6 +465,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/profiling/stap_timers.cc',
'src/core/lib/security/authorization/authorization_engine.cc',
'src/core/lib/security/authorization/evaluate_args.cc',
'src/core/lib/security/authorization/matchers.cc',
'src/core/lib/security/context/security_context.cc',
'src/core/lib/security/credentials/alts/alts_credentials.cc',
'src/core/lib/security/credentials/alts/check_gcp_environment.cc',

@ -397,3 +397,15 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "matchers_test",
srcs = ["matchers_test.cc"],
external_deps = ["gtest"],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,218 @@
// Copyright 2021 gRPC authors.
//
// 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.
#include "src/core/lib/security/authorization/matchers.h"
#include <gtest/gtest.h>
namespace grpc_core {
TEST(StringMatcherTest, ExactMatchCaseSensitive) {
auto string_matcher =
StringMatcher::Create(StringMatcher::Type::EXACT,
/*matcher=*/"exact", /*case_sensitive=*/true);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("exact"));
EXPECT_FALSE(string_matcher->Match("Exact"));
EXPECT_FALSE(string_matcher->Match("exacz"));
}
TEST(StringMatcherTest, ExactMatchCaseInsensitive) {
auto string_matcher =
StringMatcher::Create(StringMatcher::Type::EXACT,
/*matcher=*/"exact", /*case_sensitive=*/false);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("Exact"));
EXPECT_FALSE(string_matcher->Match("Exacz"));
}
TEST(StringMatcherTest, PrefixMatchCaseSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::PREFIX,
/*matcher=*/"prefix",
/*case_sensitive=*/true);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("prefix-test"));
EXPECT_FALSE(string_matcher->Match("xx-prefix-test"));
EXPECT_FALSE(string_matcher->Match("Prefix-test"));
EXPECT_FALSE(string_matcher->Match("pre-test"));
}
TEST(StringMatcherTest, PrefixMatchCaseInsensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::PREFIX,
/*matcher=*/"prefix",
/*case_sensitive=*/false);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("PREfix-test"));
EXPECT_FALSE(string_matcher->Match("xx-PREfix-test"));
EXPECT_FALSE(string_matcher->Match("PRE-test"));
}
TEST(StringMatcherTest, SuffixMatchCaseSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SUFFIX,
/*matcher=*/"suffix",
/*case_sensitive=*/true);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("test-suffix"));
EXPECT_FALSE(string_matcher->Match("test-Suffix"));
EXPECT_FALSE(string_matcher->Match("test-suffix-xx"));
EXPECT_FALSE(string_matcher->Match("test-suffiz"));
}
TEST(StringMatcherTest, SuffixMatchCaseInSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SUFFIX,
/*matcher=*/"suffix",
/*case_sensitive=*/false);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("Test-SUFFIX"));
EXPECT_FALSE(string_matcher->Match("Test-SUFFIX-xx"));
EXPECT_FALSE(string_matcher->Match("Test-SUFFIZ"));
}
TEST(StringMatcherTest, InvalidRegex) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX,
/*matcher=*/"a[b-a]",
/*case_sensitive=*/true);
EXPECT_FALSE(string_matcher.ok());
EXPECT_EQ(string_matcher.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(string_matcher.status().message(),
"Invalid regex string specified in matcher.");
}
TEST(StringMatcherTest, SafeRegexMatchCaseSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX,
/*matcher=*/"regex.*",
/*case_sensitive=*/true);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("regex-test"));
EXPECT_FALSE(string_matcher->Match("xx-regex-test"));
EXPECT_FALSE(string_matcher->Match("Regex-test"));
EXPECT_FALSE(string_matcher->Match("test-regex"));
}
TEST(StringMatcherTest, SafeRegexMatchCaseInSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX,
/*matcher=*/"regex.*",
/*case_sensitive=*/false);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("regex-test"));
EXPECT_TRUE(string_matcher->Match("Regex-test"));
EXPECT_FALSE(string_matcher->Match("xx-Regex-test"));
EXPECT_FALSE(string_matcher->Match("test-regex"));
}
TEST(StringMatcherTest, ContainsMatchCaseSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::CONTAINS,
/*matcher=*/"contains",
/*case_sensitive=*/true);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("test-contains"));
EXPECT_TRUE(string_matcher->Match("test-contains-test"));
EXPECT_FALSE(string_matcher->Match("test-Contains"));
EXPECT_FALSE(string_matcher->Match("test-containz"));
}
TEST(StringMatcherTest, ContainsMatchCaseInSensitive) {
auto string_matcher = StringMatcher::Create(StringMatcher::Type::CONTAINS,
/*matcher=*/"contains",
/*case_sensitive=*/false);
ASSERT_TRUE(string_matcher.ok());
EXPECT_TRUE(string_matcher->Match("Test-Contains"));
EXPECT_TRUE(string_matcher->Match("Test-Contains-Test"));
EXPECT_FALSE(string_matcher->Match("Test-Containz"));
}
TEST(HeaderMatcherTest, StringMatcher) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::EXACT,
/*matcher=*/"exact");
ASSERT_TRUE(header_matcher.ok());
EXPECT_TRUE(header_matcher->Match("exact"));
EXPECT_FALSE(header_matcher->Match("Exact"));
EXPECT_FALSE(header_matcher->Match("exacz"));
}
TEST(HeaderMatcherTest, StringMatcherWithInvertMatch) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::EXACT,
/*matcher=*/"exact",
/*range_start=*/0, /*range_end=*/0,
/*present_match=*/false, /*invert_match=*/true);
ASSERT_TRUE(header_matcher.ok());
EXPECT_FALSE(header_matcher->Match("exact"));
EXPECT_TRUE(header_matcher->Match("Exact"));
EXPECT_TRUE(header_matcher->Match("exacz"));
}
TEST(HeaderMatcherTest, InvalidRegex) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::SAFE_REGEX,
/*matcher=*/"a[b-a]",
/*range_start=*/0, /*range_end=*/0,
/*present_match=*/false, /*invert_match=*/true);
EXPECT_FALSE(header_matcher.ok());
EXPECT_EQ(header_matcher.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(header_matcher.status().message(),
"Invalid regex string specified in matcher.");
}
TEST(HeaderMatcherTest, RangeMatcherValidRange) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::RANGE,
/*matcher=*/"", /*range_start=*/10,
/*range_end*/ 20);
ASSERT_TRUE(header_matcher.ok());
EXPECT_TRUE(header_matcher->Match("16"));
EXPECT_TRUE(header_matcher->Match("10"));
EXPECT_FALSE(header_matcher->Match("3"));
EXPECT_FALSE(header_matcher->Match("20"));
}
TEST(HeaderMatcherTest, RangeMatcherInvalidRange) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::RANGE,
/*matcher=*/"", /*range_start=*/20,
/*range_end*/ 10);
EXPECT_FALSE(header_matcher.ok());
EXPECT_EQ(header_matcher.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(
header_matcher.status().message(),
"Invalid range specifier specified: end cannot be smaller than start.");
}
TEST(HeaderMatcherTest, PresentMatcherTrue) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::PRESENT,
/*matcher=*/"", /*range_start=*/0,
/*range_end=*/0, /*present_match=*/true);
ASSERT_TRUE(header_matcher.ok());
EXPECT_TRUE(header_matcher->Match("any_value"));
EXPECT_FALSE(header_matcher->Match(absl::nullopt));
}
TEST(HeaderMatcherTest, PresentMatcherFalse) {
auto header_matcher =
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::PRESENT,
/*matcher=*/"", /*range_start=*/0,
/*range_end=*/0, /*present_match=*/false);
ASSERT_TRUE(header_matcher.ok());
EXPECT_FALSE(header_matcher->Match("any_value"));
EXPECT_TRUE(header_matcher->Match(absl::nullopt));
}
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -29,32 +29,30 @@ namespace testing {
namespace {
XdsApi::StringMatcher ExactMatcher(const char* string) {
return XdsApi::StringMatcher(XdsApi::StringMatcher::StringMatcherType::EXACT,
string);
StringMatcher ExactMatcher(const char* string) {
return StringMatcher::Create(StringMatcher::Type::EXACT, string).value();
}
XdsApi::StringMatcher PrefixMatcher(const char* string,
bool ignore_case = false) {
return XdsApi::StringMatcher(XdsApi::StringMatcher::StringMatcherType::PREFIX,
string, ignore_case);
StringMatcher PrefixMatcher(const char* string, bool case_sensitive = true) {
return StringMatcher::Create(StringMatcher::Type::PREFIX, string,
case_sensitive)
.value();
}
XdsApi::StringMatcher SuffixMatcher(const char* string,
bool ignore_case = false) {
return XdsApi::StringMatcher(XdsApi::StringMatcher::StringMatcherType::SUFFIX,
string, ignore_case);
StringMatcher SuffixMatcher(const char* string, bool case_sensitive = true) {
return StringMatcher::Create(StringMatcher::Type::SUFFIX, string,
case_sensitive)
.value();
}
XdsApi::StringMatcher ContainsMatcher(const char* string,
bool ignore_case = false) {
return XdsApi::StringMatcher(
XdsApi::StringMatcher::StringMatcherType::CONTAINS, string, ignore_case);
StringMatcher ContainsMatcher(const char* string, bool case_sensitive = true) {
return StringMatcher::Create(StringMatcher::Type::CONTAINS, string,
case_sensitive)
.value();
}
XdsApi::StringMatcher SafeRegexMatcher(const char* string) {
return XdsApi::StringMatcher(
XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX, string);
StringMatcher SafeRegexMatcher(const char* string) {
return StringMatcher::Create(StringMatcher::Type::SAFE_REGEX, string).value();
}
TEST(XdsSanMatchingTest, EmptySansList) {
@ -210,15 +208,15 @@ TEST(XdsSanMatchingTest, PrefixMatchIgnoreCase) {
std::vector<const char*> sans = {"aBc.cOm"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{PrefixMatcher("AbC", true /* ignore_case */)}));
{PrefixMatcher("AbC", false /* case_sensitive */)}));
sans = {"abc.com"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{PrefixMatcher("AbC", true /* ignore_case */)}));
{PrefixMatcher("AbC", false /* case_sensitive */)}));
sans = {"xyz.com"};
EXPECT_FALSE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{PrefixMatcher("AbC", true /* ignore_case */)}));
{PrefixMatcher("AbC", false /* case_sensitive */)}));
}
TEST(XdsSanMatchingTest, SuffixMatch) {
@ -237,15 +235,15 @@ TEST(XdsSanMatchingTest, SuffixMatchIgnoreCase) {
std::vector<const char*> sans = {"abc.com"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{SuffixMatcher(".CoM", true /* ignore_case */)}));
{SuffixMatcher(".CoM", false /* case_sensitive */)}));
sans = {"AbC.cOm"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{SuffixMatcher(".CoM", true /* ignore_case */)}));
{SuffixMatcher(".CoM", false /* case_sensitive */)}));
sans = {"abc.xyz"};
EXPECT_FALSE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{SuffixMatcher(".CoM", true /* ignore_case */)}));
{SuffixMatcher(".CoM", false /* case_sensitive */)}));
}
TEST(XdsSanMatchingTest, ContainsMatch) {
@ -264,19 +262,19 @@ TEST(XdsSanMatchingTest, ContainsMatchIgnoresCase) {
std::vector<const char*> sans = {"abc.com"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{ContainsMatcher("AbC", true /* ignore_case */)}));
{ContainsMatcher("AbC", false /* case_sensitive */)}));
sans = {"xyz.abc.com"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{ContainsMatcher("AbC", true /* ignore_case */)}));
{ContainsMatcher("AbC", false /* case_sensitive */)}));
sans = {"foo.aBc.com"};
EXPECT_TRUE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{ContainsMatcher("AbC", true /* ignore_case */)}));
{ContainsMatcher("AbC", false /* case_sensitive */)}));
sans = {"foo.Ab.com"};
EXPECT_FALSE(TestOnlyXdsVerifySubjectAlternativeNames(
sans.data(), sans.size(),
{ContainsMatcher("AbC", true /* ignore_case */)}));
{ContainsMatcher("AbC", false /* case_sensitive */)}));
}
TEST(XdsSanMatchingTest, RegexMatch) {

@ -3301,7 +3301,7 @@ TEST_P(LdsRdsTest, RouteMatchHasInvalidPathRegex) {
const auto& response_state = RouteConfigurationResponseState(0);
EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
EXPECT_EQ(response_state.error_message,
"Invalid regex string specified in path matcher.");
"path matcher: Invalid regex string specified in matcher.");
}
// Tests that LDS client should send a NACK if route has an action other than
@ -3458,7 +3458,7 @@ TEST_P(LdsRdsTest, RouteHeaderMatchInvalidRegex) {
const auto& response_state = RouteConfigurationResponseState(0);
EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
EXPECT_EQ(response_state.error_message,
"Invalid regex string specified in header matcher.");
"header matcher: Invalid regex string specified in matcher.");
}
TEST_P(LdsRdsTest, RouteHeaderMatchInvalidRange) {
@ -3478,8 +3478,8 @@ TEST_P(LdsRdsTest, RouteHeaderMatchInvalidRange) {
const auto& response_state = RouteConfigurationResponseState(0);
EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED);
EXPECT_EQ(response_state.error_message,
"Invalid range header matcher specifier specified: end "
"cannot be smaller than start.");
"header matcher: Invalid range specifier specified: end cannot be "
"smaller than start.");
}
// Tests that LDS client should choose the default route (with no matching

@ -1889,6 +1889,8 @@ src/core/lib/security/authorization/authorization_engine.cc \
src/core/lib/security/authorization/authorization_engine.h \
src/core/lib/security/authorization/evaluate_args.cc \
src/core/lib/security/authorization/evaluate_args.h \
src/core/lib/security/authorization/matchers.cc \
src/core/lib/security/authorization/matchers.h \
src/core/lib/security/authorization/mock_cel/activation.h \
src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h \
src/core/lib/security/authorization/mock_cel/cel_expression.h \

@ -1730,6 +1730,8 @@ src/core/lib/security/authorization/authorization_engine.cc \
src/core/lib/security/authorization/authorization_engine.h \
src/core/lib/security/authorization/evaluate_args.cc \
src/core/lib/security/authorization/evaluate_args.h \
src/core/lib/security/authorization/matchers.cc \
src/core/lib/security/authorization/matchers.h \
src/core/lib/security/authorization/mock_cel/activation.h \
src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h \
src/core/lib/security/authorization/mock_cel/cel_expression.h \

@ -5095,6 +5095,30 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "matchers_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save