Skip to content

Commit

Permalink
feat: implement consumer credential (#11601)
Browse files Browse the repository at this point in the history
  • Loading branch information
dspo authored Sep 26, 2024
1 parent d38d5b6 commit 263143d
Show file tree
Hide file tree
Showing 24 changed files with 2,428 additions and 80 deletions.
14 changes: 0 additions & 14 deletions apisix/admin/consumers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
local core = require("apisix.core")
local plugins = require("apisix.admin.plugins")
local resource = require("apisix.admin.resource")
local plugin = require("apisix.plugin")
local pairs = pairs


local function check_conf(username, conf, need_username, schema)
Expand All @@ -36,18 +34,6 @@ local function check_conf(username, conf, need_username, schema)
if not ok then
return nil, {error_msg = "invalid plugins configuration: " .. err}
end

local count_auth_plugin = 0
for name, conf in pairs(conf.plugins) do
local plugin_obj = plugin.get(name)
if plugin_obj.type == 'auth' then
count_auth_plugin = count_auth_plugin + 1
end
end

if count_auth_plugin == 0 then
return nil, {error_msg = "require one auth plugin"}
end
end

if conf.group_id then
Expand Down
74 changes: 74 additions & 0 deletions apisix/admin/credentials.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
--
-- 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 plugins = require("apisix.admin.plugins")
local plugin = require("apisix.plugin")
local resource = require("apisix.admin.resource")
local pairs = pairs

local function check_conf(_id, conf, _need_id, schema)
local ok, err = core.schema.check(schema, conf)
if not ok then
return nil, {error_msg = "invalid configuration: " .. err}
end

if conf.plugins then
ok, err = plugins.check_schema(conf.plugins, core.schema.TYPE_CONSUMER)
if not ok then
return nil, {error_msg = "invalid plugins configuration: " .. err}
end

for name, _ in pairs(conf.plugins) do
local plugin_obj = plugin.get(name)
if not plugin_obj then
return nil, {error_msg = "unknown plugin " .. name}
end
if plugin_obj.type ~= "auth" then
return nil, {error_msg = "only supports auth type plugins in consumer credential"}
end
end
end

return true, nil
end

-- get_credential_etcd_key is used to splice the credential's etcd key (without prefix)
-- from credential_id and sub_path.
-- Parameter credential_id is from the uri or payload; sub_path is in the form of
-- {consumer_name}/credentials or {consumer_name}/credentials/{credential_id}.
-- Only if GET credentials list, credential_id is nil, sub_path is like {consumer_name}/credentials,
-- so return value is /consumers/{consumer_name}/credentials.
-- In the other methods, credential_id is not nil, return value is
-- /consumers/{consumer_name}/credentials/{credential_id}.
local function get_credential_etcd_key(credential_id, _conf, sub_path, _args)
if credential_id then
local uri_segs = core.utils.split_uri(sub_path)
local consumer_name = uri_segs[1]
return "/consumers/" .. consumer_name .. "/credentials/" .. credential_id
end

return "/consumers/" .. sub_path
end

return resource.new({
name = "credentials",
kind = "credential",
schema = core.schema.credential,
checker = check_conf,
get_resource_etcd_key = get_credential_etcd_key,
unsupported_methods = {"post", "patch"}
})
9 changes: 8 additions & 1 deletion apisix/admin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ local resources = {
services = require("apisix.admin.services"),
upstreams = require("apisix.admin.upstreams"),
consumers = require("apisix.admin.consumers"),
credentials = require("apisix.admin.credentials"),
schema = require("apisix.admin.schema"),
ssls = require("apisix.admin.ssl"),
plugins = require("apisix.admin.plugins"),
Expand Down Expand Up @@ -184,6 +185,12 @@ local function run()
end
end

if seg_res == "consumers" and #uri_segs >= 6 and uri_segs[6] == "credentials" then
seg_sub_path = seg_id .. "/" .. seg_sub_path
seg_res = uri_segs[6]
seg_id = uri_segs[7]
end

local resource = resources[seg_res]
if not resource then
core.response.exit(404, {error_msg = "Unsupported resource type: ".. seg_res})
Expand Down Expand Up @@ -228,7 +235,7 @@ local function run()

if code then
if method == "get" and plugin.enable_data_encryption then
if seg_res == "consumers" then
if seg_res == "consumers" or seg_res == "credentials" then
utils.decrypt_params(plugin.decrypt_conf, data, core.schema.TYPE_CONSUMER)
elseif seg_res == "plugin_metadata" then
utils.decrypt_params(plugin.decrypt_conf, data, core.schema.TYPE_METADATA)
Expand Down
44 changes: 44 additions & 0 deletions apisix/admin/resource.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
local core = require("apisix.core")
local utils = require("apisix.admin.utils")
local apisix_ssl = require("apisix.ssl")
local apisix_consumer = require("apisix.consumer")
local setmetatable = setmetatable
local tostring = tostring
local ipairs = ipairs
Expand Down Expand Up @@ -157,6 +158,12 @@ function _M:get(id, conf, sub_path)
key = key .. "/" .. id
end

-- some resources(consumers) have sub resources(credentials),
-- the key format of sub resources will differ from the main resource
if self.get_resource_etcd_key then
key = self.get_resource_etcd_key(id, conf, sub_path)
end

local res, err = core.etcd.get(key, not id)
if not res then
core.log.error("failed to get ", self.kind, "[", key, "] from etcd: ", err)
Expand All @@ -170,6 +177,12 @@ function _M:get(id, conf, sub_path)
end
end

-- consumers etcd range response will include credentials, so need to filter out them
if self.name == "consumers" and res.body.list then
res.body.list = apisix_consumer.filter_consumers_list(res.body.list)
res.body.total = #res.body.list
end

utils.fix_count(res.body, id)
return res.status, res.body
end
Expand Down Expand Up @@ -249,6 +262,26 @@ function _M:put(id, conf, sub_path, args)

key = key .. "/" .. id

if self.get_resource_etcd_key then
key = self.get_resource_etcd_key(id, conf, sub_path, args)
end

if self.name == "credentials" then
local consumer_key = apisix_consumer.get_consumer_key_from_credential_key(key)
local res, err = core.etcd.get(consumer_key, false)
if not res then
return 503, {error_msg = err}
end
if res.status == 404 then
return res.status, {error_msg = "consumer not found"}
end
if res.status ~= 200 then
core.log.debug("failed to get consumer for the credential, credential key: ", key,
", consumer key: ", consumer_key, ", res.status: ", res.status)
return res.status, {error_msg = "failed to get the consumer"}
end
end

if self.name ~= "plugin_metadata" then
local ok, err = utils.inject_conf_with_prev_conf(self.kind, key, conf)
if not ok then
Expand Down Expand Up @@ -296,13 +329,24 @@ function _M:delete(id, conf, sub_path, uri_args)

key = key .. "/" .. id

if self.get_resource_etcd_key then
key = self.get_resource_etcd_key(id, conf, sub_path, uri_args)
end

if self.delete_checker and uri_args.force ~= "true" then
local code, err = self.delete_checker(id)
if err then
return code, err
end
end

if self.name == "consumers" then
local res, err = core.etcd.rmdir(key .. "/credentials/")
if not res then
return 503, {error_msg = err}
end
end

local res, err = core.etcd.delete(key)
if not res then
core.log.error("failed to delete ", self.kind, "[", key, "] in etcd: ", err)
Expand Down
Loading

0 comments on commit 263143d

Please sign in to comment.