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/