Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support anonymous consumer #11917

Merged
merged 7 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions apisix/consumer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ function _M.plugin(plugin_name)
return plugin_conf[plugin_name]
end

function _M.consumers_conf(plugin_name)
return _M.plugin(plugin_name)
end


-- attach chosen consumer to the ctx, used in auth plugin
function _M.attach_consumer(ctx, consumer, conf)
Expand Down Expand Up @@ -208,6 +212,20 @@ function _M.consumers_kv(plugin_name, consumer_conf, key_attr)
return consumers
end


function _M.find_consumer(plugin_name, key, key_value)
local consumer
local consumer_conf
consumer_conf = _M.plugin(plugin_name)
if not consumer_conf then
return nil, nil, "Missing related consumer"
end
local consumers = _M.consumers_kv(plugin_name, consumer_conf, key)
consumer = consumers[key_value]
return consumer, consumer_conf
end


local function check_consumer(consumer, key)
local data_valid
local err
Expand Down Expand Up @@ -251,5 +269,33 @@ function _M.init_worker()
end
end

local function get_anonymous_consumer_from_local_cache(name)
local anon_consumer_raw = consumers:get(name)

if not anon_consumer_raw or not anon_consumer_raw.value or
not anon_consumer_raw.value.id or not anon_consumer_raw.modifiedIndex then
return nil, nil, "failed to get anonymous consumer " .. name
end

-- make structure of anon_consumer similar to that of consumer_mod.consumers_kv's response
local anon_consumer = anon_consumer_raw.value
anon_consumer.consumer_name = anon_consumer_raw.value.id
anon_consumer.modifiedIndex = anon_consumer_raw.modifiedIndex

local anon_consumer_conf = {
conf_version = anon_consumer_raw.modifiedIndex
}

return anon_consumer, anon_consumer_conf
end


function _M.get_anonymous_consumer(name)
local anon_consumer, anon_consumer_conf, err
anon_consumer, anon_consumer_conf, err = get_anonymous_consumer_from_local_cache(name)

return anon_consumer, anon_consumer_conf, err
end


return _M
58 changes: 36 additions & 22 deletions apisix/plugins/basic-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local core = require("apisix.core")
local ngx = ngx
local ngx_re = require("ngx.re")
local consumer = require("apisix.consumer")
local schema_def = require("apisix.schema_def")
local auth_utils = require("apisix.utils.auth")

local lrucache = core.lrucache.new({
Expand All @@ -33,6 +34,7 @@ local schema = {
default = false,
}
},
anonymous_consumer = schema_def.anonymous_consumer_schema,
}

