This is an automated email from the ASF dual-hosted git repository.
wenming 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 5b7b1de1f feat: add multi-auth plugin (#10482)
5b7b1de1f is described below
commit 5b7b1de1feeb9f6b5a57c93b13617d7f6dfa68c8
Author: Madhawa Gunasekara <[email protected]>
AuthorDate: Mon Nov 20 02:36:02 2023 +0100
feat: add multi-auth plugin (#10482)
Co-authored-by: Madhawa Gunasekara <[email protected]>
---
apisix/plugins/multi-auth.lua | 89 +++++
conf/config-default.yaml | 1 +
docs/en/latest/config.json | 3 +-
.../latest/getting-started/key-authentication.md | 1 +
docs/en/latest/plugins/multi-auth.md | 144 +++++++
.../latest/getting-started/key-authentication.md | 1 +
t/admin/plugins.t | 3 +-
t/debug/debug-mode.t | 1 +
t/plugin/multi-auth.t | 414 +++++++++++++++++++++
9 files changed, 655 insertions(+), 2 deletions(-)
diff --git a/apisix/plugins/multi-auth.lua b/apisix/plugins/multi-auth.lua
new file mode 100644
index 000000000..5c6a82579
--- /dev/null
+++ b/apisix/plugins/multi-auth.lua
@@ -0,0 +1,89 @@
+--
+-- 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.
+--
+local core = require("apisix.core")
+local require = require
+local pairs = pairs
+
+local schema = {
+ type = "object",
+ title = "work with route or service object",
+ properties = {
+ auth_plugins = { type = "array", minItems = 2 }
+ },
+ required = { "auth_plugins" },
+}
+
+
+local plugin_name = "multi-auth"
+
+local _M = {
+ version = 0.1,
+ priority = 2600,
+ type = 'auth',
+ name = plugin_name,
+ schema = schema
+}
+
+function _M.check_schema(conf)
+ local ok, err = core.schema.check(schema, conf)
+ if not ok then
+ return false, err
+ end
+
+ local auth_plugins = conf.auth_plugins
+ for k, auth_plugin in pairs(auth_plugins) do
+ for auth_plugin_name, auth_plugin_conf in pairs(auth_plugin) do
+ local auth = require("apisix.plugins." .. auth_plugin_name)
+ if auth == nil then
+ return false, auth_plugin_name .. " plugin did not found"
+ else
+ if auth.type ~= 'auth' then
+ return false, auth_plugin_name .. " plugin is not
supported"
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+function _M.rewrite(conf, ctx)
+ local auth_plugins = conf.auth_plugins
+ local status_code
+ for k, auth_plugin in pairs(auth_plugins) do
+ for auth_plugin_name, auth_plugin_conf in pairs(auth_plugin) do
+ local auth = require("apisix.plugins." .. auth_plugin_name)
+ -- returns 401 HTTP status code if authentication failed,
otherwise returns nothing.
+ local auth_code = auth.rewrite(auth_plugin_conf, ctx)
+ status_code = auth_code
+ if auth_code == nil then
+ core.log.debug(auth_plugin_name .. " succeed to authenticate
the request")
+ goto authenticated
+ else
+ core.log.debug(auth_plugin_name .. " failed to authenticate
the request, code: "
+ .. auth_code)
+ end
+ end
+ end
+
+ :: authenticated ::
+ if status_code ~= nil then
+ return 401, { message = "Authorization Failed" }
+ end
+end
+
+return _M
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index 4e257f4d4..8717d1398 100755
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -457,6 +457,7 @@ plugins: # plugin list (sorted by
priority)
- uri-blocker # priority: 2900
- request-validation # priority: 2800
- chaitin-waf # priority: 2700
+ - multi-auth # priority: 2600
- openid-connect # priority: 2599
- cas-auth # priority: 2597
- authz-casbin # priority: 2560
diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json
index 20d44433f..3482517bd 100644
--- a/docs/en/latest/config.json
+++ b/docs/en/latest/config.json
@@ -111,7 +111,8 @@
"plugins/authz-casbin",
"plugins/ldap-auth",
"plugins/opa",
- "plugins/forward-auth"
+ "plugins/forward-auth",
+ "plugins/multi-auth"
]
},
{
diff --git a/docs/en/latest/getting-started/key-authentication.md
b/docs/en/latest/getting-started/key-authentication.md
index 725bcd352..f6e8a8383 100644
--- a/docs/en/latest/getting-started/key-authentication.md
+++ b/docs/en/latest/getting-started/key-authentication.md
@@ -28,6 +28,7 @@ APISIX has a flexible plugin extension system and a number of
existing plugins f
- [LDAP](https://apisix.apache.org/docs/apisix/plugins/ldap-auth/)
- [Open Policy Agent (OPA)](https://apisix.apache.org/docs/apisix/plugins/opa/)
- [Forward
Authentication](https://apisix.apache.org/docs/apisix/plugins/forward-auth/)
+- [Multiple
Authentications](https://apisix.apache.org/docs/apisix/plugins/multi-auth/)
In this tutorial, you will create a _consumer_ with _key authentication_, and
learn how to enable and disable key authentication.
diff --git a/docs/en/latest/plugins/multi-auth.md
b/docs/en/latest/plugins/multi-auth.md
new file mode 100644
index 000000000..703fac19a
--- /dev/null
+++ b/docs/en/latest/plugins/multi-auth.md
@@ -0,0 +1,144 @@
+---
+title: multi-auth
+keywords:
+ - Apache APISIX
+ - API Gateway
+ - Plugin
+ - Multi Auth
+ - multi-auth
+description: This document contains information about the Apache APISIX
multi-auth Plugin.
+---
+
+<!--
+#
+# 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.
+#
+-->
+
+## Description
+
+The `multi-auth` Plugin is used to add multiple authentication methods to a
Route or a Service. It supports plugins of type 'auth'. You can combine
different authentication methods using "or" relationship with `multi-auth`
plugin. If you want to use multiple methods in an "and" relationship, apply
specific authentication plugins directly to the route or service.
+
+## Attributes
+
+For Route:
+
+| Name | Type | Required | Default | Description
|
+|--------------|-------|----------|---------|-----------------------------------------------------------------------|
+| auth_plugins | array | True | - | Add supporting auth plugins
configuration. expects at least 2 plugins |
+
+## Enable Plugin
+
+To enable the Plugin, you have to create a Consumer object with multiple
authentication configurations:
+
+```shell
+curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "username": "foo",
+ "plugins": {
+ "basic-auth": {
+ "username": "foo",
+ "password": "bar"
+ },
+ "key-auth": {
+ "key": "auth-one"
+ }
+ }
+}'
+```
+
+You can also use the [APISIX Dashboard](/docs/dashboard/USER_GUIDE) to
complete the operation through a web UI.
+
+Once you have created a Consumer object, you can then configure a Route or a
Service to authenticate requests:
+
+```shell
+curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "methods": ["GET"],
+ "uri": "/hello",
+ "plugins": {
+ "multi-auth":{
+ "auth_plugins":[
+ {
+ "basic-auth":{ }
+ },
+ {
+ "key-auth":{
+ "query":"apikey",
+ "hide_credentials":true,
+ "header":"apikey"
+ }
+ }
+ ]
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ }
+}'
+```
+
+## Example usage
+
+After you have configured the Plugin as mentioned above, you can make a
request to the Route as shown below:
+
+request with basic-auth
+
+```shell
+curl -i -ufoo:bar http://127.0.0.1:9080/hello
+```
+
+request with key-auth
+
+```shell
+curl http://127.0.0.2:9080/hello -H 'apikey: auth-one' -i
+```
+
+```
+HTTP/1.1 200 OK
+...
+hello, world
+```
+
+If the request is not authorized, an error will be thrown:
+
+```shell
+HTTP/1.1 401 Unauthorized
+...
+{"message":"Authorization Failed"}
+```
+
+## Delete Plugin
+
+To remove the `multi-auth` Plugin, you can delete the corresponding JSON
configuration from the Plugin configuration. APISIX will automatically reload
and you do not have to restart for this to take effect.
+
+```shell
+curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "methods": ["GET"],
+ "uri": "/hello",
+ "plugins": {},
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ }
+}'
+```
diff --git a/docs/zh/latest/getting-started/key-authentication.md
b/docs/zh/latest/getting-started/key-authentication.md
index ddcce9029..022f020ae 100644
--- a/docs/zh/latest/getting-started/key-authentication.md
+++ b/docs/zh/latest/getting-started/key-authentication.md
@@ -28,6 +28,7 @@ APISIX 拥有灵活的插件扩展系统,目前有很多可用于用户身份
- [LDAP](https://apisix.apache.org/zh/docs/apisix/plugins/ldap-auth/)
- [Open Policy Agent
(OPA)](https://apisix.apache.org/zh/docs/apisix/plugins/opa/)
- [Forward
Authentication](https://apisix.apache.org/zh/docs/apisix/plugins/forward-auth/)
+- [Multiple
Authentications](https://apisix.apache.org/docs/apisix/plugins/multi-auth/)
本教程中,你将创建一个带有 _密钥验证_ 插件的 _消费者_,并学习如何启用和停用身份验证插件。
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index e7bcf09e9..e7b736fd9 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -75,6 +75,7 @@ csrf
uri-blocker
request-validation
chaitin-waf
+multi-auth
openid-connect
cas-auth
authz-casbin
@@ -311,7 +312,7 @@
qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"
}
}
--- response_body eval
-qr/\[\{"name":"wolf-rbac","priority":2555\},\{"name":"ldap-auth","priority":2540\},\{"name":"hmac-auth","priority":2530\},\{"name":"basic-auth","priority":2520\},\{"name":"jwt-auth","priority":2510\},\{"name":"key-auth","priority":2500\}\]/
+qr/\[\{"name":"multi-auth","priority":2600\},\{"name":"wolf-rbac","priority":2555\},\{"name":"ldap-auth","priority":2540\},\{"name":"hmac-auth","priority":2530\},\{"name":"basic-auth","priority":2520\},\{"name":"jwt-auth","priority":2510\},\{"name":"key-auth","priority":2500\}\]/
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index 5d645e8e6..f2bdbb2c9 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -53,6 +53,7 @@ loaded plugin and sort by priority: 3000 name: ip-restriction
loaded plugin and sort by priority: 2990 name: referer-restriction
loaded plugin and sort by priority: 2900 name: uri-blocker
loaded plugin and sort by priority: 2800 name: request-validation
+loaded plugin and sort by priority: 2600 name: multi-auth
loaded plugin and sort by priority: 2599 name: openid-connect
loaded plugin and sort by priority: 2555 name: wolf-rbac
loaded plugin and sort by priority: 2530 name: hmac-auth
diff --git a/t/plugin/multi-auth.t b/t/plugin/multi-auth.t
new file mode 100644
index 000000000..78ec19481
--- /dev/null
+++ b/t/plugin/multi-auth.t
@@ -0,0 +1,414 @@
+#
+# 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(2);
+no_long_string();
+no_root_location();
+no_shuffle();
+run_tests;
+
+__DATA__
+
+=== TEST 1: add consumer with basic-auth and key-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": "foo",
+ "plugins": {
+ "basic-auth": {
+ "username": "foo",
+ "password": "bar"
+ },
+ "key-auth": {
+ "key": "auth-one"
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: enable multi auth plugin using admin api
+--- 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": {
+ "multi-auth": {
+ "auth_plugins": [
+ {
+ "basic-auth": {}
+ },
+ {
+ "key-auth": {
+ "query": "apikey",
+ "hide_credentials": true,
+ "header": "apikey"
+ }
+ },
+ {
+ "jwt-auth": {
+ "cookie": "jwt",
+ "query": "jwt",
+ "hide_credentials": true,
+ "header": "authorization"
+ }
+ }
+ ]
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 3: verify, missing authorization
+--- request
+GET /hello
+--- error_code: 401
+--- response_body
+{"message":"Authorization Failed"}
+
+
+
+=== TEST 4: verify basic-auth
+--- request
+GET /hello
+--- more_headers
+Authorization: Basic Zm9vOmJhcg==
+--- response_body
+hello world
+--- error_log
+find consumer foo
+
+
+
+=== TEST 5: verify key-auth
+--- request
+GET /hello
+--- more_headers
+apikey: auth-one
+--- response_body
+hello world
+
+
+
+=== TEST 6: verify, invalid basic credentials
+--- request
+GET /hello
+--- more_headers
+Authorization: Basic YmFyOmJhcgo=
+--- error_code: 401
+--- response_body
+{"message":"Authorization Failed"}
+
+
+
+=== TEST 7: verify, invalid api key
+--- request
+GET /hello
+--- more_headers
+apikey: auth-two
+--- error_code: 401
+--- response_body
+{"message":"Authorization Failed"}
+
+
+
+=== TEST 8: enable multi auth plugin using admin api, without any auth_plugins
configuration
+--- 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": {
+ "multi-auth": { }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 400
+--- response_body_like eval
+qr/\{"error_msg":"failed to check the configuration of plugin multi-auth err:
property \\"auth_plugins\\" is required"\}/
+
+
+
+=== TEST 9: enable multi auth plugin using admin api, with auth_plugins
configuration but with one authorization plugin
+--- 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": {
+ "multi-auth": {
+ "auth_plugins": [
+ {
+ "basic-auth": {}
+ }
+ ]
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 400
+--- response_body_like eval
+qr/\{"error_msg":"failed to check the configuration of plugin multi-auth err:
property \\"auth_plugins\\" validation failed: expect array to have at least 2
items"\}/
+
+
+
+=== TEST 10: create public API route (jwt-auth sign)
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/2',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "public-api": {}
+ },
+ "uri": "/apisix/plugin/jwt/sign"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 11: add consumer with username and jwt-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": "jack",
+ "plugins": {
+ "jwt-auth": {
+ "key": "user-key",
+ "secret": "my-secret-key"
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 12: sign / verify jwt-auth
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key',
+ ngx.HTTP_GET
+ )
+
+ if code > 200 then
+ ngx.status = code
+ ngx.say(err)
+ return
+ end
+
+ local code, _, res = t('/hello?jwt=' .. sign,
+ ngx.HTTP_GET
+ )
+
+ ngx.status = code
+ ngx.print(res)
+ }
+ }
+--- request
+GET /t
+--- response_body
+hello world
+
+
+
+=== TEST 13: verify multi-auth with plugin config will cause the conf_version
change
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+
+ local code, err = t('/apisix/admin/plugin_configs/1',
+ ngx.HTTP_PUT,
+ [[{
+ "desc": "Multiple Authentication",
+ "plugins": {
+ "multi-auth": {
+ "auth_plugins": [
+ {
+ "basic-auth": {}
+ },
+ {
+ "key-auth": {
+ "query": "apikey",
+ "hide_credentials": true,
+ "header": "apikey"
+ }
+ },
+ {
+ "jwt-auth": {
+ "cookie": "jwt",
+ "query": "jwt",
+ "hide_credentials": true,
+ "header": "authorization"
+ }
+ }
+ ]
+ }
+ }
+ }]]
+ )
+ if code > 300 then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ local code, err = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "plugin_config_id": 1
+ }]]
+ )
+ if code > 300 then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+ ngx.sleep(0.1)
+
+ local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key',
+ ngx.HTTP_GET
+ )
+
+ if code > 200 then
+ ngx.status = code
+ ngx.say(err)
+ return
+ end
+
+ local code, _, res = t('/hello?jwt=' .. sign,
+ ngx.HTTP_GET
+ )
+
+ ngx.status = code
+ ngx.print(res)
+ }
+ }
+--- request
+GET /t
+--- response_body
+hello world