-
Notifications
You must be signed in to change notification settings - Fork 2.6k
fix: consumer key duplication check #12040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
ce80722
489a985
a3a4ac2
fa3caa8
ef0a8ea
80ce431
93a4b63
5e0fe0f
5660ad2
6cb6b78
02daa2c
3b2aa93
7d2d930
7fe1827
38430a0
95c2a92
de68197
b5efe91
f5f883d
e3af265
3717039
07b2eca
9caab8f
74d4219
5a528fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,9 @@ | |
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 consumer = require("apisix.consumer") | ||
|
||
local function check_conf(username, conf, need_username, schema) | ||
local ok, err = core.schema.check(schema, conf) | ||
|
@@ -32,7 +34,50 @@ local function check_conf(username, conf, need_username, schema) | |
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} | ||
return nil, { | ||
error_msg = "invalid plugins configuration: " .. err | ||
} | ||
end | ||
|
||
-- check duplicate key | ||
for plugin_name, plugin_conf in pairs(conf.plugins or {}) do | ||
local plugin_obj = plugin.get(plugin_name) | ||
if not plugin_obj then | ||
return nil, {error_msg = "unknown plugin " .. plugin_name} | ||
end | ||
|
||
if plugin_obj.type == "auth" then | ||
local decrypted_conf = core.table.deepcopy(plugin_conf) | ||
plugin.decrypt_conf(plugin_name, decrypted_conf, core.schema.TYPE_CONSUMER) | ||
|
||
local plugin_key_map = { | ||
["key-auth"] = "key", | ||
["basic-auth"] = "username", | ||
["jwt-auth"] = "key", | ||
["hmac-auth"] = "key_id" | ||
} | ||
|
||
local key_field = plugin_key_map[plugin_name] | ||
|
||
if key_field then | ||
local key_value = decrypted_conf[key_field] | ||
|
||
if key_value then | ||
local consumer, _, err = consumer | ||
.find_consumer(plugin_name, key_field, key_value) | ||
if err then | ||
core.log.warn("failed to find consumer: ", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand the logic here. In the If yes, then there is no point in printing a warn log. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When fixing the failure test, I found that when calling the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can add this info as a comment in the code.
AlinsRan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
if consumer and consumer.username ~= conf.username then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of this is to allow the user to edit a pre-existing consumer. If the consuner being edited matches the username of the consumer being looked up, then the same consumer is being edited, and this behavior should be allowed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it. What about my first question?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. username is a unique field for the consumer and must exist. https://apisix.apache.org/docs/apisix/terminology/consumer/#configuration-options |
||
return nil, { | ||
error_msg = "duplicate key found with consumer: " | ||
.. consumer.username | ||
} | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,9 +18,10 @@ local core = require("apisix.core") | |
local plugins = require("apisix.admin.plugins") | ||
local plugin = require("apisix.plugin") | ||
local resource = require("apisix.admin.resource") | ||
local consumer = require("apisix.consumer") | ||
local pairs = pairs | ||
|
||
local function check_conf(_id, conf, _need_id, schema) | ||
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} | ||
|
@@ -32,14 +33,48 @@ local function check_conf(_id, conf, _need_id, schema) | |
return nil, {error_msg = "invalid plugins configuration: " .. err} | ||
end | ||
|
||
for name, _ in pairs(conf.plugins) do | ||
for name, plugin_conf 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 | ||
|
||
-- check duplicate key | ||
local decrypted_conf = core.table.deepcopy(plugin_conf) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are too many nested judgments inside, but I think we can extract this logic into a universal function that can be used by both consumers and credentials. |
||
plugin.decrypt_conf(name, decrypted_conf, core.schema.TYPE_CONSUMER) | ||
|
||
local plugin_key_map = { | ||
["key-auth"] = "key", | ||
["basic-auth"] = "username", | ||
["jwt-auth"] = "key", | ||
["hmac-auth"] = "key_id" | ||
} | ||
|
||
local key_field = plugin_key_map[name] | ||
if key_field then | ||
local key_value = decrypted_conf[key_field] | ||
|
||
if key_value then | ||
local consumer, _, err = consumer | ||
.find_consumer(name, key_field, key_value) | ||
|
||
if err then | ||
core.log.warn("failed to find consumer: ", err) | ||
end | ||
|
||
if consumer and consumer.credential_id ~= id then | ||
return nil, { | ||
error_msg = "duplicate key found with consumer: " | ||
.. consumer.username | ||
} | ||
end | ||
end | ||
end | ||
|
||
end | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,7 +110,7 @@ passed | |
"desc": "basic-auth for jack", | ||
"plugins": { | ||
"basic-auth": { | ||
"username": "the-user", | ||
"username": "the-new-user", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After adding the duplicate judgment, it cannot be added here again, so I made a modification. |
||
"password": "the-password" | ||
} | ||
} | ||
|
@@ -119,7 +119,7 @@ passed | |
"value":{ | ||
"desc":"basic-auth for jack", | ||
"id":"credential_a", | ||
"plugins":{"basic-auth":{"username":"the-user","password":"WvF5kpaLvIzjuk4GNIMTJg=="}} | ||
"plugins":{"basic-auth":{"username":"the-new-user","password":"WvF5kpaLvIzjuk4GNIMTJg=="}} | ||
}, | ||
"key":"/apisix/consumers/jack/credentials/credential_a" | ||
}]] | ||
|
@@ -492,3 +492,138 @@ GET /t | |
--- error_code: 400 | ||
--- response_body | ||
{"error_msg":"missing credential id"} | ||
|
||
|
||
|
||
=== TEST 17: create a consumer bar | ||
--- 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": "bar" }]]) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
|
||
|
||
|
||
=== TEST 18: create a credential with key-auth for the consumer bar | ||
--- config | ||
location /t { | ||
content_by_lua_block { | ||
local t = require("lib.test_admin").test | ||
local code, body = t('/apisix/admin/consumers/bar/credentials/credential_c', | ||
ngx.HTTP_PUT, | ||
[[{ | ||
"desc": "key-auth for bar", | ||
"plugins": { | ||
"key-auth": { | ||
"key": "the-key-bar" | ||
} | ||
} | ||
}]] | ||
) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
|
||
|
||
|
||
=== TEST 19: can not create a credential with duplicate key | ||
--- config | ||
location /t { | ||
content_by_lua_block { | ||
local t = require("lib.test_admin").test | ||
local code, body = t('/apisix/admin/consumers/bar/credentials/credential_d', | ||
ngx.HTTP_PUT, | ||
[[{ | ||
"desc": "key-auth for bar", | ||
"plugins": { | ||
"key-auth": { | ||
"key": "the-key-bar" | ||
} | ||
} | ||
}]] | ||
) | ||
|
||
ngx.status = code | ||
ngx.print(body) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
--- error_code: 400 | ||
--- response_body | ||
{"error_msg":"duplicate key found with consumer: bar"} | ||
|
||
|
||
|
||
=== TEST 20: can update credential credential_c with same key | ||
--- config | ||
location /t { | ||
content_by_lua_block { | ||
local t = require("lib.test_admin").test | ||
|
||
-- update desc, keep same key | ||
local code, body = t('/apisix/admin/consumers/bar/credentials/credential_c', | ||
ngx.HTTP_PUT, | ||
[[{ | ||
"desc": "new description", | ||
"plugins": { | ||
"key-auth": { | ||
"key": "the-key-bar" | ||
} | ||
} | ||
}]] | ||
) | ||
|
||
ngx.status = code | ||
ngx.say(body) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
--- response_body | ||
passed | ||
--- error_code: 200 | ||
|
||
|
||
|
||
=== TEST 21: delete credential credential_c | ||
--- config | ||
location /t { | ||
content_by_lua_block { | ||
local t = require("lib.test_admin").test | ||
local code, body = t('/apisix/admin/consumers/bar/credentials/credential_c', ngx.HTTP_DELETE) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
|
||
|
||
|
||
=== TEST 22: delete consumer bar | ||
--- config | ||
location /t { | ||
content_by_lua_block { | ||
local t = require("lib.test_admin").test | ||
local code, body = t('/apisix/admin/consumers/bar', ngx.HTTP_DELETE) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
|
||
|
||
|
||
=== TEST 23: delete consumer jack | ||
--- 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) | ||
} | ||
} | ||
--- request | ||
GET /t | ||
nic-6443 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -314,3 +314,5 @@ GET /t | |
} | ||
--- response_body | ||
all done | ||
--- error_log | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The testing of these changes is an expected change. These tests have an admin api call to the consumer and use a non-existent secret reference, which in the new logic triggers a secret lookup failure and an error log. It didn't break the original test path. |
||
failed to fetch secret value: no secret conf, secret_uri: $secret://aws/mysecret/jack/key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can keep this line unchanged.