diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 050c0abf0d6c..934894d405d7 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -122,6 +122,9 @@ removed_config_or_runtime: Removed ``envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns`` runtime flag and legacy code paths. new_features: +- area: jwt_authn + change: | + Added missing implementation to jwt_authn matchers to allow glob pattern matching. - area: tls change: | Added :ref:`prefer_client_ciphers diff --git a/source/extensions/filters/http/jwt_authn/matcher.cc b/source/extensions/filters/http/jwt_authn/matcher.cc index bab21c5441d4..1bdb41fabb9d 100644 --- a/source/extensions/filters/http/jwt_authn/matcher.cc +++ b/source/extensions/filters/http/jwt_authn/matcher.cc @@ -7,6 +7,7 @@ #include "source/common/common/matchers.h" #include "source/common/common/regex.h" #include "source/common/http/path_utility.h" +#include "source/common/protobuf/message_validator_impl.h" #include "source/common/router/config_impl.h" #include "absl/strings/match.h" @@ -188,6 +189,43 @@ class PathSeparatedPrefixMatcherImpl : public BaseMatcherImpl { }; } // namespace +class PathMatchPolicyMatcherImpl : public BaseMatcherImpl { +public: + PathMatchPolicyMatcherImpl(const RequirementRule& rule, + Server::Configuration::CommonFactoryContext& context) + : BaseMatcherImpl(rule, context), uri_template_matcher_(createUriTemplateMatcher(rule)) {} + + bool matches(const Http::RequestHeaderMap& headers) const override { + if (BaseMatcherImpl::matchRoute(headers) && + uri_template_matcher_->match(headers.getPathValue())) { + ENVOY_LOG(debug, "Path match policy requirement '{}' matched.", + uri_template_matcher_->uriTemplate()); + return true; + } + + return false; + } + +private: + const Router::PathMatcherSharedPtr uri_template_matcher_; + + static Router::PathMatcherSharedPtr createUriTemplateMatcher(const RequirementRule& rule) { + auto& factory = Config::Utility::getAndCheckFactory( + rule.match().path_match_policy()); + ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig( + rule.match().path_match_policy().typed_config(), + ProtobufMessage::getStrictValidationVisitor(), factory); + + absl::StatusOr matcher = factory.createPathMatcher(*config); + + if (!matcher.ok()) { + throw EnvoyException(std::string(matcher.status().message())); + } + + return matcher.value(); + } +}; + MatcherConstPtr Matcher::create(const RequirementRule& rule, Server::Configuration::CommonFactoryContext& context) { switch (rule.match().path_specifier_case()) { @@ -201,10 +239,9 @@ MatcherConstPtr Matcher::create(const RequirementRule& rule, return std::make_unique(rule, context); case RouteMatch::PathSpecifierCase::kPathSeparatedPrefix: return std::make_unique(rule, context); - case RouteMatch::PathSpecifierCase::kPathMatchPolicy: - // TODO(silverstar194): Implement matcher for template based match - throw EnvoyException("RouteMatch: path_match_policy is not supported"); - break; + case RouteMatch::PathSpecifierCase::kPathMatchPolicy: { + return std::make_unique(rule, context); + } case RouteMatch::PathSpecifierCase::PATH_SPECIFIER_NOT_SET: break; // Fall through to PANIC. } diff --git a/test/extensions/filters/http/jwt_authn/matcher_test.cc b/test/extensions/filters/http/jwt_authn/matcher_test.cc index 885cba5a57bd..916b8e993a4c 100644 --- a/test/extensions/filters/http/jwt_authn/matcher_test.cc +++ b/test/extensions/filters/http/jwt_authn/matcher_test.cc @@ -253,6 +253,30 @@ TEST_F(MatcherTest, TestMatchPathSeparatedPrefixBaseCondition) { EXPECT_FALSE(matcher->matches(headers)); } +TEST_F(MatcherTest, TestMatchPathMatchPolicy) { + const char config[] = R"(match: + path_match_policy: + name: envoy.path.match.uri_template.uri_template_matcher + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig + path_template: "/bar/*/foo" + )"; + MatcherConstPtr matcher = createMatcher(config); + auto headers = TestRequestHeaderMapImpl{{":path", "/bar/test/foo"}}; + EXPECT_TRUE(matcher->matches(headers)); +} + +TEST_F(MatcherTest, TestMatchPathMatchPolicyError) { + const char config[] = R"(match: + path_match_policy: + name: envoy.path.match.uri_template.uri_template_matcher + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig + wrong_key: "/bar/*/foo" + )"; + EXPECT_THROW_WITH_REGEX(createMatcher(config), EnvoyException, "INVALID_ARGUMENT") +} + } // namespace } // namespace JwtAuthn } // namespace HttpFilters