-
Notifications
You must be signed in to change notification settings - Fork 5.3k
ext_authz: support external authorization checks when a route contains a direct response entry #14989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ext_authz: support external authorization checks when a route contains a direct response entry #14989
Changes from all commits
b894b41
8285f44
e90cb9b
f6020cd
3b939e3
42460e3
6b95ba1
46b3dae
b485073
076c5e6
4f13d5e
4a96811
b9e977e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -376,8 +376,16 @@ void Filter::continueDecoding() { | |
| } | ||
|
|
||
| Filter::PerRouteFlags Filter::getPerRouteFlags(const Router::RouteConstSharedPtr& route) const { | ||
| if (route == nullptr || route->routeEntry() == nullptr) { | ||
| return PerRouteFlags{true /*skip_check_*/, false /*skip_request_body_buffering_*/}; | ||
| if (route == nullptr) { | ||
| // If there's no route, skip authorization checks. This is an optimization to avoid work | ||
| // if we know the request won't be forwarded upstream anyway. | ||
| return PerRouteFlags::skipCheckFlags(); | ||
| } | ||
|
|
||
| if (route->routeEntry() == nullptr && route->directResponseEntry() == nullptr) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you quickly scan in the docs, if we have the opportunity to inform this explicitly in the docs?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dio Good callout. I read the docs. I think we're actually going to skip authz checks for redirect actions still. Maybe this feature would be more complete if we:
This way, this "bugfix" is now instead a new feature that won't break existing users that expect the current behavior. Principle of least surprise is violated, in theory, but we can mitigate that with documentation and I think this is better than possibly introducing a breaking change when we meant to make things better.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, I was wrong. Redirects and direct replies are both "direct responses" on the route object so I believe this change will correctly apply to them all. I should probably add an integration test. |
||
| // If there's a route, but no route entry nor direct response entry, then skip authorization | ||
| // checks. This is another optimization. | ||
| return PerRouteFlags::skipCheckFlags(); | ||
| } | ||
|
|
||
| const auto* specific_per_route_config = | ||
|
|
@@ -388,7 +396,7 @@ Filter::PerRouteFlags Filter::getPerRouteFlags(const Router::RouteConstSharedPtr | |
| specific_per_route_config->disableRequestBodyBuffering()}; | ||
| } | ||
|
|
||
| return PerRouteFlags{false /*skip_check_*/, false /*skip_request_body_buffering_*/}; | ||
| return PerRouteFlags::defaultFlags(); | ||
| } | ||
|
|
||
| } // namespace ExtAuthz | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -268,6 +268,10 @@ class Filter : public Logger::Loggable<Logger::Id::filter>, | |
| struct PerRouteFlags { | ||
| const bool skip_check_; | ||
| const bool skip_request_body_buffering_; | ||
|
|
||
| static constexpr PerRouteFlags defaultFlags() { return PerRouteFlags{false, false}; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add comments on what the bools are here and below? e.g., |
||
|
|
||
| static constexpr PerRouteFlags skipCheckFlags() { return PerRouteFlags{true, false}; } | ||
| }; | ||
| PerRouteFlags getPerRouteFlags(const Router::RouteConstSharedPtr& route) const; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1599,9 +1599,42 @@ TEST_P(HttpFilterTestParam, DisabledOnRouteWithRequestBody) { | |
| EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); | ||
| } | ||
|
|
||
| // Test that the request continues when the filter_callbacks has no route. | ||
| TEST_P(HttpFilterTestParam, NoRoute) { | ||
| // Test that the request stops at decodeHeaders when filter_callbacks has a route entry. | ||
| TEST_P(HttpFilterTestParam, StopIterationWithRouteEntry) { | ||
| NiceMock<Router::MockRouteEntry> route_entry; | ||
| prepareCheck(); | ||
| EXPECT_CALL(*client_, check(_, _, _, _)); | ||
| EXPECT_CALL(*filter_callbacks_.route_, routeEntry()).WillRepeatedly(Return(&route_entry)); | ||
| EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, | ||
| filter_->decodeHeaders(request_headers_, true)); | ||
| } | ||
|
|
||
| // Test that the request stops at decodeHeaders when filter_callbacks has no route entry, but it | ||
| // does have a direct response entry. | ||
| TEST_P(HttpFilterTestParam, StopIterationWithDirectResponseEntry) { | ||
| NiceMock<Router::MockDirectResponseEntry> direct_response_entry; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we have a test with redirect? |
||
| prepareCheck(); | ||
| EXPECT_CALL(*client_, check(_, _, _, _)); | ||
| EXPECT_CALL(*filter_callbacks_.route_, routeEntry()).WillRepeatedly(Return(nullptr)); | ||
| EXPECT_CALL(*filter_callbacks_.route_, directResponseEntry()) | ||
| .WillOnce(Return(&direct_response_entry)); | ||
| EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, | ||
| filter_->decodeHeaders(request_headers_, true)); | ||
| } | ||
|
|
||
| // Test that the request continues when filter_callbacks has no route | ||
| TEST_P(HttpFilterTestParam, ContinueIterationWithNoRoute) { | ||
| EXPECT_CALL(filter_callbacks_, route()).WillRepeatedly(Return(nullptr)); | ||
| EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); | ||
| EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); | ||
| EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); | ||
| } | ||
|
|
||
| // Test that the request continues when filter_callbacks has no route entry / direct response entry. | ||
| TEST_P(HttpFilterTestParam, ContinueIterationWithNoRouteEntryOrDirectResponseEntry) { | ||
| EXPECT_CALL(*client_, check(_, _, _, _)).Times(0); | ||
| EXPECT_CALL(*filter_callbacks_.route_, routeEntry()).WillOnce(Return(nullptr)); | ||
| EXPECT_CALL(*filter_callbacks_.route_, directResponseEntry()).WillOnce(Return(nullptr)); | ||
| EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); | ||
| EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); | ||
| EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this out in the ext_authz docs?