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

monkeydluffy 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 e6adec520 change(ua-restriction): allowlist and denylist can't be 
enabled at the same time (#9841)
e6adec520 is described below

commit e6adec5202785ce70311acf08c334302df7d6996
Author: Fucheng Jiang <[email protected]>
AuthorDate: Mon Jul 24 09:45:43 2023 +0800

    change(ua-restriction): allowlist and denylist can't be enabled at the same 
time (#9841)
---
 apisix/plugins/ua-restriction.lua        |  78 ++++---
 docs/en/latest/plugins/ua-restriction.md |   2 +-
 docs/zh/latest/plugins/ua-restriction.md |   2 +-
 t/plugin/ua-restriction.t                | 358 ++++++++++++++-----------------
 4 files changed, 215 insertions(+), 225 deletions(-)

diff --git a/apisix/plugins/ua-restriction.lua 
b/apisix/plugins/ua-restriction.lua
index ec74e7592..577dc2b67 100644
--- a/apisix/plugins/ua-restriction.lua
+++ b/apisix/plugins/ua-restriction.lua
@@ -22,11 +22,8 @@ local type = type
 local str_strip = stringx.strip
 local re_find = ngx.re.find
 
-local MATCH_NONE = 0
-local MATCH_ALLOW = 1
-local MATCH_DENY = 2
-
-local lrucache_useragent = core.lrucache.new({ ttl = 300, count = 4096 })
+local lrucache_allow = core.lrucache.new({ ttl = 300, count = 4096 })
+local lrucache_deny = core.lrucache.new({ ttl = 300, count = 4096 })
 
 local schema = {
     type = "object",
@@ -58,6 +55,10 @@ local schema = {
             default = "Not allowed"
         },
     },
+    oneOf = {
+        {required = {"allowlist"}},
+        {required = {"denylist"}}
+    }
 }
 
 local plugin_name = "ua-restriction"
@@ -69,27 +70,56 @@ local _M = {
     schema = schema,
 }
 
-local function match_user_agent(user_agent, conf)
-    user_agent = str_strip(user_agent)
-    if conf.allowlist then
-        for _, rule in ipairs(conf.allowlist) do
+local function check_with_allow_list(user_agents, allowlist)
+    local check = function (user_agent)
+        user_agent = str_strip(user_agent)
+
+        for _, rule in ipairs(allowlist) do
             if re_find(user_agent, rule, "jo") then
-                return MATCH_ALLOW
+                return true
             end
         end
+        return false
     end
 
-    if conf.denylist then
-        for _, rule in ipairs(conf.denylist) do
+    if type(user_agents) == "table" then
+        for _, v in ipairs(user_agents) do
+            if lrucache_allow(v, allowlist, check, v) then
+                return true
+            end
+        end
+        return false
+    else
+        return lrucache_allow(user_agents, allowlist, check, user_agents)
+    end
+end
+
+
+local function check_with_deny_list(user_agents, denylist)
+    local check = function (user_agent)
+        user_agent = str_strip(user_agent)
+
+        for _, rule in ipairs(denylist) do
             if re_find(user_agent, rule, "jo") then
-                return MATCH_DENY
+                return false
             end
         end
+        return true
     end
 
-    return MATCH_NONE
+    if type(user_agents) == "table" then
+        for _, v in ipairs(user_agents) do
+            if lrucache_deny(v, denylist, check, v) then
+                return false
+            end
+        end
+        return true
+    else
+        return lrucache_deny(user_agents, denylist, check, user_agents)
+    end
 end
 
+
 function _M.check_schema(conf)
     local ok, err = core.schema.check(schema, conf)
 
@@ -118,6 +148,7 @@ function _M.check_schema(conf)
     return true
 end
 
+
 function _M.access(conf, ctx)
     local user_agent = core.request.header(ctx, "User-Agent")
 
@@ -128,21 +159,16 @@ function _M.access(conf, ctx)
             return 403, { message = conf.message }
         end
     end
-    local match = MATCH_NONE
-    if type(user_agent) == "table" then
-        for _, v in ipairs(user_agent) do
-            if type(v) == "string" then
-                match = lrucache_useragent(v, conf, match_user_agent, v, conf)
-                if match > MATCH_ALLOW then
-                    break
-                end
-            end
-        end
+
+    local is_passed
+
+    if conf.allowlist then
+        is_passed = check_with_allow_list(user_agent, conf.allowlist)
     else
-        match = lrucache_useragent(user_agent, conf, match_user_agent, 
user_agent, conf)
+        is_passed = check_with_deny_list(user_agent, conf.denylist)
     end
 
-    if match > MATCH_ALLOW then
+    if not is_passed then
         return 403, { message = conf.message }
     end
 end
diff --git a/docs/en/latest/plugins/ua-restriction.md 
b/docs/en/latest/plugins/ua-restriction.md
index d9dce2ba0..538bf9950 100644
--- a/docs/en/latest/plugins/ua-restriction.md
+++ b/docs/en/latest/plugins/ua-restriction.md
@@ -43,7 +43,7 @@ A common scenario is to set crawler rules. `User-Agent` is 
the identity of the c
 
 :::note
 
-Both `allowlist` and `denylist` can be used on their own. If they are used 
together, the `allowlist` matches before the `denylist`.
+Both `allowlist` and `denylist` can't be used at the same time.
 
 :::
 
diff --git a/docs/zh/latest/plugins/ua-restriction.md 
b/docs/zh/latest/plugins/ua-restriction.md
index 3f31e092c..d8e21b62f 100644
--- a/docs/zh/latest/plugins/ua-restriction.md
+++ b/docs/zh/latest/plugins/ua-restriction.md
@@ -43,7 +43,7 @@ description: 本文介绍了 Apache APISIX ua-restriction 插件的使用方法
 
 :::note
 
-`allowlist` 和 `denylist` 可以同时启用。同时启用时,插件会根据 `User-Agent` 先检查 `allowlist`,再检查 
`denylist`。
+`allowlist` 和 `denylist` 不可以同时启用。
 
 :::
 
diff --git a/t/plugin/ua-restriction.t b/t/plugin/ua-restriction.t
index 0e8a9544b..56f07b39b 100644
--- a/t/plugin/ua-restriction.t
+++ b/t/plugin/ua-restriction.t
@@ -38,13 +38,12 @@ run_tests;
 
 __DATA__
 
-=== TEST 1: set allowlist, denylist, bypass_missing and user-defined message
+=== TEST 1: set both allowlist and denylist
 --- config
     location /t {
         content_by_lua_block {
             local plugin = require("apisix.plugins.ua-restriction")
             local conf = {
-               bypass_missing = true,
                allowlist = {
                     "my-bot1",
                     "my-bot2"
@@ -53,18 +52,18 @@ __DATA__
                     "my-bot1",
                     "my-bot2"
                },
-               message = "User-Agent Forbidden",
             }
             local ok, err = plugin.check_schema(conf)
             if not ok then
                 ngx.say(err)
+                return
             end
 
             ngx.say(require("toolkit.json").encode(conf))
         }
     }
 --- response_body
-{"allowlist":["my-bot1","my-bot2"],"bypass_missing":true,"denylist":["my-bot1","my-bot2"],"message":"User-Agent
 Forbidden"}
+value should match only one schema, but matches both schemas 1 and 2
 
 
 
@@ -216,88 +215,30 @@ User-Agent:my-bot2
 
 
 
-=== TEST 9: hit route and user-agent match denylist regex
+=== TEST 9: hit route and user-agent in denylist with reverse order multiple 
user-agent
 --- request
 GET /hello
 --- more_headers
-User-Agent:Baiduspider/3.0
+User-Agent:my-bot2
+User-Agent:my-bot1
 --- error_code: 403
 --- response_body
 {"message":"Not allowed"}
 
 
 
-=== TEST 10: hit route and user-agent not in denylist
---- request
-GET /hello
---- more_headers
-User-Agent:foo/bar
---- error_code: 200
---- response_body
-hello world
-
-
-
-=== TEST 11: set allowlist
---- 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,
-                 [[{
-                        "uri": "/hello",
-                        "upstream": {
-                            "type": "roundrobin",
-                            "nodes": {
-                                "127.0.0.1:1980": 1
-                            }
-                        },
-                        "plugins": {
-                            "ua-restriction": {
-                                 "allowlist": [
-                                     "my-bot1",
-                                     "(Baiduspider)/(\\d+)\\.(\\d+)"
-                                 ]
-                            }
-                        }
-                }]]
-                )
-
-            if code >= 300 then
-                ngx.status = code
-            end
-            ngx.say(body)
-        }
-    }
---- response_body
-passed
-
-
-
-=== TEST 12: hit route and user-agent in allowlist
---- request
-GET /hello
---- more_headers
-User-Agent:my-bot1
---- error_code: 200
---- response_body
-hello world
-
-
-
-=== TEST 13: hit route and user-agent match allowlist regex
+=== TEST 10: hit route and user-agent match denylist regex
 --- request
 GET /hello
 --- more_headers
 User-Agent:Baiduspider/3.0
