Skip to content
Open
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
9 changes: 9 additions & 0 deletions apisix/plugins/multi-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ function _M.rewrite(conf, ctx)
local status_code
local errors = {}

for _, auth_plugin in pairs(auth_plugins) do
for auth_plugin_name, auth_plugin_conf in pairs(auth_plugin) do
local auth = plugin.get(auth_plugin_name)
if auth.clear_auth_headers then
auth.clear_auth_headers(auth_plugin_conf, ctx)
end
end
end

for k, auth_plugin in pairs(auth_plugins) do
for auth_plugin_name, auth_plugin_conf in pairs(auth_plugin) do
local auth = plugin.get(auth_plugin_name)
Expand Down
145 changes: 102 additions & 43 deletions apisix/plugins/wolf-rbac.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
local core = require("apisix.core")
local consumer = require("apisix.consumer")
local json = require("apisix.core.json")
local auth_utils = require("apisix.utils.auth")
local sleep = core.sleep
local ngx_re = require("ngx.re")
local http = require("resty.http")
Expand All @@ -31,39 +32,68 @@ local req_read_body = ngx.req.read_body
local req_get_body_data = ngx.req.get_body_data

local plugin_name = "wolf-rbac"
local default_server = "http://127.0.0.1:12180"
local default_header_prefix = "X-"


local schema = {
type = "object",
properties = {
appid = {
type = "string",
default = "unset"
},
server = {
type = "string",
default = "http://127.0.0.1:12180"
default = default_server
},
header_prefix = {
type = "string",
default = "X-"
default = default_header_prefix
},
ssl_verify = {
type = "boolean",
},
}
}

local consumer_schema = {
type = "object",
properties = {
appid = {
type = "string",
},
},
required = {"appid"},
}

local _M = {
version = 0.1,
priority = 2555,
type = 'auth',
name = plugin_name,
schema = schema,
consumer_schema = consumer_schema,
}


local token_version = 'V1'
local public_api_uris = {
["/apisix/plugin/wolf-rbac/login"] = true,
["/apisix/plugin/wolf-rbac/change_pwd"] = true,
["/apisix/plugin/wolf-rbac/user_info"] = true,
}


local function clear_identity_headers(conf, ctx)
local prefix = conf.header_prefix or default_header_prefix
core.request.set_header(ctx, prefix .. "UserId", nil)
core.request.set_header(ctx, prefix .. "Username", nil)
core.request.set_header(ctx, prefix .. "Nickname", nil)
end


function _M.clear_auth_headers(conf, ctx)
clear_identity_headers(conf, ctx)
end


local function create_rbac_token(appid, wolf_token)
return token_version .. "#" .. appid .. "#" .. wolf_token
end
Expand Down Expand Up @@ -142,7 +172,11 @@ local function http_get(uri, myheaders, timeout, ssl_verify)
end


function _M.check_schema(conf)
function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_CONSUMER then
return core.schema.check(consumer_schema, conf)
end

local ok, err = core.schema.check(schema, conf)
if not ok then
return false, err
Expand All @@ -158,6 +192,29 @@ function _M.check_schema(conf)
end


local function get_route_plugin_conf(ctx)
for i = 1, #ctx.plugins, 2 do
if ctx.plugins[i].name == plugin_name then
return ctx.plugins[i + 1]
end
end

return core.response.exit(500, fail_response("Missing wolf-rbac configuration"))
end


local function is_wolf_public_api(ctx)
for i = 1, #ctx.plugins, 2 do
if ctx.plugins[i].name == "public-api" then
local conf = ctx.plugins[i + 1]
return public_api_uris[conf.uri or ctx.var.uri] == true
end
end

return false
end


local function fetch_rbac_token(ctx)
if ctx.var.arg_rbac_token then
return ngx.unescape_uri(ctx.var.arg_rbac_token)
Expand Down Expand Up @@ -239,6 +296,14 @@ end


function _M.rewrite(conf, ctx)
if not auth_utils.is_running_under_multi_auth(ctx) then
clear_identity_headers(conf, ctx)
end

if is_wolf_public_api(ctx) then
return
end

local url = ctx.var.uri
local action = ctx.var.request_method
local client_ip = core.request.get_remote_client_ip(ctx)
Expand Down Expand Up @@ -276,34 +341,19 @@ function _M.rewrite(conf, ctx)
return 401, fail_response("Invalid appid in rbac token")
end
core.log.info("consumer appid: ", appid)
local server = cur_consumer.auth_conf.server
local ssl_verify = cur_consumer.auth_conf.ssl_verify

