You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/11/16 06:31:41 UTC
[apisix] branch master updated: feat: support hide credentials for jwt-auth plugin (#8206)
This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 2f4a4dba2 feat: support hide credentials for jwt-auth plugin (#8206)
2f4a4dba2 is described below
commit 2f4a4dba2c0cbc426ae99133c2546112ea71dba8
Author: pixelpig <62...@qq.com>
AuthorDate: Wed Nov 16 14:31:35 2022 +0800
feat: support hide credentials for jwt-auth plugin (#8206)
Co-authored-by: Alex Zhang <to...@apache.org>
Co-authored-by: 罗泽轩 <sp...@gmail.com>
Co-authored-by: root <ro...@pixelpig.localdomain>
Fixes https://github.com/apache/apisix/issues/8122
---
apisix/plugins/jwt-auth.lua | 57 ++++++-
docs/en/latest/plugins/basic-auth.md | 2 +-
docs/en/latest/plugins/jwt-auth.md | 1 +
docs/zh/latest/plugins/basic-auth.md | 2 +-
docs/zh/latest/plugins/jwt-auth.md | 1 +
t/plugin/jwt-auth3.t | 302 +++++++++++++++++++++++++++++++++++
6 files changed, 361 insertions(+), 4 deletions(-)
diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua
index 36006975f..a3c366f1f 100644
--- a/apisix/plugins/jwt-auth.lua
+++ b/apisix/plugins/jwt-auth.lua
@@ -19,6 +19,7 @@ local jwt = require("resty.jwt")
local consumer_mod = require("apisix.consumer")
local resty_random = require("resty.random")
local vault = require("apisix.core.vault")
+local new_tab = require ("table.new")
local ngx_encode_base64 = ngx.encode_base64
local ngx_decode_base64 = ngx.decode_base64
@@ -26,6 +27,9 @@ local ipairs = ipairs
local ngx = ngx
local ngx_time = ngx.time
local sub_str = string.sub
+local table_insert = table.insert
+local table_concat = table.concat
+local ngx_re_gmatch = ngx.re.gmatch
local plugin_name = "jwt-auth"
local pcall = pcall
@@ -48,6 +52,10 @@ local schema = {
cookie = {
type = "string",
default = "jwt"
+ },
+ hide_credentials = {
+ type = "boolean",
+ default = false
}
},
}
@@ -188,10 +196,41 @@ function _M.check_schema(conf, schema_type)
return true
end
+local function remove_specified_cookie(src, key)
+ local cookie_key_pattern = "([a-zA-Z0-9-_]*)"
+ local cookie_val_pattern = "([a-zA-Z0-9-._]*)"
+ local t = new_tab(1, 0)
+
+ local it, err = ngx_re_gmatch(src, cookie_key_pattern .. "=" .. cookie_val_pattern, "jo")
+ if not it then
+ core.log.error("match origins failed: ", err)
+ return src
+ end
+ while true do
+ local m, err = it()
+ if err then
+ core.log.error("iterate origins failed: ", err)
+ return src
+ end
+ if not m then
+ break
+ end
+ if m[1] ~= key then
+ table_insert(t, m[0])
+ end
+ end
+
+ return table_concat(t, "; ")
+end
local function fetch_jwt_token(conf, ctx)
local token = core.request.header(ctx, conf.header)
if token then
+ if conf.hide_credentials then
+ -- hide for header
+ core.request.set_header(ctx, conf.header, nil)
+ end
+
local prefix = sub_str(token, 1, 7)
if prefix == 'Bearer ' or prefix == 'bearer ' then
return sub_str(token, 8)
@@ -200,8 +239,14 @@ local function fetch_jwt_token(conf, ctx)
return token
end
- token = ctx.var["arg_" .. conf.query]
+ local uri_args = core.request.get_uri_args(ctx) or {}
+ token = uri_args[conf.query]
if token then
+ if conf.hide_credentials then
+ -- hide for query
+ uri_args[conf.query] = nil
+ core.request.set_uri_args(ctx, uri_args)
+ end
return token
end
@@ -209,6 +254,14 @@ local function fetch_jwt_token(conf, ctx)
if not val then
return nil, "JWT not found in cookie"
end
+
+ if conf.hide_credentials then
+ -- hide for cookie
+ local src = core.request.header(ctx, "Cookie")
+ local reset_val = remove_specified_cookie(src, conf.cookie)
+ core.request.set_header(ctx, "Cookie", reset_val)
+ end
+
return val
end
@@ -357,8 +410,8 @@ local function algorithm_handler(consumer, method_only)
end
end
-
function _M.rewrite(conf, ctx)
+ -- fetch token and hide credentials if necessary
local jwt_token, err = fetch_jwt_token(conf, ctx)
if not jwt_token then
core.log.info("failed to fetch JWT token: ", err)
diff --git a/docs/en/latest/plugins/basic-auth.md b/docs/en/latest/plugins/basic-auth.md
index 687748391..a55dd1467 100644
--- a/docs/en/latest/plugins/basic-auth.md
+++ b/docs/en/latest/plugins/basic-auth.md
@@ -46,7 +46,7 @@ For Route:
| Name | Type | Required | Default | Description |
|------------------|---------|----------|---------|------------------------------------------------------------------------|
-| hide_credentials | boolean | False | false | Set to true to pass the authorization request headers to the Upstream. |
+| hide_credentials | boolean | False | false | Set to true will not pass the authorization request headers to the Upstream. |
## Enabling the Plugin
diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md
index 00e2d307d..fffb01846 100644
--- a/docs/en/latest/plugins/jwt-auth.md
+++ b/docs/en/latest/plugins/jwt-auth.md
@@ -66,6 +66,7 @@ For Route:
| header | string | False | authorization | The header to get the token from. |
| query | string | False | jwt | The query string to get the token from. Lower priority than header. |
| cookie | string | False | jwt | The cookie to get the token from. Lower priority than query. |
+| hide_credentials | boolean | False | false | Set to true will not pass the authorization request of header\query\cookie to the Upstream.|
## API
diff --git a/docs/zh/latest/plugins/basic-auth.md b/docs/zh/latest/plugins/basic-auth.md
index b324e128c..dc2c597bb 100644
--- a/docs/zh/latest/plugins/basic-auth.md
+++ b/docs/zh/latest/plugins/basic-auth.md
@@ -46,7 +46,7 @@ Route 端:
| 名称 | 类型 | 必选项 | 默认值 | 描述 |
| ---------------- | ------- | ------ | ------ | --------------------------------------------------------------- |
-| hide_credentials | boolean | 否 | false | 该参数设置为 `true` 时,则会将 Authorization 请求头传递给 Upstream。|
+| hide_credentials | boolean | 否 | false | 该参数设置为 `true` 时,则不会将 Authorization 请求头传递给 Upstream。|
## 启用插件
diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md
index af7f1f1ca..b4582f642 100644
--- a/docs/zh/latest/plugins/jwt-auth.md
+++ b/docs/zh/latest/plugins/jwt-auth.md
@@ -66,6 +66,7 @@ Route 端:
| header | string | 否 | authorization | 设置我们从哪个 header 获取 token。 |
| query | string | 否 | jwt | 设置我们从哪个 query string 获取 token,优先级低于 header。 |
| cookie | string | 否 | jwt | 设置我们从哪个 cookie 获取 token,优先级低于 query。 |
+| hide_credentials | boolean | 否 | false | 该参数设置为 `true` 时,则不会将含有认证信息的 header\query\cookie 传递给 Upstream。|
## 接口
diff --git a/t/plugin/jwt-auth3.t b/t/plugin/jwt-auth3.t
new file mode 100755
index 000000000..6a9777132
--- /dev/null
+++ b/t/plugin/jwt-auth3.t
@@ -0,0 +1,302 @@
+#
+# 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.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+no_shuffle();
+
+add_block_preprocessor(sub {
+ my ($block) = @_;
+
+ if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+ $block->set_value("no_error_log", "[error]");
+ }
+
+ if (!defined $block->request) {
+ $block->set_value("request", "GET /t");
+ if (!$block->response_body) {
+ $block->set_value("response_body", "passed\n");
+ }
+ }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: add consumer with username and plugins
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/consumers',
+ ngx.HTTP_PUT,
+ [[{
+ "username": "jack",
+ "plugins": {
+ "jwt-auth": {
+ "key": "user-key",
+ "secret": "my-secret-key"
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 2: enable jwt auth plugin using admin api with custom parameter
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "header": "jwt-header",
+ "query": "jwt-query",
+ "cookie": "jwt-cookie",
+ "hide_credentials": false
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/echo"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 3: verify (in header) not hiding credentials
+--- request
+GET /echo
+--- more_headers
+jwt-header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+--- response_headers
+jwt-header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+
+
+
+=== TEST 4: verify (in cookie) not hiding credentials
+--- request
+GET /echo
+--- more_headers
+Cookie: jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+--- response_headers
+Cookie: jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+
+
+
+=== TEST 5: enable jwt auth plugin using admin api without hiding credentials
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "header": "jwt-header",
+ "query": "jwt-query",
+ "cookie": "jwt-cookie",
+ "hide_credentials": false
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/plugin_proxy_rewrite_args"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 6: verify (in query) without hiding credentials
+--- request
+GET /plugin_proxy_rewrite_args?foo=bar&hello=world&jwt-query=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+--- response_body
+uri: /plugin_proxy_rewrite_args
+foo: bar
+hello: world
+jwt-query: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+
+
+
+=== TEST 7: enable jwt auth plugin using admin api with hiding credentials
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "header": "jwt-header",
+ "query": "jwt-query",
+ "cookie": "jwt-cookie",
+ "hide_credentials": true
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/echo"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 8: verify (in header) with hiding credentials
+--- request
+GET /echo
+--- more_headers
+jwt-header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+--- response_headers
+!jwt-header
+
+
+
+=== TEST 9: enable jwt auth plugin using admin api with hiding credentials
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "header": "jwt-header",
+ "query": "jwt-query",
+ "cookie": "jwt-cookie",
+ "hide_credentials": true
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/plugin_proxy_rewrite_args"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 10: verify (in query) with hiding credentials
+--- request
+GET /plugin_proxy_rewrite_args?foo=bar&hello=world&jwt-query=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
+--- response_body
+uri: /plugin_proxy_rewrite_args
+foo: bar
+hello: world
+
+
+
+=== TEST 11: verify (in cookie) with hiding credentials
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "header": "jwt-header",
+ "query": "jwt-query",
+ "cookie": "jwt-cookie",
+ "hide_credentials": true
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "httpbin.org:80": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/get"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+
+
+
+=== TEST 12: verify (in cookie) with hiding credentials
+--- request
+GET /get
+--- more_headers
+Cookie: hello=world; jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs; foo=bar
+--- response_body eval
+qr/hello=world; foo=bar/