---- error_code: 200
+--- error_code: 403
 --- response_body
-hello world
+{"message":"Not allowed"}
 
 
 
-=== TEST 14: hit route and user-agent not in allowlist
+=== TEST 11: hit route and user-agent not in denylist
 --- request
 GET /hello
 --- more_headers
@@ -308,7 +249,7 @@ hello world
 
 
 
-=== TEST 15: set config: user-agent in both allowlist and denylist
+=== TEST 12: set allowlist
 --- config
     location /t {
         content_by_lua_block {
@@ -326,11 +267,7 @@ hello world
                         "plugins": {
                             "ua-restriction": {
                                  "allowlist": [
-                                     "foo/bar",
-                                     "(Baiduspider)/(\\d+)\\.(\\d+)"
-                                 ],
-                                 "denylist": [
-                                     "foo/bar",
+                                     "my-bot1",
                                      "(Baiduspider)/(\\d+)\\.(\\d+)"
                                  ]
                             }
@@ -349,157 +286,62 @@ passed
 
 
 
-=== TEST 16: hit route and user-agent in both allowlist and denylist, 
pass(part 1)
+=== TEST 13: hit route and user-agent in allowlist
 --- request
 GET /hello
 --- more_headers
-User-Agent:foo/bar
+User-Agent:my-bot1
 --- error_code: 200
 --- response_body
 hello world
 
 
 
-=== TEST 17: hit route and user-agent in both allowlist and denylist, 
pass(part 2)
+=== TEST 14: hit route and user-agent match allowlist regex
 --- request
 GET /hello
 --- more_headers
-User-Agent:Baiduspider/1.0
+User-Agent:Baiduspider/3.0
 --- error_code: 200
 --- response_body
 hello world
 
 
 
-=== TEST 18: bypass_missing test, using default, reset conf(part1)
---- 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,
-                 [[{
-                        "uri": "/hello",
-                        "upstream": {
-                            "type": "roundrobin",
-                            "nodes": {
-                                "127.0.0.1:1980": 1
-                            }
-                        },
-                        "plugins": {
-                            "ua-restriction": {
-                            }
-                        }
-                }]]
-                )
-
-            if code >= 300 then
-                ngx.status = code
-            end
-            ngx.say(body)
-        }
-    }
---- response_body
-passed
-
-
-
-=== TEST 19: bypass_missing test, using default, send request without 
User-Agent(part2)
+=== TEST 15: hit route and user-agent not in allowlist
 --- request
 GET /hello
