Refactor Policy Enforcer to Policy Checker
- Renamed the `PolicyEnforcer` interface and related implementations to `PolicyChecker` for clarity and consistency. - Updated configuration keys in YAML files to reflect the new `checkPolicy` terminology. - Adjusted related code, tests, and documentation to support the new naming convention and ensure compatibility. - Enhanced comments and examples for the `checkPolicy` configuration to improve usability.
This commit is contained in:
194
pkg/plugin/implementation/opapolicychecker/README.md
Normal file
194
pkg/plugin/implementation/opapolicychecker/README.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# OPA Policy Checker Plugin
|
||||
|
||||
Validates incoming Beckn messages against network-defined business rules using [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) and the Rego policy language. Non-compliant messages are rejected with a `BadRequest` error code.
|
||||
|
||||
## Features
|
||||
|
||||
- Evaluates business rules defined in Rego policies
|
||||
- Supports multiple policy sources: remote URL, local file, directory, or OPA bundle (`.tar.gz`)
|
||||
- Structured result format: `{"valid": bool, "violations": []string}`
|
||||
- Fail-closed on empty/undefined query results — misconfigured policies are treated as violations
|
||||
- Runtime config forwarding: adapter config values are accessible in Rego as `data.config.<key>`
|
||||
- Action-based enforcement: apply policies only to specific beckn actions (e.g., `confirm`, `search`)
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
checkPolicy:
|
||||
id: opapolicychecker
|
||||
config:
|
||||
type: file
|
||||
location: ./pkg/plugin/implementation/opapolicychecker/testdata/example.rego
|
||||
query: "data.policy.result"
|
||||
actions: "confirm,search"
|
||||
steps:
|
||||
- checkPolicy
|
||||
- addRoute
|
||||
```
|
||||
|
||||
### Configuration Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `type` | string | Yes | - | Policy source type: `url`, `file`, `dir`, or `bundle` |
|
||||
| `location` | string | Yes | - | Path or URL to the policy source (`.tar.gz` for bundles) |
|
||||
| `query` | string | Yes | - | Rego query path to evaluate (e.g., `data.policy.result`) |
|
||||
| `actions` | string | No | *(all)* | Comma-separated beckn actions to enforce |
|
||||
| `enabled` | string | No | `"true"` | Enable or disable the plugin |
|
||||
| `debugLogging` | string | No | `"false"` | Enable verbose OPA evaluation logging |
|
||||
| `refreshIntervalSeconds` | string | No | - | Reload policies every N seconds (0 or omit = disabled) |
|
||||
| *any other key* | string | No | - | Forwarded to Rego as `data.config.<key>` |
|
||||
|
||||
|
||||
|
||||
## Policy Hot-Reload
|
||||
|
||||
When `refreshIntervalSeconds` is set, a background goroutine periodically re-fetches and recompiles the policy source without restarting the adapter:
|
||||
|
||||
- **Atomic swap**: the old evaluator stays fully active until the new one is compiled — no gap in enforcement
|
||||
- **Non-fatal errors**: if the reload fails (e.g., file temporarily unreachable or parse error), the error is logged and the previous policy stays active
|
||||
- **Goroutine lifecycle**: the reload loop is tied to the adapter context and stops cleanly on shutdown
|
||||
|
||||
```yaml
|
||||
config:
|
||||
type: file
|
||||
location: ./policies/compliance.rego
|
||||
query: "data.policy.result"
|
||||
refreshIntervalSeconds: "300" # reload every 5 minutes
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Initialization (Load Time)
|
||||
|
||||
1. **Load Policy Source**: Fetches `.rego` files from the configured `location` — URL, file, directory, or OPA bundle
|
||||
2. **Compile Policies**: Compiles all Rego modules into a single optimized `PreparedEvalQuery`
|
||||
3. **Set Query**: Prepares the OPA query from the configured `query` path (e.g., `data.policy.result`)
|
||||
|
||||
### Request Evaluation (Runtime)
|
||||
|
||||
1. **Check Action Match**: If `actions` is configured, skip evaluation for non-matching actions
|
||||
2. **Evaluate OPA Query**: Run the prepared query with the full beckn message as `input`
|
||||
3. **Handle Result**:
|
||||
- If the query returns no result (undefined) → **violation** (fail-closed)
|
||||
- If result is `{"valid": bool, "violations": []string}` → use structured format
|
||||
- If result is a `set` or `[]string` → each string is a violation
|
||||
- If result is a `bool` → `false` = violation
|
||||
- If result is a `string` → non-empty = violation
|
||||
4. **Reject or Allow**: If violations are found, NACK the request with all violation messages
|
||||
|
||||
### Supported Query Output Formats
|
||||
|
||||
| Rego Output | Behavior |
|
||||
|-------------|----------|
|
||||
| `{"valid": bool, "violations": ["string"]}` | Structured result format (recommended) |
|
||||
| `set()` / `[]string` | Each string is a violation message |
|
||||
| `bool` (`true`/`false`) | `false` = denied, `true` = allowed |
|
||||
| `string` | Non-empty = violation |
|
||||
| Empty/undefined | **Violation** (fail-closed) — indicates misconfigured query path |
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Local File
|
||||
|
||||
```yaml
|
||||
checkPolicy:
|
||||
id: opapolicychecker
|
||||
config:
|
||||
type: file
|
||||
location: ./pkg/plugin/implementation/opapolicychecker/testdata/example.rego
|
||||
query: "data.policy.result"
|
||||
```
|
||||
|
||||
### Remote URL
|
||||
|
||||
```yaml
|
||||
checkPolicy:
|
||||
id: opapolicychecker
|
||||
config:
|
||||
type: url
|
||||
location: https://policies.example.com/compliance.rego
|
||||
query: "data.policy.result"
|
||||
```
|
||||
|
||||
### Local Directory (multiple `.rego` files)
|
||||
|
||||
```yaml
|
||||
checkPolicy:
|
||||
id: opapolicychecker
|
||||
config:
|
||||
type: dir
|
||||
location: ./policies
|
||||
query: "data.policy.result"
|
||||
```
|
||||
|
||||
### OPA Bundle (`.tar.gz`)
|
||||
|
||||
```yaml
|
||||
checkPolicy:
|
||||
id: opapolicychecker
|
||||
config:
|
||||
type: bundle
|
||||
location: https://nfo.example.org/policies/bundle.tar.gz
|
||||
query: "data.retail.validation.result"
|
||||
```
|
||||
|
||||
## Writing Policies
|
||||
|
||||
Policies are written in [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/). The plugin passes the full beckn message body as `input` and any adapter config values as `data.config`:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import rego.v1
|
||||
|
||||
# Default result: valid with no violations.
|
||||
default result := {
|
||||
"valid": true,
|
||||
"violations": []
|
||||
}
|
||||
|
||||
# Compute the result from collected violations.
|
||||
result := {
|
||||
"valid": count(violations) == 0,
|
||||
"violations": violations
|
||||
}
|
||||
|
||||
# Require provider on confirm
|
||||
violations contains "confirm: missing provider" if {
|
||||
input.context.action == "confirm"
|
||||
not input.message.order.provider
|
||||
}
|
||||
|
||||
# Configurable threshold from adapter config
|
||||
violations contains "delivery lead time too short" if {
|
||||
input.context.action == "confirm"
|
||||
lead := input.message.order.fulfillments[_].start.time.duration
|
||||
to_number(lead) < to_number(data.config.minDeliveryLeadHours)
|
||||
}
|
||||
```
|
||||
|
||||
See [`testdata/example.rego`](./testdata/example.rego) for a full working example.
|
||||
|
||||
## Relationship with Schema Validator
|
||||
|
||||
`opapolicychecker` and `schemav2validator` serve different purposes:
|
||||
|
||||
- **Schemav2Validator**: Validates message **structure** against OpenAPI/JSON Schema specs
|
||||
- **OPA Policy Checker**: Evaluates **business rules** via OPA/Rego policies
|
||||
|
||||
Configure them side-by-side in your adapter steps as needed.
|
||||
|
||||
## Plugin ID vs Step Name
|
||||
|
||||
- **Plugin ID** (used in `id:`): `opapolicychecker` (lowercase, implementation-specific)
|
||||
- **Step name** (used in `steps:` list and YAML key): `checkPolicy` (camelCase verb)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `github.com/open-policy-agent/opa` — OPA Go SDK for policy evaluation and bundle loading
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- **No bundle signature verification**: When using `type: bundle`, bundle signature verification is skipped. This is planned for a future enhancement.
|
||||
- **Network-level scoping**: Policies apply to all messages handled by the adapter instance. Per-network policy mapping (by `networkId`) is tracked for follow-up.
|
||||
Reference in New Issue
Block a user