This is an automated email from the ASF dual-hosted git repository.

baerwang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu-samples.git


The following commit(s) were added to refs/heads/main by this push:
     new 8d46df6  Merge pull request #117 from nanjiek/add-the-opa-remote-test
8d46df6 is described below

commit 8d46df66203b18572d428a77ae15e2400ab7888e
Author: Sirui Huang <[email protected]>
AuthorDate: Tue Dec 23 23:07:45 2025 +0800

    Merge pull request #117 from nanjiek/add-the-opa-remote-test
    
    add the opa tests for embedded and the sammple of server-mode
---
 README.md                                          |   2 +-
 README_CN.md                                       |   2 +-
 plugins/opa/README.md                              | 250 ++++++++++-----------
 plugins/opa/README_CN.md                           | 234 ++++++++++---------
 .../opa/{ => embedded}/docker/docker-compose.yml   |  54 ++---
 plugins/opa/{ => embedded}/pixiu/conf.yaml         | 150 ++++++-------
 plugins/opa/{ => embedded}/server/app/server.go    |   5 +-
 plugins/opa/embedded/test/pixiu_test.go            |  92 ++++++++
 plugins/opa/server-mode/docker/docker-compose.yml  |  47 ++++
 plugins/opa/{ => server-mode}/pixiu/conf.yaml      | 142 ++++++------
 plugins/opa/server-mode/remote/policy.rego         |  25 +++
 .../server/app/server.go}                          |  47 ++--
 plugins/opa/server-mode/test/pixiu_test.go         |  91 ++++++++
 13 files changed, 696 insertions(+), 445 deletions(-)

diff --git a/README.md b/README.md
index 65d615f..39cce3c 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@ This project includes multiple samples covering conversions 
such as HTTP to Dubb
 * **plugins**: Pixiu plugin examples
 
   * `ratelimit`: Pixiu rate limiting plugin
-  * `opa`: Pixiu Open Policy Agent (OPA) integration example for policy-based 
access control
+  * `opa`: Pixiu Open Policy Agent (OPA) integration example for policy-based 
access control (embedded Rego sample and server-mode sample)
 
 * **seata**: Demonstrates how to configure the Seata filter to interact with 
Seata TC for distributed transaction coordination
 
diff --git a/README_CN.md b/README_CN.md
index 39b0020..2755f12 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -51,7 +51,7 @@
 
 - plugins:此目录包含 pixiu 的一些插件
   - plugins/ratelimit:pixiu 的 ratelimit 插件
-  - plugins/opa: pixiu 的 Open Policy Agent 策略控制能力集成示例
+  - plugins/opa: pixiu 的 Open Policy Agent 策略控制能力集成示例(包含嵌入式 Rego 与 Server 
模式两种示例)
   
 - seata:演示了如何配置 Seata filter 与 Seata TC 交互对分布式事务进行协调
 
