mirror of
https://gitee.com/iresty/apisix.git
synced 2024-11-30 02:57:49 +08:00
feat: Added authz-casbin plugin and doc and tests for it (#4710)
Signed-off-by: Rushikesh Tote <rushi.tote@gmail.com> Co-authored-by: 罗泽轩 <spacewanderlzx@gmail.com> Co-authored-by: tzssangglass <tzssangglass@gmail.com>
This commit is contained in:
parent
65a2d63717
commit
183351cd9f
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -89,7 +89,7 @@ jobs:
|
||||
tar zxvf ${{ steps.branch_env.outputs.fullname }}
|
||||
|
||||
- name: Linux Get dependencies
|
||||
run: sudo apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl
|
||||
run: sudo apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl libpcre3 libpcre3-dev
|
||||
|
||||
- name: Linux Before install
|
||||
run: sudo ./ci/${{ matrix.os_name }}_runner.sh before_install
|
||||
|
137
apisix/plugins/authz-casbin.lua
Normal file
137
apisix/plugins/authz-casbin.lua
Normal file
@ -0,0 +1,137 @@
|
||||
--
|
||||
-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
-- contributor license agreements. See the NOTICE file distributed with
|
||||
-- this work for additional information regarding copyright ownership.
|
||||
-- The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
-- (the "License"); you may not use this file except in compliance with
|
||||
-- the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
|
||||
local casbin = require("casbin")
|
||||
local core = require("apisix.core")
|
||||
local plugin = require("apisix.plugin")
|
||||
local ngx = ngx
|
||||
local get_headers = ngx.req.get_headers
|
||||
|
||||
local plugin_name = "authz-casbin"
|
||||
|
||||
local schema = {
|
||||
type = "object",
|
||||
properties = {
|
||||
model_path = { type = "string" },
|
||||
policy_path = { type = "string" },
|
||||
model = { type = "string" },
|
||||
policy = { type = "string" },
|
||||
username = { type = "string"}
|
||||
},
|
||||
oneOf = {
|
||||
{required = {"model_path", "policy_path", "username"}},
|
||||
{required = {"model", "policy", "username"}}
|
||||
},
|
||||
additionalProperties = false
|
||||
}
|
||||
|
||||
local metadata_schema = {
|
||||
type = "object",
|
||||
properties = {
|
||||
model = {type = "string"},
|
||||
policy = {type = "string"},
|
||||
},
|
||||
required = {"model", "policy"},
|
||||
additionalProperties = false
|
||||
}
|
||||
|
||||
local _M = {
|
||||
version = 0.1,
|
||||
priority = 2560,
|
||||
name = plugin_name,
|
||||
schema = schema,
|
||||
metadata_schema = metadata_schema
|
||||
}
|
||||
|
||||
function _M.check_schema(conf, schema_type)
|
||||
if schema_type == core.schema.TYPE_METADATA then
|
||||
return core.schema.check(metadata_schema, conf)
|
||||
end
|
||||
local ok, err = core.schema.check(schema, conf)
|
||||
if ok then
|
||||
return true
|
||||
else
|
||||
local metadata = plugin.plugin_metadata(plugin_name)
|
||||
if metadata and metadata.value and conf.username then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false, err
|
||||
end
|
||||
|
||||
local casbin_enforcer
|
||||
|
||||
local function new_enforcer_if_need(conf)
|
||||
if conf.model_path and conf.policy_path then
|
||||
local model_path = conf.model_path
|
||||
local policy_path = conf.policy_path
|
||||
if not conf.casbin_enforcer then
|
||||
conf.casbin_enforcer = casbin:new(model_path, policy_path)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if conf.model and conf.policy then
|
||||
local model = conf.model
|
||||
local policy = conf.policy
|
||||
if not conf.casbin_enforcer then
|
||||
conf.casbin_enforcer = casbin:newEnforcerFromText(model, policy)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local metadata = plugin.plugin_metadata(plugin_name)
|
||||
if not (metadata and metadata.value.model and metadata.value.policy) then
|
||||
return nil, "not enough configuration to create enforcer"
|
||||
end
|
||||
|
||||
local modifiedIndex = metadata.modifiedIndex
|
||||
if not casbin_enforcer or casbin_enforcer.modifiedIndex ~= modifiedIndex then
|
||||
local model = metadata.value.model
|
||||
local policy = metadata.value.policy
|
||||
casbin_enforcer = casbin:newEnforcerFromText(model, policy)
|
||||
casbin_enforcer.modifiedIndex = modifiedIndex
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function _M.rewrite(conf, ctx)
|
||||
-- creates an enforcer when request sent for the first time
|
||||
local ok, err = new_enforcer_if_need(conf)
|
||||
if not ok then
|
||||
return 503, {message = err}
|
||||
end
|
||||
|
||||
local path = ctx.var.uri
|
||||
local method = ctx.var.method
|
||||
local username = get_headers()[conf.username] or "anonymous"
|
||||
|
||||
if conf.casbin_enforcer then
|
||||
if not conf.casbin_enforcer:enforce(username, path, method) then
|
||||
return 403, {message = "Access Denied"}
|
||||
end
|
||||
else
|
||||
if not casbin_enforcer:enforce(username, path, method) then
|
||||
return 403, {message = "Access Denied"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
return _M
|
@ -99,6 +99,9 @@ t/toolkit
|
||||
# Exclude subcomponents files
|
||||
apisix/balancer/ewma.lua
|
||||
|
||||
# Exclude plugin-specific configuration files
|
||||
t/plugin/authz-casbin
|
||||
|
||||
[Options]
|
||||
# Not all code files allow licenses to appear starting at the first character
|
||||
# of the file. This option tells the scan to allow licenses to appear starting
|
||||
|
@ -27,7 +27,7 @@ install_dependencies() {
|
||||
|
||||
# install openresty to make apisix's rpm test work
|
||||
yum install -y yum-utils && yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
|
||||
yum install -y openresty openresty-debug openresty-openssl111-debug-devel
|
||||
yum install -y openresty openresty-debug openresty-openssl111-debug-devel pcre pcre-devel
|
||||
|
||||
# install luarocks
|
||||
./utils/linux-install-luarocks.sh
|
||||
|
@ -285,6 +285,7 @@ plugins: # plugin list (sorted by priority)
|
||||
- uri-blocker # priority: 2900
|
||||
- request-validation # priority: 2800
|
||||
- openid-connect # priority: 2599
|
||||
- authz-casbin # priority: 2560
|
||||
- wolf-rbac # priority: 2555
|
||||
- hmac-auth # priority: 2530
|
||||
- basic-auth # priority: 2520
|
||||
|
@ -64,7 +64,8 @@
|
||||
"plugins/authz-keycloak",
|
||||
"plugins/wolf-rbac",
|
||||
"plugins/openid-connect",
|
||||
"plugins/hmac-auth"
|
||||
"plugins/hmac-auth",
|
||||
"plugins/authz-casbin"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ sudo yum install yum-utils
|
||||
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
|
||||
|
||||
# install OpenResty and some compilation tools
|
||||
sudo yum install -y openresty curl git gcc openresty-openssl111-devel unzip
|
||||
sudo yum install -y openresty curl git gcc openresty-openssl111-devel unzip pcre pcre-devel
|
||||
|
||||
# install LuaRocks
|
||||
curl https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh -sL | bash -
|
||||
@ -81,7 +81,7 @@ tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \
|
||||
sudo cp -a etcd etcdctl /usr/bin/
|
||||
|
||||
# install OpenResty and some compilation tools
|
||||
sudo yum install -y openresty curl git gcc openresty-openssl111-devel
|
||||
sudo yum install -y openresty curl git gcc openresty-openssl111-devel pcre pcre-devel
|
||||
|
||||
# install LuaRocks
|
||||
curl https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh -sL | bash -
|
||||
@ -107,7 +107,7 @@ tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \
|
||||
sudo cp -a etcd etcdctl /usr/bin/
|
||||
|
||||
# install OpenResty and some compilation tools
|
||||
sudo apt-get install -y git openresty curl openresty-openssl111-dev make gcc
|
||||
sudo apt-get install -y git openresty curl openresty-openssl111-dev make gcc libpcre3 libpcre3-dev
|
||||
|
||||
# install LuaRocks
|
||||
curl https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh -sL | bash -
|
||||
@ -138,7 +138,7 @@ tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \
|
||||
sudo cp -a etcd etcdctl /usr/bin/
|
||||
|
||||
# install OpenResty and some compilation tools
|
||||
sudo apt-get install -y git openresty curl make openresty-openssl111-dev
|
||||
sudo apt-get install -y git openresty curl make openresty-openssl111-dev libpcre3 libpcre3-dev
|
||||
|
||||
# install LuaRocks
|
||||
curl https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh -sL | bash -
|
||||
@ -151,7 +151,7 @@ nohup etcd &
|
||||
|
||||
```shell
|
||||
# install OpenResty, etcd and some compilation tools
|
||||
brew install openresty/brew/openresty luarocks lua@5.1 etcd curl git
|
||||
brew install openresty/brew/openresty luarocks lua@5.1 etcd curl git pcre
|
||||
|
||||
# start etcd server
|
||||
brew services start etcd
|
||||
|
250
docs/en/latest/plugins/authz-casbin.md
Normal file
250
docs/en/latest/plugins/authz-casbin.md
Normal file
@ -0,0 +1,250 @@
|
||||
---
|
||||
title: authz-casbin
|
||||
---
|
||||
|
||||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
- [**Name**](#name)
|
||||
- [**Attributes**](#attributes)
|
||||
- [**Metadata**](#metadata)
|
||||
- [**How To Enable**](#how-to-enable)
|
||||
- [**Test Plugin**](#test-plugin)
|
||||
- [**Disable Plugin**](#disable-plugin)
|
||||
- [**Examples**](#examples)
|
||||
|
||||
## Name
|
||||
|
||||
`authz-casbin` is an authorization plugin based on [Lua Casbin](https://github.com/casbin/lua-casbin/). This plugin supports powerful authorization scenarios based on various access control models.
|
||||
|
||||
For detailed documentation on how to create model and policy, refer [Casbin](https://casbin.org/docs/en/supported-models).
|
||||
|
||||
## Attributes
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ----------- | ------ | ----------- | ------- | ----- | ------------------------------------------------------------ |
|
||||
| model_path | string | required | | | The path of the Casbin model configuration file. |
|
||||
| policy_path | string | required | | | The path of the Casbin policy file. |
|
||||
| model | string | required | | | The Casbin model configuration in text format. |
|
||||
| policy | string | required | | | The Casbin policy in text format. |
|
||||
| username | string | required | | | The header you will be using in request to pass the username (subject). |
|
||||
|
||||
**NOTE**: You must either specify `model_path`, `policy_path` and `username` in plugin config or specify `model`, `policy` and `username` in the plugin config for the configuration to be valid. Or if you wish to use a global Casbin configuration, you can first specify `model` and `policy` in the plugin metadata and only `username` in the plugin configuration, all routes will use the plugin metadata configuration in this way.
|
||||
|
||||
## Metadata
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ----------- | ------ | ----------- | ------- | ----- | ---------------------------------------------------------------------- |
|
||||
| model | string | required | | | The Casbin model configuration in text format. |
|
||||
| policy | string | required | | | The Casbin policy in text format. |
|
||||
|
||||
## How To Enable
|
||||
|
||||
You can enable the plugin on any route either by using the model/policy file paths or directly using the model/policy text.
|
||||
|
||||
### By using file paths
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
|
||||
{
|
||||
"plugins": {
|
||||
"authz-casbin": {
|
||||
"model_path": "/path/to/model.conf",
|
||||
"policy_path": "/path/to/policy.csv",
|
||||
"username": "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/*"
|
||||
}'
|
||||
```
|
||||
|
||||
This will create a Casbin enforcer from the model and policy files at your first request.
|
||||
|
||||
### By using model/policy text
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
|
||||
{
|
||||
"plugins": {
|
||||
"authz-casbin": {
|
||||
"model": "[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
|
||||
|
||||
"policy": "p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, alice, admin",
|
||||
|
||||
"username": "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/*"
|
||||
}'
|
||||
```
|
||||
|
||||
This will create a Casbin enforcer from the model and policy text at your first request.
|
||||
|
||||
### By using model/policy text using plugin metadata
|
||||
|
||||
First, send a `PUT` request to add the model and policy text to the plugin's metadata using the Admin API. All routes configured in this way will use a single Casbin enforcer with plugin metadata configuration. You can also update the model/policy this way, the plugin will automatically update itself with the updated configuration.
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/authz-casbin -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
|
||||
{
|
||||
"model": "[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
|
||||
|
||||
"policy": "p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, alice, admin"
|
||||
}'
|
||||
```
|
||||
|
||||
Then add this plugin on a route by sending the following request. Note, there is no requirement for model/policy now.
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
|
||||
{
|
||||
"plugins": {
|
||||
"authz-casbin": {
|
||||
"username": "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/*"
|
||||
}'
|
||||
```
|
||||
|
||||
**NOTE**: The plugin route configuration has a higher precedence than the plugin metadata configuration. Hence if the model/policy configuration is present in the plugin route config, the plugin will use that instead of the metadata config.
|
||||
|
||||
## Test Plugin
|
||||
|
||||
We defined the example model as:
|
||||
|
||||
```conf
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)
|
||||
```
|
||||
|
||||
And the example policy as:
|
||||
|
||||
```conf
|
||||
p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, alice, admin
|
||||
```
|
||||
|
||||
This means that anyone can access the homepage (`/`) using `GET` request method while only users with admin permissions can access other pages and use other request methods.
|
||||
|
||||
For example, here anyone can access the homepage with the GET request method and the request proceeds normally:
|
||||
|
||||
```shell
|
||||
curl -i http://127.0.0.1:9080/ -X GET
|
||||
```
|
||||
|
||||
If some unauthorized user `bob` tries to access any other page, they will get a 403 error:
|
||||
|
||||
```shell
|
||||
curl -i http://127.0.0.1:9080/res -H 'user: bob' -X GET
|
||||
HTTP/1.1 403 Forbidden
|
||||
```
|
||||
|
||||
But someone with admin permissions like `alice`can access it:
|
||||
|
||||
```shell
|
||||
curl -i http://127.0.0.1:9080/res -H 'user: alice' -X GET
|
||||
```
|
||||
|
||||
## Disable Plugin
|
||||
|
||||
Remove the corresponding json configuration in the plugin configuration to disable the `authz-casbin` plugin.
|
||||
APISIX plugins are hot-reloaded, therefore no need to restart APISIX.
|
||||
|
||||
```shell
|
||||
$ curl http://127.0.0.1:2379/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d value='
|
||||
{
|
||||
"methods": ["GET"],
|
||||
"uri": "/*",
|
||||
"plugins": {},
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Checkout examples for model and policy conguration [here](https://github.com/casbin/lua-casbin/tree/master/examples).
|
@ -67,6 +67,7 @@ dependencies = {
|
||||
"lua-resty-consul = 0.3-2",
|
||||
"penlight = 1.9.2-1",
|
||||
"ext-plugin-proto = 0.2.1",
|
||||
"casbin = 1.26.0",
|
||||
}
|
||||
|
||||
build = {
|
||||
|
2
t/admin/plugins.t
vendored
2
t/admin/plugins.t
vendored
@ -40,7 +40,7 @@ __DATA__
|
||||
--- request
|
||||
GET /apisix/admin/plugins/list
|
||||
--- response_body_like eval
|
||||
qr/\["client-control","ext-plugin-pre-req","zipkin","request-id","fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","ua-restriction","referer-restriction","uri-blocker","request-validation","openid-connect","wolf-rbac","hmac-auth","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","api-breaker","limit-conn","limit-count","limit-req","gzip","server-info","traffic-split","redirect","response-rewrite","grpc-transcode","prometheus","echo","http-logger","sls-logger","tcp-logger","kafka-logger","syslog","udp-logger","example-plugin","serverless-post-function","ext-plugin-post-req"\]/
|
||||
qr/\["client-control","ext-plugin-pre-req","zipkin","request-id","fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","ua-restriction","referer-restriction","uri-blocker","request-validation","openid-connect","authz-casbin","wolf-rbac","hmac-auth","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","api-breaker","limit-conn","limit-count","limit-req","gzip","server-info","traffic-split","redirect","response-rewrite","grpc-transcode","prometheus","echo","http-logger","sls-logger","tcp-logger","kafka-logger","syslog","udp-logger","example-plugin","serverless-post-function","ext-plugin-post-req"\]/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
484
t/plugin/authz-casbin.t
vendored
Normal file
484
t/plugin/authz-casbin.t
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
use t::APISIX 'no_plan';
|
||||
|
||||
repeat_each(1);
|
||||
no_long_string();
|
||||
no_root_location();
|
||||
run_tests;
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.authz-casbin")
|
||||
local conf = {
|
||||
model_path = "/path/to/model.conf",
|
||||
policy_path = "/path/to/policy.csv",
|
||||
username = "user"
|
||||
}
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
ngx.say("done")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
done
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 2: username missing
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.authz-casbin")
|
||||
local conf = {
|
||||
model_path = "/path/to/model.conf",
|
||||
policy_path = "/path/to/policy.csv"
|
||||
}
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
else
|
||||
ngx.say("done")
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
value should match only one schema, but matches none
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 3: put model and policy text in metadata
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.authz-casbin")
|
||||
local t = require("lib.test_admin").test
|
||||
local code, body = t('/apisix/admin/plugin_metadata/authz-casbin',
|
||||
ngx.HTTP_PUT,
|
||||
[[{
|
||||
"model": "[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
|
||||
|
||||
"policy": "p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, alice, admin"
|
||||
}]]
|
||||
)
|
||||
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 4: Enforcer from text without files
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.authz-casbin")
|
||||
local t = require("lib.test_admin").test
|
||||
|
||||
local conf = {
|
||||
username = "user"
|
||||
}
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
|
||||
ngx.say("done")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
done
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 5: enable authz-casbin by Admin API
|
||||
--- 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": {
|
||||
"authz-casbin": {
|
||||
"username" : "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/hello"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 6: no username header passed
|
||||
--- request
|
||||
GET /hello
|
||||
--- error_code: 403
|
||||
--- response_body_like eval
|
||||
qr/"Access Denied"/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 7: username passed but user not authorized
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: bob
|
||||
--- error_code: 403
|
||||
--- response_body
|
||||
{"message":"Access Denied"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 8: authorized user
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: admin
|
||||
--- error_code: 200
|
||||
--- response_body
|
||||
hello world
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 9: authorized user (rbac)
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: alice
|
||||
--- error_code: 200
|
||||
--- response_body
|
||||
hello world
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 10: unauthorized user before policy update
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: jack
|
||||
--- error_code: 403
|
||||
--- response_body
|
||||
{"message":"Access Denied"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 11: update model and policy text in metadata
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.authz-casbin")
|
||||
local t = require("lib.test_admin").test
|
||||
local code, body = t('/apisix/admin/plugin_metadata/authz-casbin',
|
||||
ngx.HTTP_PUT,
|
||||
[[{
|
||||
"model": "[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
|
||||
|
||||
"policy": "p, *, /, GET
|
||||
p, admin, *, *
|
||||
p, jack, /hello, GET
|
||||
g, alice, admin"
|
||||
}]]
|
||||
)
|
||||
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 12: authorized user after policy update
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: jack
|
||||
--- error_code: 200
|
||||
--- response_body
|
||||
hello world
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 13: enable authz-casbin using model/policy files
|
||||
--- 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": {
|
||||
"authz-casbin": {
|
||||
"model_path": "t/plugin/authz-casbin/model.conf",
|
||||
"policy_path": "t/plugin/authz-casbin/policy.csv",
|
||||
"username" : "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/hello"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 14: authorized user as per policy
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: alice
|
||||
--- error_code: 200
|
||||
--- response_body
|
||||
hello world
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 15: unauthorized user as per policy
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: bob
|
||||
--- error_code: 403
|
||||
--- response_body
|
||||
{"message":"Access Denied"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 16: enable authz-casbin using model/policy text
|
||||
--- 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": {
|
||||
"authz-casbin": {
|
||||
"model": "
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
|
||||
"policy": "
|
||||
p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, jack, admin",
|
||||
"username" : "user"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/hello"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 17: authorized user as per policy
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: jack
|
||||
--- error_code: 200
|
||||
--- response_body
|
||||
hello world
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 18: unauthorized user as per policy
|
||||
--- request
|
||||
GET /hello
|
||||
--- more_headers
|
||||
user: bob
|
||||
--- error_code: 403
|
||||
--- response_body
|
||||
{"message":"Access Denied"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 19: disable authz-casbin by Admin API
|
||||
--- 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": {},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/hello"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
14
t/plugin/authz-casbin/model.conf
Normal file
14
t/plugin/authz-casbin/model.conf
Normal file
@ -0,0 +1,14 @@
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)
|
3
t/plugin/authz-casbin/policy.csv
Normal file
3
t/plugin/authz-casbin/policy.csv
Normal file
@ -0,0 +1,3 @@
|
||||
p, *, /, GET
|
||||
p, admin, *, *
|
||||
g, alice, admin
|
|
Loading…
Reference in New Issue
Block a user