7.4 KiB
7.4 KiB
OPA Policy Checker Plugin
Validates incoming Beckn messages against network-defined business rules using Open Policy Agent (OPA) 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) - Configurable fetch timeout for remote policy and bundle sources
- Warns at startup when policy enforcement is explicitly disabled
Configuration
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 |
fetchTimeoutSeconds |
string | No | "30" |
Timeout in seconds for fetching remote .rego files or bundles |
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 stops when the adapter context is cancelled or when plugin
Close()is invoked during shutdown
config:
type: file
location: ./policies/compliance.rego
query: "data.policy.result"
refreshIntervalSeconds: "300" # reload every 5 minutes
How It Works
Initialization (Load Time)
- Load Policy Source: Fetches
.regofiles from the configuredlocation— URL, file, directory, or OPA bundle - Compile Policies: Compiles all Rego modules into a single optimized
PreparedEvalQuery - Set Query: Prepares the OPA query from the configured
querypath (e.g.,data.policy.result)
Request Evaluation (Runtime)
- Check Action Match: If
actionsis configured, skip evaluation for non-matching actions. The plugin assumes standard adapter routes look like/{participant}/{direction}/{action}such as/bpp/caller/confirm; non-standard paths fall back tocontext.actionfrom the JSON body. - Evaluate OPA Query: Run the prepared query with the full beckn message as
input - 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
setor[]string→ each string is a violation - If result is a
bool→false= violation - If result is a
string→ non-empty = violation
- 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
checkPolicy:
id: opapolicychecker
config:
type: file
location: ./pkg/plugin/implementation/opapolicychecker/testdata/example.rego
query: "data.policy.result"
Remote URL
checkPolicy:
id: opapolicychecker
config:
type: url
location: https://policies.example.com/compliance.rego
query: "data.policy.result"
fetchTimeoutSeconds: "10"
Local Directory (multiple .rego files)
checkPolicy:
id: opapolicychecker
config:
type: dir
location: ./policies
query: "data.policy.result"
OPA Bundle (.tar.gz)
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. The plugin passes the full beckn message body as input and any adapter config values as data.config:
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 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. - Non-standard route shapes: URL-based action extraction assumes the standard Beckn adapter route shape
/{participant}/{direction}/{action}and falls back tocontext.actionfor other path layouts.