You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/12/01 07:43:39 UTC

[apisix] branch master updated: feat: allow create consumers with multiple auth plugins (#2898)

This is an automated email from the ASF dual-hosted git repository.

membphis 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 a63b6b8  feat: allow create consumers with multiple auth plugins (#2898)
a63b6b8 is described below

commit a63b6b8c6e08d8af2327450dbdd1d73a92c27393
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Tue Dec 1 15:42:59 2020 +0800

    feat: allow create consumers with multiple auth plugins (#2898)
    
    Fix #2850
---
 apisix/admin/consumers.lua          |   3 -
 apisix/consumer.lua                 |   2 -
 apisix/init.lua                     |   4 +
 doc/admin-api.md                    |   4 +-
 doc/zh-cn/admin-api.md              |   4 +-
 t/config-center-yaml/stream-route.t |   1 -
 t/node/consumer-plugin.t            | 236 +++++++++++++++++++++++++++++-------
 7 files changed, 204 insertions(+), 50 deletions(-)

diff --git a/apisix/admin/consumers.lua b/apisix/admin/consumers.lua
index feaa708..14bd5cb 100644
--- a/apisix/admin/consumers.lua
+++ b/apisix/admin/consumers.lua
@@ -49,9 +49,6 @@ local function check_conf(conf)
             local plugin_obj = plugin.get(name)
             if plugin_obj.type == 'auth' then
                 count_auth_plugin = count_auth_plugin + 1
-                if count_auth_plugin > 1 then
-                    return nil, {error_msg = "only one auth plugin is allowed"}
-                end
             end
         end
 
diff --git a/apisix/consumer.lua b/apisix/consumer.lua
index a631544..3163e29 100644
--- a/apisix/consumer.lua
+++ b/apisix/consumer.lua
@@ -57,8 +57,6 @@ local function plugin_consumer()
                 new_consumer.auth_conf = config
                 core.log.info("consumer:", core.json.delay_encode(new_consumer))
                 core.table.insert(plugins[name].nodes, new_consumer)
-
-                break
             end
         end
 
diff --git a/apisix/init.lua b/apisix/init.lua
index 827a1fb..30b729e 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -516,6 +516,10 @@ function _M.http_access_phase()
                 api_ctx.consumer,
                 api_ctx
             )
+
+            core.log.info("find consumer ", api_ctx.consumer.username,
+                          ", config changed: ", changed)
+
             if changed then
                 core.table.clear(api_ctx.plugins)
                 api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
diff --git a/doc/admin-api.md b/doc/admin-api.md
index 51e70c5..ba23f7e 100644
--- a/doc/admin-api.md
+++ b/doc/admin-api.md
@@ -450,7 +450,7 @@ Config Example:
 }
 ```
 
-The binding authentication and authorization plug-in is a bit special. When it needs to be used in conjunction with the consumer, it needs to provide user name, password and other information; on the other hand, when it is bound with route / service, it does not require any parameters. Because at this time, it is based on the user request data to infer which consumer the user corresponds to.
+The binding authentication plug-in is a bit special. When it needs to be used in conjunction with the consumer, it needs to provide user name, password and other information; on the other hand, when it is bound with route / service, it does not require any parameters. Because at this time, it is based on the user request data to infer which consumer the user corresponds to.
 
 Example:
 
@@ -476,6 +476,8 @@ Date: Thu, 26 Dec 2019 08:17:49 GMT
 {"node":{"value":{"username":"jack","plugins":{"key-auth":{"key":"auth-one"},"limit-count":{"time_window":60,"count":2,"rejected_code":503,"key":"remote_addr","policy":"local"}}},"createdIndex":64,"key":"\/apisix\/consumers\/jack","modifiedIndex":64},"prevNode":{"value":"{\"username\":\"jack\",\"plugins\":{\"key-auth\":{\"key\":\"auth-one\"},\"limit-count\":{\"time_window\":60,\"count\":2,\"rejected_code\":503,\"key\":\"remote_addr\",\"policy\":\"local\"}}}","createdIndex":63,"key":"\/ap [...]
 ```
 
+Since `v2.2`, we can bind multiple authentication plugins to the same consumer.
+
 > Response Parameters
 
 Return response from etcd currently.
diff --git a/doc/zh-cn/admin-api.md b/doc/zh-cn/admin-api.md
index 5dae7d5..1e7cc4e 100644
--- a/doc/zh-cn/admin-api.md
+++ b/doc/zh-cn/admin-api.md
@@ -463,7 +463,7 @@ consumer 对象 json 配置内容:
 }
 ```
 
-绑定认证授权插件有些特别,当它需要与 consumer 联合使用时,需要提供用户名、密码等信息;另一方面,当它与 route/service 绑定时,是不需要任何参数的。因为这时候是根据用户请求数据来反向推出用户对应的是哪个 consumer
+绑定认证插件有些特别,当它需要与 consumer 联合使用时,需要提供用户名、密码等信息;另一方面,当它与 route/service 绑定时,是不需要任何参数的。因为这时候是根据用户请求数据来反向推出用户对应的是哪个 consumer
 
 示例:
 
@@ -491,6 +491,8 @@ Date: Thu, 26 Dec 2019 08:17:49 GMT
 {"node":{"value":{"username":"jack","plugins":{"key-auth":{"key":"auth-one"},"limit-count":{"time_window":60,"count":2,"rejected_code":503,"key":"remote_addr","policy":"local"}}},"createdIndex":64,"key":"\/apisix\/consumers\/jack","modifiedIndex":64},"prevNode":{"value":"{\"username\":\"jack\",\"plugins\":{\"key-auth\":{\"key\":\"auth-one\"},\"limit-count\":{\"time_window\":60,\"count\":2,\"rejected_code\":503,\"key\":\"remote_addr\",\"policy\":\"local\"}}}","createdIndex":63,"key":"\/ap [...]
 ```
 