local res = check_url_permission(server, appid, action, url,
client_ip, wolf_token, ssl_verify)
local res = check_url_permission(conf.server or default_server, appid, action, url,
client_ip, wolf_token, conf.ssl_verify)
core.log.info(" check_url_permission(appid: ", appid,
", action: ", action, ", url: ", url,
") res status: ", res.status, ", err: ", res.err)

local username = nil
local nickname = nil
local prefix = cur_consumer.auth_conf.header_prefix or ''
-- drop client-supplied identity headers before trusting the auth response
core.request.set_header(ctx, prefix .. "UserId", nil)
core.request.set_header(ctx, prefix .. "Username", nil)
core.request.set_header(ctx, prefix .. "Nickname", nil)
if type(res.userInfo) == 'table' then
local userInfo = res.userInfo
ctx.userInfo = userInfo
local userId = userInfo.id
username = userInfo.username
nickname = userInfo.nickname or userInfo.username
core.response.set_header(prefix .. "UserId", userId)
core.response.set_header(prefix .. "Username", username)
core.response.set_header(prefix .. "Nickname", ngx.escape_uri(nickname))
core.request.set_header(ctx, prefix .. "UserId", userId)
core.request.set_header(ctx, prefix .. "Username", username)
core.request.set_header(ctx, prefix .. "Nickname", ngx.escape_uri(nickname))
end

if res.status ~= 200 then
Expand All @@ -312,12 +362,21 @@ function _M.rewrite(conf, ctx)
") failed, res status: ", res.status, ", err: ", res.err)
return res.status, fail_response(res.err, { username = username, nickname = nickname })
end
if type(res.userInfo) == 'table' then
local prefix = conf.header_prefix or default_header_prefix
local userId = res.userInfo.id
core.response.set_header(prefix .. "UserId", userId)
core.response.set_header(prefix .. "Username", username)
core.response.set_header(prefix .. "Nickname", ngx.escape_uri(nickname))
core.request.set_header(ctx, prefix .. "UserId", userId)
core.request.set_header(ctx, prefix .. "Username", username)
core.request.set_header(ctx, prefix .. "Nickname", ngx.escape_uri(nickname))
end
consumer.attach_consumer(ctx, cur_consumer, consumer_conf)
core.log.info("wolf-rbac check permission passed")
end

