Skip to content

Commit

Permalink
feat: allow create consumers with multiple auth plugins (#2898)
Browse files Browse the repository at this point in the history
Fix #2850
  • Loading branch information
spacewander authored Dec 1, 2020
1 parent 1526ea5 commit a63b6b8
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 50 deletions.
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 @@ -516,6 +516,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

0 comments on commit a63b6b8

Please sign in to comment.