local consumer_schema = {
Expand Down Expand Up @@ -122,47 +124,59 @@ local function extract_auth_header(authorization)
end


function _M.rewrite(conf, ctx)
core.log.info("plugin access phase, conf: ", core.json.delay_encode(conf))

-- 1. extract authorization from header
local function find_consumer(ctx)
local auth_header = core.request.header(ctx, "Authorization")
if not auth_header then
core.response.set_header("WWW-Authenticate", "Basic realm='.'")
return 401, { message = "Missing authorization in request" }
return nil, nil, "Missing authorization in request"
end

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
return nil, nil, err
end
core.log.warn(err)
return 401, { message = "Invalid authorization in request" }
return nil, nil, "Invalid authorization in request"
end

-- 2. get user info from consumer plugin
local consumer_conf = consumer.plugin(plugin_name)
if not consumer_conf then
return 401, { message = "Missing related consumer" }
local cur_consumer, consumer_conf, err = consumer.find_consumer(plugin_name,
"username", username)
if not cur_consumer then
err = "failed to find user: " .. (err or "invalid user")
if auth_utils.is_running_under_multi_auth(ctx) then
return nil, nil, err
end
core.log.warn(err)
return nil, nil, "Invalid user authorization"
end

local consumers = consumer.consumers_kv(plugin_name, consumer_conf, "username")

-- 3. check user exists
local cur_consumer = consumers[username]
if not cur_consumer then
return 401, { message = "Invalid user authorization" }
if cur_consumer.auth_conf.password ~= password then
return nil, nil, "Invalid user authorization"
end
core.log.info("consumer: ", core.json.delay_encode(cur_consumer))

return cur_consumer, consumer_conf, err
end

-- 4. check the password is correct
if cur_consumer.auth_conf.password ~= password then
return 401, { message = "Invalid user authorization" }

function _M.rewrite(conf, ctx)
core.log.info("plugin access phase, conf: ", core.json.delay_encode(conf))

local cur_consumer, consumer_conf, err = find_consumer(ctx)
if not cur_consumer then
if not conf.anonymous_consumer then
return 401, { message = err }
end
cur_consumer, consumer_conf, err = consumer.get_anonymous_consumer(conf.anonymous_consumer)
if not cur_consumer then
err = "basic-auth failed to authenticate the request, code: 401. error: " .. err
core.log.error(err)
return 401, { message = "Invalid user authorization" }
end
end

-- 5. hide `Authorization` request header if `hide_credentials` is `true`
core.log.info("consumer: ", core.json.delay_encode(cur_consumer))

if conf.hide_credentials then
core.request.set_header(ctx, "Authorization", nil)
end
Expand Down
69 changes: 43 additions & 26 deletions apisix/plugins/hmac-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 schema_def = require("apisix.schema_def")
local auth_utils = require("apisix.utils.auth")

local schema = {
Expand Down Expand Up @@ -62,6 +63,7 @@ local schema = {
default = false,
},
hide_credentials = {type = "boolean", default = false},
anonymous_consumer = schema_def.anonymous_consumer_schema,
},
}

Expand Down Expand Up @@ -124,19 +126,13 @@ local function get_consumer(key_id)
return nil, "missing key_id"
end

local consumer_conf = consumer.plugin(plugin_name)
if not consumer_conf then
return nil, "Missing related consumer"
local cur_consumer, _, err = consumer.find_consumer(plugin_name, "key_id", key_id)
if not cur_consumer then
return nil, err or "Invalid key_id"
end
core.log.info("consumer: ", core.json.delay_encode(consumer, true))

local consumers = consumer.consumers_kv(plugin_name, consumer_conf, "key_id")
local consumer = consumers[key_id]
if not consumer then
return nil, "Invalid key_id"
end
core.log.info("consumer: ", core.json.delay_encode(consumer))

return consumer
return cur_consumer
end


Expand Down Expand Up @@ -187,6 +183,10 @@ end


local function validate(ctx, conf, params)
if not params then
return nil
end

if not params.keyId or not params.signature then
return nil, "keyId or signature missing"
end
Expand Down Expand Up @@ -321,34 +321,51 @@ local function retrieve_hmac_fields(ctx)
return hmac_params
end


function _M.rewrite(conf, ctx)
local function find_consumer(conf, ctx)
local params,err = retrieve_hmac_fields(ctx)
if err then
err = "client request can't be validated: " .. err
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
if not auth_utils.is_running_under_multi_auth(ctx) then
core.log.warn("client request can't be validated: ", err)
end
core.log.warn(err)
return 401, {message = err}
return nil, nil, "client request can't be validated: " .. err
end

if conf.hide_credentials then
core.request.set_header("Authorization", nil)
end
local validated_consumer, err = validate(ctx, conf, params)
if not validated_consumer then
err = "client request can't be validated: " .. (err or "Invalid signature")
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
return nil, nil, err
end
core.log.warn(err)
return 401, {message = "client request can't be validated"}
return nil, nil, "client request can't be validated"
end

local consumers_conf = consumer.consumers_conf(plugin_name)
return validated_consumer, consumers_conf, err
end


function _M.rewrite(conf, ctx)
local cur_consumer, consumers_conf, err = find_consumer(conf, ctx)
if not cur_consumer then
if not conf.anonymous_consumer then
return 401, { message = err }
end
cur_consumer, consumers_conf, err = consumer.get_anonymous_consumer(conf.anonymous_consumer)
if not cur_consumer then
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
end
core.log.error(err)
return 401, { message = "Invalid user authorization" }
end
end

