Files
onix/pkg/plugin/implementation/opapolicychecker/README.md
Ayush Rawat 80e7b299f1 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.
2026-03-23 04:08:13 +05:30

6.7 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)

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
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
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 boolfalse = 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

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"

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.