local constants = require "kong.constants" local groups = require "kong.plugins.acl.groups" local kong_meta = require "kong.meta" local setmetatable = setmetatable local concat = table.concat local error = error local kong = kong local EMPTY = require("kong.tools.table").EMPTY local DENY = "DENY" local ALLOW = "ALLOW" local mt_cache = { __mode = "k" } local config_cache = setmetatable({}, mt_cache) local function get_to_be_blocked(config, groups, in_group) local to_be_blocked if config.type == DENY then to_be_blocked = in_group else to_be_blocked = not in_group end if to_be_blocked == false then -- we're allowed, convert 'false' to the header value, if needed -- if not needed, set dummy value to save mem for potential long strings to_be_blocked = config.hide_groups_header and "" or concat(groups, ", ") end return to_be_blocked end local ACLHandler = {} ACLHandler.PRIORITY = 950 ACLHandler.VERSION = kong_meta.version function ACLHandler:access(conf) -- simplify our plugins 'conf' table local config = config_cache[conf] if not config then local config_type = (conf.deny or EMPTY)[1] and DENY or ALLOW config = { hide_groups_header = conf.hide_groups_header, type = config_type, groups = config_type == DENY and conf.deny or conf.allow, cache = setmetatable({}, mt_cache), } config_cache[conf] = config end local to_be_blocked -- get the consumer/credentials local consumer_id = groups.get_current_consumer_id() if not consumer_id then local authenticated_groups = groups.get_authenticated_groups() if not authenticated_groups then if kong.client.get_credential() then return kong.response.error(403, "You cannot consume this service") end return kong.response.error(401) end local in_group = groups.group_in_groups(config.groups, authenticated_groups) to_be_blocked = get_to_be_blocked(config, authenticated_groups, in_group) else local credential = kong.client.get_credential() local authenticated_groups if (not credential) or conf.always_use_authenticated_groups then -- authenticated groups overrides anonymous groups authenticated_groups = groups.get_authenticated_groups() end if authenticated_groups then consumer_id = nil local in_group = groups.group_in_groups(config.groups, authenticated_groups) to_be_blocked = get_to_be_blocked(config, authenticated_groups, in_group) else -- get the consumer groups, since we need those as cache-keys to make sure -- we invalidate properly if they change local consumer_groups, err = groups.get_consumer_groups(consumer_id) if err then return error(err) end if not consumer_groups then if config.type == DENY then consumer_groups = EMPTY else if credential then return kong.response.error(403, "You cannot consume this service") end return kong.response.error(401) end end -- 'to_be_blocked' is either 'true' if it's to be blocked, or the header -- value if it is to be passed to_be_blocked = config.cache[consumer_groups] if to_be_blocked == nil then local in_group = groups.consumer_in_groups(config.groups, consumer_groups) to_be_blocked = get_to_be_blocked(config, consumer_groups, in_group) -- update cache config.cache[consumer_groups] = to_be_blocked end end end if to_be_blocked == true then -- NOTE: we only catch the boolean here! return kong.response.error(403, "You cannot consume this service") end if not conf.hide_groups_header and to_be_blocked then kong.service.request.set_header(consumer_id and constants.HEADERS.CONSUMER_GROUPS or constants.HEADERS.AUTHENTICATED_GROUPS, to_be_blocked) end end return ACLHandler