From 9a3bd0d155adb5697cddc065ed5e2a987d750846 Mon Sep 17 00:00:00 2001 From: Madhawa Gunasekara Date: Mon, 23 Oct 2023 14:25:27 +0200 Subject: [PATCH] Add multi-auth plugin --- apisix/plugins/multi-auth.lua | 90 +++++++++++++ conf/config-default.yaml | 1 + t/admin/plugins.t | 1 + t/debug/debug-mode.t | 1 + t/plugin/multi-auth.t | 244 ++++++++++++++++++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 apisix/plugins/multi-auth.lua create mode 100644 t/plugin/multi-auth.t diff --git a/apisix/plugins/multi-auth.lua b/apisix/plugins/multi-auth.lua new file mode 100644 index 0000000000000..b6e0400606b66 --- /dev/null +++ b/apisix/plugins/multi-auth.lua @@ -0,0 +1,90 @@ +-- +-- 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 schema = { + type = "object", + title = "work with route or service object", + properties = { + auth_plugins = { type = "array", minItems = 2 }, + hide_credentials = { + type = "boolean", + default = false, + } + }, + 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) + core.log.info("multi-auth plugin access phase, conf: ", core.json.delay_encode(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 key, value in pairs(auth_plugin) do + local auth = require("apisix.plugins." .. key) + if auth == nil then + return false, key .. " plugin did not found" + else + if auth.type ~= 'auth' then + return false, key .. " 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 key, value in pairs(auth_plugin) do + local auth = require("apisix.plugins." .. key) + local auth_code, b = auth.rewrite(value, ctx) + status_code = auth_code + if auth_code == nil then + core.log.debug("Authentication is successful" .. key .. " plugin") + goto authenticated + else + core.log.warn("Authentication is failed" .. key .. " plugin, 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 09c459677fccc..251426df54c69 100755 --- a/conf/config-default.yaml +++ b/conf/config-default.yaml @@ -456,6 +456,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/t/admin/plugins.t b/t/admin/plugins.t index e7bcf09e97b46..68206bf3d76d4 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 diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t index 5d645e8e62ec9..f2bdbb2c9a752 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 0000000000000..fc4e492aa5425 --- /dev/null +++ b/t/plugin/multi-auth.t @@ -0,0 +1,244 @@ +# +# 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. +# +BEGIN { + $ENV{VAULT_TOKEN} = "root"; +} + +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 +--- request +GET /hello +--- more_headers +Authorization: Basic Zm9vOmJhcg== +--- response_body +hello world +--- error_log +find consumer foo + + + +=== TEST 5: verify key +--- 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 +--- 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 +{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin multi-auth err: property \"auth_plugins\" is required"} + + + +=== TEST 9: 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": {} + } + ] + } + }, + "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 +{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin multi-auth err: property \"auth_plugins\" validation failed: expect array to have at least 2 items"}