From 0549d8e51ea460c34e9f47aa111f65d63f9c8934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 02:12:09 +0800 Subject: [PATCH 01/11] implement support credentials --- apisix/admin/consumers.lua | 14 - apisix/admin/credentials.lua | 70 +++ apisix/admin/init.lua | 9 +- apisix/admin/resource.lua | 42 ++ apisix/consumer.lua | 114 ++++- apisix/core/config_etcd.lua | 4 +- apisix/core/etcd.lua | 67 ++- apisix/schema_def.lua | 14 + t/admin/credentials.t | 476 ++++++++++++++++++ t/node/consumer-plugin.t | 32 -- t/node/consumer-plugin2.t | 2 + t/node/credential-plugin-basic-auth.t | 121 +++++ .../credential-plugin-incremental-effective.t | 109 ++++ t/node/credential-plugin-jwt-auth.t | 121 +++++ t/node/credential-plugin-key-auth.t | 121 +++++ t/node/credential-plugin-multi-credentials.t | 220 ++++++++ t/node/credential-plugin-set-request-header.t | 229 +++++++++ ...credential-plugin-work-with-other-plugin.t | 155 ++++++ 18 files changed, 1846 insertions(+), 74 deletions(-) create mode 100644 apisix/admin/credentials.lua create mode 100644 t/admin/credentials.t create mode 100644 t/node/credential-plugin-basic-auth.t create mode 100644 t/node/credential-plugin-incremental-effective.t create mode 100644 t/node/credential-plugin-jwt-auth.t create mode 100644 t/node/credential-plugin-key-auth.t create mode 100644 t/node/credential-plugin-multi-credentials.t create mode 100644 t/node/credential-plugin-set-request-header.t create mode 100644 t/node/credential-plugin-work-with-other-plugin.t diff --git a/apisix/admin/consumers.lua b/apisix/admin/consumers.lua index 84485231f830..e02789069c64 100644 --- a/apisix/admin/consumers.lua +++ b/apisix/admin/consumers.lua @@ -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) @@ -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 diff --git a/apisix/admin/credentials.lua b/apisix/admin/credentials.lua new file mode 100644 index 000000000000..6dd6951896dc --- /dev/null +++ b/apisix/admin/credentials.lua @@ -0,0 +1,70 @@ +-- +-- 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"} +}) diff --git a/apisix/admin/init.lua b/apisix/admin/init.lua index 4b4c7ec3add9..d02de6667f23 100644 --- a/apisix/admin/init.lua +++ b/apisix/admin/init.lua @@ -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"), @@ -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}) @@ -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) diff --git a/apisix/admin/resource.lua b/apisix/admin/resource.lua index 2a87716027e1..5b12e04dd239 100644 --- a/apisix/admin/resource.lua +++ b/apisix/admin/resource.lua @@ -157,6 +157,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) @@ -170,6 +176,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 @@ -249,6 +261,25 @@ 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 @@ -296,6 +327,10 @@ 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 @@ -303,6 +338,13 @@ function _M:delete(id, conf, sub_path, uri_args) 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) diff --git a/apisix/consumer.lua b/apisix/consumer.lua index 6bbd6ca186c6..653ca6a17603 100644 --- a/apisix/consumer.lua +++ b/apisix/consumer.lua @@ -33,6 +33,50 @@ local lrucache = core.lrucache.new({ ttl = 300, count = 512 }) +local function remove_etcd_prefix(key) + local prefix = "" + local local_conf = config_local.local_conf() + if local_conf.etcd and local_conf.etcd.prefix then + prefix = local_conf.etcd.prefix + end + return string_sub(key, #prefix + 1) +end + +-- /{etcd.prefix}/consumers/{consumer_name}/credentials/{credential_id} --> {consumer_name} +local function get_consumer_name_from_credential_etcd_key(key) + local uri_segs = core.utils.split_uri(remove_etcd_prefix(key)) + return uri_segs[3] +end + +local function is_credential_etcd_key(key) + if not key then + return false + end + + local uri_segs = core.utils.split_uri(remove_etcd_prefix(key)) + return uri_segs[2] == "consumers" and uri_segs[4] == "credentials" +end + +local function get_credential_id_from_etcd_key(key) + local uri_segs = core.utils.split_uri(remove_etcd_prefix(key)) + return uri_segs[5] +end + +local function filter_consumers_list(data_list) + if #data_list == 0 then + return data_list + end + + local list = {} + for _, item in ipairs(data_list) do + if not (type(item) == "table" and is_credential_etcd_key(item.key)) then + core.table.insert(list, item) + end + end + + return list +end + local function plugin_consumer() local plugins = {} @@ -40,12 +84,15 @@ local function plugin_consumer() return plugins end - for _, consumer in ipairs(consumers.values) do - if type(consumer) ~= "table" then + -- consumers.values is the list that got from etcd by prefix key {etcd_prefix}/consumers. + -- So it contains consumers and credentials. + -- The val in the for-loop may be a Consumer or a Credential. + for _, val in ipairs(consumers.values) do + if type(val) ~= "table" then goto CONTINUE end - for name, config in pairs(consumer.value.plugins or {}) do + for name, config in pairs(val.value.plugins or {}) do local plugin_obj = plugin.get(name) if plugin_obj and plugin_obj.type == "auth" then if not plugins[name] then @@ -55,14 +102,38 @@ local function plugin_consumer() } end - local new_consumer = core.table.clone(consumer.value) + -- if the val is a Consumer, clone it to the local consumer; + -- if the val is a Credential, to get the Consumer by consumer_name and then clone it to the local consumer. + local consumer + if is_credential_etcd_key(val.key) then + local consumer_name = get_consumer_name_from_credential_etcd_key(val.key) + local the_consumer = consumers:get(consumer_name) + if the_consumer and the_consumer.value then + consumer = core.table.clone(the_consumer.value) + consumer.credential_id = get_credential_id_from_etcd_key(val.key) + else + -- Normally wouldn't get here: it should belong to a consumer for any credential. + core.log.error("failed to get the consumer for the credential, a wild credential has appeared! credential key: ", + val.key, ", consumer name: ", consumer_name) + goto CONTINUE + end + else + consumer = core.table.clone(val.value) + end + + -- if the consumer has labels, set the field custom_id to it. + -- the custom_id is used to set in the request headers to the upstream. + if consumer.labels then + consumer.custom_id = consumer.labels["custom_id"] + end + -- Note: the id here is the key of consumer data, which -- is 'username' field in admin - new_consumer.consumer_name = new_consumer.id - new_consumer.auth_conf = config - new_consumer.modifiedIndex = consumer.modifiedIndex - core.log.info("consumer:", core.json.delay_encode(new_consumer)) - core.table.insert(plugins[name].nodes, new_consumer) + consumer.consumer_name = consumer.id + consumer.auth_conf = config + consumer.modifiedIndex = val.modifiedIndex + core.log.info("consumer:", core.json.delay_encode(consumer)) + core.table.insert(plugins[name].nodes, consumer) end end @@ -72,6 +143,12 @@ local function plugin_consumer() return plugins end +_M.filter_consumers_list = filter_consumers_list + +function _M.get_consumer_key_from_credential_key(key) + local uri_segs = core.utils.split_uri(key) + return "/consumers/" .. uri_segs[3] +end function _M.plugin(plugin_name) local plugin_conf = core.lrucache.global("/consumers", @@ -86,6 +163,10 @@ function _M.attach_consumer(ctx, consumer, conf) ctx.consumer_name = consumer.consumer_name ctx.consumer_group_id = consumer.group_id ctx.consumer_ver = conf.conf_version + + core.request.set_header(ctx, "X-Consumer-Username", consumer.username) + core.request.set_header(ctx, "X-Credential-Identifier", consumer.credential_id) + core.request.set_header(ctx, "X-Consumer-Custom-ID", consumer.custom_id) end @@ -94,7 +175,7 @@ function _M.consumers() return nil, nil end - return consumers.values, consumers.conf_version + return filter_consumers_list(consumers.values), consumers.conf_version end @@ -120,8 +201,18 @@ function _M.consumers_kv(plugin_name, consumer_conf, key_attr) return consumers end +local function check_consumer(consumer, key) + local data_valid + local err + if is_credential_etcd_key(key) then + data_valid, err = check_schema(core.schema.credential, consumer) + else + data_valid, err = check_schema(core.schema.consumer, consumer) + end + if not data_valid then + return data_valid, err + end -local function check_consumer(consumer) return plugin_checker(consumer, core.schema.TYPE_CONSUMER) end @@ -140,7 +231,6 @@ function _M.init_worker() local err local cfg = { automatic = true, - item_schema = core.schema.consumer, checker = check_consumer, } if core.config.type ~= "etcd" then diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua index 6e06a368f25d..5734106e7a7b 100644 --- a/apisix/core/config_etcd.lua +++ b/apisix/core/config_etcd.lua @@ -538,7 +538,7 @@ local function load_full_data(self, dir_res, headers) end if data_valid and self.checker then - data_valid, err = self.checker(item.value) + data_valid, err = self.checker(item.value, item.key) if not data_valid then log.error("failed to check item data of [", self.key, "] err:", err, " ,val: ", json.delay_encode(item.value)) @@ -674,7 +674,7 @@ local function sync_data(self) end if data_valid and res.value and self.checker then - data_valid, err = self.checker(res.value) + data_valid, err = self.checker(res.value, res.key) if not data_valid then log.error("failed to check item data of [", self.key, "] err:", err, " ,val: ", json.delay_encode(res.value)) diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua index a537c8840a7a..b9060bc5f2aa 100644 --- a/apisix/core/etcd.lua +++ b/apisix/core/etcd.lua @@ -154,6 +154,22 @@ local function kvs_to_node(kvs) end _M.kvs_to_node = kvs_to_node +local function kvs_to_nodes(res, exclude_dir) + res.body.node.dir = true + res.body.node.nodes = setmetatable({}, array_mt) + if exclude_dir then + for i=2, #res.body.kvs do + res.body.node.nodes[i-1] = kvs_to_node(res.body.kvs[i]) + end + else + for i=1, #res.body.kvs do + res.body.node.nodes[i] = kvs_to_node(res.body.kvs[i]) + end + end + return res +end + + local function not_found(res) res.body.message = "Key not found" res.reason = "Not found" @@ -201,21 +217,22 @@ function _M.get_format(res, real_key, is_dir, formatter) else -- In etcd v2, the direct key asked for is `node`, others which under this dir are `nodes` -- While in v3, this structure is flatten and all keys related the key asked for are `kvs` - res.body.node = { - key = real_key, - dir = true, - nodes = setmetatable({}, array_mt) - } - local kvs = res.body.kvs - if #kvs >= 1 and not kvs[1].value then - res.body.node.createdIndex = tonumber(kvs[1].create_revision) - res.body.node.modifiedIndex = tonumber(kvs[1].mod_revision) - for i=2, #kvs do - res.body.node.nodes[i-1] = kvs_to_node(kvs[i]) + res.body.node = kvs_to_node(res.body.kvs[1]) + -- we have a init_dir (for etcd v2) value that can't be deserialized with json, + -- but we don't put init_dir for new resource type like consumer credential + if not res.body.kvs[1].value then + -- remove last "/" when necessary + if string.byte(res.body.node.key, -1) == 47 then + res.body.node.key = string.sub(res.body.node.key, 1, #res.body.node.key-1) end + res = kvs_to_nodes(res, true) else - for i=1, #kvs do - res.body.node.nodes[i] = kvs_to_node(kvs[i]) + -- get dir key by remove last part of node key, + -- for example: /apisix/consumers/jack -> /apisix/consumers + local last_slash_index = string.find(res.body.node.key, "/[^/]*$") + if last_slash_index then + res.body.node.key = string.sub(res.body.node.key, 1, last_slash_index-1) + res = kvs_to_nodes(res, false) end end end @@ -484,6 +501,30 @@ function _M.delete(key) return res, nil end +function _M.rmdir(key, opts) + local etcd_cli, prefix, err = get_etcd_cli() + if not etcd_cli then + return nil, err + end + + local res, err = etcd_cli:rmdir(prefix .. key, opts) + if not res then + return nil, err + end + + res.headers["X-Etcd-Index"] = res.body.header.revision + + if not res.body.deleted then + return not_found(res), nil + end + + v3_adapter.to_v3(res.body, "delete") + res.body.node = {} + res.body.key = prefix .. key + + return res, nil +end + --- -- Get etcd cluster and server version. -- diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index 2e289db58a0c..b4241ff2d9f5 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -710,6 +710,20 @@ _M.consumer = { additionalProperties = false, } +_M.credential = { + type = "object", + properties = { + id = id_schema, + plugins = { + type = "object", + maxProperties = 1, + }, + labels = labels_def, + create_time = timestamp_def, + update_time = timestamp_def, + desc = desc_def, + }, +} _M.upstream = upstream_schema diff --git a/t/admin/credentials.t b/t/admin/credentials.t new file mode 100644 index 000000000000..3e58fbbcd368 --- /dev/null +++ b/t/admin/credentials.t @@ -0,0 +1,476 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +run_tests; + +__DATA__ + +=== TEST 1: create a credential for invalid consumer: consumer not found error +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": { + "key": "the-key" + } + } + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 404 +--- response_body +{"error_msg":"consumer not found"} + + + +=== TEST 2: add a consumer +--- 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", + "desc": "new consumer", + "plugins": { + "basic-auth": { + "username": "the-user", + "password": "the-password" + } + } + }]], + [[{ + "key": "/apisix/consumers/jack", + "value": + { + "username":"jack", + "desc": "new consumer", + "plugins": { + "basic-auth": { + "username": "the-user", + "password": "the-password" + } + } + } + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: add a credentials with basic-auth for the consumer jack, should success +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a', + ngx.HTTP_PUT, + [[{ + "desc": "basic-auth for jack", + "plugins": { + "basic-auth": { + "username": "the-user", + "password": "the-password" + } + } + }]], + [[{ + "value":{ + "desc":"basic-auth for jack", + "id":"credential_a", + "plugins":{"basic-auth":{"username":"the-user","password":"the-password"}} + }, + "key":"/apisix/consumers/jack/credentials/credential_a" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: add a credential with key-auth for the consumer jack, should success +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b', + ngx.HTTP_PUT, + [[{ + "desc": "key-auth for jack", + "plugins": { + "key-auth": { + "key": "the-key" + } + } + }]], + [[{ + "value":{ + "desc":"key-auth for jack", + "id":"credential_b", + "plugins":{"key-auth":{"key":"the-key"}} + }, + "key":"/apisix/consumers/jack/credentials/credential_b" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 5: add a credential with a plugin which is not a auth plugin, should fail +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b', + ngx.HTTP_PUT, + [[{ + "desc": "limit-conn for jack", + "plugins": { + "limit-conn": { + "conn": 1, + "burst": 0, + "default_conn_delay": 0.1, + "rejected_code": 503, + "key_type": "var", + "key": "http_a" + } + } + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body +{"error_msg":"only supports auth type plugins in consumer credential"} + + + +=== TEST 6: list consumers: should not contain credential +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin").test + local code, body, res = t('/apisix/admin/consumers', ngx.HTTP_GET) + + ngx.status = code + res = json.decode(res) + assert(res.total == 1) + assert(res.list[1].key == "/apisix/consumers/jack") + } + } +--- request +GET /t +--- response_body + + + +=== TEST 7: list credentials: should contain credential_a and credential_b +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin").test + local code, body, res = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET) + + ngx.status = code + res = json.decode(res) + assert(res.total == 2) + assert(res.list[1].key == "/apisix/consumers/jack/credentials/credential_a") + assert(res.list[2].key == "/apisix/consumers/jack/credentials/credential_b") + } + } +--- request +GET /t +--- response_body + + +=== TEST 8: get a credential +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b', + ngx.HTTP_GET, + nil, + [[{ + "key": "/apisix/consumers/jack/credentials/credential_b", + "value": { + "desc": "key-auth for jack", + "plugins": {"key-auth": {"key": "the-key"} + }} + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 9: update credential: should ok +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b', + ngx.HTTP_PUT, + [[{ + "desc": "new description", + "plugins": { + "key-auth": { + "key": "new-key" + } + } + }]], + [[{ + "key": "/apisix/consumers/jack/credentials/credential_b", + "value": { + "desc": "new description", + "plugins": { + "key-auth": { + "key": "new-key" + } + } + } + }]] + ) + + ngx.status = code + ngx.say(body) + + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 10: delete credential +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a', ngx.HTTP_DELETE) + + assert(code == 200) + ngx.status = code + + code, body, res = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET) + res = json.decode(res) + assert(res.total == 1) + assert(res.list[1].key == "/apisix/consumers/jack/credentials/credential_b") + } + } +--- request +GET /t +--- response_body + + + +=== TEST 11: create a credential has more than one plugin: should not ok +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/xxx-yyy-zzz', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "the-key"}, + "basic-auth": {"username": "the-user", "password": "the-password"} + } + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body +{"error_msg":"invalid configuration: property \"plugins\" validation failed: expect object to have at most 1 properties"} + + + +=== TEST 12: delete consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack', + ngx.HTTP_DELETE + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 13: list credentials: should get 404 because the consumer is deleted +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 404 +--- response_body +{"message":"Key not found"} + + + +=== TEST 14: add a consumer +--- 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" + }]] + ) + + if ngx.status >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 15: add a credential with key-auth for the consumer jack (id in the payload but not in uri), should success +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials', + ngx.HTTP_PUT, + [[{ + "id": "d79a5aa3", + "desc": "key-auth for jack", + "plugins": { + "key-auth": { + "key": "the-key" + } + } + }]], + [[{ + "value":{ + "desc":"key-auth for jack", + "id":"d79a5aa3", + "plugins":{"key-auth":{"key":"the-key"}} + }, + "key":"/apisix/consumers/jack/credentials/d79a5aa3" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 16: add a credential with key-auth for the consumer jack but missing id in uri and payload, should fail +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials', + ngx.HTTP_PUT, + [[{ + "desc": "key-auth for jack", + "plugins": { + "key-auth": { + "key": "the-key" + } + } + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body +{"error_msg":"missing credential id"} diff --git a/t/node/consumer-plugin.t b/t/node/consumer-plugin.t index 76e3f25bb634..b4bf968d1da8 100644 --- a/t/node/consumer-plugin.t +++ b/t/node/consumer-plugin.t @@ -124,38 +124,6 @@ apikey: auth-one -=== TEST 6: missing auth plugins (not allow) ---- 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": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } - } - }]] - ) - - ngx.status = code - ngx.print(body) - } - } ---- request -GET /t ---- error_code: 400 ---- response_body -{"error_msg":"require one auth plugin"} - - - === TEST 7: use the new configuration after the consumer's configuration is updated --- config location /t { diff --git a/t/node/consumer-plugin2.t b/t/node/consumer-plugin2.t index d48387c179fa..6c79ad88dd26 100644 --- a/t/node/consumer-plugin2.t +++ b/t/node/consumer-plugin2.t @@ -109,6 +109,7 @@ apikey: auth-jack host: localhost x-api-engine: APISIX x-consumer-id: 1 +x-consumer-username: jack x-real-ip: 127.0.0.1 @@ -206,6 +207,7 @@ apikey: auth-jack host: localhost x-api-engine: APISIX x-consumer-id: 1 +x-consumer-username: jack x-real-ip: 127.0.0.1 diff --git a/t/node/credential-plugin-basic-auth.t b/t/node/credential-plugin-basic-auth.t new file mode 100644 index 000000000000..cfbc0cb61829 --- /dev/null +++ b/t/node/credential-plugin-basic-auth.t @@ -0,0 +1,121 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable basic-auth on the route /hello +--- 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": { + "basic-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 2: create a consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create a credential with basic-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', + ngx.HTTP_PUT, + [[{ + "plugins": { + "basic-auth": {"username": "foo", "password": "bar"} + } + }]], + [[{ + "value":{ + "id":"34010989-ce4e-4d61-9493-b54cca8edb31", + "plugins":{ + "basic-auth":{"username":"foo","password":"bar"} + } + }, + "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: access with invalid basic-auth (invalid password) +--- request +GET /hello +--- more_headers +Authorization: Basic Zm9vOmZvbwo= +--- error_code: 401 +--- response_body +{"message":"Invalid user authorization"} + + + +=== TEST 5: access with valid basic-auth +--- request +GET /hello +--- more_headers +Authorization: Basic Zm9vOmJhcg== +--- response_body +hello world diff --git a/t/node/credential-plugin-incremental-effective.t b/t/node/credential-plugin-incremental-effective.t new file mode 100644 index 000000000000..00824f9ebe8e --- /dev/null +++ b/t/node/credential-plugin-incremental-effective.t @@ -0,0 +1,109 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: test continuous watch etcd changes without APISIX reload +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- enable key-auth on /hello + t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + ngx.sleep(0.2) -- On some machines, changes may not be instantly watched, so sleep makes the test more robust. + + -- request /hello without key-auth should response status 401 + local code, body = t('/hello', ngx.HTTP_GET) + assert(code == 401) + + -- add a consumer jack + t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username":"jack" + }]], + [[{ + "key": "/apisix/consumers/jack", + "value": + { + "username":"jack" + } + }]] + ) + + -- create first credential for consumer jack + t('/apisix/admin/consumers/jack/credentials/the-first-one', + ngx.HTTP_PUT, + [[{ + "plugins":{"key-auth":{"key":"p7a3k6r4t9"}} + }]], + [[{ + "value":{ + "id":"the-first-one", + "plugins":{"key-auth":{"key":"p7a3k6r4t9"}} + }, + "key":"/apisix/consumers/jack/credentials/the-first-one" + }]] + ) + ngx.sleep(0.2) + + -- request /hello with credential a + local headers = {} + headers["apikey"] = "p7a3k6r4t9" + code, body = t('/hello', ngx.HTTP_GET, "", nil, headers) + assert(code == 200) + + -- create second credential for consumer jack + t('/apisix/admin/consumers/jack/credentials/the-second-one', + ngx.HTTP_PUT, + [[{ + "plugins":{"key-auth":{"key":"v8p3q6r7t9"}} + }]], + [[{ + "value":{ + "id":"the-second-one", + "plugins":{"key-auth":{"key":"v8p3q6r7t9"}} + }, + "key":"/apisix/consumers/jack/credentials/the-second-one" + }]] + ) + ngx.sleep(0.2) + + -- request /hello with credential b + headers["apikey"] = "v8p3q6r7t9" + code, body = t('/hello', ngx.HTTP_GET, "", nil, headers) + assert(code == 200) + + -- delete the first credential + code, body = t('/apisix/admin/consumers/jack/credentials/the-first-one', ngx.HTTP_DELETE) + assert(code == 200) + ngx.sleep(0.2) + + -- request /hello with credential a + headers["apikey"] = "p7a3k6r4t9" + code, body = t('/hello', ngx.HTTP_GET, "", nil, headers) + assert(code == 401) + } + } +--- request +GET /t diff --git a/t/node/credential-plugin-jwt-auth.t b/t/node/credential-plugin-jwt-auth.t new file mode 100644 index 000000000000..05c35086d99f --- /dev/null +++ b/t/node/credential-plugin-jwt-auth.t @@ -0,0 +1,121 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable jwt-auth on the route /hello +--- 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": { + "jwt-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 2: create a consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create a credential with jwt-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": {"key": "user-key", "secret": "my-secret-key"} + } + }]], + [[{ + "value":{ + "id":"34010989-ce4e-4d61-9493-b54cca8edb31", + "plugins":{ + "jwt-auth": {"key": "user-key", "secret": "my-secret-key"} + } + }, + "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: access with invalid JWT token +--- request +GET /hello +--- more_headers +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqd3QtdmF1bHQta2V5IiwiZXhwIjoxNjk1MTM4NjM1fQ.Au2liSZ8eQXUJR3SJESwNlIfqZdNyRyxIJK03L4dk_g +--- error_code: 401 +--- response_body +{"message":"Invalid user key in JWT token"} + + + +=== TEST 5: access with valid JWT token in header +--- request +GET /hello +--- more_headers +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs +--- response_body +hello world diff --git a/t/node/credential-plugin-key-auth.t b/t/node/credential-plugin-key-auth.t new file mode 100644 index 000000000000..d79aebd453f2 --- /dev/null +++ b/t/node/credential-plugin-key-auth.t @@ -0,0 +1,121 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable key-auth on the route /hello +--- 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": { + "key-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 2: create consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create a credential with key-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "p7a3k6r4t9"} + } + }]], + [[{ + "value":{ + "id":"34010989-ce4e-4d61-9493-b54cca8edb31", + "plugins":{ + "key-auth": {"key": "p7a3k6r4t9"} + } + }, + "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: request with an invalid key: should be not OK +--- request +GET /hello +--- more_headers +apikey: 123 +--- error_code: 401 +--- response_body +{"message":"Invalid API key in request"} + + + +=== TEST 5: request with the valid key: should be OK +--- request +GET /hello +--- more_headers +apikey: p7a3k6r4t9 +--- response_body +hello world diff --git a/t/node/credential-plugin-multi-credentials.t b/t/node/credential-plugin-multi-credentials.t new file mode 100644 index 000000000000..085c64323121 --- /dev/null +++ b/t/node/credential-plugin-multi-credentials.t @@ -0,0 +1,220 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable key-auth plugin on /hello +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- basic-auth on route 1 + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-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 2: create a consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create the first credential with the key-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/the-first-one', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "p7a3k6r4t9"} + } + }]], + [[{ + "value":{ + "id":"the-first-one", + "plugins":{ + "key-auth": {"key": "p7a3k6r4t9"} + } + }, + "key":"/apisix/consumers/jack/credentials/the-first-one" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: create the second credential with the key-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/the-second-one', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "v8p3q6r7t9"} + } + }]], + [[{ + "value":{ + "id":"the-second-one", + "plugins":{ + "key-auth": {"key": "v8p3q6r7t9"} + } + }, + "key":"/apisix/consumers/jack/credentials/the-second-one" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 5: request /hello with the key of the first credential: should be OK +--- request +GET /hello +--- more_headers +apikey: p7a3k6r4t9 +--- response_body +hello world + + + +=== TEST 6: request /hello with the key of second credential: should be OK +--- request +GET /hello +--- more_headers +apikey: v8p3q6r7t9 +--- response_body +hello world + + + +=== TEST 7: delete the first credential +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/the-first-one', ngx.HTTP_DELETE) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 8: request /hello with the key of the first credential: should be not OK +--- request +GET /hello +--- more_headers +apikey: p7a3k6r4t9 +--- error_code: 401 + + + +=== TEST 9: request /hello with the key of the second credential: should be OK +--- request +GET /hello +--- more_headers +apikey: v8p3q6r7t9 +--- response_body +hello world + + + +=== TEST 10: delete the second credential +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/the-second-one', ngx.HTTP_DELETE) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 11: request /hello with the key of the second credential: should be not OK +--- request +GET /hello +--- more_headers +apikey: v8p3q6r7t9 +--- error_code: 401 diff --git a/t/node/credential-plugin-set-request-header.t b/t/node/credential-plugin-set-request-header.t new file mode 100644 index 000000000000..9fe83e5a1b10 --- /dev/null +++ b/t/node/credential-plugin-set-request-header.t @@ -0,0 +1,229 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable key-auth on the route /echo +--- 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": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/echo" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: create consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create a credential with key-auth plugin enabled and 'custom_id' label for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "p7a3k6r4t9"} + }, + "labels": { + "custom_id": "271fc4a264bb" + } + }]], + [[{ + "value":{ + "id":"34010989-ce4e-4d61-9493-b54cca8edb31", + "plugins":{ + "key-auth": {"key": "p7a3k6r4t9"} + }, + "labels": { + "custom_id": "271fc4a264bb" + } + }, + "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: request the route: 'x-consumer-username' and 'x-credential-identifier' is in response headers and 'x-consumer-custom-id' is not +--- request +GET /echo HTTP/1.1 +--- more_headers +apikey: p7a3k6r4t9 +--- response_headers +x-consumer-username: jack +x-credential-identifier: 34010989-ce4e-4d61-9493-b54cca8edb31 +!x-consumer-custom-id + + + +=== TEST 5: update the consumer add label "custom_id" +--- 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", + "labels": { + "custom_id": "495aec6a" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: request the route: the value of 'x-consumer-custom-id' come from the consumer but not the credential or downstream +--- request +GET /echo HTTP/1.1 +--- more_headers +apikey: p7a3k6r4t9 +x-consumer-custom-id: 271fc4a264bb +--- response_headers +x-consumer-username: jack +x-credential-identifier: 34010989-ce4e-4d61-9493-b54cca8edb31 +x-consumer-custom-id: 495aec6a + + + +=== TEST 7: delete the credential +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', ngx.HTTP_DELETE) + + assert(code == 200) + ngx.status = code + } + } +--- request +GET /t +--- response_body + + + +=== TEST 8: update the consumer to enable a key-auth plugin +--- 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": { + "key-auth": { + "key": "p7a3k6r4t9" + } + } + }]], + [[{ + "value": { + "username": "jack", + "plugins": { + "key-auth": { + "key": "p7a3k6r4t9" + } + } + }, + "key": "/apisix/consumers/jack" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 9: request the route with headers x-credential-identifier and x-consumer-custom-id: these headers will be removed +--- request +GET /echo HTTP/1.1 +--- more_headers +apikey: p7a3k6r4t9 +x-credential-identifier: 34010989-ce4e-4d61-9493-b54cca8edb31 +x-consumer-custom-id: 271fc4a264bb +--- response_headers +x-consumer-username: jack +!x-credential-identifier +!x-consumer-custom-id diff --git a/t/node/credential-plugin-work-with-other-plugin.t b/t/node/credential-plugin-work-with-other-plugin.t new file mode 100644 index 000000000000..e2b5b4011f29 --- /dev/null +++ b/t/node/credential-plugin-work-with-other-plugin.t @@ -0,0 +1,155 @@ +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +run_tests; + +__DATA__ + + +=== TEST 1: enable key-auth on /hello +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- basic-auth on route 1 + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-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 2: create a consumer +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: create a credential with the key-auth plugin enabled for the consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {"key": "p7a3k6r4t9"} + } + }]], + [[{ + "value":{ + "id":"34010989-ce4e-4d61-9493-b54cca8edb31", + "plugins":{ + "key-auth": {"key": "p7a3k6r4t9"} + } + }, + "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: request the route /hello multi times: should be OK +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello", "GET /hello"] +--- more_headers +apikey: p7a3k6r4t9 +--- error_code eval +[200, 200, 200, 200] + + + +=== TEST 5: enable plugin `limit-count` for the consumer +--- 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": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: request the route /hello multi times: should be not OK, exceed the limit-count +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello", "GET /hello"] +--- more_headers +apikey: p7a3k6r4t9 +--- error_code eval +[200, 200, 503, 503] From 87a2ac3a849ae8c247bc06db3999f20344f1c2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 02:18:22 +0800 Subject: [PATCH 02/11] license --- t/admin/credentials.t | 8 ++++---- t/node/credential-plugin-basic-auth.t | 17 +++++++++++++++++ .../credential-plugin-incremental-effective.t | 17 +++++++++++++++++ t/node/credential-plugin-jwt-auth.t | 17 +++++++++++++++++ t/node/credential-plugin-key-auth.t | 17 +++++++++++++++++ t/node/credential-plugin-multi-credentials.t | 17 +++++++++++++++++ t/node/credential-plugin-set-request-header.t | 17 +++++++++++++++++ .../credential-plugin-work-with-other-plugin.t | 17 +++++++++++++++++ 8 files changed, 123 insertions(+), 4 deletions(-) diff --git a/t/admin/credentials.t b/t/admin/credentials.t index 3e58fbbcd368..e97bc38e2155 100644 --- a/t/admin/credentials.t +++ b/t/admin/credentials.t @@ -193,14 +193,14 @@ GET /t --- config location /t { content_by_lua_block { - local json = require("toolkit.json") + local json = require("toolkit.json") local t = require("lib.test_admin").test local code, body, res = t('/apisix/admin/consumers', ngx.HTTP_GET) ngx.status = code - res = json.decode(res) - assert(res.total == 1) - assert(res.list[1].key == "/apisix/consumers/jack") + res = json.decode(res) + assert(res.total == 1) + assert(res.list[1].key == "/apisix/consumers/jack") } } --- request diff --git a/t/node/credential-plugin-basic-auth.t b/t/node/credential-plugin-basic-auth.t index cfbc0cb61829..01fa914b913d 100644 --- a/t/node/credential-plugin-basic-auth.t +++ b/t/node/credential-plugin-basic-auth.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-incremental-effective.t b/t/node/credential-plugin-incremental-effective.t index 00824f9ebe8e..cdd380912d5c 100644 --- a/t/node/credential-plugin-incremental-effective.t +++ b/t/node/credential-plugin-incremental-effective.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-jwt-auth.t b/t/node/credential-plugin-jwt-auth.t index 05c35086d99f..765a0e2191ad 100644 --- a/t/node/credential-plugin-jwt-auth.t +++ b/t/node/credential-plugin-jwt-auth.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-key-auth.t b/t/node/credential-plugin-key-auth.t index d79aebd453f2..2ab7fe0114cf 100644 --- a/t/node/credential-plugin-key-auth.t +++ b/t/node/credential-plugin-key-auth.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-multi-credentials.t b/t/node/credential-plugin-multi-credentials.t index 085c64323121..1fa12a6d6152 100644 --- a/t/node/credential-plugin-multi-credentials.t +++ b/t/node/credential-plugin-multi-credentials.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-set-request-header.t b/t/node/credential-plugin-set-request-header.t index 9fe83e5a1b10..8aeb275c64a9 100644 --- a/t/node/credential-plugin-set-request-header.t +++ b/t/node/credential-plugin-set-request-header.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); diff --git a/t/node/credential-plugin-work-with-other-plugin.t b/t/node/credential-plugin-work-with-other-plugin.t index e2b5b4011f29..646f4b138d71 100644 --- a/t/node/credential-plugin-work-with-other-plugin.t +++ b/t/node/credential-plugin-work-with-other-plugin.t @@ -1,3 +1,20 @@ +# 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'; repeat_each(1); From 6765adfee6fb387273c340d5c8e0a74b7b3de94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 02:35:57 +0800 Subject: [PATCH 03/11] fix lint --- apisix/admin/credentials.lua | 12 ++++++++---- apisix/admin/resource.lua | 4 +++- apisix/consumer.lua | 10 +++++++--- t/admin/credentials.t | 17 +++++++++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apisix/admin/credentials.lua b/apisix/admin/credentials.lua index 6dd6951896dc..3622867528d8 100644 --- a/apisix/admin/credentials.lua +++ b/apisix/admin/credentials.lua @@ -46,10 +46,14 @@ local function check_conf(_id, conf, _need_id, schema) 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}. +-- 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) diff --git a/apisix/admin/resource.lua b/apisix/admin/resource.lua index 5b12e04dd239..ff5c97e18d89 100644 --- a/apisix/admin/resource.lua +++ b/apisix/admin/resource.lua @@ -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 @@ -275,7 +276,8 @@ function _M:put(id, conf, sub_path, args) 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) + 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 diff --git a/apisix/consumer.lua b/apisix/consumer.lua index 653ca6a17603..938dbebcc7ff 100644 --- a/apisix/consumer.lua +++ b/apisix/consumer.lua @@ -15,13 +15,16 @@ -- limitations under the License. -- local core = require("apisix.core") +local config_local = require("apisix.core.config_local") local secret = require("apisix.secret") local plugin = require("apisix.plugin") local plugin_checker = require("apisix.plugin").plugin_checker +local check_schema = require("apisix.core.schema").check local error = error local ipairs = ipairs local pairs = pairs local type = type +local string_sub = string.sub local consumers @@ -103,7 +106,8 @@ local function plugin_consumer() end -- if the val is a Consumer, clone it to the local consumer; - -- if the val is a Credential, to get the Consumer by consumer_name and then clone it to the local consumer. + -- if the val is a Credential, to get the Consumer by consumer_name and then clone it to + -- the local consumer. local consumer if is_credential_etcd_key(val.key) then local consumer_name = get_consumer_name_from_credential_etcd_key(val.key) @@ -113,8 +117,8 @@ local function plugin_consumer() consumer.credential_id = get_credential_id_from_etcd_key(val.key) else -- Normally wouldn't get here: it should belong to a consumer for any credential. - core.log.error("failed to get the consumer for the credential, a wild credential has appeared! credential key: ", - val.key, ", consumer name: ", consumer_name) + core.log.error("failed to get the consumer for the credential, a wild credential has appeared!", + " credential key: ", val.key, ", consumer name: ", consumer_name) goto CONTINUE end else diff --git a/t/admin/credentials.t b/t/admin/credentials.t index e97bc38e2155..1b7512d7b902 100644 --- a/t/admin/credentials.t +++ b/t/admin/credentials.t @@ -1,3 +1,20 @@ +# +# 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'; repeat_each(1); From 7954aa3db15ff1f3988409474ca057516151bc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 03:46:52 +0800 Subject: [PATCH 04/11] fix encrypt --- apisix/consumer.lua | 10 ++++++---- t/admin/credentials.t | 10 +++++----- t/node/credential-plugin-basic-auth.t | 2 +- t/node/credential-plugin-jwt-auth.t | 2 +- t/node/credential-plugin-key-auth.t | 2 +- t/node/credential-plugin-multi-credentials.t | 4 ++-- t/node/credential-plugin-set-request-header.t | 4 ++-- t/node/credential-plugin-work-with-other-plugin.t | 2 +- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apisix/consumer.lua b/apisix/consumer.lua index 938dbebcc7ff..f69d069d6aab 100644 --- a/apisix/consumer.lua +++ b/apisix/consumer.lua @@ -106,8 +106,8 @@ local function plugin_consumer() end -- if the val is a Consumer, clone it to the local consumer; - -- if the val is a Credential, to get the Consumer by consumer_name and then clone it to - -- the local consumer. + -- if the val is a Credential, to get the Consumer by consumer_name and then clone + -- it to the local consumer. local consumer if is_credential_etcd_key(val.key) then local consumer_name = get_consumer_name_from_credential_etcd_key(val.key) @@ -116,8 +116,10 @@ local function plugin_consumer() consumer = core.table.clone(the_consumer.value) consumer.credential_id = get_credential_id_from_etcd_key(val.key) else - -- Normally wouldn't get here: it should belong to a consumer for any credential. - core.log.error("failed to get the consumer for the credential, a wild credential has appeared!", + -- Normally wouldn't get here: + -- it should belong to a consumer for any credential. + core.log.error("failed to get the consumer for the credential,", + " a wild credential has appeared!", " credential key: ", val.key, ", consumer name: ", consumer_name) goto CONTINUE end diff --git a/t/admin/credentials.t b/t/admin/credentials.t index 1b7512d7b902..52828f986672 100644 --- a/t/admin/credentials.t +++ b/t/admin/credentials.t @@ -81,7 +81,7 @@ GET /t "plugins": { "basic-auth": { "username": "the-user", - "password": "the-password" + "password": "WvF5kpaLvIzjuk4GNIMTJg==" } } } @@ -119,7 +119,7 @@ passed "value":{ "desc":"basic-auth for jack", "id":"credential_a", - "plugins":{"basic-auth":{"username":"the-user","password":"the-password"}} + "plugins":{"basic-auth":{"username":"the-user","password":"WvF5kpaLvIzjuk4GNIMTJg=="}} }, "key":"/apisix/consumers/jack/credentials/credential_a" }]] @@ -155,7 +155,7 @@ passed "value":{ "desc":"key-auth for jack", "id":"credential_b", - "plugins":{"key-auth":{"key":"the-key"}} + "plugins":{"key-auth":{"key":"JCX7x1qN5e9kHt0GuJfWpw=="}} }, "key":"/apisix/consumers/jack/credentials/credential_b" }]] @@ -295,7 +295,7 @@ passed "desc": "new description", "plugins": { "key-auth": { - "key": "new-key" + "key": "523EisB/dvqlIT9RzfF3ZQ==" } } } @@ -448,7 +448,7 @@ passed "value":{ "desc":"key-auth for jack", "id":"d79a5aa3", - "plugins":{"key-auth":{"key":"the-key"}} + "plugins":{"key-auth":{"key":"JCX7x1qN5e9kHt0GuJfWpw=="}} }, "key":"/apisix/consumers/jack/credentials/d79a5aa3" }]] diff --git a/t/node/credential-plugin-basic-auth.t b/t/node/credential-plugin-basic-auth.t index 01fa914b913d..65c6b5a710ac 100644 --- a/t/node/credential-plugin-basic-auth.t +++ b/t/node/credential-plugin-basic-auth.t @@ -100,7 +100,7 @@ passed "value":{ "id":"34010989-ce4e-4d61-9493-b54cca8edb31", "plugins":{ - "basic-auth":{"username":"foo","password":"bar"} + "basic-auth":{"username":"foo","password":"+kOEVUuRc5rC5ZwvvAMLwg=="} } }, "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" diff --git a/t/node/credential-plugin-jwt-auth.t b/t/node/credential-plugin-jwt-auth.t index 765a0e2191ad..55a2e2642875 100644 --- a/t/node/credential-plugin-jwt-auth.t +++ b/t/node/credential-plugin-jwt-auth.t @@ -100,7 +100,7 @@ passed "value":{ "id":"34010989-ce4e-4d61-9493-b54cca8edb31", "plugins":{ - "jwt-auth": {"key": "user-key", "secret": "my-secret-key"} + "jwt-auth": {"key": "user-key", "secret": "kK0lkbzXrE7aiTiyK/Z0Sw=="} } }, "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" diff --git a/t/node/credential-plugin-key-auth.t b/t/node/credential-plugin-key-auth.t index 2ab7fe0114cf..6b2d833c316b 100644 --- a/t/node/credential-plugin-key-auth.t +++ b/t/node/credential-plugin-key-auth.t @@ -100,7 +100,7 @@ passed "value":{ "id":"34010989-ce4e-4d61-9493-b54cca8edb31", "plugins":{ - "key-auth": {"key": "p7a3k6r4t9"} + "key-auth": {"key": "fsFPtg7BtXMXkvSnS9e1zw=="} } }, "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" diff --git a/t/node/credential-plugin-multi-credentials.t b/t/node/credential-plugin-multi-credentials.t index 1fa12a6d6152..a5aa0747c6d7 100644 --- a/t/node/credential-plugin-multi-credentials.t +++ b/t/node/credential-plugin-multi-credentials.t @@ -102,7 +102,7 @@ passed "value":{ "id":"the-first-one", "plugins":{ - "key-auth": {"key": "p7a3k6r4t9"} + "key-auth": {"key": "fsFPtg7BtXMXkvSnS9e1zw=="} } }, "key":"/apisix/consumers/jack/credentials/the-first-one" @@ -136,7 +136,7 @@ passed "value":{ "id":"the-second-one", "plugins":{ - "key-auth": {"key": "v8p3q6r7t9"} + "key-auth": {"key": "QwGua2GjZjOiq+Mj3Mef2g=="} } }, "key":"/apisix/consumers/jack/credentials/the-second-one" diff --git a/t/node/credential-plugin-set-request-header.t b/t/node/credential-plugin-set-request-header.t index 8aeb275c64a9..49d636251e0e 100644 --- a/t/node/credential-plugin-set-request-header.t +++ b/t/node/credential-plugin-set-request-header.t @@ -103,7 +103,7 @@ passed "value":{ "id":"34010989-ce4e-4d61-9493-b54cca8edb31", "plugins":{ - "key-auth": {"key": "p7a3k6r4t9"} + "key-auth": {"key": "fsFPtg7BtXMXkvSnS9e1zw=="} }, "labels": { "custom_id": "271fc4a264bb" @@ -214,7 +214,7 @@ GET /t "username": "jack", "plugins": { "key-auth": { - "key": "p7a3k6r4t9" + "key": "fsFPtg7BtXMXkvSnS9e1zw==" } } }, diff --git a/t/node/credential-plugin-work-with-other-plugin.t b/t/node/credential-plugin-work-with-other-plugin.t index 646f4b138d71..091badcb5a32 100644 --- a/t/node/credential-plugin-work-with-other-plugin.t +++ b/t/node/credential-plugin-work-with-other-plugin.t @@ -102,7 +102,7 @@ passed "value":{ "id":"34010989-ce4e-4d61-9493-b54cca8edb31", "plugins":{ - "key-auth": {"key": "p7a3k6r4t9"} + "key-auth": {"key": "fsFPtg7BtXMXkvSnS9e1zw=="} } }, "key":"/apisix/consumers/jack/credentials/34010989-ce4e-4d61-9493-b54cca8edb31" From bbdde53e966c7d73fe7e6a8e4a1ff36571b47f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 09:19:13 +0800 Subject: [PATCH 05/11] reindex --- t/admin/credentials.t | 1 + t/node/consumer-plugin.t | 12 ++++++------ t/node/credential-plugin-basic-auth.t | 1 - t/node/credential-plugin-incremental-effective.t | 1 - t/node/credential-plugin-jwt-auth.t | 1 - t/node/credential-plugin-key-auth.t | 1 - t/node/credential-plugin-multi-credentials.t | 1 - t/node/credential-plugin-set-request-header.t | 1 - t/node/credential-plugin-work-with-other-plugin.t | 1 - 9 files changed, 7 insertions(+), 13 deletions(-) diff --git a/t/admin/credentials.t b/t/admin/credentials.t index 52828f986672..15119829c2e3 100644 --- a/t/admin/credentials.t +++ b/t/admin/credentials.t @@ -246,6 +246,7 @@ GET /t --- response_body + === TEST 8: get a credential --- config location /t { diff --git a/t/node/consumer-plugin.t b/t/node/consumer-plugin.t index b4bf968d1da8..b5e6d7ee5b27 100644 --- a/t/node/consumer-plugin.t +++ b/t/node/consumer-plugin.t @@ -124,7 +124,7 @@ apikey: auth-one -=== TEST 7: use the new configuration after the consumer's configuration is updated +=== TEST 6: use the new configuration after the consumer's configuration is updated --- config location /t { content_by_lua_block { @@ -189,7 +189,7 @@ GET /t -=== TEST 8: consumer with multiple auth plugins +=== TEST 7: consumer with multiple auth plugins --- config location /t { content_by_lua_block { @@ -226,7 +226,7 @@ passed -=== TEST 9: bind to routes +=== TEST 8: bind to routes --- config location /t { content_by_lua_block { @@ -283,7 +283,7 @@ passed -=== TEST 10: hit consumer, key-auth +=== TEST 9: hit consumer, key-auth --- request GET /hello --- more_headers @@ -295,7 +295,7 @@ find consumer John_Doe -=== TEST 11: hit consumer, hmac-auth +=== TEST 10: hit consumer, hmac-auth --- config location /t { content_by_lua_block { @@ -351,7 +351,7 @@ find consumer John_Doe -=== TEST 12: the plugins bound on the service should use the latest configuration +=== TEST 11: the plugins bound on the service should use the latest configuration --- config location /t { content_by_lua_block { diff --git a/t/node/credential-plugin-basic-auth.t b/t/node/credential-plugin-basic-auth.t index 65c6b5a710ac..c2e55acd4702 100644 --- a/t/node/credential-plugin-basic-auth.t +++ b/t/node/credential-plugin-basic-auth.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable basic-auth on the route /hello --- config location /t { diff --git a/t/node/credential-plugin-incremental-effective.t b/t/node/credential-plugin-incremental-effective.t index cdd380912d5c..ae619dfce3ae 100644 --- a/t/node/credential-plugin-incremental-effective.t +++ b/t/node/credential-plugin-incremental-effective.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: test continuous watch etcd changes without APISIX reload --- config location /t { diff --git a/t/node/credential-plugin-jwt-auth.t b/t/node/credential-plugin-jwt-auth.t index 55a2e2642875..f95498d6b115 100644 --- a/t/node/credential-plugin-jwt-auth.t +++ b/t/node/credential-plugin-jwt-auth.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable jwt-auth on the route /hello --- config location /t { diff --git a/t/node/credential-plugin-key-auth.t b/t/node/credential-plugin-key-auth.t index 6b2d833c316b..558616d647e3 100644 --- a/t/node/credential-plugin-key-auth.t +++ b/t/node/credential-plugin-key-auth.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable key-auth on the route /hello --- config location /t { diff --git a/t/node/credential-plugin-multi-credentials.t b/t/node/credential-plugin-multi-credentials.t index a5aa0747c6d7..6b60bb37b94e 100644 --- a/t/node/credential-plugin-multi-credentials.t +++ b/t/node/credential-plugin-multi-credentials.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable key-auth plugin on /hello --- config location /t { diff --git a/t/node/credential-plugin-set-request-header.t b/t/node/credential-plugin-set-request-header.t index 49d636251e0e..51148d038512 100644 --- a/t/node/credential-plugin-set-request-header.t +++ b/t/node/credential-plugin-set-request-header.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable key-auth on the route /echo --- config location /t { diff --git a/t/node/credential-plugin-work-with-other-plugin.t b/t/node/credential-plugin-work-with-other-plugin.t index 091badcb5a32..14bfc13c91cc 100644 --- a/t/node/credential-plugin-work-with-other-plugin.t +++ b/t/node/credential-plugin-work-with-other-plugin.t @@ -24,7 +24,6 @@ run_tests; __DATA__ - === TEST 1: enable key-auth on /hello --- config location /t { From 6e058a5f2071ddcdc8bd9c4b97c89e0a9993b5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 10:24:12 +0800 Subject: [PATCH 06/11] fix kvs_to_nodes --- apisix/core/etcd.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua index b9060bc5f2aa..e3785091bdf2 100644 --- a/apisix/core/etcd.lua +++ b/apisix/core/etcd.lua @@ -232,8 +232,8 @@ function _M.get_format(res, real_key, is_dir, formatter) local last_slash_index = string.find(res.body.node.key, "/[^/]*$") if last_slash_index then res.body.node.key = string.sub(res.body.node.key, 1, last_slash_index-1) - res = kvs_to_nodes(res, false) end + res = kvs_to_nodes(res, false) end end From 8b4949ab7fd56112a4938654b888dcf92a4bfd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Wed, 25 Sep 2024 17:46:34 +0800 Subject: [PATCH 07/11] doc: credential terminology document in zh --- docs/zh/latest/terminology/credential.md | 152 +++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 docs/zh/latest/terminology/credential.md diff --git a/docs/zh/latest/terminology/credential.md b/docs/zh/latest/terminology/credential.md new file mode 100644 index 000000000000..bc09aa173b42 --- /dev/null +++ b/docs/zh/latest/terminology/credential.md @@ -0,0 +1,152 @@ +--- +title: Credential +keywords: + - APISIX + - API 网关 + - 凭证 + - Credential +description: 本文介绍了 Apache APISIX Credential 对象的作用以及如何使用 Credential。 +--- + + + +## 描述 + +Credential 是存放 [Consumer](./consumer.md) 凭证配置的对象。 +一个 Consumer 可以使用不同类型的多个凭证。 +当你需要为一个 Consumer 配置不同类型的多个凭证时,就会用到 Credential。 + +目前,Credential 可以配置的身份认证插件包括 `basic-auth`、`hmac-auth`、`jwt-auth` 以及 `key-auth`。 + +## 配置选项 + 定义 Credential 的字段如下: + +| 名称 | 必选项 | 描述 | +|---------|-----|-----------------------------------------------------| +| desc | 否 | Credential 描述。 | +| labels | 否 | Credential 标签。 | +| plugins | 否 | Credential 对应的插件配置。详细信息,请参考 [Plugins](./plugin.md)。 | + + +:::note 注意 + +如需了解更多关于 Consumer 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 + +::: + +## 使用示例 + +[Consumer 使用示例](./consumer.md#使用示例) 介绍了如何对 Consumer 配置认证插件,并介绍了如何配合其他插件使用。 +在该示例中,该 Consumer 只有一个 key-auth 类型的凭证。 +现在我们假设该 Consumer 需要使用多个凭证,你可以使用 Credential 来支持这一点。 + +:::note + +您可以这样从 `config.yaml` 中获取 `admin_key` 并存入环境变量: + +```bash +admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g') +``` + +::: + +1. 创建 Consumer,这次我们不指定认证插件,而是稍后使用 Credential 来配置认证插件。 + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "username": "jack" + }' + ``` + +2. 为 Consumer 配置 2 个 启用 `key-auth` 的 Credential。 + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/key-auth-one \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": { + "key": "auth-one" + } + } + }' + ``` + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/key-auth-two \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": { + "key": "auth-two" + } + } + }' + ``` + +3. 创建路由,设置路由规则和启用插件配置。 + + ```shell + curl http://127.0.0.1:9180/apisix/admin/routes/1 \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }' + ``` + +4. 测试插件 + +分别使用 `auth-one` 和 `auth-two` 两个 key 来测试请求,都响应正常。 + + ```shell + curl http://127.0.0.1:9080/hello -H 'apikey: auth-one' -I + curl http://127.0.0.1:9080/hello -H 'apikey: auth-two' -I + ``` + +为该 Consumer 启用 `limit-count` 插件。 + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "username": "jack", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" + } + } + }' + ``` + +分别使用这两个 key 连续 3 次以上请求该路由,测试返回 `503`,请求被限制。 From 2d36fa7048fb928180c77edc3549e741446cada7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Thu, 26 Sep 2024 04:01:55 +0800 Subject: [PATCH 08/11] add api doc --- docs/en/latest/admin-api.md | 65 ++++++++++ docs/en/latest/terminology/credential.md | 151 +++++++++++++++++++++++ docs/zh/latest/admin-api.md | 64 ++++++++++ docs/zh/latest/terminology/credential.md | 8 +- 4 files changed, 284 insertions(+), 4 deletions(-) create mode 100644 docs/en/latest/terminology/credential.md diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index b6f03cbd5855..7a3a819a6a60 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -868,6 +868,71 @@ Since `v2.2`, we can bind multiple authentication plugins to the same consumer. Currently, the response is returned from etcd. +## Credential + +Credential is used to hold the authentication credentials for the Consumer. +Credentials are used when multiple credentials need to be configured for a Consumer. + +### Credential API + +Credential resource request address:/apisix/admin/consumers/{username}/credentials/{credential_id} + +### Request Methods + +| Method | Request URI | Request Body | Description | +| ------ |----------------------------------------------------------------|--------------|------------------------------------------------| +| GET | /apisix/admin/consumers/{username}/credentials | NUll | Fetches list of all credentials of the Consumer | +| GET | /apisix/admin/consumers/{username}/credentials/{credential_id} | NUll | Fetches the Credential by `credential_id` | +| PUT | /apisix/admin/consumers/{username}/credentials/{credential_id} | {...} | Create or update a Creddential | +| DELETE | /apisix/admin/consumers/{username}/credentials/{credential_id} | NUll | Delete the Credential | + +### Request Body Parameters + +| Parameter | Required | Type | Description | Example | +| ----------- |-----| ------- |------------------------------------------------------------|-------------------------------------------------| +| plugins | False | Plugin | Auth plugins configuration. | | +| desc | False | Auxiliary | Description of usage scenarios. | credential xxxx | +| labels | False | Match Rules | Attributes of the Credential specified as key-value pairs. | {"version":"v2","build":"16","env":"production"} | + +Example Configuration: + +```shell +{ + "plugins": { + "key-auth": { + "key": "auth-one" + } + }, + "desc": "hello world" +} +``` + +### Example API usage + +Prerequisite: Consumer has been created. + +Create Credential and specify the authentication plugin `key-auth`: + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials/auth-one \ + -H "X-API-KEY: $admin_key" -X PUT -i -d ' + { + "plugins": { + "key-auth": { + "key": "auth-one" + } + } + }' + ``` + + ``` + HTTP/1.1 200 OK + Date: Thu, 26 Dec 2019 08:17:49 GMT + ... + + {"key":"\/apisix\/consumers\/jack\/credentials\/auth-one","value":{"update_time":1666260780,"plugins":{"key-auth":{"key":"auth-one"}},"create_time":1666260780}} + ``` + ## Upstream Upstream is a virtual host abstraction that performs load balancing on a given set of service nodes according to the configured rules. diff --git a/docs/en/latest/terminology/credential.md b/docs/en/latest/terminology/credential.md new file mode 100644 index 000000000000..0534e58fb632 --- /dev/null +++ b/docs/en/latest/terminology/credential.md @@ -0,0 +1,151 @@ +--- +title: Credential +keywords: + - APISIX + - API 网关 + - 凭证 + - Credential +description: 本文介绍了 Apache APISIX Credential 对象的作用以及如何使用 Credential。 +--- + + + +## Description + +Credential is the object that holds the [Consumer](./consumer.md) credential configuration. +A Consumer can use multiple credentials of different types. +Credentials are used when you need to configure multiple credentials for a Consumer. + +Currently, Credential can be configured with the authentication plugins `basic-auth`, `hmac-auth`, `jwt-auth`, and `key-auth`. + +### Configuration options + +The fields for defining a Credential are defined as below. + +| Field | Required | Description | +|---------|-----|---------------------------------------------------------------------------------------------------------| +| desc | 否 | Decriptiion of the Credential. | +| labels | 否 | Labels of the Credential. | +| plugins | 否 | The plugin configuration corresponding to Credential. For more information, see [Plugins](./plugin.md). | + +:::note + +如需了解更多关于 Consumer 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 + +::: + +## Example + +[Consumer Example](./consumer.md#example) describes how to configure the auth plugin for Consumer and how to use it with other plugins. +In this example, the Consumer has only one credential of type key-auth. +Now suppose the user needs to configure multiple credentials for that Consumer, you can use Credential to support this. + +:::note +You can fetch the `admin_key` from `config.yaml` and save to an environment variable with the following command: + +```bash +admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g') +``` + +::: + +1. Create the Consumer without specifying the auth plug-n, but use Credential to configure the auth plugin later. + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "username": "jack" + }' + ``` + +2. Create 2 `key-auth` 的 Credentials for the Consumer. + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/key-auth-one \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": { + "key": "auth-one" + } + } + }' + ``` + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/key-auth-two \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": { + "key": "auth-two" + } + } + }' + ``` + +3. Create a route and enable `key-auth` plugin on it. + + ```shell + curl http://127.0.0.1:9180/apisix/admin/routes/1 \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "plugins": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }' + ``` + +4. Test. + +Test the request with the `auth-one` and `auth-two` keys, and they both respond correctly. + + ```shell + curl http://127.0.0.1:9080/hello -H 'apikey: auth-one' -I + curl http://127.0.0.1:9080/hello -H 'apikey: auth-two' -I + ``` + +Enable the `limit-count` plugin for the Consumer. + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers \ + -H "X-API-KEY: $admin_key" -X PUT -d ' + { + "username": "jack", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" + } + } + }' + ``` + +Requesting the route more than 3 times in a row with each of the two keys, the test returns `503` and the request is restricted. diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index 19d97d7808b7..afbdf5a38087 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -882,6 +882,70 @@ Consumer 对象 JSON 配置示例: 目前是直接返回与 etcd 交互后的结果。 +## Credential + +Credential 用以存放 Consumer 的认证凭证。当需要为 Consumer 配置多个凭证时,可以使用 Credential。 + +### 请求地址 {#credential-uri} + +Credential 资源请求地址:/apisix/admin/consumers/{username}/credentials/{credential_id} + +### 请求方法 {#consumer-request-methods} + +| 名称 | 请求 URI | 请求 body | 描述 | +| ------ |----------------------------------------------------------------| --------- | ------------- | +| GET | /apisix/admin/consumers/{username}/credentials | 无 | 获取资源列表。| +| GET | /apisix/admin/consumers/{username}/credentials/{credential_id} | 无 | 获取资源。 | +| PUT | /apisix/admin/consumers/{username}/credentials/{credential_id} | {...} | 创建资源。 | +| DELETE | /apisix/admin/consumers/{username}/credentials/{credential_id} | 无 | 删除资源。 | + +### body 请求参数 {#credential-body-request-methods} + +| 名称 | 必选项 | 类型 | 描述 | 示例值 | +| ----------- |-----| ------- |-----------------------| ------------------------------------------------ | +| plugins | 是 | Plugin | 该 Credential 对应的插件配置。 | | +| desc | 否 | 辅助 | Credential 描述。 | | +| labels | 否 | 匹配规则 | 标识附加属性的键值对。 | {"version":"v2","build":"16","env":"production"} | + +Credential 对象 JSON 配置示例: + +```shell +{ + "plugins": { + "key-auth": { + "key": "auth-one" + } + }, + "desc": "hello world" +} +``` + +### 使用示例 {#credential-example} + +前提:已创建 Consumer。 + +创建 Credential,并指定认证插件 `key-auth`: + + ```shell + curl http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials/auth-one \ + -H "X-API-KEY: $admin_key" -X PUT -i -d ' + { + "plugins": { + "key-auth": { + "key": "auth-one" + } + } + }' + ``` + + ``` + HTTP/1.1 200 OK + Date: Thu, 26 Dec 2019 08:17:49 GMT + ... + + {"key":"\/apisix\/consumers\/jack\/credentials\/auth-one","value":{"update_time":1666260780,"plugins":{"key-auth":{"key":"auth-one"}},"create_time":1666260780}} + ``` + ## Upstream Upstream 是虚拟主机抽象,对给定的多个服务节点按照配置规则进行负载均衡。Upstream 的地址信息可以直接配置到 `Route`(或 `Service`) 上,当 Upstream 有重复时,需要用“引用”方式避免重复。 diff --git a/docs/zh/latest/terminology/credential.md b/docs/zh/latest/terminology/credential.md index bc09aa173b42..788ee0d915b5 100644 --- a/docs/zh/latest/terminology/credential.md +++ b/docs/zh/latest/terminology/credential.md @@ -36,6 +36,7 @@ Credential 是存放 [Consumer](./consumer.md) 凭证配置的对象。 目前,Credential 可以配置的身份认证插件包括 `basic-auth`、`hmac-auth`、`jwt-auth` 以及 `key-auth`。 ## 配置选项 + 定义 Credential 的字段如下: | 名称 | 必选项 | 描述 | @@ -44,8 +45,7 @@ Credential 是存放 [Consumer](./consumer.md) 凭证配置的对象。 | labels | 否 | Credential 标签。 | | plugins | 否 | Credential 对应的插件配置。详细信息,请参考 [Plugins](./plugin.md)。 | - -:::note 注意 +:::note 如需了解更多关于 Consumer 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 @@ -55,7 +55,7 @@ Credential 是存放 [Consumer](./consumer.md) 凭证配置的对象。 [Consumer 使用示例](./consumer.md#使用示例) 介绍了如何对 Consumer 配置认证插件,并介绍了如何配合其他插件使用。 在该示例中,该 Consumer 只有一个 key-auth 类型的凭证。 -现在我们假设该 Consumer 需要使用多个凭证,你可以使用 Credential 来支持这一点。 +现在假设用户需要为该 Consumer 配置多个凭证,你可以使用 Credential 来支持这一点。 :::note @@ -67,7 +67,7 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/ ::: -1. 创建 Consumer,这次我们不指定认证插件,而是稍后使用 Credential 来配置认证插件。 +1. 创建 Consumer。不指定认证插件,而是稍后使用 Credential 来配置认证插件。 ```shell curl http://127.0.0.1:9180/apisix/admin/consumers \ From 9264d828248454fb0d1a7600c0061fe42046dc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Thu, 26 Sep 2024 09:13:31 +0800 Subject: [PATCH 09/11] index doc --- docs/en/latest/config.json | 1 + docs/zh/latest/config.json | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index ad9c1e051523..5c43fc55af81 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -49,6 +49,7 @@ "terminology/api-gateway", "terminology/consumer", "terminology/consumer-group", + "terminology/credential", "terminology/global-rule", "terminology/plugin", "terminology/plugin-config", diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index 4e35f1c4d848..15547299a166 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -38,6 +38,7 @@ "terminology/api-gateway", "terminology/consumer", "terminology/consumer-group", + "terminology/credential", "terminology/global-rule", "terminology/plugin", "terminology/plugin-config", From d80ff01ea45ec6f8f0021e87bae893530dba31b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Thu, 26 Sep 2024 11:42:56 +0800 Subject: [PATCH 10/11] fix doc --- docs/en/latest/admin-api.md | 4 ++-- docs/en/latest/terminology/credential.md | 4 ++-- docs/zh/latest/admin-api.md | 4 ++-- docs/zh/latest/terminology/credential.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index 7a3a819a6a60..c7a236da77d0 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -909,9 +909,9 @@ Example Configuration: ### Example API usage -Prerequisite: Consumer has been created. +Prerequisite: Consumer `jack` has been created. -Create Credential and specify the authentication plugin `key-auth`: +Create the `key-auth` Credential for consumer `jack`: ```shell curl http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials/auth-one \ diff --git a/docs/en/latest/terminology/credential.md b/docs/en/latest/terminology/credential.md index 0534e58fb632..ba75237aa1a5 100644 --- a/docs/en/latest/terminology/credential.md +++ b/docs/en/latest/terminology/credential.md @@ -47,7 +47,7 @@ The fields for defining a Credential are defined as below. :::note -如需了解更多关于 Consumer 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 +For more information about the Credential object, you can refer to the [Admin API Credential](../admin-api.md#credential) resource guide. ::: @@ -76,7 +76,7 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/ }' ``` -2. Create 2 `key-auth` 的 Credentials for the Consumer. +2. Create 2 `key-auth` Credentials for the Consumer. ```shell curl http://127.0.0.1:9180/apisix/admin/consumers/jack/key-auth-one \ diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index afbdf5a38087..f5cd5b144b05 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -922,9 +922,9 @@ Credential 对象 JSON 配置示例: ### 使用示例 {#credential-example} -前提:已创建 Consumer。 +前提:已创建 Consumer `jack`。 -创建 Credential,并指定认证插件 `key-auth`: +创建 Credential,并启用认证插件 `key-auth`: ```shell curl http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials/auth-one \ diff --git a/docs/zh/latest/terminology/credential.md b/docs/zh/latest/terminology/credential.md index 788ee0d915b5..4d183620240f 100644 --- a/docs/zh/latest/terminology/credential.md +++ b/docs/zh/latest/terminology/credential.md @@ -47,7 +47,7 @@ Credential 是存放 [Consumer](./consumer.md) 凭证配置的对象。 :::note -如需了解更多关于 Consumer 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 +如需了解更多关于 Credential 对象的信息,你可以参考 [Admin API Credential](../admin-api.md#credential) 资源介绍。 ::: From 5978a85b96f591376972943f0a307ebdee0cfb79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=82=9F=E7=A9=BA?= Date: Thu, 26 Sep 2024 11:46:10 +0800 Subject: [PATCH 11/11] fix doc --- docs/en/latest/terminology/credential.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/latest/terminology/credential.md b/docs/en/latest/terminology/credential.md index ba75237aa1a5..560d4314a704 100644 --- a/docs/en/latest/terminology/credential.md +++ b/docs/en/latest/terminology/credential.md @@ -2,10 +2,10 @@ title: Credential keywords: - APISIX - - API 网关 - - 凭证 + - API Gateway + - Consumer - Credential -description: 本文介绍了 Apache APISIX Credential 对象的作用以及如何使用 Credential。 +description: This article describes what the Apache APISIX Credential object does and how to use it. ---