+--- more_headers
+User-Agent:foo/bar
 --- error_code: 403
 --- response_body
 {"message":"Not allowed"}
 
 
 
-=== TEST 20: bypass_missing test, set to true(part1)
---- 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,
-                 [[{
-                        "uri": "/hello",
-                        "upstream": {
-                            "type": "roundrobin",
-                            "nodes": {
-                                "127.0.0.1:1980": 1
-                            }
-                        },
-                        "plugins": {
-                            "ua-restriction": {
-                                "bypass_missing": true
-                            }
-                        }
-                }]]
-                )
-
-            if code >= 300 then
-                ngx.status = code
-            end
-            ngx.say(body)
-        }
-    }
---- response_body
-passed
-
-
-
-=== TEST 21: bypass_missing test, set to true, send request without 
User-Agent(part2)
+=== TEST 16:  hit route and user-agent in allowlist with multiple user-agent
 --- request
 GET /hello
---- error_code: 200
+--- more_headers
+User-Agent:foo/bar
+User-Agent:my-bot1
 --- response_body
 hello world
 
 
 
-=== TEST 22: bypass_missing test, set to false(part1)
---- 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,
-                 [[{
-                        "uri": "/hello",
-                        "upstream": {
-                            "type": "roundrobin",
-                            "nodes": {
-                                "127.0.0.1:1980": 1
-                            }
-                        },
-                        "plugins": {
-                            "ua-restriction": {
-                                "bypass_missing": false
-                            }
-                        }
-                }]]
-                )
-
-            if code >= 300 then
-                ngx.status = code
-            end
-            ngx.say(body)
-        }
-    }
---- response_body
-passed
-
-
-
-=== TEST 23: bypass_missing test, set to false, send request without 
User-Agent(part2)
+=== TEST 17:  hit route and user-agent in allowlist with reverse order 
multiple user-agent
 --- request
 GET /hello
