Polar operators
Operators are used to combine terms in rule bodies into expressions.
Unification (=)
Unification is the basic matching operation in Polar. Two values are said to unify if they are equal or if there is a consistent set of variable bindings that makes them equal. Unification is defined recursively over lists: two lists unify if all of their corresponding elements unify.
The unification operator (=
) checks if its left and right operands unify; for
example, "a" = "a"
, x = "a"
, or ["a", "b"] = [x, "b"]
where the variable
x
is either bound to "a"
or unbound.
Conjunction (and)
The and
operator is used to state that a pair of conditions in a rule's body
must both hold. For example, the rule…
oso_employee(first, last) if is_user(first, last) and is_employee("Oso", first, last);
…will be satisfied if the person is a user and an Oso employee.
Disjunction (or)
The or
operator will be true if either its left or its right operand is
true. Disjunctions can always be replaced by multiple rules with identical
heads but different bodies, but the or
operator may help simplify writing
rules with alternatives. For example:
is_user(first, last) if oso_employee(first, last) or is_guest(first, last);# The `or` can be rewritten as a pair of rules:is_user(first, last) if oso_employee(first, last);is_user(first, last) if is_guest(first, last);
Negation (not)
The not
operator is used to check that a certain fact does not exist.
For example:
allow(user, action, resource) if not is_banned(user) and has_permission(user, action, resource);
This rule only allows a user to perform an action on a resource if the policy grants them that permission and the user has not been banned.
As overusing negation can make logic indirect and hard to understand, it is limited to
facts; you cannot negate a compound expression (e.g. one containing and
or or
)
nor an expression that refers to another rule in your policy.
Additionally, all variables used in a negated fact must also be used
in a fact that is not negated.
List membership (in)
The in
operator can be used to iterate over lists of strings. An in
operation looks like this:
x in ["a", "b", "c"]
The left operand, x
, is a variable that will be
unified with each element in the list. If the right operand is
not a list of strings, the in
operation will fail.
In the following example, the variable x
will be bound to "a"
, "b"
, and
"c"
, in turn, and then the x = "a"
check will evaluate. This expression
will only succeed for the first item in the list, "a"
.
x in ["a", "b", "c"] and x = "a"
The left operand does not need to be a variable. For example, the following
expression will succeed twice since "a"
is in the first and fourth
positions in the list:
"a" in ["a", "b", "c", "a"]
Integer comparisons
The <
, <=
, >
, and >=
operators compare integer values.
For example, if you have a fact expires_at(File:foo, 1670280790)
indicating the time that the "foo" file expires:
expires_after_y2k38(resource) if expires_at(resource, time) and time > 2147483647
matches
operator
When writing complex rules you can assert that a variable is valid only
for values of a specific type with the matches
operator by writing:
<variable> matches <Type>
You typically only want use matches
expressions when introducing a new
variable in a rule, e.g. to join two rules.
For example, consider this policy snippet:
has_role(user: User, role: String, resource: Resource) if group matches Group and has_group(user, group) and has_role(group, role, resource);
This introduces a variable named group
, asserting that it must be of type
Group
. This rule is satisfied only if both the has_group
and has_role
facts can be satisfied with the same Group
value.
Up next
- Rules + facts to understand how to write Polar rules using operators.
- Constants to see runtime-evaluated constants Polar offers
Talk to an Oso Engineer
If you want to learn more about Polar, schedule a 1x1 with an Oso engineer. We're happy to help.