+从 `v2.2` 版本之后,同一个 consumer 可以绑定多个认证插件。
+
 > 应答参数
 
 目前是直接返回与 etcd 交互后的结果。
diff --git a/t/config-center-yaml/stream-route.t b/t/config-center-yaml/stream-route.t
index 2046797..77d2dd9 100644
--- a/t/config-center-yaml/stream-route.t
+++ b/t/config-center-yaml/stream-route.t
@@ -48,7 +48,6 @@ run_tests();
 
 __DATA__
 
-
 === TEST 1: sanity
 --- apisix_yaml
 stream_routes:
diff --git a/t/node/consumer-plugin.t b/t/node/consumer-plugin.t
index 2748b01..2764080 100644
--- a/t/node/consumer-plugin.t
+++ b/t/node/consumer-plugin.t
@@ -151,47 +151,7 @@ apikey: auth-one
 
 
 
-=== TEST 6: two auth plugins (not allow)
---- 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": {
-                        "limit-count": {
-                            "count": 2,
-                            "time_window": 60,
-                            "rejected_code": 503,
-                            "key": "remote_addr"
-                        },
-                        "key-auth": {
-                            "key": "auth-one"
-                        },
-                        "jwt-auth": {
-                            "key": "auth-one"
-                        }
-                    }
-                }]]
-                )
-
-            ngx.status = code
-            ngx.print(body)
-        }
-    }
---- request
-GET /t
---- error_code: 400
---- response_body
-{"error_msg":"only one auth plugin is allowed"}
---- no_error_log
-[error]
-
-
-
-=== TEST 7: missing auth plugins (not allow)
+=== TEST 6: missing auth plugins (not allow)
 --- config
     location /t {
         content_by_lua_block {
@@ -225,7 +185,7 @@ GET /t
 
 
 
-=== TEST 8: use the new configuration after the consumer's configuration is updated
+=== TEST 7: use the new configuration after the consumer's configuration is updated
 --- config
     location /t {
         content_by_lua_block {
@@ -289,3 +249,195 @@ GET /t
 {"200":4,"503":1}
 --- no_error_log
 [error]
+
+
+
+=== TEST 8: consumer with multiple auth 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": "John_Doe",
+                    "desc": "new consumer",
+                    "plugins": {
+                            "key-auth": {
+                                "key": "consumer-plugin-John_Doe"
+                            },
+                            "hmac-auth": {
+                                "access_key": "my-access-key",
+                                "secret_key": "my-secret-key",
+                                "clock_skew": 1
+                            }
+                        }
+                }]],
+                [[{
+                    "node": {
+                        "value": {
+                            "username": "John_Doe",
+                            "desc": "new consumer",
+                            "plugins": {
+                                "key-auth": {
+                                    "key": "consumer-plugin-John_Doe"
+                                },
+                                "hmac-auth": {
+                                    "access_key": "my-access-key",
+                                    "secret_key": "my-secret-key",
+                                    "clock_skew": 1
+                                }
+                            }
+                        }
+                    },
+                    "action": "set"
+                }]]
+                )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 9: bind to routes
+--- 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": {
+                        "key-auth": {}
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.log(ngx.ERR, "failed to bind route 1")
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/routes/2',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "hmac-auth": {}
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/status"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: hit consumer, key-auth
+--- request
+GET /hello
+--- more_headers
+apikey: consumer-plugin-John_Doe
+--- response_body
+hello world
+--- error_log
+find consumer John_Doe
+--- no_error_log
+[error]
+
+
+
+=== TEST 11: hit consumer, hmac-auth
+--- config
+location /t {
+    content_by_lua_block {
+        local ngx_time = ngx.time
+        local ngx_http_time = ngx.http_time
+        local core = require("apisix.core")
+        local t = require("lib.test_admin")
+        local hmac = require("resty.hmac")
+        local ngx_encode_base64 = ngx.encode_base64
+
+        local secret_key = "my-secret-key"
+        local timestamp = ngx_time()
+        local gmt = ngx_http_time(timestamp)
+        local access_key = "my-access-key"
+        local custom_header_a = "asld$%dfasf"
+        local custom_header_b = "23879fmsldfk"
+
+        local signing_string = {
+            "GET",
+            "/status",
+            "",
+            access_key,
+            gmt,
+            "x-custom-header-a:" .. custom_header_a,
+            "x-custom-header-b:" .. custom_header_b
+        }
+        signing_string = core.table.concat(signing_string, "\n") .. "\n"
+        core.log.info("signing_string:", signing_string)
+
+        local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string)
+        core.log.info("signature:", ngx_encode_base64(signature))
+        local headers = {}
+        headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
+        headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
+        headers["Date"] = gmt
+        headers["X-HMAC-ACCESS-KEY"] = access_key
+        headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b"
+        headers["x-custom-header-a"] = custom_header_a
+        headers["x-custom-header-b"] = custom_header_b
+
+        local code, body = t.test('/status',
+            ngx.HTTP_GET,
+            nil,
+            nil,
+            headers
+        )
+
+        ngx.status = code
+        ngx.say(body)
+    }
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+find consumer John_Doe