Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: allow create consumers with multiple auth plugins #2898

Merged
merged 1 commit into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions apisix/admin/consumers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ local function check_conf(conf)
local plugin_obj = plugin.get(name)
if plugin_obj.type == 'auth' then
count_auth_plugin = count_auth_plugin + 1
if count_auth_plugin > 1 then
return nil, {error_msg = "only one auth plugin is allowed"}
end
end
end

Expand Down
2 changes: 0 additions & 2 deletions apisix/consumer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ local function plugin_consumer()
new_consumer.auth_conf = config
core.log.info("consumer:", core.json.delay_encode(new_consumer))
core.table.insert(plugins[name].nodes, new_consumer)

break
end
end

Expand Down
4 changes: 4 additions & 0 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ function _M.http_access_phase()
api_ctx.consumer,
api_ctx
)

core.log.info("find consumer ", api_ctx.consumer.username,
", config changed: ", changed)

if changed then
core.table.clear(api_ctx.plugins)
api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
Expand Down
4 changes: 3 additions & 1 deletion doc/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ Config Example:
}
```

The binding authentication and authorization plug-in is a bit special. When it needs to be used in conjunction with the consumer, it needs to provide user name, password and other information; on the other hand, when it is bound with route / service, it does not require any parameters. Because at this time, it is based on the user request data to infer which consumer the user corresponds to.
The binding authentication plug-in is a bit special. When it needs to be used in conjunction with the consumer, it needs to provide user name, password and other information; on the other hand, when it is bound with route / service, it does not require any parameters. Because at this time, it is based on the user request data to infer which consumer the user corresponds to.

Example:

Expand All @@ -476,6 +476,8 @@ Date: Thu, 26 Dec 2019 08:17:49 GMT
{"node":{"value":{"username":"jack","plugins":{"key-auth":{"key":"auth-one"},"limit-count":{"time_window":60,"count":2,"rejected_code":503,"key":"remote_addr","policy":"local"}}},"createdIndex":64,"key":"\/apisix\/consumers\/jack","modifiedIndex":64},"prevNode":{"value":"{\"username\":\"jack\",\"plugins\":{\"key-auth\":{\"key\":\"auth-one\"},\"limit-count\":{\"time_window\":60,\"count\":2,\"rejected_code\":503,\"key\":\"remote_addr\",\"policy\":\"local\"}}}","createdIndex":63,"key":"\/apisix\/consumers\/jack","modifiedIndex":63},"action":"set"}
```

Since `v2.2`, we can bind multiple authentication plugins to the same consumer.

> Response Parameters

Return response from etcd currently.
Expand Down
4 changes: 3 additions & 1 deletion doc/zh-cn/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ consumer 对象 json 配置内容:
}
```

绑定认证授权插件有些特别,当它需要与 consumer 联合使用时,需要提供用户名、密码等信息;另一方面,当它与 route/service 绑定时,是不需要任何参数的。因为这时候是根据用户请求数据来反向推出用户对应的是哪个 consumer
绑定认证插件有些特别,当它需要与 consumer 联合使用时,需要提供用户名、密码等信息;另一方面,当它与 route/service 绑定时,是不需要任何参数的。因为这时候是根据用户请求数据来反向推出用户对应的是哪个 consumer

示例:

Expand Down Expand Up @@ -491,6 +491,8 @@ Date: Thu, 26 Dec 2019 08:17:49 GMT
{"node":{"value":{"username":"jack","plugins":{"key-auth":{"key":"auth-one"},"limit-count":{"time_window":60,"count":2,"rejected_code":503,"key":"remote_addr","policy":"local"}}},"createdIndex":64,"key":"\/apisix\/consumers\/jack","modifiedIndex":64},"prevNode":{"value":"{\"username\":\"jack\",\"plugins\":{\"key-auth\":{\"key\":\"auth-one\"},\"limit-count\":{\"time_window\":60,\"count\":2,\"rejected_code\":503,\"key\":\"remote_addr\",\"policy\":\"local\"}}}","createdIndex":63,"key":"\/apisix\/consumers\/jack","modifiedIndex":63},"action":"set"}
```

