From 3ca68f67cebfa6a86b241eb46a021af5561e5da3 Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Fri, 22 Nov 2024 11:39:22 +0545 Subject: [PATCH] feat: plugins in multi-auth returns error instead of logging it --- apisix/plugins/basic-auth.lua | 5 + apisix/plugins/hmac-auth.lua | 13 +- apisix/plugins/jwt-auth.lua | 19 ++- apisix/plugins/multi-auth.lua | 15 ++- apisix/utils/auth.lua | 7 ++ t/plugin/multi-auth2.t | 223 ++++++++++++++++++++++++++++++++++ 6 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 apisix/utils/auth.lua create mode 100644 t/plugin/multi-auth2.t diff --git a/apisix/plugins/basic-auth.lua b/apisix/plugins/basic-auth.lua index c91ddee77a44..ec10c648a54f 100644 --- a/apisix/plugins/basic-auth.lua +++ b/apisix/plugins/basic-auth.lua @@ -18,6 +18,8 @@ local core = require("apisix.core") local ngx = ngx local ngx_re = require("ngx.re") local consumer = require("apisix.consumer") +local auth_utils = require("apisix.utils.auth") + local lrucache = core.lrucache.new({ ttl = 300, count = 512 }) @@ -132,6 +134,9 @@ function _M.rewrite(conf, ctx) local username, password, err = extract_auth_header(auth_header) if err then + if auth_utils.is_running_under_multi_auth(ctx) then + return 401, err + end core.log.warn(err) return 401, { message = "Invalid authorization in request" } end diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua index 88aafaf7e833..905318ed0dde 100644 --- a/apisix/plugins/hmac-auth.lua +++ b/apisix/plugins/hmac-auth.lua @@ -28,6 +28,7 @@ local ngx_encode_base64 = ngx.encode_base64 local plugin_name = "hmac-auth" local ALLOWED_ALGORITHMS = {"hmac-sha1", "hmac-sha256", "hmac-sha512"} local resty_sha256 = require("resty.sha256") +local auth_utils = require("apisix.utils.auth") local schema = { type = "object", @@ -324,7 +325,11 @@ end function _M.rewrite(conf, ctx) local params,err = retrieve_hmac_fields(ctx) if err then - core.log.warn("client request can't be validated: ", err) + err = "client request can't be validated: " .. err + if auth_utils.is_running_under_multi_auth(ctx) then + return 401, err + end + core.log.warn(err) return 401, {message = "client request can't be validated: " .. err} end @@ -333,7 +338,11 @@ function _M.rewrite(conf, ctx) end local validated_consumer, err = validate(ctx, conf, params) if not validated_consumer then - core.log.warn("client request can't be validated: ", err or "Invalid signature") + err = "client request can't be validated: " .. (err or "Invalid signature") + if auth_utils.is_running_under_multi_auth(ctx) then + return 401, err + end + core.log.warn(err) return 401, {message = "client request can't be validated"} end diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 740efcdc6b4e..23b92ce07386 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 new_tab = require ("table.new") +local auth_utils = require("apisix.utils.auth") local ngx_encode_base64 = ngx.encode_base64 local ngx_decode_base64 = ngx.decode_base64 @@ -236,7 +237,11 @@ function _M.rewrite(conf, ctx) local jwt_obj = jwt:load_jwt(jwt_token) core.log.info("jwt object: ", core.json.delay_encode(jwt_obj)) if not jwt_obj.valid then - core.log.warn("JWT token invalid: ", jwt_obj.reason) + if auth_utils.is_running_under_multi_auth(ctx) then + err = "JWT token invalid: " .. jwt_obj.reason + return 401, err + end + core.log.warn(err) return 401, {message = "JWT token invalid"} end @@ -260,7 +265,11 @@ function _M.rewrite(conf, ctx) local auth_secret, err = get_auth_secret(consumer.auth_conf) if not auth_secret then - core.log.error("failed to retrieve secrets, err: ", err) + err = "failed to retrieve secrets, err: " .. err + if auth_utils.is_running_under_multi_auth(ctx) then + return 401, err + end + core.log.error(err) return 503, {message = "failed to verify jwt"} end local claim_specs = jwt:get_default_validation_options(jwt_obj) @@ -270,7 +279,11 @@ function _M.rewrite(conf, ctx) core.log.info("jwt object: ", core.json.delay_encode(jwt_obj)) if not jwt_obj.verified then - core.log.warn("failed to verify jwt: ", jwt_obj.reason) + err = "failed to verify jwt: " .. jwt_obj.reason + if auth_utils.is_running_under_multi_auth(ctx) then + return 401, err + end + core.log.warn(err) return 401, {message = "failed to verify jwt"} end diff --git a/apisix/plugins/multi-auth.lua b/apisix/plugins/multi-auth.lua index 8557344741d4..14b1a2cfc19a 100644 --- a/apisix/plugins/multi-auth.lua +++ b/apisix/plugins/multi-auth.lua @@ -68,24 +68,33 @@ end function _M.rewrite(conf, ctx) local auth_plugins = conf.auth_plugins local status_code + local errors = {} + 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) + local auth_code, err = auth.rewrite(auth_plugin_conf, ctx) + if type(err) == "table" then + err = err.message -- compat + end + 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) + core.table.insert(errors, auth_plugin_name .. " failed to authenticate the request, code: " + .. auth_code .. ". error: " .. err) end end end :: authenticated :: if status_code ~= nil then + for _, error in ipairs(errors) do + core.log.warn(error) + end return 401, { message = "Authorization Failed" } end end diff --git a/apisix/utils/auth.lua b/apisix/utils/auth.lua new file mode 100644 index 000000000000..6292f6af72af --- /dev/null +++ b/apisix/utils/auth.lua @@ -0,0 +1,7 @@ +local _M = {} + +function _M.is_running_under_multi_auth(ctx) + return ctx._plugin_name == "multi-auth" +end + +return _M diff --git a/t/plugin/multi-auth2.t b/t/plugin/multi-auth2.t new file mode 100644 index 000000000000..b2e9ce724170 --- /dev/null +++ b/t/plugin/multi-auth2.t @@ -0,0 +1,223 @@ +# +# 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'; + +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" + }, + "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 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": {} + }, + { + "jwt-auth": {} + }, + { + "hmac-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 +--- response_body +passed + + + +=== TEST 5: invalid basic-auth credentials +--- request +GET /hello +--- more_headers +Authorization: Basic YmFyOmJhcgo= +--- error_code: 401 +--- response_body +{"message":"Authorization Failed"} +--- error_log +basic-auth failed to authenticate the request, code: 401. error: Invalid user authorization +jwt-auth failed to authenticate the request, code: 401. error: JWT token invalid: invalid jwt string +hmac-auth failed to authenticate the request, code: 401. error: client request can't be validated: Authorization header does not start with 'Signature' + + + +=== TEST 6: valid basic-auth creds +--- request +GET /hello +--- more_headers +Authorization: Basic Zm9vOmJhcg== +--- response_body +hello world +--- no_error_log +failed to authenticate the request + + + +=== TEST 7: missing hmac auth authorization header +--- request +GET /hello +--- error_code: 401 +--- response_body +{"message":"Authorization Failed"} +--- error_log +hmac-auth failed to authenticate the request, code: 401. error: client request can't be validated: missing Authorization header + + + +=== TEST 8: hmac auth missing algorithm +--- request +GET /hello +--- more_headers +Authorization: Signature keyId="my-access-key",headers="@request-target date" ,signature="asdf" +Date: Thu, 24 Sep 2020 06:39:52 GMT +--- error_code: 401 +--- response_body +{"message":"Authorization Failed"} +--- error_log +hmac-auth failed to authenticate the request, code: 401. error: client request can't be validated: algorithm missing + + + +=== 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: test with expired jwt token +--- request +GET /hello +--- error_code: 401 +--- response_body +{"message":"Authorization Failed"} +--- error_log +jwt-auth failed to authenticate the request, code: 401. error: failed to verify jwt: 'exp' claim expired at Tue, 23 Jul 2019 08:28:21 GMT +--- more_headers +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTU2Mzg3MDUwMX0.pPNVvh-TQsdDzorRwa-uuiLYiEBODscp9wv0cwD6c68 + + + +=== TEST 13: test with jwt token containing wrong signature +--- request +GET /hello +--- error_code: 401 +--- response_body +{"message":"Authorization Failed"} +--- error_log +jwt-auth failed to authenticate the request, code: 401. error: failed to verify jwt: signature mismatch: fNtFJnNnJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs +--- more_headers +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNnJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs + + + +=== TEST 14: verify jwt-auth +--- request +GET /hello +--- more_headers +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs +--- response_body +hello world +--- no_error_log +failed to authenticate the request