Update extensions & release 1.0.0-rc (#281)

This commit is contained in:
澄潭
2023-04-09 14:16:10 +08:00
committed by GitHub
parent f5edac0c58
commit 069b636c10
38 changed files with 721 additions and 128 deletions

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -31,7 +45,7 @@ cc_library(
visibility = ["//visibility:public"],
alwayslink = 1,
deps = [
"//common:rule_util",
"//common:rule_util_nullvm",
"//common:json_util",
"//common:crypto_util",
"@com_google_absl//absl/strings",

View File

@@ -308,8 +308,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}

View File

@@ -909,7 +909,8 @@ TEST_F(BasicAuthTest, GlobalDeny) {
}
TEST_F(BasicAuthTest, GlobalWithConsumerDeny) {
std::string configuration = R"(
{
std::string configuration = R"(
{
"consumers" : [
{"credential" : "ok:test", "name" : "consumer_ok"},
@@ -932,36 +933,171 @@ TEST_F(BasicAuthTest, GlobalWithConsumerDeny) {
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
cred_ = "wrong-cred";
route_name_ = "config";
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
cred_ = "wrong-cred";
route_name_ = "not match";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
authority_ = "www.example.com";
cred_ = "admin2:admin2";
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
cred_ = "wrong-cred";
route_name_ = "config";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "config";
cred_ = "admin4:admin4";
authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
authority_ = "www.example.com";
cred_ = "admin2:admin2";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "config";
cred_ = "admin4:admin4";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
}
{
std::string configuration = R"(
{
"global_auth": true,
"consumers" : [
{"credential" : "ok:test", "name" : "consumer_ok"},
{"credential" : "admin2:admin2", "name" : "consumer2"},
{"credential" : "admin:admin", "name" : "consumer"}
],
"_rules_" : [
{
"_match_route_" : ["test", "config"],
"consumers" : [
{"credential" : "admin3:admin3", "name" : "consumer3"},
{"credential" : "YWRtaW41OmFkbWluNQ==", "name" : "consumer5"}
]
},
{
"_match_domain_" : ["test.com", "*.example.com"],
"consumers" : [
{"credential" : "admin4:admin4", "name" : "consumer4"}
]
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
cred_ = "wrong-cred";
route_name_ = "not match";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
cred_ = "wrong-cred";
route_name_ = "config";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
authority_ = "www.example.com";
cred_ = "admin2:admin2";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "config";
cred_ = "admin4:admin4";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
}
}
TEST_F(BasicAuthTest, OnConfigureNoRulesAuth) {
// enable global auth
{
std::string configuration = R"(
{
"consumers" : [
{"credential" : "getuser1:123456", "name" : "consumer1"}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
cred_ = "admin:admin";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
cred_ = "getuser1:123456";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
// disable global auth
{
std::string configuration = R"(
{
"consumers" : [
{"credential" : "getuser1:123456", "name" : "consumer1"}
],
"global_auth": false
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
cred_ = "admin:admin";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
cred_ = "getuser1:123456";
authorization_header_ =
"Basic " + Base64::encode(cred_.data(), cred_.size());
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
}
} // namespace basic_auth

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -11,10 +25,10 @@ wasm_cc_binary(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:regex_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -31,9 +45,9 @@ cc_library(
"@com_google_absl//absl/strings",
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util",
"//common:http_util_nullvm",
"//common:regex_util",
"//common:rule_util",
"//common:rule_util_nullvm",
],
)

View File

@@ -78,13 +78,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_WARN("cannot parse allow");
return false;
}
try {
rule.allow.push_back(
std::make_unique<ReMatcher>(regex.first.value()));
} catch (const std::runtime_error& e) {
LOG_WARN(e.what());
auto re = std::make_unique<ReMatcher>(regex.first.value());
if (!re->error().empty()) {
LOG_WARN(re->error());
return false;
}
rule.allow.push_back(std::move(re));
return true;
})) {
LOG_WARN("failed to parse configuration for allow.");
@@ -96,12 +96,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_WARN("cannot parse deny");
return false;
}
try {
rule.deny.push_back(std::make_unique<ReMatcher>(regex.first.value()));
} catch (const std::runtime_error& e) {
LOG_WARN(e.what());
auto re = std::make_unique<ReMatcher>(regex.first.value());
if (!re->error().empty()) {
LOG_WARN(re->error());
return false;
}
rule.deny.push_back(std::move(re));
return true;
})) {
LOG_WARN("failed to parse configuration for deny.");
@@ -114,8 +115,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
if (size == 0) {
// support empty config

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -11,9 +25,9 @@ wasm_cc_binary(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)

View File

@@ -149,8 +149,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -13,10 +27,10 @@ wasm_cc_binary(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:crypto_util",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -37,8 +51,8 @@ cc_library(
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:crypto_util",
"//common:http_util",
"//common:rule_util",
"//common:http_util_nullvm",
"//common:rule_util_nullvm",
],
)

View File

@@ -389,8 +389,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -17,9 +31,9 @@ wasm_cc_binary(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)

View File

@@ -135,11 +135,16 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
[&](const json& from_header) -> bool {
JSON_FIND_FIELD(from_header, name);
JSON_FIELD_VALUE_AS(std::string, from_header, name);
JSON_FIND_FIELD(from_header, value_prefix);
JSON_FIELD_VALUE_AS(std::string, from_header,
value_prefix);
from_headers.push_back(FromHeader{
from_header_name, from_header_value_prefix});
std::string header_value_prefix;
auto from_header_value_prefix_json =
from_header.find("value_prefix");
if (from_header_value_prefix_json != from_header.end()) {
JSON_FIELD_VALUE_AS(std::string, from_header,
value_prefix);
header_value_prefix = from_header_value_prefix;
}
from_headers.push_back(
FromHeader{from_header_name, header_value_prefix});
return true;
})) {
LOG_WARN("failed to parse 'from_headers' in consumer: " +
@@ -229,6 +234,18 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_INFO("at least one consumer has to be configured for a rule.");
return false;
}
std::vector<std::string> enable_headers;
if (!JsonArrayIterate(configuration, "enable_headers",
[&](const json& enable_header_json) -> bool {
JSON_VALUE_AS(std::string, enable_header_json,
enable_header, "invalid item");
enable_headers.push_back(enable_header);
return true;
})) {
LOG_WARN("failed to parse 'enable_headers'");
return false;
}
rule.enable_headers = std::move(enable_headers);
return true;
}
@@ -307,6 +324,20 @@ Status PluginRootContext::consumerVerify(
bool PluginRootContext::checkPlugin(
const JwtAuthConfigRule& rule,
const std::optional<std::unordered_set<std::string>>& allow_set) {
if (!rule.enable_headers.empty()) {
bool skip_auth = true;
for (const auto& enable_header : rule.enable_headers) {
auto header_ptr = getRequestHeader(enable_header);
if (header_ptr->size() > 0) {
LOG_DEBUG("enable by header: " + header_ptr->toString());
skip_auth = false;
break;
}
}
if (skip_auth) {
return true;
}
}
std::optional<Status> err_status;
bool verified = false;
uint64_t now = getCurrentTimeNanoseconds() / 1e9;
@@ -354,8 +385,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}

View File

@@ -73,6 +73,7 @@ struct Consumer {
struct JwtAuthConfigRule {
std::vector<Consumer> consumers;
std::vector<std::string> enable_headers;
};
// PluginRootContext is the root context for all streams processed by the

View File

@@ -84,6 +84,9 @@ class JwtAuthTest : public ::testing::Test {
if (header == "Authorization") {
*result = jwt_header_;
}
if (header == "x-custom-header") {
*result = custom_header_;
}
return WasmResult::Ok;
});
ON_CALL(*mock_context_, addHeaderMapValue(WasmHeaderMapType::RequestHeaders,
@@ -118,6 +121,7 @@ class JwtAuthTest : public ::testing::Test {
std::string authority_;
std::string route_name_;
std::string jwt_header_;
std::string custom_header_;
uint64_t current_time_;
};
@@ -146,7 +150,8 @@ TEST_F(JwtAuthTest, RSA) {
}
TEST_F(JwtAuthTest, OCT) {
std::string configuration = R"(
{
std::string configuration = R"(
{
"consumers": [
{
@@ -156,17 +161,65 @@ TEST_F(JwtAuthTest, OCT) {
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
current_time_ = 1665673819 * 1e9;
jwt_header_ =
R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
current_time_ = 1665673819 * 1e9;
jwt_header_ =
R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
{
std::string configuration = R"(
{
"consumers": [
{
"name": "consumer-2",
"issuer": "abcd",
"jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}"
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
current_time_ = 1665673819 * 1e9;
jwt_header_ =
R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKm1)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
}
{
std::string configuration = R"(
{
"consumers": [
{
"name": "consumer-2",
"issuer": "abcd",
"jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}"
}
],
"global_auth": false
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
current_time_ = 1665673819 * 1e9;
jwt_header_ =
R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKm1)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
}
TEST_F(JwtAuthTest, AuthZ) {
@@ -264,6 +317,97 @@ TEST_F(JwtAuthTest, ClaimToHeader) {
FilterHeadersStatus::Continue);
}
TEST_F(JwtAuthTest, CustomHeader) {
std::string configuration = R"(
{
"consumers": [
{
"name": "consumer-2",
"issuer": "abcd",
"from_headers": [
{
"name": "x-custom-header",
"value_prefix": "token "
},
{
"name": "Authorization"
}
],
"jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}"
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
custom_header_ =
R"(token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
custom_header_.clear();
jwt_header_ =
R"(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
TEST_F(JwtAuthTest, SkipAuthHeader) {
std::string configuration = R"(
{
"consumers": [
{
"name": "consumer-1",
"issuer": "abc",
"jwks": "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"123\",\"alg\":\"RS256\",\"n\":\"i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw\"}]}"
},
{
"name": "consumer-2",
"issuer": "abcd",
"jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}"
}
],
"enable_headers": ["x-custom-header"],
"_rules_": [{
"_match_route_": [
"test1"
],
"allow": [
"consumer-1"
]
},
{
"_match_route_": [
"test2"
],
"allow": [
"consumer-2"
]
}
]
})";
BufferBase buffer;
buffer.set({configuration.data(), configuration.size()});
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
current_time_ = 1665673819 * 1e9;
jwt_header_ =
R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)";
route_name_ = "test1";
custom_header_ = "123";
EXPECT_CALL(*mock_context_, sendLocalResponse(403, testing::_, testing::_,
testing::_, testing::_));
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
custom_header_ = "";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
} // namespace jwt_auth
} // namespace null_plugin
} // namespace proxy_wasm

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -12,9 +26,9 @@ wasm_cc_binary(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -33,8 +47,8 @@ cc_library(
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util",
"//common:rule_util",
"//common:http_util_nullvm",
"//common:rule_util_nullvm",
],
)

View File

@@ -211,8 +211,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -13,9 +27,9 @@ wasm_cc_binary(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -34,8 +48,8 @@ cc_library(
"@com_google_absl//absl/strings",
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util",
"//common:rule_util",
"//common:http_util_nullvm",
"//common:rule_util_nullvm",
],
)

View File

@@ -18,6 +18,7 @@
#include <unordered_map>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
namespace {
@@ -40,11 +41,16 @@ bool getToken(int rule_id, const std::string &key) {
for (int i = 0; i < maxGetTokenRetry; i++) {
if (WasmResult::Ok !=
getSharedData(tokenBucketKey, &token_bucket_data, &cas)) {
return false;
continue;
}
uint64_t token_left =
*reinterpret_cast<const uint64_t *>(token_bucket_data->data());
LOG_DEBUG(absl::StrFormat(
"ratelimit get token: id:%d, tokenBucketKey:%s, token left:%u", rule_id,
tokenBucketKey, token_left));
if (token_left == 0) {
LOG_DEBUG(absl::StrFormat("get token failed, id:%d, tokenBucketKey:%s",
rule_id, tokenBucketKey));
return false;
}
token_left -= 1;
@@ -52,12 +58,18 @@ bool getToken(int rule_id, const std::string &key) {
tokenBucketKey,
{reinterpret_cast<const char *>(&token_left), sizeof(token_left)}, cas);
if (res == WasmResult::Ok) {
LOG_DEBUG(
absl::StrFormat("ratelimit token update success: id:%d, "
"tokenBucketKey:%s, token left:%u",
rule_id, tokenBucketKey, token_left));
return true;
}
if (res == WasmResult::CasMismatch) {
continue;
}
return false;
LOG_WARN(absl::StrFormat("got invalid result:%d, id:%d, tokenBucketKey:%s",
res, rule_id, tokenBucketKey));
return true;
}
LOG_WARN("get token failed with cas mismatch");
@@ -86,12 +98,21 @@ void refillToken(const std::vector<std::pair<int, LimitItem>> &rules) {
if (now - last_update < rule.second.refill_interval_nanosec) {
continue;
}
LOG_DEBUG(
absl::StrFormat("ratelimit rule need refilled, id:%s, "
"lastRefilledKey:%s, now:%u, last_update:%u",
id, lastRefilledKey, now, last_update));
// Otherwise, try set last updated time. If updated failed because of cas
// mismatch, the bucket is going to be refilled by other VMs.
auto res = setSharedData(
lastRefilledKey, {reinterpret_cast<const char *>(&now), sizeof(now)},
last_update_cas);
if (res == WasmResult::CasMismatch) {
LOG_DEBUG(
absl::StrFormat("ratelimit update lastRefilledKey casmismatch, the "
"bucket is going to be refilled by other VMs, id:%s, "
"lastRefilledKey:%s",
id, lastRefilledKey));
continue;
}
do {
@@ -115,6 +136,10 @@ void refillToken(const std::vector<std::pair<int, LimitItem>> &rules) {
last_update_cas)) {
continue;
}
LOG_DEBUG(
absl::StrFormat("ratelimit token refilled: id:%s, "
"tokenBucketKey:%s, token left:%u",
id, tokenBucketKey, token_left));
break;
} while (true);
}
@@ -138,6 +163,10 @@ bool initializeTokenBucket(
setSharedData(tokenBucketKey,
{reinterpret_cast<const char *>(&rule.second.max_tokens),
sizeof(uint64_t)});
LOG_INFO(absl::StrFormat(
"ratelimit rule created: id:%s, lastRefilledKey:%s, "
"tokenBucketKey:%s, max_tokens:%u",
id, lastRefilledKey, tokenBucketKey, rule.second.max_tokens));
continue;
}
// reconfigure
@@ -172,6 +201,10 @@ bool initializeTokenBucket(
}
break;
} while (true);
LOG_INFO(absl::StrFormat(
"ratelimit rule reconfigured: id:%s, lastRefilledKey:%s, "
"tokenBucketKey:%s, max_tokens:%u",
id, lastRefilledKey, tokenBucketKey, rule.second.max_tokens));
}
return true;
}

View File

@@ -169,6 +169,7 @@ bool PluginRootContext::checkPlugin(int rule_id,
return true;
}
if (!getToken(rule_id, key)) {
LOG_INFO(absl::StrCat("request rate limited by key: ", key));
tooManyRequest();
return false;
}
@@ -181,8 +182,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
const auto& rules = getRules();
for (const auto& rule : rules) {
@@ -191,7 +191,7 @@ bool PluginRootContext::onConfigure(size_t size) {
}
}
initializeTokenBucket(limits_);
proxy_set_tick_period_milliseconds(1000);
proxy_set_tick_period_milliseconds(500);
return true;
}

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -11,9 +25,9 @@ wasm_cc_binary(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"//common:rule_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -30,8 +44,8 @@ cc_library(
"@com_google_absl//absl/strings",
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util",
"//common:rule_util",
"//common:http_util_nullvm",
"//common:rule_util_nullvm",
],
)

View File

@@ -108,24 +108,24 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
return false;
}
if (!JsonArrayIterate(
configuration, "block_bodies", [&](const json& item) -> bool {
configuration, "block_bodys", [&](const json& item) -> bool {
auto body = JsonValueAs<std::string>(item);
if (body.second != Wasm::Common::JsonParserResultDetail::OK) {
LOG_WARN("cannot parse block_bodies");
LOG_WARN("cannot parse block_bodys");
return false;
}
if (rule.case_sensitive) {
rule.block_bodies.push_back(std::move(body.first.value()));
rule.block_bodys.push_back(std::move(body.first.value()));
} else {
rule.block_bodies.push_back(
rule.block_bodys.push_back(
absl::AsciiStrToLower(body.first.value()));
}
return true;
})) {
LOG_WARN("failed to parse configuration for block_bodies.");
LOG_WARN("failed to parse configuration for block_bodys.");
return false;
}
if (rule.block_bodies.empty() && rule.block_headers.empty() &&
if (rule.block_bodys.empty() && rule.block_headers.empty() &&
rule.block_urls.empty()) {
LOG_WARN("there is no block rules");
return false;
@@ -137,8 +137,7 @@ bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
setInvalidConfig();
return true;
return false;
}
return true;
}
@@ -197,7 +196,7 @@ bool PluginRootContext::checkHeader(const RequestBlockConfigRule& rule,
}
}
}
if (!rule.block_bodies.empty()) {
if (!rule.block_bodys.empty()) {
check_body = true;
}
return true;
@@ -212,7 +211,7 @@ bool PluginRootContext::checkBody(const RequestBlockConfigRule& rule,
bodystr = absl::AsciiStrToLower(request_body);
body = bodystr;
}
for (const auto& block_body : rule.block_bodies) {
for (const auto& block_body : rule.block_bodys) {
if (absl::StrContains(body, block_body)) {
sendLocalResponse(rule.blocked_code, "", rule.blocked_message, {});
return false;

View File

@@ -45,7 +45,7 @@ struct RequestBlockConfigRule {
bool case_sensitive = true;
std::vector<std::string> block_urls;
std::vector<std::string> block_headers;
std::vector<std::string> block_bodies;
std::vector<std::string> block_bodys;
};
// PluginRootContext is the root context for all streams processed by the

View File

@@ -128,7 +128,7 @@ TEST_F(RequestBlockTest, CaseSensitive) {
{
"block_urls": ["?foo=bar", "swagger.html"],
"block_headers": ["headerKey", "headerValue"],
"block_bodies": ["Hello World"]
"block_bodys": ["Hello World"]
})";
config_.set({configuration.data(), configuration.size()});
@@ -188,7 +188,7 @@ TEST_F(RequestBlockTest, CaseInsensitive) {
"blocked_code": 404,
"block_urls": ["?foo=bar", "swagger.html"],
"block_headers": ["headerKey", "headerValue"],
"block_bodies": ["Hello World"]
"block_bodys": ["Hello World"]
})";
config_.set({configuration.data(), configuration.size()});

View File

@@ -1,3 +1,17 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
@@ -10,8 +24,8 @@ wasm_cc_binary(
deps = [
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
"//common:http_util",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics",
],
)
@@ -30,7 +44,7 @@ cc_library(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util",
"//common:http_util_nullvm",
],
)