Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
12230f0
Add query_parameters_to_add
esmet Aug 31, 2021
a934913
Fix format
esmet Sep 6, 2021
cad93ec
Fix format
esmet Sep 6, 2021
426a467
Clean up some things
esmet Sep 6, 2021
25b2eeb
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 9, 2021
5c9c01e
Add a test for the grpc implementation.
esmet Sep 9, 2021
06a6338
Formatting
esmet Sep 9, 2021
b9fc09e
Add release notes
esmet Sep 10, 2021
251655e
Fix labels in release notes
esmet Sep 10, 2021
7686b3c
Try fixing labels again
esmet Sep 13, 2021
9edf4c0
Fix one more time
esmet Sep 13, 2021
591f75a
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 14, 2021
b2b3dec
Add QueryParamsVector
esmet Sep 21, 2021
e67a46e
Format
esmet Sep 21, 2021
43aae44
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 21, 2021
95872b4
Refactor towards replaceQueryString, add unit tests for it and stripQ…
esmet Sep 21, 2021
68185f1
Formatting
esmet Sep 21, 2021
2d63f10
tools: fix protoprint to respect CLANG_FORMAT env var
esmet Sep 21, 2021
08ba422
Fix changelog
esmet Sep 21, 2021
617f90e
Add query_parameters_to_remove, move QueryParameter to core API
esmet Sep 22, 2021
32786f4
Formatting
esmet Sep 23, 2021
46004e6
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 23, 2021
e740588
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 24, 2021
427be58
Docs fixups
esmet Sep 28, 2021
181dcc1
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 29, 2021
4474842
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Sep 30, 2021
ddcd3aa
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Oct 4, 2021
a2674e3
Fix initialization bugs
esmet Oct 6, 2021
020918e
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Oct 6, 2021
19383d6
Fix bad merge on the changelog
esmet Oct 6, 2021
7b52929
Fix include in query_params.h
esmet Oct 7, 2021
8837536
Fix order
esmet Oct 7, 2021
5fe5ee3
Merge remote-tracking branch 'upstream/main' into ext-authz-query-string
esmet Oct 7, 2021
a9d7f5a
More test cases
esmet Oct 8, 2021
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
9 changes: 9 additions & 0 deletions api/envoy/config/core/v3/base.proto
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,15 @@ message RuntimeFeatureFlag {
string runtime_key = 2 [(validate.rules).string = {min_len: 1}];
}

// Query parameter name/value pair.
message QueryParameter {
// The key of the query parameter. Case sensitive.
string key = 1 [(validate.rules).string = {min_len: 1}];

// The value of the query parameter.
string value = 2;
}