local function get_args()
local ctx = ngx.ctx.api_ctx
local function get_args(ctx)
local args, err
req_read_body()
if string.find(ctx.var.http_content_type or "","application/json",
Expand Down Expand Up @@ -396,8 +455,8 @@ local function request_to_wolf_server(method, uri, headers, body, ssl_verify)
return body
end

local function wolf_rbac_login()
local args = get_args()
local function wolf_rbac_login(ctx)
local args = get_args(ctx)
if not args then
return core.response.exit(400, fail_response("invalid request"))
end
Expand All @@ -406,12 +465,13 @@ local function wolf_rbac_login()
end

local appid = args.appid
local consumer = get_consumer(appid)
get_consumer(appid)
core.log.info("consumer appid: ", appid)

local uri = consumer.auth_conf.server .. '/wolf/rbac/login.rest'
local conf = get_route_plugin_conf(ctx)
local uri = (conf.server or default_server) .. '/wolf/rbac/login.rest'
local headers = new_headers()
local body = request_to_wolf_server('POST', uri, headers, args, consumer.auth_conf.ssl_verify)
local body = request_to_wolf_server('POST', uri, headers, args, conf.ssl_verify)

local userInfo = body.data.userInfo
local wolf_token = body.data.token
Expand Down Expand Up @@ -442,35 +502,34 @@ local function get_wolf_token(ctx)
return tokenInfo
end

local function wolf_rbac_change_pwd()
local args = get_args()

local ctx = ngx.ctx.api_ctx
local function wolf_rbac_change_pwd(ctx)
local args = get_args(ctx)
local tokenInfo = get_wolf_token(ctx)
local appid = tokenInfo.appid
local wolf_token = tokenInfo.wolf_token
local consumer = get_consumer(appid)
get_consumer(appid)
core.log.info("consumer appid: ", appid)

local uri = consumer.auth_conf.server .. '/wolf/rbac/change_pwd'
local conf = get_route_plugin_conf(ctx)
local uri = (conf.server or default_server) .. '/wolf/rbac/change_pwd'
local headers = new_headers()
headers['x-rbac-token'] = wolf_token
request_to_wolf_server('POST', uri, headers, args, consumer.auth_conf.ssl_verify)
request_to_wolf_server('POST', uri, headers, args, conf.ssl_verify)
core.response.exit(200, success_response('success to change password', { }))
end

local function wolf_rbac_user_info()
local ctx = ngx.ctx.api_ctx
local function wolf_rbac_user_info(ctx)
local tokenInfo = get_wolf_token(ctx)
local appid = tokenInfo.appid
local wolf_token = tokenInfo.wolf_token
local consumer = get_consumer(appid)
get_consumer(appid)
core.log.info("consumer appid: ", appid)

local uri = consumer.auth_conf.server .. '/wolf/rbac/user_info'
local conf = get_route_plugin_conf(ctx)
local uri = (conf.server or default_server) .. '/wolf/rbac/user_info'
local headers = new_headers()
headers['x-rbac-token'] = wolf_token
local body = request_to_wolf_server('GET', uri, headers, {}, consumer.auth_conf.ssl_verify)
local body = request_to_wolf_server('GET', uri, headers, {}, conf.ssl_verify)
local userInfo = body.data.userInfo
core.response.exit(200, success_response(nil, {user_info = userInfo}))
end
Expand Down
28 changes: 19 additions & 9 deletions docs/en/latest/plugins/wolf-rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ The `wolf-rbac` Plugin provides a [role-based access control](https://en.wikiped

## Attributes

| Name | Type | Required | Default | Description |
|---------------|--------|----------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| server | string | False | "http://127.0.0.1:12180" | Service address of wolf server. |
| appid | string | False | "unset" | App id added in wolf console. This field supports saving the value in Secret Manager using the [APISIX Secret](../terminology/secret.md) resource. |
| header_prefix | string | False | "X-" | Prefix for a custom HTTP header. After authentication is successful, three headers will be added to the request header (for backend) and response header (for frontend) namely: `X-UserId`, `X-Username`, and `X-Nickname`. |
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| appid | string | True | N/A | Consumer application ID configured in Wolf. This field supports saving the value in Secret Manager using the [APISIX Secret](../terminology/secret.md) resource. |
| server | string | False | "http://127.0.0.1:12180" | Route or Service address of the Wolf server. |
| header_prefix | string | False | "X-" | Route or Service prefix for the identity headers added after successful authentication: `{prefix}UserId`, `{prefix}Username`, and `{prefix}Nickname`. |
| ssl_verify | boolean | False | false | Whether to verify the Wolf server's TLS certificate. Configured on a Route or Service. |

## API

Expand Down Expand Up @@ -79,7 +80,6 @@ curl http://127.0.0.1:9180/apisix/admin/consumers -H "X-API-KEY: $admin_key" -X
"username":"wolf_rbac",
"plugins":{
"wolf-rbac":{
"server":"http://127.0.0.1:12180",
"appid":"restful"
}
},
Expand All @@ -101,7 +101,10 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X
"methods": ["GET"],
"uri": "/*",
"plugins": {
"wolf-rbac": {}
"wolf-rbac": {
"server": "http://127.0.0.1:12180",
"header_prefix": "X-"
}
},
"upstream": {
"type": "roundrobin",
Expand All @@ -114,6 +117,10 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X

You can also use the [APISIX Dashboard](/docs/dashboard/USER_GUIDE) to complete the operation through a web UI.

## Upgrade

Move `server`, `header_prefix`, and `ssl_verify` from each Consumer to every Route or Service that uses `wolf-rbac`. Keep only `appid` on the Consumer. If Consumers on one Route used different values, split them across Routes or Services, or standardize the values before upgrading.

<!--
![add a consumer](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/plugin/wolf-rbac-1.png)

Expand All @@ -129,12 +136,15 @@ curl http://127.0.0.1:9180/apisix/admin/routes/wal -H "X-API-KEY: $admin_key" -X
{
"uri": "/apisix/plugin/wolf-rbac/login",
"plugins": {
"public-api": {}
"public-api": {},
"wolf-rbac": {
"server": "http://127.0.0.1:12180"
}
}
}'
```

Similarly, you can setup the Routes for `change_pwd` and `user_info`.
Similarly, configure both Plugins on the Routes for `change_pwd` and `user_info`.

You can now login and get a wolf `rbac_token`:

Expand Down
Loading
Loading