From aebcb08824c58b9d463b338904ccb14afeef4b5c Mon Sep 17 00:00:00 2001 From: Tianyuan Fu Date: Wed, 21 Dec 2022 15:30:45 +0800 Subject: [PATCH] Add param to forward headers from the auth server to the origin --- plugins/authproxy/Makefile.inc | 4 +++ plugins/authproxy/authproxy.cc | 39 +++++++++++++++++++++++ plugins/authproxy/tests/authproxy_test.cc | 39 +++++++++++++++++++++++ plugins/authproxy/utils.cc | 18 +++++++++++ plugins/authproxy/utils.h | 9 ++++++ 5 files changed, 109 insertions(+) create mode 100644 plugins/authproxy/tests/authproxy_test.cc diff --git a/plugins/authproxy/Makefile.inc b/plugins/authproxy/Makefile.inc index a46a242bef1..edfd68adeff 100644 --- a/plugins/authproxy/Makefile.inc +++ b/plugins/authproxy/Makefile.inc @@ -19,3 +19,7 @@ authproxy_authproxy_la_SOURCES = \ authproxy/authproxy.cc \ authproxy/utils.cc \ authproxy/utils.h + +check_PROGRAMS += authproxy/authproxy_test +authproxy_authproxy_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(abs_top_srcdir)/tests/include +authproxy_authproxy_test_SOURCES = authproxy/tests/authproxy_test.cc \ No newline at end of file diff --git a/plugins/authproxy/authproxy.cc b/plugins/authproxy/authproxy.cc index 74f20ce7fd7..c16e0a58dd4 100644 --- a/plugins/authproxy/authproxy.cc +++ b/plugins/authproxy/authproxy.cc @@ -39,6 +39,7 @@ #include using std::strlen; +using std::string_view; struct AuthRequestContext; @@ -55,10 +56,12 @@ static TSCont AuthOsDnsContinuation; struct AuthOptions { std::string hostname; + std::string forward_header_prefix; int hostport = -1; AuthRequestTransform transform = nullptr; bool force = false; bool cache_internal_requests = false; + std::string_view forwardHeaderPrefix; AuthOptions() = default; ~AuthOptions() = default; @@ -622,6 +625,38 @@ StateAuthorized(AuthRequestContext *auth, void *) TSHttpTxnConfigIntSet(auth->txn, TS_CONFIG_HTTP_CACHE_IGNORE_AUTHENTICATION, 1); } + if (!options->forward_header_prefix.empty()) { + // Copy headers with configured prefix in the authentication response to the original request + TSMLoc field_loc; + TSMLoc next_field_loc; + TSMBuffer request_bufp; + TSMLoc request_hdr; + + TSReleaseAssert(TSHttpTxnClientReqGet(auth->txn, &request_bufp, &request_hdr) == TS_SUCCESS); + field_loc = TSMimeHdrFieldGet(auth->rheader.buffer, auth->rheader.header, 0); + TSReleaseAssert(field_loc != TS_NULL_MLOC); + + while (field_loc) { + int key_len = 0; + int val_len = 0; + + char *key = const_cast(TSMimeHdrFieldNameGet(auth->rheader.buffer, auth->rheader.header, field_loc, &key_len)); + char *val = + const_cast(TSMimeHdrFieldValueStringGet(auth->rheader.buffer, auth->rheader.header, field_loc, -1, &val_len)); + + if (key && val && ContainsPrefix(string_view(key, key_len), options->forward_header_prefix)) { + // Append the matched header key/val to the original request + HttpSetMimeHeader(request_bufp, request_hdr, string_view(key, key_len), string_view(val, val_len)); + } + + // Validate the next header field + next_field_loc = TSMimeHdrFieldNext(auth->rheader.buffer, auth->rheader.header, field_loc); + TSHandleMLocRelease(auth->rheader.buffer, auth->rheader.header, field_loc); + field_loc = next_field_loc; + } + } + + // Proceed with the modified request TSHttpTxnReenable(auth->txn, TS_EVENT_HTTP_CONTINUE); return TS_EVENT_CONTINUE; } @@ -693,6 +728,7 @@ AuthParseOptions(int argc, const char **argv) {const_cast("auth-transform"), required_argument, nullptr, 't'}, {const_cast("force-cacheability"), no_argument, nullptr, 'c'}, {const_cast("cache-internal"), no_argument, nullptr, 'i'}, + {const_cast("forward-header-prefix"), required_argument, nullptr, 'f'}, {nullptr, 0, nullptr, 0}, }; @@ -717,6 +753,9 @@ AuthParseOptions(int argc, const char **argv) case 'i': options->cache_internal_requests = true; break; + case 'f': + options->forward_header_prefix = optarg; + break; case 't': if (strcasecmp(optarg, "redirect") == 0) { options->transform = AuthWriteRedirectedRequest; diff --git a/plugins/authproxy/tests/authproxy_test.cc b/plugins/authproxy/tests/authproxy_test.cc new file mode 100644 index 00000000000..c9026cb3202 --- /dev/null +++ b/plugins/authproxy/tests/authproxy_test.cc @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 +#define CATCH_CONFIG_MAIN /* include main function */ +#include /* catch unit-test framework */ +#include "../utils.h" + +using std::string_view; +TEST_CASE("Util methods", "[authproxy][utility]") +{ + SECTION("ContainsPrefix()") + { + CHECK(ContainsPrefix(string_view{"abcdef"}, "abc") == true); + CHECK(ContainsPrefix(string_view{"abc"}, "abcdef") == false); + CHECK(ContainsPrefix(string_view{"abcdef"}, "abd") == false); + CHECK(ContainsPrefix(string_view{"abc"}, "abc") == true); + CHECK(ContainsPrefix(string_view{""}, "") == true); + CHECK(ContainsPrefix(string_view{"abc"}, "") == true); + CHECK(ContainsPrefix(string_view{""}, "abc") == false); + CHECK(ContainsPrefix(string_view{"abcdef"}, "abc\0") == true); + CHECK(ContainsPrefix(string_view{"abcdef\0"}, "abc\0") == true); + } +} diff --git a/plugins/authproxy/utils.cc b/plugins/authproxy/utils.cc index bbc1ac19c5c..e9a9a7568a6 100644 --- a/plugins/authproxy/utils.cc +++ b/plugins/authproxy/utils.cc @@ -26,6 +26,7 @@ #include #include #include +#include void HttpDebugHeader(TSMBuffer mbuf, TSMLoc mhdr) @@ -81,6 +82,23 @@ HttpSetMimeHeader(TSMBuffer mbuf, TSMLoc mhdr, const char *name, const char *val TSHandleMLocRelease(mbuf, mhdr, mloc); } +void +HttpSetMimeHeader(TSMBuffer mbuf, TSMLoc mhdr, const std::string_view name, const std::string_view value) +{ + TSMLoc mloc; + mloc = TSMimeHdrFieldFind(mbuf, mhdr, name.data(), name.size()); + if (mloc == TS_NULL_MLOC) { + TSReleaseAssert(TSMimeHdrFieldCreateNamed(mbuf, mhdr, name.data(), name.size(), &mloc) == TS_SUCCESS); + } else { + TSReleaseAssert(TSMimeHdrFieldValuesClear(mbuf, mhdr, mloc) == TS_SUCCESS); + } + + TSReleaseAssert(TSMimeHdrFieldValueStringInsert(mbuf, mhdr, mloc, 0 /* index */, value.data(), value.size()) == TS_SUCCESS); + TSReleaseAssert(TSMimeHdrFieldAppend(mbuf, mhdr, mloc) == TS_SUCCESS); + + TSHandleMLocRelease(mbuf, mhdr, mloc); +} + unsigned HttpGetContentLength(TSMBuffer mbuf, TSMLoc mhdr) { diff --git a/plugins/authproxy/utils.h b/plugins/authproxy/utils.h index 51cf01c387f..bd672ee729b 100644 --- a/plugins/authproxy/utils.h +++ b/plugins/authproxy/utils.h @@ -18,6 +18,8 @@ #pragma once +#include +#include #include #include #include @@ -102,8 +104,15 @@ unsigned HttpGetContentLength(TSMBuffer mbuf, TSMLoc mhdr); // Set the value of an arbitrary HTTP header. void HttpSetMimeHeader(TSMBuffer mbuf, TSMLoc mhdr, const char *name, const char *value); void HttpSetMimeHeader(TSMBuffer mbuf, TSMLoc mhdr, const char *name, unsigned value); +void HttpSetMimeHeader(TSMBuffer mbuf, TSMLoc mhdr, const std::string_view name, const std::string_view value); // Dump the given HTTP header to the debug log. void HttpDebugHeader(TSMBuffer mbuf, TSMLoc mhdr); +// Check if the string contains the prefix +inline bool +ContainsPrefix(const std::string_view str, const std::string &prefix) +{ + return str.size() < prefix.size() ? false : (strncmp(str.data(), prefix.data(), prefix.size()) == 0); +} // vim: set ts=4 sw=4 et :