├── LICENSE ├── README.md ├── doc └── imgs │ ├── wolf-rbac-1.png │ └── wolf-rbac-2.png └── lua └── wolf-rbac.lua /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 igeeky.io@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introductions 2 | 3 | `wolf-rbac`是一个基于[APISIX](https://github.com/apache/incubator-apisix)认证及授权(rbac)插件,它需要与 `consumer` 一起配合才能工作。同时需要添加 `wolf-rbac` 到一个 `service` 或 `route`中。 4 | rbac功能由[wolf](https://github.com/iGeeky/wolf)提供, 有关wolf的更多信息, 请参考[wolf文档](https://github.com/iGeeky/wolf)。 5 | 6 | 7 | 8 | ## Installation 9 | 10 | ### 请先自行安装apisix. 11 | 12 | ### 下载,安装 13 | ``` 14 | git clone https://github.com/iGeeky/apisix-plugin-rbac 15 | cd apisix-plugin-rbac 16 | cp lua/wolf-rbac.lua /usr/local/apisix/lua/apisix/plugins 17 | 18 | # 开发模式, 直接拷贝到源代码目录. 19 | cp lua/wolf-rbac.lua path/to/incubator-apisix/lua/apisix/plugins/ 20 | ``` 21 | 22 | ### 修改配置, 添加插件 23 | 24 | 修改配置文件 `/usr/local/apisix/conf/config.yaml`, 添加`wolf-rbac`到`plugins`中. 25 | ``` 26 | - wolf-rbac 27 | ``` 28 | 修改完成后, 请重启apisix. 29 | 30 | ## 配置项 31 | 32 | * `server`: 设置`wolf-server`的访问地址, 默认为: `http://127.0.0.1:10080`. 33 | * `appid`: 设置应用id, 该应用id, 应该是在wolf-console中已经添加的应用id. 34 | 35 | 36 | ## Getting Started 37 | 38 | ### 安装wolf并启动服务 39 | 40 | [Wolf快速起步](https://github.com/iGeeky/wolf/blob/master/quick-start-with-docker/README-CN.md) 41 | 42 | ### 添加应用, 管理员, 普通用户, 权限, 资源 及给用户授权. 43 | 44 | [Wolf管理使用](https://github.com/iGeeky/wolf/blob/master/docs/usage.md) 45 | 46 | ### 启动一个测试应用restful-demo 47 | 48 | ``` 49 | docker run -ti --rm -p 10095:10090 igeeky/restful-demo:latest 50 | ``` 51 | 52 | ### 在apisix中启用 `wolf-rbac` 53 | 54 | 1. 创建一个 consumer 对象,并设置插件 `wolf-rbac` 的值。 55 | 56 | ```shell 57 | curl http://127.0.0.1:9080/apisix/admin/consumers -X PUT -d ' 58 | { 59 | "username":"wolf_rbac", 60 | "plugins":{ 61 | "wolf-rbac":{ 62 | "server":"http://127.0.0.1:10080", 63 | "appid":"restful" 64 | } 65 | }, 66 | "desc":"wolf-rbac" 67 | }' 68 | 69 | ``` 70 | 你可以使用浏览器打开 dashboard:`http://127.0.0.1:9080/apisix/dashboard/`,通过 web 界面来完成上面的操作,先增加一个 consumer: 71 | ![](doc/imgs/wolf-rbac-1.png) 72 | 73 | 然后在 consumer 页面中添加 wolf-rbac 插件: 74 | ![](doc/imgs/wolf-rbac-2.png) 75 | 76 | 注意: 上面填写的`appid`需要在wolf控制中已经存在的. 77 | 78 | 2. 创建 Upstream 79 | 80 | ```shell 81 | curl http://127.0.0.1:9080/apisix/admin/upstreams/1 -X PUT -d ' 82 | { 83 | "desc":"restful", 84 | "nodes":{"127.0.0.1:10095":1}, 85 | "type":"roundrobin" 86 | }' 87 | ``` 88 | 89 | 3. 创建 Route 或 Service 对象,并开启 `wolf-rbac` 插件。 90 | 91 | ```shell 92 | curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d ' 93 | { 94 | "methods": ["GET"], 95 | "uri": "/*", 96 | "plugins": { 97 | "wolf-rbac": {} 98 | }, 99 | "upstream_id": 1 100 | }' 101 | ``` 102 | 103 | 如果通过命令行,无法创建成功, 或无法生效, 请使用apisix-dashboard进行添加. 104 | 105 | ### Test Plugin 106 | 107 | #### 首先进行登录获取 `wolf-rbac` token: 108 | 109 | 下面的`appid`, `username`, `password`必须为wolf系统中真实存在的. 110 | 111 | * 以POST application/json方式登陆. 112 | 113 | ```shell 114 | curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/login -i \ 115 | -H "Content-Type: application/json" \ 116 | -d '{"appid": "restful", "username":"test", "password":"user-password"}' 117 | 118 | HTTP/1.1 200 OK 119 | Date: Wed, 24 Jul 2019 10:33:31 GMT 120 | Content-Type: text/plain 121 | Transfer-Encoding: chunked 122 | Connection: keep-alive 123 | Server: APISIX web server 124 | {"rbac_token":"V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc4ODIzNzQ3LCJleHAiOjE1Nzk0Mjg1NDd9.4rGPHv9hWPZ4G0p2F82gQciY8WIE2qdYNvAT_22X_Co","user_info":{"nickname":"test","username":"test","id":"749"}} 125 | ``` 126 | 127 | * 以POST x-www-form-urlencoded方式登陆 128 | 129 | ```shell 130 | curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/login -i \ 131 | -H "Content-Type: application/x-www-form-urlencoded" \ 132 | -d 'appid=restful&username=test&password=user-password' 133 | ``` 134 | 135 | 136 | #### 使用获取到的 token 进行请求尝试 137 | 138 | * 缺少 token 139 | 140 | ```shell 141 | curl http://127.0.0.1:9080/api/user/list -i 142 | 143 | HTTP/1.1 401 Unauthorized 144 | ... 145 | {"message":"Missing rbac token in request"} 146 | ``` 147 | 148 | * token 放到请求头(Authorization)中: 149 | 150 | ```shell 151 | curl http://127.0.0.1:9080/api/user/list \ 152 | -H 'Authorization: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc4ODIzNzQ3LCJleHAiOjE1Nzk0Mjg1NDd9.4rGPHv9hWPZ4G0p2F82gQciY8WIE2qdYNvAT_22X_Co' -i 153 | 154 | HTTP/1.1 200 OK 155 | {....} 156 | ``` 157 | 158 | * token 放到请求头(x-rbac-token)中: 159 | 160 | ```shell 161 | curl http://127.0.0.1:9080/api/user/list \ 162 | -H 'x-rbac-token: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc4ODIzNzQ3LCJleHAiOjE1Nzk0Mjg1NDd9.4rGPHv9hWPZ4G0p2F82gQciY8WIE2qdYNvAT_22X_Co' -i 163 | 164 | HTTP/1.1 200 OK 165 | {....} 166 | ``` 167 | 168 | * token 放到请求参数中: 169 | 170 | ```shell 171 | curl 'http://127.0.0.1:9080/api/user/list?rbac_token=V1%23restful%23eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc4ODIzNzQ3LCJleHAiOjE1Nzk0Mjg1NDd9.4rGPHv9hWPZ4G0p2F82gQciY8WIE2qdYNvAT_22X_Co' -i 172 | 173 | HTTP/1.1 200 OK 174 | {....} 175 | ``` 176 | 177 | * token 放到 cookie 中: 178 | 179 | ```shell 180 | curl http://127.0.0.1:9080/api/user/list \ 181 | --cookie x-rbac-token=V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc4ODIzNzQ3LCJleHAiOjE1Nzk0Mjg1NDd9.4rGPHv9hWPZ4G0p2F82gQciY8WIE2qdYNvAT_22X_Co -i 182 | 183 | HTTP/1.1 200 OK 184 | {....} 185 | ``` 186 | 187 | 188 | ## License 189 | 190 | [MIT](./LICENSE) 191 | 192 | -------------------------------------------------------------------------------- /doc/imgs/wolf-rbac-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGeeky/apisix-plugin-rbac/bdb0bd117a7db57a07cfb05c2753345d4a24c9c6/doc/imgs/wolf-rbac-1.png -------------------------------------------------------------------------------- /doc/imgs/wolf-rbac-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGeeky/apisix-plugin-rbac/bdb0bd117a7db57a07cfb05c2753345d4a24c9c6/doc/imgs/wolf-rbac-2.png -------------------------------------------------------------------------------- /lua/wolf-rbac.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Licensed to the Apache Software Foundation (ASF) under one or more 3 | -- contributor license agreements. See the NOTICE file distributed with 4 | -- this work for additional information regarding copyright ownership. 5 | -- The ASF licenses this file to You under the Apache License, Version 2.0 6 | -- (the "License"); you may not use this file except in compliance with 7 | -- the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, software 12 | -- distributed under the License is distributed on an "AS IS" BASIS, 13 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | -- See the License for the specific language governing permissions and 15 | -- limitations under the License. 16 | -- 17 | 18 | local core = require("apisix.core") 19 | local consumer = require("apisix.consumer") 20 | local json = require("apisix.core.json") 21 | local ngx_re = require("ngx.re") 22 | local http = require("resty.http") 23 | local ipairs = ipairs 24 | local ngx = ngx 25 | local tostring = tostring 26 | local rawget = rawget 27 | local rawset = rawset 28 | local setmetatable = setmetatable 29 | local type = type 30 | local string = string 31 | 32 | local plugin_name = "wolf-rbac" 33 | 34 | 35 | local schema = { 36 | type = "object", 37 | properties = { 38 | appid = { 39 | type = "string", 40 | default = "unset" 41 | }, 42 | server = { 43 | type = "string", 44 | default = "http://127.0.0.1:10080" 45 | }, 46 | header_prefix = { 47 | type = "string", 48 | default = "X-" 49 | }, 50 | } 51 | } 52 | 53 | local _M = { 54 | version = 0.1, 55 | priority = 2555, 56 | type = 'auth', 57 | name = plugin_name, 58 | schema = schema, 59 | } 60 | 61 | 62 | local create_consume_cache 63 | do 64 | local consumer_ids = {} 65 | 66 | function create_consume_cache(consumers) 67 | core.table.clear(consumer_ids) 68 | 69 | for _, consumer in ipairs(consumers.nodes) do 70 | core.log.info("consumer node: ", core.json.delay_encode(consumer)) 71 | consumer_ids[consumer.auth_conf.appid] = consumer 72 | end 73 | 74 | return consumer_ids 75 | end 76 | 77 | end -- do 78 | 79 | local token_version = 'V1' 80 | local function create_rbac_token(appid, wolf_token) 81 | return token_version .. "#" .. appid .. "#" .. wolf_token 82 | end 83 | 84 | local function fail_response(message, init_values) 85 | local response = init_values or {} 86 | response.message = message 87 | return response 88 | end 89 | 90 | local function success_response(message, init_values) 91 | local response = init_values or {} 92 | response.message = message 93 | return response 94 | end 95 | 96 | local function parse_rbac_token(rbac_token) 97 | local res, err = ngx_re.split(rbac_token, "#", nil, nil, 3) 98 | if not res then 99 | return nil, err 100 | end 101 | 102 | if #res ~= 3 or res[1] ~= token_version then 103 | return nil, 'invalid rbac token: version' 104 | end 105 | local appid = res[2] 106 | local wolf_token = res[3] 107 | 108 | return {appid = appid, wolf_token = wolf_token} 109 | end 110 | 111 | local function new_headers() 112 | local t = {} 113 | local lt = {} 114 | local _mt = { 115 | __index = function(t, k) 116 | return rawget(lt, string.lower(k)) 117 | end, 118 | __newindex = function(t, k, v) 119 | rawset(t, k, v) 120 | rawset(lt, string.lower(k), v) 121 | end, 122 | } 123 | return setmetatable(t, _mt) 124 | end 125 | 126 | -- timeout in ms 127 | local function http_req(method, uri, body, myheaders, timeout) 128 | if myheaders == nil then myheaders = new_headers() end 129 | 130 | local httpc = http.new() 131 | if timeout then 132 | httpc:set_timeout(timeout) 133 | end 134 | 135 | local params = {method = method, headers = myheaders, body = body, 136 | ssl_verify = false} 137 | local res, err = httpc:request_uri(uri, params) 138 | if err then 139 | core.log.error("FAIL REQUEST [ ",core.json.delay_encode( 140 | {method = method, uri = uri, body = body, headers = myheaders}), 141 | " ] failed! res is nil, err:", err) 142 | return nil, err 143 | end 144 | 145 | return res 146 | end 147 | 148 | local function http_get(uri, myheaders, timeout) 149 | return http_req("GET", uri, nil, myheaders, timeout) 150 | end 151 | 152 | 153 | function _M.check_schema(conf) 154 | core.log.info("input conf: ", core.json.delay_encode(conf)) 155 | 156 | local ok, err = core.schema.check(schema, conf) 157 | if not ok then 158 | return false, err 159 | end 160 | 161 | return true 162 | end 163 | 164 | 165 | local function fetch_rbac_token(ctx) 166 | if ctx.var.arg_rbac_token then 167 | return ngx.unescape_uri(ctx.var.arg_rbac_token) 168 | end 169 | 170 | if ctx.var.http_authorization then 171 | return ctx.var.http_authorization 172 | end 173 | 174 | if ctx.var.http_x_rbac_token then 175 | return ctx.var.http_x_rbac_token 176 | end 177 | 178 | return ctx.var['cookie_x-rbac-token'] 179 | end 180 | 181 | 182 | local function check_url_permission(server, appid, action, resName, client_ip, wolf_token) 183 | local retry_max = 3 184 | local errmsg 185 | local userInfo 186 | local res 187 | local err 188 | local access_check_url = server .. "/wolf/rbac/access_check" 189 | local headers = new_headers() 190 | headers["x-rbac-token"] = wolf_token 191 | headers["Content-Type"] = "application/json; charset=utf-8" 192 | local args = { appID = appid, resName = resName, action = action, clientIP = client_ip} 193 | local url = access_check_url .. "?" .. ngx.encode_args(args) 194 | local timeout = 1000 * 10 195 | 196 | for i = 1, retry_max do 197 | -- TODO: read apisix info. 198 | res, err = http_get(url, headers, timeout) 199 | if err then 200 | break 201 | else 202 | core.log.info("check permission request:", url, ", status:", res.status, 203 | ",body:", core.json.delay_encode(res.body)) 204 | if res.status < 500 then 205 | break 206 | else 207 | core.log.info("request [curl -v ", url, "] failed! status:", res.status) 208 | if i < retry_max then 209 | ngx.sleep(0.1) 210 | end 211 | end 212 | end 213 | end 214 | 215 | if err then 216 | core.log.error("fail request: ", url, ", err:", err) 217 | return { 218 | status = 500, 219 | err = "request to wolf-server failed, err:" .. err 220 | } 221 | end 222 | 223 | if res.status ~= 200 and res.status ~= 401 then 224 | return { 225 | status = 500, 226 | err = 'request to wolf-server failed, status:' .. res.status 227 | } 228 | end 229 | 230 | local body, err = json.decode(res.body) 231 | if err then 232 | errmsg = 'check permission failed! parse response json failed!' 233 | core.log.error( "json.decode(", res.body, ") failed! err:", err) 234 | return {status = res.status, err = errmsg} 235 | else 236 | if body.data then 237 | userInfo = body.data.userInfo 238 | end 239 | errmsg = body.reason 240 | return {status = res.status, err = errmsg, userInfo = userInfo} 241 | end 242 | end 243 | 244 | 245 | function _M.rewrite(conf, ctx) 246 | local url = ctx.var.uri 247 | local action = ctx.var.request_method 248 | local client_ip = ctx.var.http_x_real_ip or core.request.get_ip(ctx) 249 | local perm_item = {action = action, url = url, clientIP = client_ip} 250 | core.log.info("hit wolf-rbac rewrite") 251 | 252 | local rbac_token = fetch_rbac_token(ctx) 253 | if rbac_token == nil then 254 | core.log.info("no permission to access ", 255 | core.json.delay_encode(perm_item), ", need login!") 256 | return 401, fail_response("Missing rbac token in request") 257 | end 258 | 259 | local tokenInfo, err = parse_rbac_token(rbac_token) 260 | core.log.info("token info: ", core.json.delay_encode(tokenInfo), 261 | ", err: ", err) 262 | if err then 263 | return 401, fail_response('invalid rbac token: parse failed') 264 | end 265 | 266 | local appid = tokenInfo.appid 267 | local wolf_token = tokenInfo.wolf_token 268 | perm_item.appid = appid 269 | perm_item.wolf_token = wolf_token 270 | 271 | local consumer_conf = consumer.plugin(plugin_name) 272 | if not consumer_conf then 273 | return 401, fail_response("Missing related consumer") 274 | end 275 | 276 | local consumers = core.lrucache.plugin(plugin_name, "consumers_key", 277 | consumer_conf.conf_version, 278 | create_consume_cache, consumer_conf) 279 | 280 | core.log.info("------ consumers: ", core.json.delay_encode(consumers)) 281 | local consumer = consumers[appid] 282 | if not consumer then 283 | core.log.error("consumer [", appid, "] not found") 284 | return 401, fail_response("Invalid appid in rbac token") 285 | end 286 | core.log.info("consumer: ", core.json.delay_encode(consumer)) 287 | local server = consumer.auth_conf.server 288 | 289 | local res = check_url_permission(server, appid, action, url, 290 | client_ip, wolf_token) 291 | core.log.info(" check_url_permission(", core.json.delay_encode(perm_item), 292 | ") res: ",core.json.delay_encode(res)) 293 | 294 | local username = nil 295 | local nickname = nil 296 | if type(res.userInfo) == 'table' then 297 | local userInfo = res.userInfo 298 | ctx.userInfo = userInfo 299 | local userId = userInfo.id 300 | username = userInfo.username 301 | nickname = userInfo.nickname or userInfo.username 302 | local prefix = consumer.auth_conf.header_prefix or '' 303 | core.response.set_header(prefix .. "UserId", userId) 304 | core.response.set_header(prefix .. "Username", username) 305 | core.response.set_header(prefix .. "Nickname", ngx.escape_uri(nickname)) 306 | core.request.set_header(prefix .. "UserId", userId) 307 | core.request.set_header(prefix .. "Username", username) 308 | core.request.set_header(prefix .. "Nickname", ngx.escape_uri(nickname)) 309 | end 310 | 311 | if res.status ~= 200 then 312 | -- no permission. 313 | core.log.error(" check_url_permission(", 314 | core.json.delay_encode(perm_item), 315 | ") failed, res: ",core.json.delay_encode(res)) 316 | return 401, fail_response(res.err, 317 | { username = username, nickname = nickname } 318 | ) 319 | end 320 | core.log.info("wolf-rbac check permission passed") 321 | end 322 | 323 | local function get_args() 324 | local ctx = ngx.ctx.api_ctx 325 | local args, err 326 | ngx.req.read_body() 327 | if string.find(ctx.var.http_content_type or "","application/json", 328 | 1, true) then 329 | args, err = json.decode(ngx.req.get_body_data()) 330 | if err then 331 | core.log.error("json.decode(", ngx.req.get_body_data(), ") failed! ", err) 332 | end 333 | else 334 | args = ngx.req.get_post_args() 335 | end 336 | 337 | return args 338 | end 339 | 340 | local function get_consumer(appid) 341 | local consumer_conf = consumer.plugin(plugin_name) 342 | if not consumer_conf then 343 | core.response.exit(500) 344 | end 345 | 346 | local consumers = core.lrucache.plugin(plugin_name, "consumers_key", 347 | consumer_conf.conf_version, 348 | create_consume_cache, consumer_conf) 349 | 350 | core.log.info("------ consumers: ", core.json.delay_encode(consumers)) 351 | local consumer = consumers[appid] 352 | if not consumer then 353 | core.log.info("request appid [", appid, "] not found") 354 | core.response.exit(400, 355 | fail_response("appid [" .. tostring(appid) .. "] not found") 356 | ) 357 | end 358 | return consumer 359 | end 360 | 361 | local function request_to_wolf_server(method, uri, headers, body) 362 | headers["Content-Type"] = "application/json; charset=utf-8" 363 | local timeout = 1000 * 5 364 | local request_debug = core.json.delay_encode( 365 | { 366 | method = method, uri = uri, body = body, 367 | headers = headers,timeout = timeout 368 | } 369 | ) 370 | 371 | core.log.info("request [", request_debug, "] ....") 372 | local res, err = http_req(method, uri, core.json.encode(body), headers, timeout) 373 | if err or not res then 374 | core.log.error("request [", request_debug, "] failed! err: ", err) 375 | return core.response.exit(500, 376 | fail_response("request to wolf-server failed! " .. tostring(err)) 377 | ) 378 | end 379 | core.log.info("request [", request_debug, "] status: ", res.status, 380 | ", body: ", res.body) 381 | 382 | if res.status ~= 200 then 383 | core.log.error("request [", request_debug, "] failed! status: ", 384 | res.status) 385 | return core.response.exit(500, 386 | fail_response("request to wolf-server failed! status:" 387 | .. tostring(res.status)) 388 | ) 389 | end 390 | local body, err = json.decode(res.body) 391 | if err or not body then 392 | core.log.error("request [", request_debug, "] failed! err:", err) 393 | return core.response.exit(500, fail_response("request to wolf-server failed!")) 394 | end 395 | if not body.ok then 396 | core.log.error("request [", request_debug, "] failed! response body:", 397 | core.json.delay_encode(body)) 398 | return core.response.exit(200, fail_response(body.reason)) 399 | end 400 | 401 | core.log.info("request [", request_debug, "] success! response body:", 402 | core.json.delay_encode(body)) 403 | return body 404 | end 405 | 406 | local function wolf_rbac_login() 407 | local args = get_args() 408 | if not args then 409 | return core.response.exit(400, fail_response("invalid request")) 410 | end 411 | if not args.appid then 412 | return core.response.exit(400, fail_response("appid is missing")) 413 | end 414 | 415 | local appid = args.appid 416 | local consumer = get_consumer(appid) 417 | core.log.info("consumer: ", core.json.delay_encode(consumer)) 418 | 419 | local uri = consumer.auth_conf.server .. '/wolf/rbac/login.rest' 420 | local headers = new_headers() 421 | local body = request_to_wolf_server('POST', uri, headers, args) 422 | 423 | local userInfo = body.data.userInfo 424 | local wolf_token = body.data.token 425 | 426 | local rbac_token = create_rbac_token(appid, wolf_token) 427 | core.response.exit(200, success_response(nil, {rbac_token = rbac_token, user_info = userInfo})) 428 | end 429 | 430 | local function get_wolf_token(ctx) 431 | core.log.info("hit wolf-rbac change_password api") 432 | local rbac_token = fetch_rbac_token(ctx) 433 | if rbac_token == nil then 434 | local url = ctx.var.uri 435 | local action = ctx.var.request_method 436 | local client_ip = core.request.get_ip(ctx) 437 | local perm_item = {action = action, url = url, clientIP = client_ip} 438 | core.log.info("no permission to access ", 439 | core.json.delay_encode(perm_item), ", need login!") 440 | return core.response.exit(401, fail_response("Missing rbac token in request")) 441 | end 442 | 443 | local tokenInfo, err = parse_rbac_token(rbac_token) 444 | core.log.info("token info: ", core.json.delay_encode(tokenInfo), 445 | ", err: ", err) 446 | if err then 447 | return core.response.exit(401, fail_response('invalid rbac token: parse failed')) 448 | end 449 | return tokenInfo 450 | end 451 | 452 | local function wolf_rbac_change_pwd() 453 | local args = get_args() 454 | 455 | local ctx = ngx.ctx.api_ctx 456 | local tokenInfo = get_wolf_token(ctx) 457 | local appid = tokenInfo.appid 458 | local wolf_token = tokenInfo.wolf_token 459 | local consumer = get_consumer(appid) 460 | core.log.info("consumer: ", core.json.delay_encode(consumer)) 461 | 462 | local uri = consumer.auth_conf.server .. '/wolf/rbac/change_pwd' 463 | local headers = new_headers() 464 | headers['x-rbac-token'] = wolf_token 465 | request_to_wolf_server('POST', uri, headers, args) 466 | core.response.exit(200, success_response('success to change password', { })) 467 | end 468 | 469 | local function wolf_rbac_user_info() 470 | local ctx = ngx.ctx.api_ctx 471 | local tokenInfo = get_wolf_token(ctx) 472 | local appid = tokenInfo.appid 473 | local wolf_token = tokenInfo.wolf_token 474 | local consumer = get_consumer(appid) 475 | core.log.info("consumer: ", core.json.delay_encode(consumer)) 476 | 477 | local uri = consumer.auth_conf.server .. '/wolf/rbac/user_info' 478 | local headers = new_headers() 479 | headers['x-rbac-token'] = wolf_token 480 | local body = request_to_wolf_server('GET', uri, headers, {}) 481 | local userInfo = body.data.userInfo 482 | core.response.exit(200, success_response(nil, {user_info = userInfo})) 483 | end 484 | 485 | function _M.api() 486 | return { 487 | { 488 | methods = {"POST"}, 489 | uri = "/apisix/plugin/wolf-rbac/login", 490 | handler = wolf_rbac_login, 491 | }, 492 | { 493 | methods = {"PUT"}, 494 | uri = "/apisix/plugin/wolf-rbac/change_pwd", 495 | handler = wolf_rbac_change_pwd, 496 | }, 497 | { 498 | methods = {"GET"}, 499 | uri = "/apisix/plugin/wolf-rbac/user_info", 500 | handler = wolf_rbac_user_info, 501 | }, 502 | } 503 | end 504 | 505 | return _M 506 | --------------------------------------------------------------------------------