Mappers
Mappers are an optional feature that transforms non-PORC inputs into PORC expressions for policy evaluation. They are only needed when integrating with systems that cannot construct PORC expressions natively.
When to Use Mappers
Mappers are designed for limited integration scenarios where the Policy Enforcement Point (PEP) has no ability to construct PORC expressions directly. The primary use case is integration with third-party systems that use fixed protocols.
Primary Use Case: Envoy/Istio Integration
When using the Manetu PolicyEngine with Envoy's External Authorization protocol, the request format is dictated by Envoy—not by Manetu. Mappers provide an intermediate transformation layer:
Envoy ext_authz Request → Mapper → PORC → Policy Evaluation
When NOT to Use Mappers
Most integrations should NOT use mappers. If you're building a PEP in your own application, you should construct PORC expressions directly in your native programming language:
| Scenario | Recommendation |
|---|---|
| Custom application with Go/Java/Python/etc. | Build PORC directly in your code |
| API gateway you control | Build PORC in gateway middleware |
| Envoy/Istio service mesh | Use mappers (Envoy protocol is fixed) |
| Third-party system with fixed protocol | Use mappers if protocol can't be changed |
Building PORC directly in your application code is:
- More efficient: No additional transformation step
- Easier to debug: Logic is in your familiar language
- More flexible: Full access to application context
- Type-safe: Leverage your language's type system
See the Integration Guide for examples of building PORC expressions directly.
How Mappers Work
Mappers are Rego programs that receive the raw input and produce a porc output variable:
spec:
mappers:
- name: envoy-mapper
selector:
- ".*" # Regex pattern to match requests
rego: |
package mapper
porc := {
"principal": { ... },
"operation": "...",
"resource": { ... },
"context": { ... }
}
Selector Patterns
Selectors determine which mapper handles a request. Mappers are evaluated in order; the first match wins:
spec:
mappers:
- name: frontend-mapper
selector:
- ".*frontend.*"
rego: |
package mapper
# Frontend-specific mapping...
- name: default-mapper
selector:
- ".*"
rego: |
package mapper
# Default mapping...
When a mapper entry contains multiple selectors, they have an OR relationship. The mapper matches if any of its selectors match. This OR behavior applies uniformly to all selector-based entities (operations, resources, and mappers).
Envoy Integration Example
The following example shows a mapper for Envoy's ext_authz protocol:
package mapper
import rego.v1
# Safe accessor with default
get_default(obj, key, fallback) := obj[key] if obj[key]
get_default(obj, key, fallback) := fallback if not obj[key]
# Extract HTTP details from Envoy request
method := lower(get_default(input.request.http, "method", "GET"))
path := get_default(input.request.http, "path", "/")
headers := get_default(input.request.http, "headers", {})
# Extract service from SPIFFE ID
dest := split(input.destination.principal, "/")
service := dest[count(dest) - 1]
# Extract JWT claims from Authorization header
default claims := {}
auth := headers.authorization
token := split(auth, "Bearer ")[1] if auth
claims := io.jwt.decode(token)[1] if token
# Build the PORC expression
porc := {
"principal": claims,
"operation": sprintf("%s:http:%s", [service, method]),
"resource": sprintf("mrn:http:%s%s", [service, path]),
"context": input
}
This example uses the simple MRN string format, which is the recommended approach. The PolicyEngine's Resource Resolution enriches resources with metadata at evaluation time.
Use the Fully Qualified Descriptor format only when the PEP has context that the backend cannot determine.
Testing Mappers
Test mappers with sample inputs:
# Create Envoy-style input
cat > envoy-input.json << 'EOF'
{
"request": {
"http": {
"method": "GET",
"path": "/api/users/123",
"headers": {
"authorization": "Bearer eyJhbGciOiJIUzI1NiJ9..."
}
}
},
"destination": {
"principal": "spiffe://cluster.local/ns/default/sa/api-server"
}
}
EOF
# Test mapper output
mpe test mapper -b my-domain.yml -i envoy-input.json
Best Practices
- Prefer native PORC construction: Only use mappers when you cannot control the input format
- Keep mappers simple: Complex business logic belongs in policies, not mappers
- Handle missing fields gracefully: Use defaults for optional fields
- Validate inputs: Check for required fields before processing
- Use Rego v1: Include
import rego.v1for latest syntax
Related Concepts
- PORC Expressions: The format mappers produce
- Integration Guide: Building PEPs that construct PORC directly
- Envoy Integration: Deploying with Envoy/Istio