---- error_code: 403
+--- more_headers
+User-Agent:my-bot1
+User-Agent:foo/bar
 --- response_body
-{"message":"Not allowed"}
+hello world
 
 
 
-=== TEST 24: message that do not reach the minimum range
+=== TEST 18: message that do not reach the minimum range
 --- config
     location /t {
         content_by_lua_block {
@@ -530,7 +372,7 @@ qr/string too short, expected at least 1, got 0/
 
 
 
-=== TEST 25: exceeds the maximum limit of message
+=== TEST 19: exceeds the maximum limit of message
 --- config
     location /t {
         content_by_lua_block {
@@ -568,7 +410,7 @@ qr/string too long, expected at most 1024, got 1025/
 
 
 
-=== TEST 26: set custom message
+=== TEST 20: set custom message
 --- config
     location /t {
         content_by_lua_block {
@@ -606,7 +448,7 @@ passed
 
 
 
-=== TEST 27: test custom message
+=== TEST 21: test custom message
 --- request
 GET /hello
 --- more_headers
@@ -617,7 +459,7 @@ User-Agent:Baiduspider/1.0
 
 
 
-=== TEST 28: test remove ua-restriction, add denylist(part 1)
+=== TEST 22: test remove ua-restriction, add denylist(part 1)
 --- config
     location /enable {
         content_by_lua_block {
@@ -656,7 +498,7 @@ passed
 
 
 
-=== TEST 29: test remove ua-restriction, fail(part 2)
+=== TEST 23: test remove ua-restriction, fail(part 2)
 --- request
 GET /hello
 --- more_headers
@@ -667,7 +509,7 @@ User-Agent:Baiduspider/1.0
 
 
 
-=== TEST 30: test remove ua-restriction, remove plugin(part 3)
+=== TEST 24: test remove ua-restriction, remove plugin(part 3)
 --- config
     location /disable {
         content_by_lua_block {
@@ -701,7 +543,7 @@ passed
 
 
 
-=== TEST 31: test remove ua-restriction, check spider User-Agent(part 4)
+=== TEST 25: test remove ua-restriction, check spider User-Agent(part 4)
 --- request
 GET /hello
 --- more_headers
@@ -711,7 +553,7 @@ hello world
 
 
 
-=== TEST 32: set disable=true
+=== TEST 26: set disable=true
 --- config
     location /t {
         content_by_lua_block {
@@ -744,7 +586,7 @@ passed
 
 
 
-=== TEST 33: the element in allowlist is null
+=== TEST 27: the element in allowlist is null
 --- config
     location /t {
         content_by_lua_block {
@@ -771,7 +613,7 @@ done
 
 
 
-=== TEST 34: the element in denylist is null
+=== TEST 28: the element in denylist is null
 --- config
     location /t {
         content_by_lua_block {
@@ -795,3 +637,125 @@ done
 --- response_body
 property "denylist" validation failed: wrong type: expected array, got table
 done
+
+
+
+=== TEST 29: test both allowlist and denylist are not exist
+--- 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,
+                 [[{
+                        "uri": "/hello",
+                        "upstream": {
+                            "type": "roundrobin",
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            }
+                        },
+                        "plugins": {
+                            "ua-restriction": {
+                            }
+                        }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.print(body)
+        }
+    }
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin ua-restriction err: 
value should match only one schema, but matches none"}
+
+
+
+=== TEST 30: test bypass_missing
+--- 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,
+                 [[{
+                        "uri": "/hello",
+                        "upstream": {
+                            "type": "roundrobin",
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            }
+                        },
+                        "plugins": {
+                            "ua-restriction": {
+                                "allowlist": [
+                                    "my-bot1"
+                                ]
+                            }
+                        }
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 31: hit
+--- request
+GET /hello
+--- error_code: 403
+--- response_body
+{"message":"Not allowed"}
+
+
+
+=== TEST 32: test bypass_missing with true
+--- 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,
+                 [[{
+                        "uri": "/hello",
+                        "upstream": {
+                            "type": "roundrobin",
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            }
+                        },
+                        "plugins": {
+                            "ua-restriction": {
+                                "bypass_missing": true,
+                                "denylist": [
+                                    "my-bot1"
+                                ]
+                            }
+                        }
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 33: hit
+--- request
+GET /hello
+--- response_body
+hello world

Reply via email to