You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by bz...@apache.org on 2021/12/15 08:19:33 UTC
[apisix] branch master updated: feat: supprot OPA plugin complex response (#5779)
This is an automated email from the ASF dual-hosted git repository.
bzp2010 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 6ce20fc feat: supprot OPA plugin complex response (#5779)
6ce20fc is described below
commit 6ce20fcd238d207c44915f321d0e183443af0dd2
Author: Zeping Bai <bz...@apache.org>
AuthorDate: Wed Dec 15 16:19:26 2021 +0800
feat: supprot OPA plugin complex response (#5779)
---
apisix/plugins/opa.lua | 29 +++++++++++++-
apisix/plugins/opa/helper.lua | 14 +++----
ci/linux-ci-init-service.sh | 11 ------
ci/pod/docker-compose.yml | 9 ++++-
ci/pod/opa/data.json | 25 ++++++++++++
ci/pod/opa/example.rego | 45 ++++++++++++++++++++++
t/plugin/opa.t | 88 +++++++++++++++++++++++++++++++++++++++----
7 files changed, 192 insertions(+), 29 deletions(-)
diff --git a/apisix/plugins/opa.lua b/apisix/plugins/opa.lua
index f33c3d0..cfe0b58 100644
--- a/apisix/plugins/opa.lua
+++ b/apisix/plugins/opa.lua
@@ -18,6 +18,7 @@
local core = require("apisix.core")
local http = require("resty.http")
local helper = require("apisix.plugins.opa.helper")
+local type = type
local schema = {
type = "object",
@@ -89,13 +90,37 @@ function _M.access(conf, ctx)
-- parse the results of the decision
local data, err = core.json.decode(res.body)
- if err then
+ if err or not data then
core.log.error("invalid response body: ", res.body, " err: ", err)
return 503
end
if not data.result then
- return 403
+ core.log.error("invalid OPA decision format: ", res.body,
+ " err: `result` field does not exist")
+ return 503
+ end
+
+ local result = data.result
+
+ if not result.allow then
+ if result.headers then
+ core.response.set_header(result.headers)
+ end
+
+ local status_code = 403
+ if result.status_code then
+ status_code = result.status_code
+ end
+
+ local reason = nil
+ if result.reason then
+ reason = type(result.reason) == "table"
+ and core.json.encode(result.reason)
+ or result.reason
+ end
+
+ return status_code, reason
end
end
diff --git a/apisix/plugins/opa/helper.lua b/apisix/plugins/opa/helper.lua
index 2a8cf94..059ea08 100644
--- a/apisix/plugins/opa/helper.lua
+++ b/apisix/plugins/opa/helper.lua
@@ -34,13 +34,13 @@ end
local function build_http_request(conf, ctx)
return {
- scheme = core.request.get_scheme(ctx),
- method = core.request.get_method(ctx),
- host = core.request.get_host(ctx),
- port = core.request.get_port(ctx),
- path = core.request.get_path(ctx),
- header = core.request.headers(ctx),
- query = core.request.get_uri_args(ctx),
+ scheme = core.request.get_scheme(ctx),
+ method = core.request.get_method(ctx),
+ host = core.request.get_host(ctx),
+ port = core.request.get_port(ctx),
+ path = core.request.get_path(ctx),
+ headers = core.request.headers(ctx),
+ query = core.request.get_uri_args(ctx),
}
end
diff --git a/ci/linux-ci-init-service.sh b/ci/linux-ci-init-service.sh
index 765c115..6a7ffbb 100755
--- a/ci/linux-ci-init-service.sh
+++ b/ci/linux-ci-init-service.sh
@@ -34,14 +34,3 @@ docker exec -i rmqnamesrv /home/rocketmq/rocketmq-4.6.0/bin/mqadmin updateTopic
# prepare vault kv engine
docker exec -i vault sh -c "VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault secrets enable -path=kv -version=1 kv"
-
-# prepare OPA env
-curl -XPUT 'http://localhost:8181/v1/policies/example' \
---header 'Content-Type: text/plain' \
---data-raw 'package example
-
-default allow = false
-
-allow {
- input.request.header["test-header"] == "only-for-test"
-}'
diff --git a/ci/pod/docker-compose.yml b/ci/pod/docker-compose.yml
index b632a59..b5d8062 100644
--- a/ci/pod/docker-compose.yml
+++ b/ci/pod/docker-compose.yml
@@ -402,7 +402,14 @@ services:
restart: unless-stopped
ports:
- 8181:8181
- command: run -s
+ command: run -s /example.rego /data.json
+ volumes:
+ - type: bind
+ source: ./ci/pod/opa/example.rego
+ target: /example.rego
+ - type: bind
+ source: ./ci/pod/opa/data.json
+ target: /data.json
networks:
opa_net:
diff --git a/ci/pod/opa/data.json b/ci/pod/opa/data.json
new file mode 100644
index 0000000..3356559
--- /dev/null
+++ b/ci/pod/opa/data.json
@@ -0,0 +1,25 @@
+{
+ "users": {
+ "alice": {
+ "headers": {
+ "Location": "http://example.com/auth"
+ },
+ "status_code": 302
+ },
+ "bob": {
+ "headers": {
+ "test": "abcd",
+ "abcd": "test"
+ }
+ },
+ "carla": {
+ "reason": "Give you a string reason"
+ },
+ "dylon": {
+ "reason": {
+ "code": 40001,
+ "desc": "Give you a object reason"
+ }
+ }
+ }
+}
diff --git a/ci/pod/opa/example.rego b/ci/pod/opa/example.rego
new file mode 100644
index 0000000..2eb912e
--- /dev/null
+++ b/ci/pod/opa/example.rego
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+package example
+
+import input.request
+import data.users
+
+default allow = false
+
+allow {
+ request.headers["test-header"] == "only-for-test"
+ request.method == "GET"
+ startswith(request.path, "/hello")
+ request.query["test"] != "abcd"
+ request.query["user"]
+}
+
+reason = users[request.query["user"]].reason {
+ not allow
+ request.query["user"]
+}
+
+headers = users[request.query["user"]].headers {
+ not allow
+ request.query["user"]
+}
+
+status_code = users[request.query["user"]].status_code {
+ not allow
+ request.query["user"]
+}
diff --git a/t/plugin/opa.t b/t/plugin/opa.t
index 3592f68..064661a 100644
--- a/t/plugin/opa.t
+++ b/t/plugin/opa.t
@@ -71,7 +71,7 @@ property "host" validation failed: wrong type: expected string, got number
"plugins": {
"opa": {
"host": "http://127.0.0.1:8181",
- "policy": "example/allow"
+ "policy": "example"
}
},
"upstream": {
@@ -80,7 +80,7 @@ property "host" validation failed: wrong type: expected string, got number
},
"type": "roundrobin"
},
- "uri": "/hello"
+ "uris": ["/hello", "/test"]
}]]
)
@@ -95,19 +95,91 @@ passed
-=== TEST 3: hit route (with wrong header request)
+=== TEST 3: hit route (with correct request)
--- request
-GET /hello
+GET /hello?test=1234&user=none
+--- more_headers
+test-header: only-for-test
+--- response_body
+hello world
+
+
+
+=== TEST 4: hit route (with wrong header request)
+--- request
+GET /hello?test=1234&user=none
--- more_headers
test-header: not-for-test
--- error_code: 403
-=== TEST 4: hit route (with correct request)
+=== TEST 5: hit route (with wrong query request)
--- request
-GET /hello
+GET /hello?test=abcd&user=none
--- more_headers
test-header: only-for-test
---- response_body
-hello world
+--- error_code: 403
+
+
+
+=== TEST 6: hit route (with wrong method request)
+--- request
+POST /hello?test=1234&user=none
+--- more_headers
+test-header: only-for-test
+--- error_code: 403
+
+
+
+=== TEST 7: hit route (with wrong path request)
+--- request
+GET /test?test=1234&user=none
+--- more_headers
+test-header: only-for-test
+--- error_code: 403
+
+
+
+=== TEST 8: hit route (response status code and header)
+--- request
+GET /test?test=abcd&user=alice
+--- more_headers
+test-header: only-for-test
+--- error_code: 302
+--- response_headers
+Location: http://example.com/auth
+
+
+
+=== TEST 9: hit route (response multiple header reason)
+--- request
+GET /test?test=abcd&user=bob
+--- more_headers
+test-header: only-for-test
+--- error_code: 403
+--- response_headers
+test: abcd
+abcd: test
+
+
+
+=== TEST 10: hit route (response string reason)
+--- request
+GET /test?test=abcd&user=carla
+--- more_headers
+test-header: only-for-test
+--- error_code: 403
+--- response
+Give you a string reason
+
+
+
+=== TEST 11: hit route (response json reason)
+--- request
+GET /test?test=abcd&user=dylon
+--- more_headers
+test-header: only-for-test
+--- error_code: 403
+--- response
+{"code":40001,"desc":"Give you a object reason"}