if conf.hide_credentials then
core.request.set_header("Authorization", nil)
end

local consumer_conf = consumer.plugin(plugin_name)
consumer.attach_consumer(ctx, validated_consumer, consumer_conf)
core.log.info("hit hmac-auth rewrite")
consumer.attach_consumer(ctx, cur_consumer, consumers_conf)
end


Expand Down
52 changes: 34 additions & 18 deletions apisix/plugins/jwt-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ local table_insert = table.insert
local table_concat = table.concat
local ngx_re_gmatch = ngx.re.gmatch
local plugin_name = "jwt-auth"
local schema_def = require("apisix.schema_def")


local schema = {
Expand All @@ -55,6 +56,7 @@ local schema = {
default = "key",
minLength = 1,
},
anonymous_consumer = schema_def.anonymous_consumer_schema,
},
}

Expand Down Expand Up @@ -237,52 +239,46 @@ local function get_auth_secret(auth_conf)
end
end

function _M.rewrite(conf, ctx)
local function find_consumer(conf, ctx)
-- fetch token and hide credentials if necessary
local jwt_token, err = fetch_jwt_token(conf, ctx)
if not jwt_token then
core.log.info("failed to fetch JWT token: ", err)
return 401, {message = "Missing JWT token in request"}
return nil, nil, "Missing JWT token in request"
end

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
err = "JWT token invalid: " .. jwt_obj.reason
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
return nil, nil, err
end
core.log.warn(err)
return 401, {message = "JWT token invalid"}
return nil, nil, "JWT token invalid"
end

local key_claim_name = conf.key_claim_name
local user_key = jwt_obj.payload and jwt_obj.payload[key_claim_name]
if not user_key then
return 401, {message = "missing user key in JWT token"}
end

local consumer_conf = consumer_mod.plugin(plugin_name)
if not consumer_conf then
return 401, {message = "Missing related consumer"}
return nil, nil, "missing user key in JWT token"
end

local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf, "key")

local consumer = consumers[user_key]
local consumer, consumer_conf, err = consumer_mod.find_consumer(plugin_name, "key", user_key)
if not consumer then
return 401, {message = "Invalid user key in JWT token"}
core.log.warn("failed to find consumer: ", err or "invalid user key")
return nil, nil, "Invalid user key in JWT token"
end
core.log.info("consumer: ", core.json.delay_encode(consumer))

local auth_secret, err = get_auth_secret(consumer.auth_conf)
if not auth_secret then
err = "failed to retrieve secrets, err: " .. err
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
return nil, nil, err
end
core.log.error(err)
return 503, {message = "failed to verify jwt"}
return nil, nil, "failed to verify jwt"
end
local claim_specs = jwt:get_default_validation_options(jwt_obj)
claim_specs.lifetime_grace_period = consumer.auth_conf.lifetime_grace_period
Expand All @@ -293,12 +289,32 @@ function _M.rewrite(conf, ctx)
if not jwt_obj.verified then
err = "failed to verify jwt: " .. jwt_obj.reason
if auth_utils.is_running_under_multi_auth(ctx) then
return 401, err
return nil, nil, err
end
core.log.warn(err)
return 401, {message = "failed to verify jwt"}
return nil, nil, "failed to verify jwt"
end

return consumer, consumer_conf
end


function _M.rewrite(conf, ctx)
local consumer, consumer_conf, err = find_consumer(conf, ctx)
if not consumer then
if not conf.anonymous_consumer then
return 401, { message = err }
end
consumer, consumer_conf, err = consumer_mod.get_anonymous_consumer(conf.anonymous_consumer)
if not consumer then
err = "jwt-auth failed to authenticate the request, code: 401. error: " .. err
core.log.error(err)
return 401, { message = "Invalid user authorization"}
end
end

core.log.info("consumer: ", core.json.delay_encode(consumer))

consumer_mod.attach_consumer(ctx, consumer, consumer_conf)
core.log.info("hit jwt-auth rewrite")
end
Expand Down
Loading
Loading