# Policy Condition
Conditions are probably the most powerful aspect of the JSON policy framework as it gives the ability to make any statement or param conditional. They are evaluated at runtime and can take into consideration dynamic variables from the application context (environment variable, user identity, HTTP request, etc).
The purpose of the conditions is to determine if any given statement or param is classified as applicable. This is done by evaluating all the types of conditions and returning a single boolean true
if applicable.
{
"Condition": {
"Operator": "AND|OR",
"<condition_type>": {
"Operator": "AND|OR",
"<left_operand>": "<right_operand>",
...
},
...
}
}
By default, the framework supports a few types of conditions, however, it can easily be extended with custom types. For more information please refer to the respective language library reference page.
To eliminate any misunderstandings, in this section I'm using the following terms to describe certain aspects of the policy conditions:
- "Type" is the condition type like
Equals
,In
orRegex
- "Operand" is either the left or the right part of the condition pair within a group. For example, in the sample condition below, the left operand is
${USER.email}
and the right operand is*@gmail.com
- "Group" is the set of conditions of the same type. For example below is the "Like" group.
{
...
"Condition": {
"Like": {
"${USER.email}": "*@gmail.com",
"${USER.fullname}": "John*"
}
}
}
The Condition
attribute may contain more than one type of condition.
# Operator
- Type:
String
- Default Value:
AND
- Supported Values:
AND
,OR
- Optional
The Operator
attribute defines the logical operator between condition types or within a specific group. If none specified, the default operator is AND
.
{
"Condition": {
"Operator": "OR",
"Equals": {
"${DATATIME.D}": "Mon"
},
"In": {
"${USER.email}": [
"admin@mydomain.com",
"editor@mydomain.com"
]
}
}
}
The above condition is evaluated to true if today is Monday
or the user's email address is either admin@mydomain.com
or editor@mydomain.com
.
As you might notice, a group may contain more than one condition and by default, the logical AND
operator is used within the group. This way you can build conditions like "If a user's first name starts with 'Ja' and the last name starts with 'Mor'". However, you can customize the logical operator with the Operator
attribute.
{
"Condition": {
"Operator": "OR",
"Like": {
"Operator": "OR",
"${USER.first_name}": "Ja*",
"${USER.last_name}": "Mor*"
},
"In": {
"${USER.status}": [
"pastdue",
"pending"
]
}
}
}
The above conditions check if the user's first name starts with Ja
or the last name starts with Mor
, or the user's status is either pastdue
or pending
.
# Left Operand Naming Collision
Because policies are defined as JSON documents, you cannot have two exactly named left operands within the same scope. For example, if you want to cover the condition like "If user's city equals London or Dubai", the following condition is not going to work:
{
"Condition": {
"Equals": {
"${USER.city}": "London",
"${USER.city}": "Dubai"
}
}
}
Depending on the programming language, the above condition will be parsed to check only for one city (either London
or Dubai
).
To mitigate the left operand naming collision, you can define the right operand as an array of values.
{
"Condition": {
"Equals": {
"${USER.city}": [
"London",
"Dubai"
]
}
}
}
# Equals
- Type:
ConditionObject
Check if the left and the right operands are identical (contain the same value and type of value). When dynamic markers are used, you should consider applying typecasting.
{
"Condition": {
"Equals": {
"${HTTP_GET.ref}": "qa2020",
"(*bool)${SESSION.is_authenticated}": true
}
}
}
The condition above checks if HTTP query param ref
equals to qa2020
and user session's property is_authenticated
equals to boolean true
value.
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition will be evaluated to true
if the left operand matches at least one value in the array.
{
"Condition": {
"Equals": {
"${USER.city}": [
"London",
"Dubai"
]
}
}
}
The "Equals" group type may also contain more than one condition. Each condition is evaluated independently and merged together with the logical operator that is defined within the group (the default logical operator is AND
).
{
"Condition": {
"Equals": {
"Operator": "OR",
"${USER.city}": [
"London",
"Dubai"
],
"${USER.name}": "Bruce Lee"
}
}
}
The condition above returns true
if a user's city is London
or Dubai
, or the user's name is Bruce Lee
.
# NotEquals
- Type:
ConditionObject
Check if the left and the right operands are not identical (either contain different values or type of values mismatch). When dynamic markers are used, you should consider applying typecasting.
{
"Condition": {
"NotEquals": {
"${USER.name}": "John Dawn"
}
}
}
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition will be evaluated to true
if the left operand does not have the exact match to any value in the array.
{
"Condition": {
"NotEquals": {
"${BOAT.color}": [
"red",
"blue"
]
}
}
}
In the same way, as with the Equals type, the NotEquals
may contain more than one condition and they are evaluated and merged with the logical operator the same way.
# Between
- Type:
Object
Check if the left operand is within one or more ranges defined in the right operand. This type of condition does not take into consideration value type and depending on the programming language, the value dynamically casts before comparison.
The "range" is described by two values [min, max]
and the condition evaluates if the given value is greater or equals to min
and lower or equals to max
.
{
"Condition": {
"Between": {
"${USER.age}": [
13,
20
]
}
}
}
In case there is a need to compare against multiple ranges, you can define an array of ranges in the right operand as following:
{
"Condition": {
"Between": {
"${DATETIME.H}": [
[0, 6]
[18, 23]
]
}
}
}
The above statement checks if the current hour is between 12am
and 6am
or 6pm
and 11pm
.
The Between
group type may contain more than one condition. They are evaluated independently and merged with the logical operator defined in the Operator property.
# Greater
- Type:
ConditionObject
Check if the left operand is greater than the right operand. The comparing values type cast automatically by the programming language of your choice.
{
"Condition": {
"Greater": {
"2020": "${TASK.year_created}"
}
}
}
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is greater than at least one value in the array.
{
"Condition": {
"Greater": {
"2020-10-01": [
"2020-11-01",
"2020-09-17"
]
}
}
}
The above condition is evaluated to true
because 2020-10-01
is greater than 2020-09-17
.
# Less
- Type:
ConditionObject
Check if the left operand is less than the right operand. The comparing values type cast automatically by the programming language of your choice.
{
"Condition": {
"Less": {
"${ASSIGNMENT.rate}": 300.25
}
}
}
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is less than at least one value in the array.
{
"Condition": {
"Less": {
"2020": [
"2021",
"2019"
]
}
}
}
The above condition is evaluated to true
because 2020
is less than 2021
.
# GreaterOrEquals
- Type:
ConditionObject
Check if the left operand is greater or equals than the right operand. The comparing values type cast automatically by the programming language of your choice.
{
"Condition": {
"GreaterOrEquals": {
"${Cargo.weight}": 6500
}
}
}
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is greater or equals to at least one value in the array.
{
"Condition": {
"GreaterOrEquals": {
"F": [
"A",
"Z"
]
}
}
}
The above condition is evaluated to true
because F
char is greater than A
.
# LessOrEquals
- Type:
ConditionObject
Check if the left operand is less or equals to the right operand. The comparing values type cast automatically by the programming language of your choice.
{
"Condition": {
"LessOrEquals": {
"${USER.age}": 20
}
}
}
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is less or equals to at least one value in the array.
{
"Condition": {
"LessOrEquals": {
"${PACKAGE.weight}": [
10,
30.5
]
}
}
}
# In
- Type:
ConditionObject
Check if the left operand is included in the array of values from the right operand. This type of condition checks for the identical match (both value and value type has to be identical).
{
"Condition": {
"In": {
"${BOT.name}": [
"google",
"yahoo",
"${ENV.allowed_bot}"
]
}
}
}
# NotIn
- Type:
ConditionObject
Check if the left operand is not included in the array of values from the right operand. This type of condition checks for the identical match (both value and value type has to be identical).
{
"Condition": {
"NotIn": {
"${UserEntity.username}": "(*array)${DB.suspended_users}"
}
}
}
The condition above assumes that some database instance contains an array of suspended usernames and the (*array)
type case reassures that the right operand is actually the array of values.
# Like
- Type:
ConditionObject
Check if the left operand is similar to the defined pattern in the right operand. The pattern should contain a string where the *
asterisk represents any characters.
{
"Condition": {
"Like": {
"${OS.version}": "19.0.*"
}
}
}
FYI! The pattern does not really have to have the
*
asterisk in it. If none specified, then the condition behaves similarly to the Equals condition type where it compares two string.
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is similar or equals to at least one value in the array.
{
"Condition": {
"Like": {
"access": [
"ac*ss",
"role"
]
}
}
}
The above condition is evaluated to true
because the access
string matches the ac*ss
pattern.
# NotLike
- Type:
ConditionObject
Check if the left operand is not similar to the defined pattern in the right operand. The pattern should contain a string where the *
asterisk represents any characters.
{
"Condition": {
"NotLike": {
"${ENV.environment}": "staging-*"
}
}
}
FYI! The pattern does not really have to have the
*
asterisk in it. If none specified, then the condition behaves similarly to the NotEquals condition type where it compares two string.
The right operand may contain a single scalar value or array of scalar values. In the second case, the condition evaluates to true
if the left operand is not similar or equals to all values in the array.
{
"Condition": {
"NoLike": {
"${USER.name}": [
"John*",
"Mel*a"
]
}
}
}
# RegEx
- Type:
ConditionObject
Check if the left operand matches the regular expression defined in the right operand.
{
"Condition": {
"RegEx": {
"${ORDER.uid}": "^PO[\\d]{10}$"
}
}
}
In case you need to define a regular expression's flag(s), you can wrap the regex with two forward slashes /
trailing with one or more flags (e.g. g
, i
, m
, etc.). For example, the following condition defines a case-insensitive match for the value that may contain multiple newlines.
{
"Condition": {
"RegEx": {
"${CUSTOMER.full_name}": "/^[\\w\\'-]+$/im"
}
}
}