从 `v2.2` 版本之后,同一个 consumer 可以绑定多个认证插件。

> 应答参数

目前是直接返回与 etcd 交互后的结果。
Expand Down
1 change: 0 additions & 1 deletion t/config-center-yaml/stream-route.t
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ run_tests();

__DATA__


=== TEST 1: sanity
--- apisix_yaml
stream_routes:
Expand Down
236 changes: 194 additions & 42 deletions t/node/consumer-plugin.t
Original file line number Diff line number Diff line change
Expand Up @@ -151,47 +151,7 @@ apikey: auth-one



=== TEST 6: two 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"
},
"key-auth": {
"key": "auth-one"
},
"jwt-auth": {
"key": "auth-one"
}
}
}]]
)

ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"only one auth plugin is allowed"}
--- no_error_log
[error]



=== TEST 7: missing auth plugins (not allow)
=== TEST 6: missing auth plugins (not allow)
--- config
location /t {
content_by_lua_block {
Expand Down Expand Up @@ -225,7 +185,7 @@ GET /t



=== TEST 8: use the new configuration after the consumer's configuration is updated
=== TEST 7: use the new configuration after the consumer's configuration is updated
--- config
location /t {
content_by_lua_block {
Expand Down Expand Up @@ -289,3 +249,195 @@ GET /t
{"200":4,"503":1}
--- no_error_log
[error]



=== TEST 8: consumer with multiple auth plugins
--- 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": "John_Doe",
"desc": "new consumer",
"plugins": {
"key-auth": {
"key": "consumer-plugin-John_Doe"
},
"hmac-auth": {
"access_key": "my-access-key",
"secret_key": "my-secret-key",
"clock_skew": 1
}
}
}]],
[[{
"node": {
"value": {
"username": "John_Doe",
"desc": "new consumer",
"plugins": {
"key-auth": {
"key": "consumer-plugin-John_Doe"
},
"hmac-auth": {
"access_key": "my-access-key",
"secret_key": "my-secret-key",
"clock_skew": 1
}
}
}
},
"action": "set"
}]]
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 9: bind to routes
--- 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.log(ngx.ERR, "failed to bind route 1")
ngx.status = code
ngx.say(body)
return
end

local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"plugins": {
"hmac-auth": {}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/status"
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 10: hit consumer, key-auth
--- request
GET /hello
--- more_headers
apikey: consumer-plugin-John_Doe
--- response_body
hello world
--- error_log
find consumer John_Doe
--- no_error_log
[error]



=== TEST 11: hit consumer, hmac-auth
--- config
location /t {
content_by_lua_block {
local ngx_time = ngx.time
local ngx_http_time = ngx.http_time
local core = require("apisix.core")
local t = require("lib.test_admin")
local hmac = require("resty.hmac")
local ngx_encode_base64 = ngx.encode_base64

local secret_key = "my-secret-key"
local timestamp = ngx_time()
local gmt = ngx_http_time(timestamp)
local access_key = "my-access-key"
local custom_header_a = "asld$%dfasf"
local custom_header_b = "23879fmsldfk"

local signing_string = {
"GET",
"/status",
"",
access_key,
gmt,
"x-custom-header-a:" .. custom_header_a,
"x-custom-header-b:" .. custom_header_b
}
signing_string = core.table.concat(signing_string, "\n") .. "\n"
core.log.info("signing_string:", signing_string)

local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string)
core.log.info("signature:", ngx_encode_base64(signature))
local headers = {}
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
headers["Date"] = gmt
headers["X-HMAC-ACCESS-KEY"] = access_key
headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b"
headers["x-custom-header-a"] = custom_header_a
headers["x-custom-header-b"] = custom_header_b

local code, body = t.test('/status',
ngx.HTTP_GET,
nil,
nil,
headers
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]
--- error_log
find consumer John_Doe