// Header name/value pair.
message HeaderValue {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HeaderValue";
Expand Down
11 changes: 10 additions & 1 deletion api/envoy/service/auth/v3/external_auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ message DeniedHttpResponse {
}

// HTTP attributes for an OK response.
// [#next-free-field: 7]
// [#next-free-field: 9]
message OkHttpResponse {
option (udpa.annotations.versioning).previous_message_type =
"envoy.service.auth.v2.OkHttpResponse";
Expand Down Expand Up @@ -102,6 +102,15 @@ message OkHttpResponse {
// to the downstream client on success. Note that the :ref:`append field in HeaderValueOption <envoy_v3_api_field_config.core.v3.HeaderValueOption.append>`
// defaults to false when used in this message.
repeated config.core.v3.HeaderValueOption response_headers_to_add = 6;

// This field allows the authorization service to set (and overwrite) query
// string parameters on the original request before it is sent upstream.
repeated config.core.v3.QueryParameter query_parameters_to_set = 7;

// This field allows the authorization service to specify which query parameters
// should be removed from the original request before it is sent upstream. Each
// element in this list is a case-sensitive query parameter name to be removed.
Comment thread
htuch marked this conversation as resolved.
Comment thread
htuch marked this conversation as resolved.
repeated string query_parameters_to_remove = 8;
}

// Intended for gRPC and Network Authorization servers `only`.
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Removed Config or Runtime

New Features
------------
* ext_authz: added :ref:`query_parameters_to_set <envoy_v3_api_field_service.auth.v3.OkHttpResponse.query_parameters_to_set>` and :ref:`query_parameters_to_remove <envoy_v3_api_field_service.auth.v3.OkHttpResponse.query_parameters_to_remove>` for adding and removing query string parameters when using a gRPC authorization server.
* http: added support for :ref:`retriable health check status codes <envoy_v3_api_field_config.core.v3.HealthCheck.HttpHealthCheck.retriable_statuses>`.

Deprecated
Expand Down
2 changes: 2 additions & 0 deletions envoy/http/query_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <map>
#include <string>
#include <vector>

namespace Envoy {
namespace Http {
Expand All @@ -12,6 +13,7 @@ namespace Utility {
// https://github.com/apache/incubator-pagespeed-mod/blob/master/pagespeed/kernel/http/query_params.h

using QueryParams = std::map<std::string, std::string>;
using QueryParamsVector = std::vector<std::pair<std::string, std::string>>;

} // namespace Utility
} // namespace Http
Expand Down
12 changes: 12 additions & 0 deletions source/common/http/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,18 @@ std::string Utility::stripQueryString(const HeaderString& path) {
query_offset != path_str.npos ? query_offset : path_str.size());
}

std::string Utility::replaceQueryString(const HeaderString& path,
const Utility::QueryParams& params) {
std::string new_path{Http::Utility::stripQueryString(path)};

if (!params.empty()) {
const auto new_query_string = Http::Utility::queryParamsToString(params);
absl::StrAppend(&new_path, new_query_string);
}

return new_path;
}

std::string Utility::parseCookieValue(const HeaderMap& headers, const std::string& key) {
// TODO(wbpcode): Modify the headers parameter type to 'RequestHeaderMap'.
return parseCookie(headers, key, Http::Headers::get().Cookie);
Expand Down
14 changes: 14 additions & 0 deletions source/common/http/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ absl::string_view findQueryStringStart(const HeaderString& path);
*/
std::string stripQueryString(const HeaderString& path);

/**
* Replace the query string portion of a given path with a new one.
*
* e.g. replaceQueryString("/foo?key=1", {key:2}) -> "/foo?key=2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no standard on passing multiple values (for the same key, e.g. ?a=1&a=2, it is common that web framework perceives this as a "collection", for example: https://docs.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms524784(v=vs.90)?redirectedfrom=MSDN#remarks) in the query string, I think we need to be explicit here to do "replace all" or just the first occurrence. Or as wild as: https://github.com/ljharb/qs#parsing-arrays

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example in the comment might be too simple and therefore misleading, but this utility function actually replaces the entire query string with the serialized representation of the given QueryParams. It doesn't attempt to parse the existing query string and do replacements on a key-by-key basis.

* replaceQueryString("/bar", {hello:there}) -> "/bar?hello=there"
*
* @param path the original path that may or may not contain an existing query string
* @param params the new params whose string representation should be formatted onto
* the `path` above
* @return std::string the new path whose query string has been replaced by `params` and whose path
* portion from `path` remains unchanged.
*/
std::string replaceQueryString(const HeaderString& path, const QueryParams& params);

/**
* Parse a particular value out of a cookie
* @param headers supplies the headers to get the cookie from.
Expand Down
6 changes: 6 additions & 0 deletions source/extensions/filters/common/ext_authz/ext_authz.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "envoy/tracing/http_tracer.h"

#include "source/common/http/headers.h"
#include "source/common/http/utility.h"
#include "source/common/singleton/const_singleton.h"

namespace Envoy {
Expand Down Expand Up @@ -84,6 +85,11 @@ struct Response {
// A set of HTTP headers consumed by the authorization server, will be removed
// from the request to the upstream server.
std::vector<Envoy::Http::LowerCaseString> headers_to_remove;
// A set of query string parameters to be set (possibly overwritten) on the
// request to the upstream server.
Http::Utility::QueryParamsVector query_parameters_to_set;
// A set of query string parameters to remove from the request to the upstream server.
std::vector<std::string> query_parameters_to_remove;
// Optional http body used only on denied response.
std::string body;
// Optional http status used only on denied response.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,17 @@ void GrpcClientImpl::onSuccess(std::unique_ptr<envoy::service::auth::v3::CheckRe
authz_response->headers_to_remove.push_back(Http::LowerCaseString(header));
}
}

if (response->ok_response().query_parameters_to_set_size() > 0) {
for (const auto& query_parameter : response->ok_response().query_parameters_to_set()) {
authz_response->query_parameters_to_set.push_back(
std::pair(query_parameter.key(), query_parameter.value()));
}
}
if (response->ok_response().query_parameters_to_remove_size() > 0) {
for (const auto& key : response->ok_response().query_parameters_to_remove()) {
authz_response->query_parameters_to_remove.push_back(key);
}
}
// These two vectors hold header overrides of encoded response headers.
if (response->ok_response().response_headers_to_add_size() > 0) {
for (const auto& header : response->ok_response().response_headers_to_add()) {
Expand Down
18 changes: 15 additions & 3 deletions source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const Response& errorResponse() {
Http::HeaderVector{},
Http::HeaderVector{},
{{}},
Http::Utility::QueryParamsVector{},
{},
EMPTY_STRING,
Http::Code::Forbidden,
ProtobufWkt::Struct{}});
Expand Down Expand Up @@ -350,9 +352,17 @@ ResponsePtr RawHttpClientImpl::toResponse(Http::ResponseMessagePtr message) {
config_->upstreamHeaderToAppendMatchers(),
config_->clientHeaderOnSuccessMatchers(),
config_->dynamicMetadataMatchers(),
Response{CheckStatus::OK, Http::HeaderVector{}, Http::HeaderVector{},
Http::HeaderVector{}, Http::HeaderVector{}, Http::HeaderVector{},
std::move(headers_to_remove), EMPTY_STRING, Http::Code::OK,
Response{CheckStatus::OK,
Http::HeaderVector{},
Http::HeaderVector{},
Http::HeaderVector{},
Http::HeaderVector{},
Http::HeaderVector{},
std::move(headers_to_remove),
Http::Utility::QueryParamsVector{},
{},
EMPTY_STRING,
Http::Code::OK,
ProtobufWkt::Struct{}}};
return std::move(ok.response_);
}
Expand All @@ -370,6 +380,8 @@ ResponsePtr RawHttpClientImpl::toResponse(Http::ResponseMessagePtr message) {
Http::HeaderVector{},
Http::HeaderVector{},
{{}},
Http::Utility::QueryParamsVector{},
{},
message->bodyAsString(),
static_cast<Http::Code>(status_code),
ProtobufWkt::Struct{}}};
Expand Down
41 changes: 39 additions & 2 deletions source/extensions/filters/http/ext_authz/ext_authz.cc
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,13 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) {

switch (response->status) {
case CheckStatus::OK: {
// Any changes to request headers can affect how the request is going to be
// Any changes to request headers or query parameters can affect how the request is going to be

@dio dio Sep 13, 2021

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. I totally forgot that we have a route matcher to match query params.

// [#next-free-field: 7]
message QueryParameterMatcher {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.route.QueryParameterMatcher";
.

// routed. If we are changing the headers we also need to clear the route
// cache.
if (config_->clearRouteCache() &&
(!response->headers_to_set.empty() || !response->headers_to_append.empty() ||
!response->headers_to_remove.empty())) {
!response->headers_to_remove.empty() || !response->query_parameters_to_set.empty() ||
!response->query_parameters_to_remove.empty())) {
ENVOY_STREAM_LOG(debug, "ext_authz is clearing route cache", *decoder_callbacks_);
decoder_callbacks_->clearRouteCache();
}
Expand Down Expand Up @@ -286,6 +287,42 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) {
response_headers_to_set_ = std::move(response->response_headers_to_set);
}

absl::optional<Http::Utility::QueryParams> modified_query_parameters;
if (!response->query_parameters_to_set.empty()) {
modified_query_parameters =
Http::Utility::parseQueryString(request_headers_->Path()->value().getStringView());
ENVOY_STREAM_LOG(
trace, "ext_authz filter set query parameter(s) on the request:", *decoder_callbacks_);
for (const auto& [key, value] : response->query_parameters_to_set) {
ENVOY_STREAM_LOG(trace, "'{}={}'", *decoder_callbacks_, key, value);
(*modified_query_parameters)[key] = value;
}
}

if (!response->query_parameters_to_remove.empty()) {
if (!modified_query_parameters) {
modified_query_parameters =
Http::Utility::parseQueryString(request_headers_->Path()->value().getStringView());
}
ENVOY_STREAM_LOG(trace, "ext_authz filter removed query parameter(s) from the request:",
*decoder_callbacks_);
for (const auto& key : response->query_parameters_to_remove) {
ENVOY_STREAM_LOG(trace, "'{}'", *decoder_callbacks_, key);
(*modified_query_parameters).erase(key);
}
}

// We modified the query parameters in some way, so regenerate the `path` header and set it
// here.
if (modified_query_parameters) {
const auto new_path = Http::Utility::replaceQueryString(request_headers_->Path()->value(),
modified_query_parameters.value());
ENVOY_STREAM_LOG(
trace, "ext_authz filter modified query parameter(s), using new path for request: {}",
*decoder_callbacks_, new_path);
request_headers_->setPath(new_path);
}

if (cluster_) {
config_->incCounter(cluster_->statsScope(), config_->ext_authz_ok_);
}
Expand Down
61 changes: 61 additions & 0 deletions test/common/http/utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,67 @@ TEST(HttpUtility, parseQueryString) {
"bucket%7Crq_xx%7Crq_complete%7Crq_active%7Ccx_active%29%29%7C%28server.version%29"));
}

TEST(HttpUtility, stripQueryString) {
EXPECT_EQ(Utility::stripQueryString(HeaderString("/")), "/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/?")), "/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/?x=1")), "/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo")), "/foo");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo?")), "/foo");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo?hello=there")), "/foo");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/?")), "/foo/");
Comment on lines +97 to +103

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I know it's too pedantic, but I feel incompleteness without having these two lines added

  EXPECT_EQ(Utility::stripQueryString(HeaderString("/?x=1&y=2")), "/");
  EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo?hello=there&good=bye")), "/foo");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fair - will add them

EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/?x=1")), "/foo/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar")), "/foo/bar");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar?")), "/foo/bar");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar?a=b")), "/foo/bar");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar?a=b&b=c")), "/foo/bar");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar/")), "/foo/bar/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar/?")), "/foo/bar/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar/?x=1")), "/foo/bar/");
EXPECT_EQ(Utility::stripQueryString(HeaderString("/foo/bar/?x=1&y=2")), "/foo/bar/");
}

TEST(HttpUtility, replaceQueryString) {
// Replace with nothing
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/"), Utility::QueryParams()), "/");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/?"), Utility::QueryParams()), "/");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/?x=0"), Utility::QueryParams()), "/");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/a"), Utility::QueryParams()), "/a");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/a/"), Utility::QueryParams()), "/a/");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/a/?y=5"), Utility::QueryParams()), "/a/");
// Replace with x=1
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/"), Utility::QueryParams({{"x", "1"}})),
"/?x=1");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/?"), Utility::QueryParams({{"x", "1"}})),
"/?x=1");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/?x=0"), Utility::QueryParams({{"x", "1"}})),
"/?x=1");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/a?x=0"), Utility::QueryParams({{"x", "1"}})),
"/a?x=1");
EXPECT_EQ(
Utility::replaceQueryString(HeaderString("/a/?x=0"), Utility::QueryParams({{"x", "1"}})),
"/a/?x=1");
// More replacements
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo"),
Utility::QueryParams({{"x", "1"}, {"z", "3"}})),
"/foo?x=1&z=3");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo?z=2"),
Utility::QueryParams({{"x", "1"}, {"y", "5"}})),
"/foo?x=1&y=5");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo?y=9"),
Utility::QueryParams({{"x", "1"}, {"y", "5"}})),
"/foo?x=1&y=5");
// More path components
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo/bar?"),
Utility::QueryParams({{"x", "1"}, {"y", "5"}})),
"/foo/bar?x=1&y=5");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo/bar?y=9&a=b"),
Utility::QueryParams({{"x", "1"}, {"y", "5"}})),
"/foo/bar?x=1&y=5");
EXPECT_EQ(Utility::replaceQueryString(HeaderString("/foo/bar?y=11&z=7"),
Utility::QueryParams({{"a", "b"}, {"x", "1"}, {"y", "5"}})),
"/foo/bar?a=b&x=1&y=5");
}

TEST(HttpUtility, getResponseStatus) {
EXPECT_THROW(Utility::getResponseStatus(TestResponseHeaderMapImpl{}), CodecClientException);
EXPECT_EQ(200U, Utility::getResponseStatus(TestResponseHeaderMapImpl{{":status", "200"}}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,46 @@ TEST_F(ExtAuthzGrpcClientTest, AuthorizationOkWithDynamicMetadata) {
client_->onSuccess(std::move(check_response), span_);
}

// Test the client when an OK response is received with additional query string parameters.
TEST_F(ExtAuthzGrpcClientTest, AuthorizationOkWithQueryParameters) {
initialize();

auto check_response = std::make_unique<envoy::service::auth::v3::CheckResponse>();
auto status = check_response->mutable_status();

status->set_code(Grpc::Status::WellKnownGrpcStatus::Ok);

const Http::Utility::QueryParamsVector query_parameters_to_set{{"add-me", "yes"}};
for (const auto& [key, value] : query_parameters_to_set) {
auto* query_parameter = check_response->mutable_ok_response()->add_query_parameters_to_set();
query_parameter->set_key(key);
query_parameter->set_value(value);
}

const std::vector<std::string> query_parameters_to_remove{"remove-me"};
for (const auto& key : query_parameters_to_remove) {
check_response->mutable_ok_response()->add_query_parameters_to_remove(key);
}

// This is the expected authz response.
auto authz_response = Response{};
authz_response.status = CheckStatus::OK;
authz_response.query_parameters_to_set = {{"add-me", "yes"}};
authz_response.query_parameters_to_remove = {"remove-me"};

envoy::service::auth::v3::CheckRequest request;
expectCallSend(request);
client_->check(request_callbacks_, request, Tracing::NullSpan::instance(), stream_info_);

Http::TestRequestHeaderMapImpl headers;
client_->onCreateInitialMetadata(headers);

EXPECT_CALL(span_, setTag(Eq("ext_authz_status"), Eq("ext_authz_ok")));
EXPECT_CALL(request_callbacks_,
onComplete_(WhenDynamicCastTo<ResponsePtr&>(AuthzOkResponse(authz_response))));
client_->onSuccess(std::move(check_response), span_);
}

} // namespace ExtAuthz
} // namespace Common
} // namespace Filters
Expand Down
13 changes: 13 additions & 0 deletions test/extensions/filters/common/ext_authz/test_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ bool TestCommon::compareVectorOfHeaderName(const std::vector<Http::LowerCaseStri
std::set<Http::LowerCaseString>(rhs.begin(), rhs.end());
}

bool TestCommon::compareVectorOfUnorderedStrings(const std::vector<std::string>& lhs,
const std::vector<std::string>& rhs) {
return std::set<std::string>(lhs.begin(), lhs.end()) ==
std::set<std::string>(rhs.begin(), rhs.end());
}

// TODO(esmet): This belongs in a QueryParams class
bool TestCommon::compareQueryParamsVector(const Http::Utility::QueryParamsVector& lhs,
const Http::Utility::QueryParamsVector& rhs) {
return std::set<std::pair<std::string, std::string>>(lhs.begin(), lhs.end()) ==
std::set<std::pair<std::string, std::string>>(rhs.begin(), rhs.end());
}

} // namespace ExtAuthz
} // namespace Common
} // namespace Filters
Expand Down
Loading