From 3304c920abff6f5a34cee9596ec3436c4ea4d33c Mon Sep 17 00:00:00 2001 From: mguggi Date: Thu, 22 Feb 2024 11:44:00 +0100 Subject: [PATCH 1/8] Update the rockspec file so that the plugin can be built with our fork --- kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec b/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec index 91b1e2d..d391280 100644 --- a/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec +++ b/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec @@ -1,27 +1,27 @@ package = "kong-plugin-jwt-auth-rbac" version = "0.1.0-1" -local pluginName = package:match("^kong%-plugin%-(.+)$") supported_platforms = {"linux", "macosx"} + source = { - url = "git+https://github.com/prabago/kong-plugin-jwt-auth-rbac", - tag = "v0.1.0" + url = "git+https://github.com/infonova/kong-plugin-jwt-auth-rbac", + tag = "v0.4.0", } description = { summary = "A Kong plugin to authorize access based on a roles claim", - homepage = "https://github.com/prabago/kong-plugin-jwt-auth-rbac", - license = "MIT" + homepage = "https://github.com/infonova/kong-plugin-jwt-auth-rbac", + license = "MIT", } dependencies = { - "lua >= 5.1" + "lua >= 5.4", } build = { - type = "builtin", - modules = { - ["kong.plugins.jwt-auth-rbac.handler"] = "handler.lua", - ["kong.plugins.jwt-auth-rbac.schema"] = "schema.lua" - } + type = "builtin", + modules = { + ["kong.plugins.jwt-auth-rbac.handler"] = "handler.lua", + ["kong.plugins.jwt-auth-rbac.schema"] = "schema.lua", + }, } From 80548a593c12ea625e82c30adff8a1976ec25cf2 Mon Sep 17 00:00:00 2001 From: mguggi Date: Thu, 22 Feb 2024 11:40:31 +0100 Subject: [PATCH 2/8] Migrating from BasePlugin module accroding the documentation --- handler.lua | 265 +++++++++++++++++++++++++--------------------------- schema.lua | 66 +++++++++++-- 2 files changed, 184 insertions(+), 147 deletions(-) diff --git a/handler.lua b/handler.lua index f2c9ba9..43ffd11 100644 --- a/handler.lua +++ b/handler.lua @@ -1,6 +1,3 @@ -local BasePlugin = require "kong.plugins.base_plugin" - ---local responses = require "kong.tools.responses" local constants = require "kong.constants" local jwt_decoder = require "kong.plugins.jwt.jwt_parser" local responses = kong.response @@ -12,84 +9,76 @@ local ngx_log = ngx.log local policy_ALL = 'all' local policy_ANY = 'any' -local JWTAuthHandler = BasePlugin:extend() - - -JWTAuthHandler.PRIORITY = 950 -JWTAuthHandler.VERSION = "0.1.0" - - -function JWTAuthHandler:new() - JWTAuthHandler.super.new(self, "jwt-auth") -end +local JWTAuthHandler = { + VERSION = "0.4.0", + PRIORITY = 950 +} --- Filter a table -- @param filterFnc (function) filter function -- @return (table) the filtered table function table:filter(filterFnc) - local result = {} + local result = {} - for k, v in ipairs(self) do - if filterFnc(v, k, self) then - table.insert(result, v) - end - end + for k, v in ipairs(self) do + if filterFnc(v, k, self) then + table.insert(result, v) + end + end - return result + return result end --- Get index of a value at a table. -- @param any value -- @return any function table:find(value) - for k, v in ipairs(self) do - if v == value then - return k - end - end + for k, v in ipairs(self) do + if v == value then + return k + end + end end - --- checks wheter all given roles are also present in the claimed roles -- @param roles_to_check (array) an array of role names -- @param claimed_roles (table) list of roles claimed in JWT -- @return (boolean) true if all given roles are also in the claimed roles local function all_roles_in_roles_claim(roles_to_check, claimed_roles) - local result = false - local diff + local result = false + local diff - diff = table.filter(roles_to_check, function(value) - return not table.find(claimed_roles, value) - end) + diff = table.filter(roles_to_check, function(value) + return not table.find(claimed_roles, value) + end) - if #diff == 0 then - result = true - end + if #diff == 0 then + result = true + end - return result + return result end - --- checks whether a claimed role is part of a given list of roles. -- @param roles_to_check (array) an array of role names. -- @param claimed_roles (table) list of roles claimed in JWT -- @return (boolean) whether a claimed role is part of any of the given roles. local function role_in_roles_claim(roles_to_check, claimed_roles) - local result = false - for _, role_to_check in ipairs(roles_to_check) do - for _, role in ipairs(claimed_roles) do - if role == role_to_check then - result = true - break - end - end - if result then - break + local result = false + for _, role_to_check in ipairs(roles_to_check) do + for _, role in ipairs(claimed_roles) do + if role == role_to_check then + result = true + break + end + end + if result then + break + end end - end - - return result + + return result end --- split a string into substrings by reparator @@ -97,98 +86,100 @@ end -- @param sep (string) single character string (!) to separate on -- @return (table) list of separated parts local function split(str, sep) - local ret = {} - local n=1 - for w in str:gmatch("([^"..sep.."]*)") do - ret[n] = ret[n] or w:gsub("^%s*(.-)%s*$", "%1") -- strip whitespace - if w ~= "" then - ret[n] = w - n = n + 1 - end - end - return ret + local ret = {} + local n = 1 + for w in str:gmatch("([^" .. sep .. "]*)") do + ret[n] = ret[n] or w:gsub("^%s*(.-)%s*$", "%1") -- strip whitespace + if w ~= "" then + ret[n] = w + n = n + 1 + end + end + return ret end - function JWTAuthHandler:access(conf) - JWTAuthHandler.super.access(self) - - -- get the JWT from the Nginx context - local token = ngx.ctx.authenticated_jwt_token - if not token then - ngx_log(ngx_error, "[jwt-auth plugin] Cannot get JWT token, add the ", - "JWT plugin to be able to use the JWT-Auth plugin") - return kong.response.exit(403, { - message = "You cannot consume this service" - }) - --return responses.send_HTTP_FORBIDDEN("You cannot consume this service") - end - - -- decode token to get roles claim - local jwt, err = jwt_decoder:new(token) - if err then - -- return false, {status = 401, message = "Bad token; " .. tostring(err)} - return kong.response.exit(401, { message = "Bad token; " .. tostring(err)}) - end - - local msg_error_all = conf.msg_error_all - - local msg_error_any = conf.msg_error_any - local msg_error_not_roles_claimed = conf.msg_error_not_roles_claimed - local roles_cfg = conf.roles - local claims = jwt.claims - local roles = claims[conf.roles_claim_name] - local roles_table = {} - - -- check if no roles claimed.. - if not roles then - --return responses.send_HTTP_FORBIDDEN("You cannot consume this service") - return kong.response.exit(403, { - -- message = "You cannot consume this service" - message = msg_error_not_roles_claimed - }) - end - - - -- if the claim is a string (single role), make it a table - if type(roles) == "string" then - if string.find(roles, ",") then - roles_table = split(roles, ",") - - else - table.insert(roles_table, roles) - + JWTAuthHandler.super.access(self) + + -- get the JWT from the Nginx context + local token = ngx.ctx.authenticated_jwt_token + if not token then + ngx_log(ngx_error, "[jwt-auth plugin] Cannot get JWT token, add the ", + "JWT plugin to be able to use the JWT-Auth plugin") + return kong.response.exit(403, { + message = "You cannot consume this service" + }) + -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + end + + -- decode token to get roles claim + local jwt, err = jwt_decoder:new(token) + if err then + -- return false, {status = 401, message = "Bad token; " .. tostring(err)} + return kong.response.exit(401, { + message = "Bad token; " .. tostring(err) + }) + end + + local msg_error_all = conf.msg_error_all + + local msg_error_any = conf.msg_error_any + local msg_error_not_roles_claimed = conf.msg_error_not_roles_claimed + local roles_cfg = conf.roles + local claims = jwt.claims + local roles = claims[conf.roles_claim_name] + local roles_table = {} + + -- check if no roles claimed.. + if not roles then + -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + return kong.response.exit(403, { + -- message = "You cannot consume this service" + message = msg_error_not_roles_claimed + }) + end + + -- if the claim is a string (single role), make it a table + if type(roles) == "string" then + if string.find(roles, ",") then + roles_table = split(roles, ",") + + else + table.insert(roles_table, roles) + + end + roles = roles_table + end + if type(conf.roles) == "table" then + -- in declarative db-less setup the roles can be separated by a space + if string.find(conf.roles[1], " ") then + conf_roles_table = split(conf.roles[1], " ") + end + if string.find(conf.roles[1], ",") then + conf_roles_table = split(conf.roles[1], ",") + end + conf.roles = conf_roles_table + end + if conf.policy == policy_ANY and not role_in_roles_claim(conf.roles, roles) then + -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + return kong.response.exit(403, { + -- message = "You can't use these service" + detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. + "] and yours role are [" .. table.concat(roles, ", ") .. "]", + message = msg_error_any + + }) + end + + if conf.policy == policy_ALL and not all_roles_in_roles_claim(conf.roles, roles) then + -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + return kong.response.exit(403, { + -- message = "You can't use these service" + detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. + "] and yours role are [" .. table.concat(roles, ", ") .. "]", + message = msg_error_all + }) end - roles = roles_table - end - if type(conf.roles) == "table" then - -- in declarative db-less setup the roles can be separated by a space - if string.find(conf.roles[1], " ") then - conf_roles_table = split(conf.roles[1], " ") - end - if string.find(conf.roles[1], ",") then - conf_roles_table = split(conf.roles[1], ",") - end - conf.roles = conf_roles_table - end - if conf.policy == policy_ANY and not role_in_roles_claim(conf.roles, roles) then - --return responses.send_HTTP_FORBIDDEN("You cannot consume this service") - return kong.response.exit(403, { - -- message = "You can't use these service" - detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg,", ") .. "] and yours role are [" .. table.concat(roles,", ").."]", - message = msg_error_any - - }) - end - - if conf.policy == policy_ALL and not all_roles_in_roles_claim(conf.roles, roles) then - --return responses.send_HTTP_FORBIDDEN("You cannot consume this service") - return kong.response.exit(403, { - -- message = "You can't use these service" - detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg,", ") .. "] and yours role are [" .. table.concat(roles,", ").."]", - message = msg_error_all - }) - end end diff --git a/schema.lua b/schema.lua index 96fc634..2f7f01a 100644 --- a/schema.lua +++ b/schema.lua @@ -1,11 +1,57 @@ +local typedefs = require "kong.db.schema.typedefs" + return { - no_consumer = true, - fields = { - roles = {type = "array", default = {}}, - roles_claim_name = {type = "string", default = "roles"}, - msg_error_any = {type = "string", default = "To be able to use this service you must have at least one of the roles configured"}, - msg_error_all = {type = "string", default = "In order to use this service you must match all the roles configured with the associated ones in the JWT token"}, - msg_error_not_roles_claimed = {type = "string", default = "The claim roles are not informed in the JWT token"}, - policy = {type = "string", default = "any", enum = {"any", "all"}} - } -} \ No newline at end of file + name = "jwt-auth-rbac", + fields = { + { + -- this plugin will only be applied to services or routes + consumer = typedefs.no_consumer + }, + { + -- this plugin will only run within nginx http module + protocols = typedefs.protocols_http + }, + { + config = { + type = "record", + fields = {{ + roles = { + type = "array", + elements = { + type = "string" + } + } + }, { + roles_claim_name = { + type = "string", + default = "roles" + } + }, { + msg_error_any = { + type = "string", + default = "To be able to use this service you must have at least one of the roles configured" + } + }, { + msg_error_all = { + type = "string", + default = "In order to use this service you must match all the roles configured with the associated ones in the JWT token" + } + }, { + msg_error_not_roles_claimed = { + type = "string", + default = "The claim roles are not informed in the JWT token" + } + }, { + policy = { + type = "string", + default = "any", + one_of = {"any", "all"} + } + }} + } + }, + entity_checks = {{ + at_least_one_of = {"config.roles"} + }} + } +} From 0b263e58c9ea4ff90950ac5c5d5b541ede9c5181 Mon Sep 17 00:00:00 2001 From: mguggi Date: Sun, 25 Feb 2024 11:01:22 +0100 Subject: [PATCH 3/8] Fix syntax issue in the schema file --- schema.lua | 98 ++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/schema.lua b/schema.lua index 2f7f01a..357ba28 100644 --- a/schema.lua +++ b/schema.lua @@ -2,56 +2,52 @@ local typedefs = require "kong.db.schema.typedefs" return { name = "jwt-auth-rbac", - fields = { - { - -- this plugin will only be applied to services or routes - consumer = typedefs.no_consumer - }, - { - -- this plugin will only run within nginx http module - protocols = typedefs.protocols_http - }, - { - config = { - type = "record", - fields = {{ - roles = { - type = "array", - elements = { - type = "string" - } + fields = {{ + -- this plugin will only be applied to services or routes + consumer = typedefs.no_consumer + }, { + -- this plugin will only run within nginx http module + protocols = typedefs.protocols_http + }, { + config = { + type = "record", + fields = {{ + roles = { + type = "array", + elements = { + type = "string" } - }, { - roles_claim_name = { - type = "string", - default = "roles" - } - }, { - msg_error_any = { - type = "string", - default = "To be able to use this service you must have at least one of the roles configured" - } - }, { - msg_error_all = { - type = "string", - default = "In order to use this service you must match all the roles configured with the associated ones in the JWT token" - } - }, { - msg_error_not_roles_claimed = { - type = "string", - default = "The claim roles are not informed in the JWT token" - } - }, { - policy = { - type = "string", - default = "any", - one_of = {"any", "all"} - } - }} - } - }, - entity_checks = {{ - at_least_one_of = {"config.roles"} - }} - } + } + }, { + roles_claim_name = { + type = "string", + default = "roles" + } + }, { + msg_error_any = { + type = "string", + default = "To be able to use this service you must have at least one of the roles configured" + } + }, { + msg_error_all = { + type = "string", + default = "In order to use this service you must match all the roles configured with the associated ones in the JWT token" + } + }, { + msg_error_not_roles_claimed = { + type = "string", + default = "The claim roles are not informed in the JWT token" + } + }, { + policy = { + type = "string", + default = "any", + one_of = {"any", "all"} + } + }} + } + }}, + entity_checks = {{ + at_least_one_of = {"config.roles"} + }} } From bc377c65a1099b94858f4e10f0bc85fe01685a3a Mon Sep 17 00:00:00 2001 From: mguggi Date: Sun, 25 Feb 2024 11:02:18 +0100 Subject: [PATCH 4/8] Remove "old" binary file from repository --- kong-plugin-jwt-auth-rbac-0.1.0-1.src.rock | Bin 5394 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 kong-plugin-jwt-auth-rbac-0.1.0-1.src.rock diff --git a/kong-plugin-jwt-auth-rbac-0.1.0-1.src.rock b/kong-plugin-jwt-auth-rbac-0.1.0-1.src.rock deleted file mode 100644 index ecd5bb58395b61b2ef3141a24d8a5735ba20163a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5394 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 From c0b751f93f63ced2a5063a3bf4e273f7d9cb919a Mon Sep 17 00:00:00 2001 From: mguggi Date: Sun, 25 Feb 2024 18:18:54 +0100 Subject: [PATCH 5/8] Migrates from ngx to kong namespace --- handler.lua | 55 ++++++++++++++--------------------------------------- 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/handler.lua b/handler.lua index 43ffd11..e19fa43 100644 --- a/handler.lua +++ b/handler.lua @@ -1,10 +1,5 @@ local constants = require "kong.constants" local jwt_decoder = require "kong.plugins.jwt.jwt_parser" -local responses = kong.response - -local ngx_error = ngx.ERR -local ngx_debug = ngx.DEBUG -local ngx_log = ngx.log local policy_ALL = 'all' local policy_ANY = 'any' @@ -63,7 +58,6 @@ end -- @param roles_to_check (array) an array of role names. -- @param claimed_roles (table) list of roles claimed in JWT -- @return (boolean) whether a claimed role is part of any of the given roles. - local function role_in_roles_claim(roles_to_check, claimed_roles) local result = false for _, role_to_check in ipairs(roles_to_check) do @@ -99,26 +93,17 @@ local function split(str, sep) end function JWTAuthHandler:access(conf) - JWTAuthHandler.super.access(self) - - -- get the JWT from the Nginx context - local token = ngx.ctx.authenticated_jwt_token + -- get the JWT from the shared context + local token = kong.ctx.shared.authenticated_jwt_token if not token then - ngx_log(ngx_error, "[jwt-auth plugin] Cannot get JWT token, add the ", - "JWT plugin to be able to use the JWT-Auth plugin") - return kong.response.exit(403, { - message = "You cannot consume this service" - }) - -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + kong.log.err("The JWT token cannot be found, make sure that a JWT plugin is enabled") + return kong.response.exit(403, { message = "You cannot consume this service" }) end -- decode token to get roles claim local jwt, err = jwt_decoder:new(token) if err then - -- return false, {status = 401, message = "Bad token; " .. tostring(err)} - return kong.response.exit(401, { - message = "Bad token; " .. tostring(err) - }) + return kong.response.exit(401, { message = "Bad token; " .. tostring(err) }) end local msg_error_all = conf.msg_error_all @@ -129,28 +114,21 @@ function JWTAuthHandler:access(conf) local claims = jwt.claims local roles = claims[conf.roles_claim_name] local roles_table = {} - + -- check if no roles claimed.. if not roles then - -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") - return kong.response.exit(403, { - -- message = "You cannot consume this service" - message = msg_error_not_roles_claimed - }) + return kong.response.exit(403, { message = msg_error_not_roles_claimed }) end - -- if the claim is a string (single role), make it a table if type(roles) == "string" then if string.find(roles, ",") then roles_table = split(roles, ",") - else table.insert(roles_table, roles) - end roles = roles_table end - if type(conf.roles) == "table" then + if type(conf.roles) == "table" then -- in declarative db-less setup the roles can be separated by a space if string.find(conf.roles[1], " ") then conf_roles_table = split(conf.roles[1], " ") @@ -160,23 +138,18 @@ function JWTAuthHandler:access(conf) end conf.roles = conf_roles_table end - if conf.policy == policy_ANY and not role_in_roles_claim(conf.roles, roles) then - -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + + local err_detail_fmt = "The permitted role for this invocation is [%s] and yours role are [%s]" + if conf.policy == policy_ANY and not role_in_roles_claim(roles_cfg, roles) then return kong.response.exit(403, { - -- message = "You can't use these service" - detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. - "] and yours role are [" .. table.concat(roles, ", ") .. "]", + detail = string.format(err_detail_fmt, table.concat(roles_cfg, ", "), table.concat(roles, ", ")), message = msg_error_any - }) end - if conf.policy == policy_ALL and not all_roles_in_roles_claim(conf.roles, roles) then - -- return responses.send_HTTP_FORBIDDEN("You cannot consume this service") + if conf.policy == policy_ALL and not all_roles_in_roles_claim(roles_cfg, roles) then return kong.response.exit(403, { - -- message = "You can't use these service" - detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. - "] and yours role are [" .. table.concat(roles, ", ") .. "]", + detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. "] and yours role are [" .. table.concat(roles, ", ") .. "]", message = msg_error_all }) end From b9542d34cacdfee900d0c219fe30d8d230079577 Mon Sep 17 00:00:00 2001 From: mguggi Date: Sun, 25 Feb 2024 18:30:59 +0100 Subject: [PATCH 6/8] Upgrades accoring the plugin devel documentation --- kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec | 27 ----------------- kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec | 35 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec create mode 100644 kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec diff --git a/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec b/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec deleted file mode 100644 index d391280..0000000 --- a/kong-plugin-jwt-auth-rbac-0.1.0-1.rockspec +++ /dev/null @@ -1,27 +0,0 @@ -package = "kong-plugin-jwt-auth-rbac" -version = "0.1.0-1" - -supported_platforms = {"linux", "macosx"} - -source = { - url = "git+https://github.com/infonova/kong-plugin-jwt-auth-rbac", - tag = "v0.4.0", -} - -description = { - summary = "A Kong plugin to authorize access based on a roles claim", - homepage = "https://github.com/infonova/kong-plugin-jwt-auth-rbac", - license = "MIT", -} - -dependencies = { - "lua >= 5.4", -} - -build = { - type = "builtin", - modules = { - ["kong.plugins.jwt-auth-rbac.handler"] = "handler.lua", - ["kong.plugins.jwt-auth-rbac.schema"] = "schema.lua", - }, -} diff --git a/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec b/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec new file mode 100644 index 0000000..e38fed8 --- /dev/null +++ b/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec @@ -0,0 +1,35 @@ +local plugin_name = "jwt-auth-rbac" +local package_name = "kong-plugin-" .. plugin_name +local package_version = "0.4.0" +local rockspec_revision = "1" + +local github_account_name = "infonova" +local github_repo_name = package_name +local git_checkout = package_version == "dev" and "master" or package_version + +package = package_name +version = package_version .. "-" .. rockspec_revision +supported_platforms = {"linux", "macosx"} + +source = { + url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git", + branch = git_checkout, +} + +description = { + summary = "A Kong plugin to authorize access based on a roles claim", + homepage = "https://"..github_account_name..".github.io/"..github_repo_name, + license = "MIT", +} + +dependencies = { + "lua ~> 5", +} + +build = { + type = "builtin", + modules = { + ["kong.plugins.jwt-auth-rbac.handler"] = "handler.lua", + ["kong.plugins.jwt-auth-rbac.schema"] = "schema.lua", + }, +} From 1710cf6f1869cf001c35d1d3e340d677ee749555 Mon Sep 17 00:00:00 2001 From: mguggi Date: Mon, 26 Feb 2024 16:22:04 +0100 Subject: [PATCH 7/8] Support the parsed jwt token from the jwt-keycloack plugin --- handler.lua | 90 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/handler.lua b/handler.lua index e19fa43..2725e03 100644 --- a/handler.lua +++ b/handler.lua @@ -92,33 +92,36 @@ local function split(str, sep) return ret end -function JWTAuthHandler:access(conf) +local function do_authorization(conf) -- get the JWT from the shared context - local token = kong.ctx.shared.authenticated_jwt_token + local token = kong.ctx.shared.authenticated_jwt_token or kong.ctx.shared.jwt_keycloak_token if not token then kong.log.err("The JWT token cannot be found, make sure that a JWT plugin is enabled") - return kong.response.exit(403, { message = "You cannot consume this service" }) + return false, { status = 403, message = "You cannot consume this service" } end - - -- decode token to get roles claim - local jwt, err = jwt_decoder:new(token) - if err then - return kong.response.exit(401, { message = "Bad token; " .. tostring(err) }) + + local jwt, err = {} + if token then + if type(token) == "table" then + jwt = token + -- check if configured roles claim exists + if not jwt.claims[conf.roles_claim_name] then + return false, { status = 403, message = conf.msg_error_not_roles_claimed } + end + else + -- decode token to get roles claim + jwt, err = jwt_decoder:new(token) + if err then + return false, { status = 401, message = "Bad token; " .. tostring(err) } + end + end end - local msg_error_all = conf.msg_error_all + local roles = jwt.claims[conf.roles_claim_name] + local roles_cfg = conf.roles - local msg_error_any = conf.msg_error_any - local msg_error_not_roles_claimed = conf.msg_error_not_roles_claimed - local roles_cfg = conf.roles - local claims = jwt.claims - local roles = claims[conf.roles_claim_name] local roles_table = {} - - -- check if no roles claimed.. - if not roles then - return kong.response.exit(403, { message = msg_error_not_roles_claimed }) - end + -- if the claim is a string (single role), make it a table if type(roles) == "string" then if string.find(roles, ",") then @@ -128,32 +131,53 @@ function JWTAuthHandler:access(conf) end roles = roles_table end - if type(conf.roles) == "table" then + + -- if roles in the config is a string (single role), make it a table + if type(roles_cfg) == "table" then -- in declarative db-less setup the roles can be separated by a space - if string.find(conf.roles[1], " ") then - conf_roles_table = split(conf.roles[1], " ") + if string.find(roles_cfg[1], " ") then + conf_roles_table = split(roles_cfg[1], " ") end - if string.find(conf.roles[1], ",") then - conf_roles_table = split(conf.roles[1], ",") + if string.find(roles_cfg[1], ",") then + conf_roles_table = split(roles_cfg[1], ",") + end + if conf_roles_table then + roles_cfg = conf_roles_table end - conf.roles = conf_roles_table end local err_detail_fmt = "The permitted role for this invocation is [%s] and yours role are [%s]" if conf.policy == policy_ANY and not role_in_roles_claim(roles_cfg, roles) then - return kong.response.exit(403, { - detail = string.format(err_detail_fmt, table.concat(roles_cfg, ", "), table.concat(roles, ", ")), - message = msg_error_any - }) + return false, { + status = 403, + message = conf.msg_error_any, + detail = string.format(err_detail_fmt, table.concat(roles_cfg, ", "), table.concat(roles, ", ")) + } end if conf.policy == policy_ALL and not all_roles_in_roles_claim(roles_cfg, roles) then - return kong.response.exit(403, { - detail = "The permitted role for this invocation is [" .. table.concat(roles_cfg, ", ") .. "] and yours role are [" .. table.concat(roles, ", ") .. "]", - message = msg_error_all - }) + return false, { + status = 403, + message = conf.msg_error_all, + detail = string.format(err_detail_fmt, table.concat(roles_cfg, ", "), table.concat(roles, ", ")) + } end + kong.log.debug("The request was authorized using the JWT claim [" .. conf.roles_claim_name .. "]") + return true +end + +function JWTAuthHandler:access(conf) + local ok, err = do_authorization(conf) + if not ok then + if err then + if err.detail then + kong.log.err(err.detail) + end + return kong.response.exit(err.status, err.errors or { message = err.message }) + end + return kong.response.exit(500, { message = "An unexpected error occurred during authorization" }) + end end return JWTAuthHandler From 9b854287dd64251ab19b9289d2ad6771935c2093 Mon Sep 17 00:00:00 2001 From: mguggi Date: Mon, 26 Feb 2024 19:14:02 +0100 Subject: [PATCH 8/8] Change github account name to the origin name --- kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec b/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec index e38fed8..dba630d 100644 --- a/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec +++ b/kong-plugin-jwt-auth-rbac-0.4.0-1.rockspec @@ -3,7 +3,7 @@ local package_name = "kong-plugin-" .. plugin_name local package_version = "0.4.0" local rockspec_revision = "1" -local github_account_name = "infonova" +local github_account_name = "prabago" local github_repo_name = package_name local git_checkout = package_version == "dev" and "master" or package_version