Rego Keyword: contains
Rego's contains
keyword is used to incrementally build
multi-value rules
in a policy. Often, tasks like validation are defined as a series of checks
and these break down nicely into a series of contains
rules that evaluate
to a larger result. A contains
rule typically takes the following form:
my_rule contains value if {
# logic to check if the value should be set
# set the value
# value := ...
}
However, there some some different ways to use contains
in a policy which are covered
in the examples below.
If you're looking for the built-in function contains
for substring checking, you can read
about it in the built-ins section.
Examples
Building sets
While this first example is trivially simple and unlikely to be useful when building real
policies, it illustrates the fundamental reason for using the contains
keyword: building sets.
Sets are unordered collections, and they form an important building block for many policies.
In this example, we use a multi-value rule defined using the contains
keyword to create a simple
list of todos. Just remember that sets are unordered and so you should not depend on the order of
the result.
{}
{}
package play
import rego.v1
todo contains "Buy bread for lunch"
todo contains "Send off tax return"
todo contains "Do laundry"
Rule | Output Value |
---|---|
todos | ["Buy bread for lunch","Send off tax return","Do Laundry"] |
Forming a list of error codes
Contains is used for creating a set of items, this list can be defined
by policy and make reference to input
or data
and other rules.
package play
import rego.v1
errors contains "E0001" if _token_expired
errors contains "E1004" if object.get(input, "email", "") == ""
errors contains "E1209" if object.get(input, "claims", []) == []
default _token_expired := true
_token_expired if time.parse_ns("2006-01-02", input.expired_at) < time.now_ns()
{
"e-Mail": "alice@example.com",
"claims": [],
"expired_at": "2024-06-01"
}
{}
Rule | Output Value | Notes |
---|---|---|
errors | ["E0001","E1004","E1209"] | The input fails all three checks. |
_token_expired | true | This is an '_internal' rule used in setting errors |
Aggregating validation failures
Using contains
is commonly used to create validation messages
from performing a series of checks on an object. In this example, if
there are any failures
then allow
will be false. Using contains
makes it possible to build up failures
incrementally.
package play
import rego.v1
default allow := false
allow if count(reasons) == 0
reasons contains "name must be set" if {
object.get(input, "name", "") == ""
}
reasons contains "@example.com email blocked" if {
endswith(input.email, "@example.com")
}
reasons contains message if {
input.age < 18
message := sprintf(
"you must be %d year older",
[18 - input.age],
)
}
{
"email": "name@example.com",
"age": 17
}
{}
Rule | Output Value | Notes |
---|---|---|
reasons | ["@example.com email blocked","name must be set","you must be 1 year older"] | A list of three validation messages |
allow | false | Based on the number of messages in reasons |
Validating lists of objects
Using contains
is also useful when building up more complicated
data structures in policies. In this example, contains
is used to set
validation errors that are grouped by the item they refer to.
package play
import rego.v1
approval_min := 350
reasons[item.id] contains "category must be set" if {
some item in input.items
object.get(item, "category", "") == ""
}
reasons[item.id] contains message if {
some item in input.items
item.amount > approval_min
object.get(item, "approved_by", "") == ""
message := sprintf(
"items over %d must be approved",
[approval_min],
)
}
reasons[item.id] contains "approver does not exist" if {
some item in input.items
not data.approvers[item.approved_by]
}
{
"items": [
{
"id": "1bd91655",
"name": "Lunch with James",
"amount": 57.5
},
{
"id": "226c3910",
"name": "Flight to Berlin",
"amount": 389.78,
"category": "conferences"
},
{
"id": "db5a4b23",
"name": "Recurring SaaS Subscription",
"amount": 30,
"category": "saas",
"approved_by": "90400852"
}
]
}
{
"approvers": {
"7f619663": {
"name": "Alice",
"email": "alice@example.com"
}
}
}
Rule | Output Value | Notes |
---|---|---|
reasons.226c3910 | ["items over 350 must be approved"] | A list of validations per item |
reasons.1bd91655 | ["category must be set"] | A list of validations per item |
reasons.db5a4b23 | ["approver does not exist"] | A list of validations per item |
approval_min | 350 | static value set in policy code |