diff --git a/plugins/opa/README.md b/plugins/opa/README.md
index 283cbee..83a0d51 100644
--- a/plugins/opa/README.md
+++ b/plugins/opa/README.md
@@ -1,128 +1,122 @@
-# This sample for OPA filter.
-
-English | [中文](README_CN.md)
-
-The OPA filter can provide out-of-the-box authorization capability to ensure 
service security and stability.
-
-> The filter is based on [Open Policy 
Agent](https://www.openpolicyagent.org/), see more here 
[Documentation](https://www.openpolicyagent.org/docs/latest/).
-
-### Create the API:
-
-- Create a simple Http API, Reference the [Create a simple Http 
API](../../dubbogo/http/README.md), then we got a path.
-
-- just test it: `curl http://localhost:8888/UserService -H "Test_header: 1"`
-
-### Define Policy
-
-- The first step, Define the policy. The OPA filter requires a Rego policy 
provided inline. Example:
-
-```yaml
-            policy: |
-              package http.authz
-
-              default allow = false
-
-              allow {
-                input.method == "GET"
-                input.path == "/status"
-              }
-```
-
-### Define Entrypoint
-
-- The second step, Define entrypoint.The entrypoint must match the package
-
-```yaml
-                       entrypoint: "data.http.authz.allow"
-```
-
-
-
-- The third step, Ensure filter order. **The OPA filter must be placed before 
the HTTP proxy filter** in `http_filters`:
-
-```yaml
-filters:
-  - name: dgp.filter.httpconnectionmanager
-    config:
-      route_config:
-        # ... your routes
-      http_filters:
-        - name: dgp.filter.http.opa
-          config:
-            policy: |
-              package http.authz
-
-              default allow = false
-
-              allow {
-                input.method == "GET"
-                input.path == "/status"
-              }
-            entrypoint: "data.http.authz.allow"
-        # HTTP proxy filter should be after OPA filter
-        - name: dgp.filter.http.proxy
-          config:
-```
-
-
-
-### Test:
-
-```cmd
-go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/pixiu/conf.yaml
-```
-
-```
-go run /path_to/dubbo-go-pixiu-samples/plugins/opa/server/app/*
-```
-
-
-
-##### Go Test
-
-```
-go test -v /path_to/dubbo-go-pixiu-samples/plugins/opa/test
-```
-
-
-
-```
-=== RUN   TestUserServiceAllow
---- PASS: TestUserServiceAllow (0.02s)
-=== RUN   TestUserServiceDeny
---- PASS: TestUserServiceDeny (0.00s)
-=== RUN   TestOtherServiceDeny
---- PASS: TestOtherServiceDeny (0.00s)
-PASS
-```
-
-
-
-###### Use Curl to Test
-
-- Denied request:
-
-```bash
-curl -s http://127.0.0.1:8888/OtherService
-```
-
-​      Expected result:
-
-```
-null
-```
-
-
-
-- Allowed request:
-
-```
-curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"
-```
-
-​      Expected result:
-
-```
-{"message":"UserService","result":"pass"}
-```
-
+# Sample for OPA filter
+
+English | [中文](README_CN.md)
+
+The OPA filter can provide out-of-the-box authorization capability to ensure 
service security and stability.
+
+> The filter is based on [Open Policy 
Agent](https://www.openpolicyagent.org/), see more here 
[Documentation](https://www.openpolicyagent.org/docs/latest/).
+
+**Recommendation:** Use server mode (remote OPA) for production; embedded mode 
is for compatibility and simple demos.
+
+### Entrypoint Reminder
+- `entrypoint` must match your policy package/rule path (e.g., 
`data.pixiu.authz.allow`).
+- Filter order: OPA must be before the HTTP proxy filter.
+
+### Embedded Mode
+
+- Backend: `go run 
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/server/app/server.go`
+- Pixiu: `go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/pixiu/conf.yaml`
+- Pixiu key config (inline policy):
+  ```yaml
+  http_filters:
+    - name: dgp.filter.http.opa
+      config:
+        policy: |
+          package pixiu
+          default allow := false
+          allow {
+            input.path == "/UserService"
+            input.headers["Test_header"][0] == "1"
+          }
+        entrypoint: data.pixiu.allow
+    - name: dgp.filter.http.httpproxy
+      config: {}
+  clusters:
+    - name: "user"
+      endpoints:
+        - socket_address:
+            address: 127.0.0.1
+            port: 1314
+  ```
+- Go test:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/test
+  go test -v .
+  # Expected:
+  # === RUN   TestEmbeddedUserServiceAllow
+  # --- PASS: TestEmbeddedUserServiceAllow
+  # === RUN   TestEmbeddedUserServiceDeny
+  # --- PASS: TestEmbeddedUserServiceDeny
+  # === RUN   TestEmbeddedOtherServiceDeny
+  # --- PASS: TestEmbeddedOtherServiceDeny
+  # PASS
+  ```
+- Curl (through Pixiu):
+  - Deny: `curl -s http://127.0.0.1:8888/UserService` / `curl -s 
http://127.0.0.1:8888/OtherService`
+  - Allow: `curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+
+### Server Mode
+
+- OPA + uploader:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/docker
+  docker-compose up
+  ```
+  - Host port 8182 -> container 8181, policy auto uploaded to `pixiu-authz`.
+- Backend: `cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode && go 
run server/app/*.go`
+- Pixiu: `go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/pixiu/conf.yaml`
+- Pixiu key config (remote OPA):
+  ```yaml
+  http_filters:
+    - name: dgp.filter.http.opa
+      config:
+        server_url: "http://127.0.0.1:8182";
+        decision_path: "/v1/data/pixiu/authz/allow"
+        timeout_ms: 500
+    - name: dgp.filter.http.httpproxy
+      config: {}
+  clusters:
+    - name: "user"
+      endpoints:
+        - socket_address:
+            address: 127.0.0.1
+            port: 1314
+  ```
+- OPA policy (auto uploaded by compose): 
`plugins/opa/server-mode/remote/policy.rego`
+  ```rego
+  package pixiu.authz
+  default allow := false
+  allow {
+    input.path == "/UserService"
+    input.headers["Test_header"][0] == "1"
+  }
+  ```
+- Go test:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/test
+  go test -v .
+  # Expected:
+  # === RUN   TestServerModeUserServiceAllow
+  # --- PASS: TestServerModeUserServiceAllow
+  # === RUN   TestServerModeUserServiceDeny
+  # --- PASS: TestServerModeUserServiceDeny
+  # === RUN   TestServerModeOtherServiceDeny
+  # --- PASS: TestServerModeOtherServiceDeny
+  # PASS
+  ```
+- Curl (through Pixiu):
+  - Deny: `curl -s http://127.0.0.1:8888/UserService` / `curl -s 
http://127.0.0.1:8888/OtherService`
+  - Allow: `curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+- Curl (direct OPA decision, host port 8182):
+  - Allow:
+    ```bash
+    curl -s -X POST -H "Content-Type: application/json" \
+      -d '{"input":{"path":"/UserService","headers":{"Test_header":["1"]}}}' \
+      http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+    ```
+  - Deny:
+    ```bash
+    curl -s -X POST -H "Content-Type: application/json" \
+      -d '{"input":{"path":"/UserService","headers":{}}}' \
+      http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+    ```
diff --git a/plugins/opa/README_CN.md b/plugins/opa/README_CN.md
index b4739f5..48991dd 100644
--- a/plugins/opa/README_CN.md
+++ b/plugins/opa/README_CN.md
@@ -1,112 +1,122 @@
-# OPA 插件示例
-
-[English](README.md) | 中文
-
-OPA 过滤器可以提供开箱即用的授权能力,确保服务的安全性和稳定性。
-
-> 该过滤器基于 [Open Policy Agent](https://www.openpolicyagent.org/),更多内容请参阅 
[官方文档](https://www.openpolicyagent.org/docs/latest/)。
-
-### 创建 API:
-
-- 创建一个简单的 Http API,参考 [创建一个简单的Http 
API](../../dubbogo/http/README.md),然后我们获得了一个可访问的路径。
-
-- 测试命令: `curl http://localhost:8888/UserService`
-
-### 设置过滤器
-
-- 第一步,定义策略。OPA 过滤器需要在配置中内联 Rego 策略。例如:
-
-```yaml
-            policy: |
-              package http.authz
-
-              default allow = false
-
-              allow {
-                input.method == "GET"
-                input.path == "/status"
-              }
-            entrypoint: "data.http.authz.allow"
-```
-
-### 设置 **entrypoint**
-
-- 第二步,设置 entrypoint。entrypoint 必须同包匹配
-
-```yaml
-                       entrypoint: "data.http.authz.allow"
-```
-
-
-
-- 第三步,确保过滤器顺序。**OPA 过滤器必须放在 HTTP proxy 过滤器之前**,如下所示:
-
-```yaml
-filters:
-  - name: dgp.filter.httpconnectionmanager
-    config:
-      route_config:
-        # ... 你的路由
-      http_filters:
-        - name: dgp.filter.http.opa
-          config:
-            policy: |
-              package http.authz
-
-              default allow = false
-
-              allow {
-                input.method == "GET"
-                input.path == "/status"
-              }
-            entrypoint: "data.http.authz.allow"
-        # HTTP proxy 过滤器必须在 OPA 过滤器之后
-        - name: dgp.filter.http.proxy
-          config:
-```
-
-### 测试:
-
-```cmd
-go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/pixiu/conf.yaml
-go run /path_to/dubbo-go-pixiu-samples/plugins/opa/server/app/*
-```
-
-##### Go Test
-
-```
-go test -v /path_to/dubbo-go-pixiu-samples/plugins/opa/test
-=== RUN   TestUserServiceAllow
---- PASS: TestUserServiceAllow (0.02s)
-=== RUN   TestUserServiceDeny
---- PASS: TestUserServiceDeny (0.00s)
-=== RUN   TestOtherServiceDeny
---- PASS: TestOtherServiceDeny (0.00s)
-PASS
-```
-
-###### 使用 Curl 测试
-
-- 拒绝请求:
-
-```bash
-curl -s http://127.0.0.1:8888/OtherService
-```
-
-​      预期输出:
-
-```
-null
-```
-
-- 允许请求:
-
-```
-curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"
-```
-
-​      预期输出:
-
-```
-{"message":"UserService","result":"pass"}
-```
\ No newline at end of file
+# OPA 插件示例
+
+[English](README.md) | 中文
+
+OPA 过滤器提供基于策略的鉴权能力。
+
+> 过滤器基于 [Open Policy 
Agent](https://www.openpolicyagent.org/),更多见官方文档:https://www.openpolicyagent.org/docs/latest/。
+
+**推荐:** 生产环境优先使用 Server 模式(远程 OPA),嵌入模式主要用于兼容和简单演示。
+
+### Entrypoint
+- `entrypoint` 必须与策略包名/规则路径匹配(如 `data.pixiu.authz.allow`)。
+- 过滤器顺序:OPA 必须放在 HTTP proxy 过滤器之前。
+
+### 嵌入模式
+
+- 后端:`go run 
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/server/app/server.go`
+- Pixiu:`go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/pixiu/conf.yaml`
+- Pixiu 关键配置(内联策略):
+  ```yaml
+  http_filters:
+    - name: dgp.filter.http.opa
+      config:
+        policy: |
+          package pixiu
+          default allow := false
+          allow {
+            input.path == "/UserService"
+            input.headers["Test_header"][0] == "1"
+          }
+        entrypoint: data.pixiu.allow
+    - name: dgp.filter.http.httpproxy
+      config: {}
+  clusters:
+    - name: "user"
+      endpoints:
+        - socket_address:
+            address: 127.0.0.1
+            port: 1314
+  ```
+- Go test:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/test
+  go test -v .
+  # 预期:
+  # === RUN   TestEmbeddedUserServiceAllow
+  # --- PASS: TestEmbeddedUserServiceAllow
+  # === RUN   TestEmbeddedUserServiceDeny
+  # --- PASS: TestEmbeddedUserServiceDeny
+  # === RUN   TestEmbeddedOtherServiceDeny
+  # --- PASS: TestEmbeddedOtherServiceDeny
+  # PASS
+  ```
+- Curl(通过 Pixiu):
+  - 拒绝:`curl -s http://127.0.0.1:8888/UserService` / `curl -s 
http://127.0.0.1:8888/OtherService`
+  - 允许:`curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+
+### Server 模式
+
+- OPA + 上传器:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/docker
+  docker-compose up
+  ```
+  - 宿主 8182 -> 容器 8181,`policy-uploader` 自动上传策略 `pixiu-authz`。
+- 后端:`cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode && go run 
server/app/*.go`
+- Pixiu:`go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c 
/path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/pixiu/conf.yaml`
+- Pixiu 关键配置(远程 OPA):
+  ```yaml
+  http_filters:
+    - name: dgp.filter.http.opa
+      config:
+        server_url: "http://127.0.0.1:8182";
+        decision_path: "/v1/data/pixiu/authz/allow"
+        timeout_ms: 500
+    - name: dgp.filter.http.httpproxy
+      config: {}
+  clusters:
+    - name: "user"
+      endpoints:
+        - socket_address:
+            address: 127.0.0.1
+            port: 1314
+  ```
+- OPA 策略(compose 自动上传):`plugins/opa/server-mode/remote/policy.rego`
+  ```rego
+  package pixiu.authz
+  default allow := false
+  allow {
+    input.path == "/UserService"
+    input.headers["Test_header"][0] == "1"
+  }
+  ```
+- Go test:
+  ```bash
+  cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/test
+  go test -v .
+  # 预期:
+  # === RUN   TestServerModeUserServiceAllow
+  # --- PASS: TestServerModeUserServiceAllow
+  # === RUN   TestServerModeUserServiceDeny
+  # --- PASS: TestServerModeUserServiceDeny
+  # === RUN   TestServerModeOtherServiceDeny
+  # --- PASS: TestServerModeOtherServiceDeny
+  # PASS
+  ```
+- Curl(通过 Pixiu):
+  - 拒绝:`curl -s http://127.0.0.1:8888/UserService` / `curl -s 
http://127.0.0.1:8888/OtherService`
+  - 允许:`curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+- Curl(直接 OPA 决策,宿主 8182):
+  - 允许:
+    ```bash
+    curl -s -X POST -H "Content-Type: application/json" \
+      -d '{"input":{"path":"/UserService","headers":{"Test_header":["1"]}}}' \
+      http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+    ```
+  - 拒绝:
+    ```bash
+    curl -s -X POST -H "Content-Type: application/json" \
+      -d '{"input":{"path":"/UserService","headers":{}}}' \
+      http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+    ```
diff --git a/plugins/opa/docker/docker-compose.yml 
b/plugins/opa/embedded/docker/docker-compose.yml
similarity index 94%
rename from plugins/opa/docker/docker-compose.yml
rename to plugins/opa/embedded/docker/docker-compose.yml
index 6472ecb..a3d294f 100644
--- a/plugins/opa/docker/docker-compose.yml
+++ b/plugins/opa/embedded/docker/docker-compose.yml
@@ -1,27 +1,27 @@
-#
-# Licensed to 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. Apache Software Foundation (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.
-#
-
-version: '3'
-
-services:
-  zookeeper:
-    image: zookeeper
-    ports:
-      - 2181:2181
-    restart: on-failure
+#
+# Licensed to 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. Apache Software Foundation (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.
+#
+
+version: '3'
+
+services:
+  zookeeper:
+    image: zookeeper
+    ports:
+      - 2181:2181
+    restart: on-failure
\ No newline at end of file
diff --git a/plugins/opa/pixiu/conf.yaml b/plugins/opa/embedded/pixiu/conf.yaml
similarity index 94%
copy from plugins/opa/pixiu/conf.yaml
copy to plugins/opa/embedded/pixiu/conf.yaml
index 12f1a67..5687208 100644
--- a/plugins/opa/pixiu/conf.yaml
+++ b/plugins/opa/embedded/pixiu/conf.yaml
@@ -1,75 +1,75 @@
-#
-# 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.
-#
----
-static_resources:
-  listeners:
-    - name: "net/http"
-      protocol_type: "HTTP"
-      address:
-        socket_address:
-          address: "0.0.0.0"
-          port: 8888
-      filter_chains:
-        filters:
-          - name: dgp.filter.httpconnectionmanager
-            config:
-              route_config:
-                routes:
-                  - match:
-                      prefix: "/UserService"
-                    route:
-                      cluster: "user"
-                      cluster_not_found_response_code: 505
-                  - match:
-                      prefix: "/OtherService"
-                    route:
-                      cluster: "user"
-                      cluster_not_found_response_code: 505
-              http_filters:
-                - name: dgp.filter.http.opa
-                  config:
-                    policy: |
-                      package pixiu
-                      import future.keywords.if
-                      default allow := false
-
-                      allow if {
-                        input.path == "/UserService"
-                        input.headers["Test_header"][0] == "1"
-                      }
-                    entrypoint: data.pixiu.allow
-                - name: dgp.filter.http.httpproxy
-                  config:
-
-      config:
-        idle_timeout: 5s
-        read_timeout: 5s
-        write_timeout: 5s
-  clusters:
-    - name: "user"
-      lb_policy: "lb"
-      endpoints:
-        - id: 1
-          socket_address:
-            address: 127.0.0.1
-            port: 1314
-  shutdown_config:
-    timeout: "60s"
-    step_timeout: "10s"
-    reject_policy: "immediacy"
\ No newline at end of file
+#
+# 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.
+#
+---
+static_resources:
+  listeners:
+    - name: "net/http"
+      protocol_type: "HTTP"
+      address:
+        socket_address:
+          address: "0.0.0.0"
+          port: 8888
+      filter_chains:
+        filters:
+          - name: dgp.filter.httpconnectionmanager
+            config:
+              route_config:
+                routes:
+                  - match:
+                      prefix: "/UserService"
+                    route:
+                      cluster: "user"
+                      cluster_not_found_response_code: 505
+                  - match:
+                      prefix: "/OtherService"
+                    route:
+                      cluster: "user"
+                      cluster_not_found_response_code: 505
+              http_filters:
+                - name: dgp.filter.http.opa
+                  config:
+                    policy: |
+                      package pixiu
+                      import future.keywords.if
+                      default allow := false
+
+                      allow if {
+                        input.path == "/UserService"
+                        input.headers["Test_header"][0] == "1"
+                      }
+                    entrypoint: data.pixiu.allow
+                - name: dgp.filter.http.httpproxy
+                  config: {}
+
+      config:
+        idle_timeout: 5s
+        read_timeout: 5s
+        write_timeout: 5s
+  clusters:
+    - name: "user"
+      lb_policy: "lb"
+      endpoints:
+        - id: 1
+          socket_address:
+            address: 127.0.0.1
+            port: 1314
+  shutdown_config:
+    timeout: "60s"
+    step_timeout: "10s"
+    reject_policy: "immediacy"
diff --git a/plugins/opa/server/app/server.go 
b/plugins/opa/embedded/server/app/server.go
similarity index 93%
rename from plugins/opa/server/app/server.go
rename to plugins/opa/embedded/server/app/server.go
index 231becf..86f461b 100644
--- a/plugins/opa/server/app/server.go
+++ b/plugins/opa/embedded/server/app/server.go
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -37,7 +37,6 @@ func main() {
                msg := route[strings.LastIndex(route, "/")+1:]
 
                http.HandleFunc(route, func(w http.ResponseWriter, r 
*http.Request) {
-
                        log.Printf("[backend] %s %s Headers=%v", r.Method, 
r.URL.Path, r.Header)
 
                        w.Header().Set("Content-Type", "application/json")
@@ -48,6 +47,6 @@ func main() {
                })
        }
 
-       log.Println("Starting sample server on :1314 ...")
+       log.Println("Starting sample backend on :1314 ...")
        log.Fatal(http.ListenAndServe(":1314", nil))
 }
diff --git a/plugins/opa/embedded/test/pixiu_test.go 
b/plugins/opa/embedded/test/pixiu_test.go
new file mode 100644
index 0000000..9f427b8
--- /dev/null
+++ b/plugins/opa/embedded/test/pixiu_test.go
@@ -0,0 +1,92 @@
+/*
+ * 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 test
+
+import (
+       "io"
+       "net"
+       "net/http"
+       "strings"
+       "testing"
+       "time"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+const embeddedGatewayBaseURL = "http://localhost:8888";
+
+func doEmbeddedRequest(t *testing.T, path string, headerVal *string) (int, 
string) {
+       t.Helper()
+
+       client := &http.Client{Timeout: 5 * time.Second}
+       req, err := http.NewRequest("GET", embeddedGatewayBaseURL+path, nil)
+       if err != nil {
+               t.Fatalf("failed to build request: %v", err)
+       }
+
+       if headerVal != nil {
+               req.Header.Set("Test_header", *headerVal)
+       }
+
+       resp, err := client.Do(req)
+       if err != nil {
+               // Surface clearer guidance when gateway is not running
+               if ne, ok := err.(net.Error); ok && ne.Timeout() {
+                       t.Fatalf("request timeout: ensure embedded Pixiu 
gateway and backend are running: %v", err)
+               }
+               t.Fatalf("request failed: ensure embedded Pixiu gateway and 
backend are running: %v", err)
+       }
+       if resp == nil {
+               t.Fatalf("response is nil")
+       }
+       defer resp.Body.Close()
+
+       body, err := io.ReadAll(resp.Body)
+       if err != nil {
+               t.Fatalf("failed to read response body: %v", err)
+       }
+       return resp.StatusCode, string(body)
+}
+
+func TestEmbeddedUserServiceAllow(t *testing.T) {
+       header := "1"
+       status, body := doEmbeddedRequest(t, "/UserService", &header)
+
+       assert.Equal(t, http.StatusOK, status)
+       assert.True(t, strings.Contains(body, "pass"))
+       assert.True(t, strings.Contains(body, "UserService"))
+}
+
+func TestEmbeddedUserServiceDeny(t *testing.T) {
+       status, body := doEmbeddedRequest(t, "/UserService", nil)
+
+       assert.Equal(t, http.StatusForbidden, status)
+       assert.False(t, strings.Contains(body, "pass"))
+       assert.False(t, strings.Contains(body, "UserService"))
+}
+
+func TestEmbeddedOtherServiceDeny(t *testing.T) {
+       header := "1"
+       status, body := doEmbeddedRequest(t, "/OtherService", &header)
+
+       assert.Equal(t, http.StatusForbidden, status)
+       assert.False(t, strings.Contains(body, "pass"))
+       assert.False(t, strings.Contains(body, "OtherService"))
+}
diff --git a/plugins/opa/server-mode/docker/docker-compose.yml 
b/plugins/opa/server-mode/docker/docker-compose.yml
new file mode 100644
index 0000000..9ff17ea
--- /dev/null
+++ b/plugins/opa/server-mode/docker/docker-compose.yml
@@ -0,0 +1,47 @@
+# 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.
+
+version: "3.8"
+
+
+services:
+  opa:
+    image: openpolicyagent/opa:latest
+    command:
+      - "run"
+      - "--server"
+      - "--addr=:8181"
+    ports:
+      - "8182:8181"
+
+  policy-uploader:
+    image: curlimages/curl:8.6.0
+    depends_on:
+      - opa
+
+    volumes:
+      - ../remote/policy.rego:/policy/policy.rego:ro
+    entrypoint: >
+      sh -c "for i in $(seq 1 5); do
+        if curl -v -X PUT -H 'Content-Type: text/plain' --data-binary 
@/policy/policy.rego http://opa:8181/v1/policies/pixiu-authz; then
+          exit 0;
+        fi;
+        echo 'waiting for opa...';
+        sleep 1;
+      done;
+      echo 'upload policy failed';
+      exit 1"
\ No newline at end of file
diff --git a/plugins/opa/pixiu/conf.yaml 
b/plugins/opa/server-mode/pixiu/conf.yaml
similarity index 79%
rename from plugins/opa/pixiu/conf.yaml
rename to plugins/opa/server-mode/pixiu/conf.yaml
index 12f1a67..7ddb9dc 100644
--- a/plugins/opa/pixiu/conf.yaml
+++ b/plugins/opa/server-mode/pixiu/conf.yaml
@@ -1,75 +1,67 @@
-#
-# 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.
-#
----
-static_resources:
-  listeners:
-    - name: "net/http"
-      protocol_type: "HTTP"
-      address:
-        socket_address:
-          address: "0.0.0.0"
-          port: 8888
-      filter_chains:
-        filters:
-          - name: dgp.filter.httpconnectionmanager
-            config:
-              route_config:
-                routes:
-                  - match:
-                      prefix: "/UserService"
-                    route:
-                      cluster: "user"
-                      cluster_not_found_response_code: 505
-                  - match:
-                      prefix: "/OtherService"
-                    route:
-                      cluster: "user"
-                      cluster_not_found_response_code: 505
-              http_filters:
-                - name: dgp.filter.http.opa
-                  config:
-                    policy: |
-                      package pixiu
-                      import future.keywords.if
-                      default allow := false
-
-                      allow if {
-                        input.path == "/UserService"
-                        input.headers["Test_header"][0] == "1"
-                      }
-                    entrypoint: data.pixiu.allow
-                - name: dgp.filter.http.httpproxy
-                  config:
-
-      config:
-        idle_timeout: 5s
-        read_timeout: 5s
-        write_timeout: 5s
-  clusters:
-    - name: "user"
-      lb_policy: "lb"
-      endpoints:
-        - id: 1
-          socket_address:
-            address: 127.0.0.1
-            port: 1314
-  shutdown_config:
-    timeout: "60s"
-    step_timeout: "10s"
-    reject_policy: "immediacy"
\ No newline at end of file
+#
+# 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.
+#
+---
+static_resources:
+  listeners:
+    - name: "net/http"
+      protocol_type: "HTTP"
+      address:
+        socket_address:
+          address: "0.0.0.0"
+          port: 8888
+      filter_chains:
+        filters:
+          - name: dgp.filter.httpconnectionmanager
+            config:
+              route_config:
+                routes:
+                  - match:
+                      prefix: "/UserService"
+                    route:
+                      cluster: "user"
+                      cluster_not_found_response_code: 505
+                  - match:
+                      prefix: "/OtherService"
+                    route:
+                      cluster: "user"
+                      cluster_not_found_response_code: 505
+              http_filters:
+                - name: dgp.filter.http.opa
+                  config:
+                    server_url: "http://127.0.0.1:8182";
+                    decision_path: "/v1/data/pixiu/authz/allow"
+                    timeout_ms: 500
+                - name: dgp.filter.http.httpproxy
+                  config: {}
+      config:
+        idle_timeout: 5s
+        read_timeout: 5s
+        write_timeout: 5s
+  clusters:
+    - name: "user"
+      lb_policy: "lb"
+      endpoints:
+        - id: 1
+          socket_address:
+            address: 127.0.0.1
+            port: 1314
+  shutdown_config:
+    timeout: "60s"
+    step_timeout: "10s"
+    reject_policy: "immediacy"
diff --git a/plugins/opa/server-mode/remote/policy.rego 
b/plugins/opa/server-mode/remote/policy.rego
new file mode 100644
index 0000000..f65619e
--- /dev/null
+++ b/plugins/opa/server-mode/remote/policy.rego
@@ -0,0 +1,25 @@
+# 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 pixiu.authz
+
+default allow := false
+
+allow if {
+  input.path == "/UserService"
+  input.headers["Test_header"][0] == "1"
+}
diff --git a/plugins/opa/test/pixiu_test.go 
b/plugins/opa/server-mode/server/app/server.go
similarity index 52%
rename from plugins/opa/test/pixiu_test.go
rename to plugins/opa/server-mode/server/app/server.go
index bbb591d..86f461b 100644
--- a/plugins/opa/test/pixiu_test.go
+++ b/plugins/opa/server-mode/server/app/server.go
@@ -6,7 +6,7 @@
  * (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
+ *     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,
@@ -15,37 +15,38 @@
  * limitations under the License.
  */
 
-package test
+package main
 
 import (
-       "io"
+       "encoding/json"
+       "log"
        "net/http"
        "strings"
-       "testing"
-       "time"
 )
 
-import (
-       "github.com/stretchr/testify/assert"
-)
+type Resp struct {
+       Message string `json:"message"`
+       Result  string `json:"result"`
+}
 
-func TestUserServiceAllow(t *testing.T) {
-       url := "http://localhost:8888/UserService";
-       client := &http.Client{Timeout: 5 * time.Second}
+func main() {
+       routers := []string{"/UserService", "/OtherService"}
 
-       req, err := http.NewRequest("GET", url, nil)
-       assert.NoError(t, err)
+       for _, rt := range routers {
+               route := rt
+               msg := route[strings.LastIndex(route, "/")+1:]
 
-       // Must add header to pass OPA
-       req.Header.Set("Test_header", "1")
+               http.HandleFunc(route, func(w http.ResponseWriter, r 
*http.Request) {
+                       log.Printf("[backend] %s %s Headers=%v", r.Method, 
r.URL.Path, r.Header)
 
-       resp, err := client.Do(req)
-       assert.NoError(t, err)
-       assert.NotNil(t, resp)
-       assert.Equal(t, 200, resp.StatusCode)
+                       w.Header().Set("Content-Type", "application/json")
+                       _ = json.NewEncoder(w).Encode(Resp{
+                               Message: msg,
+                               Result:  "pass",
+                       })
+               })
+       }
 
-       body, _ := io.ReadAll(resp.Body)
-       // OPA allows -> backend returns "pass" JSON
-       assert.True(t, strings.Contains(string(body), "pass"))
-       assert.True(t, strings.Contains(string(body), "UserService"))
+       log.Println("Starting sample backend on :1314 ...")
+       log.Fatal(http.ListenAndServe(":1314", nil))
 }
diff --git a/plugins/opa/server-mode/test/pixiu_test.go 
b/plugins/opa/server-mode/test/pixiu_test.go
new file mode 100644
index 0000000..bf18fce
--- /dev/null
+++ b/plugins/opa/server-mode/test/pixiu_test.go
@@ -0,0 +1,91 @@
+/*
+ * 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 test
+
+import (
+       "io"
+       "net"
+       "net/http"
+       "strings"
+       "testing"
+       "time"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+const serverModeGatewayBaseURL = "http://localhost:8888";
+
+func doServerModeRequest(t *testing.T, path string, headerVal *string) (int, 
string) {
+       t.Helper()
+
+       client := &http.Client{Timeout: 5 * time.Second}
+       req, err := http.NewRequest("GET", serverModeGatewayBaseURL+path, nil)
+       if err != nil {
+               t.Fatalf("failed to build request: %v", err)
+       }
+
+       if headerVal != nil {
+               req.Header.Set("Test_header", *headerVal)
+       }
+
+       resp, err := client.Do(req)
+       if err != nil {
+               if ne, ok := err.(net.Error); ok && ne.Timeout() {
+                       t.Fatalf("request timeout: ensure server-mode Pixiu, 
OPA server, and backend are running: %v", err)
+               }
+               t.Fatalf("request failed: ensure server-mode Pixiu, OPA server, 
and backend are running: %v", err)
+       }
+       if resp == nil {
+               t.Fatalf("response is nil")
+       }
+       defer resp.Body.Close()
+
+       body, err := io.ReadAll(resp.Body)
+       if err != nil {
+               t.Fatalf("failed to read response body: %v", err)
+       }
+       return resp.StatusCode, string(body)
+}
+
+func TestServerModeUserServiceAllow(t *testing.T) {
+       header := "1"
+       status, body := doServerModeRequest(t, "/UserService", &header)
+
+       assert.Equal(t, http.StatusOK, status)
+       assert.True(t, strings.Contains(body, "pass"))
+       assert.True(t, strings.Contains(body, "UserService"))
+}
+
+func TestServerModeUserServiceDeny(t *testing.T) {
+       status, body := doServerModeRequest(t, "/UserService", nil)
+
+       assert.Equal(t, http.StatusForbidden, status)
+       assert.False(t, strings.Contains(body, "pass"))
+       assert.False(t, strings.Contains(body, "UserService"))
+}
+
+func TestServerModeOtherServiceDeny(t *testing.T) {
+       header := "1"
+       status, body := doServerModeRequest(t, "/OtherService", &header)
+
+       assert.Equal(t, http.StatusForbidden, status)
+       assert.False(t, strings.Contains(body, "pass"))
+       assert.False(t, strings.Contains(body, "OtherService"))
+}


Reply via email to