So a lot of the stuff I’ve been working on lately has revolved around Open Policy Agent (OPA). Though I’m planning a longer writeup on the subject I’ll use this blog to post some of my notes while I exlore this great piece of tehnology.

Incremental rule - sets

A common pattern when writing Rego policies is to use incremental rules to build a set of values. This is tremendously useful for e.g. returning a message for each violation of a defined policy:

deny[reason] {
    input.role != "admin"
    reason = "User not an admin"
}

deny[reason] {
    time.weekday(time.now_ns()) == "Sunday"
    reason = "Access not allowed on Sundays"
}

Any non-admin trying to access the requested resource on a Sunday would now not only be denied, but could also be given an explanation as to why:

"deny": [
    "Access not allowed on Sundays",
    "User not an admin"
]

See full example on the Rego Playground.

Incremental rules - maps

Not as commonly used, but certainly just as useful - incremental rules can also be used to build up maps. This could be used similarily to build up key/value pairs of reasons or violations, but might just as well be used for data transformations. Consider the following input provided to OPA:

{
    "street": "High street 32",
    "zip": "11766"
}

We could create an incremental map rule to gradually build a structured address object with values in a normalized format:

address["zip"] = zip_numeric {
    zip_numeric = to_number(input.zip)
}

address["street_name"] = streetname {
    streetname = trim(concat(" ", regex.find_n(`[^\d]+`, input.street, -1)), " ")
}

address["street_number"] = streetnumber {
    numbers := regex.find_n(`\d+$`, input.street, 1)
    streetnumber = to_number(numbers[0])
}

Which would increment the address rule map as follows:

{
    "address": {
        "street_name": "High street",
        "street_number": 32,
        "zip": 11766
    }
}

Again, see full example on the Rego Playground.