# Expressions (https://docs.idra.zone/docs/admin/expressions)

The expression syntax IdRa supports, a subset of the Okta Expression Language.

IdRa evaluates a safe subset of the [Okta Expression Language](https://developer.okta.com/docs/reference/okta-expression-language/). The engine is in place for group rules, attribute mappings, SAML and token claims, and policies, which adopt it feature by feature. Those screens don't accept expressions yet.

## References

| Syntax | Meaning |
|---|---|
| `user.firstName` | An attribute of the person |
| `appuser.login` | The person's attribute in the current app |
| `app.domain`, `org.subDomain` | App and organization properties |
| `idpuser.email` | The person at the identity provider |
| `session.amr`, `access.scope` | Session and token properties |
| `user.address.city`, `user.tags[0]` | Nested attributes and array elements. The index must be a whole number |

Each feature decides which of these it provides. A missing attribute is `null`, while an unknown namespace such as `usr.email` is an error.

## Values and operators

| Kind | Examples |
|---|---|
| Literals | `'text'`, `"text"`, `1234`, `3.141`, `true`, `null`, `{1, 2, 3}` |
| Concatenation | `user.firstName + " " + user.lastName` (`null` becomes empty text; two numbers add) |
| Comparison | `==`, `!=`, `<`, `>`, `<=`, `>=` |
| Boolean | `AND`, `OR`, `!` (also `&&`, `\|\|`) |
| Ternary | `user.salary > 100 ? 'Senior' : 'Junior'` |
| Elvis | `user.nickname ?: user.firstName` uses the right side when the left is `null` or empty |

Comparisons are strict and case-sensitive: `"123" == 123` is false, and `Convert.toNum` converts text first. `<`, `>`, `<=` and `>=` compare two numbers or two strings, are false if either side is `null`, and are an error otherwise.

Conditions need a boolean. `null` counts as false, so `!user.isContractor` is **true** for a person without that attribute. To require the attribute, write `user.isContractor == false`.

## Functions

| Group | Functions |
|---|---|
| `String` | `append`, `join`, `len`, `removeSpaces`, `replace`, `replaceFirst`, `startsWith`, `endsWith`, `stringContains`, `stringSwitch`, `substring`, `substringBefore`, `substringAfter`, `toUpperCase`, `toLowerCase` |
| `Arrays` | `add`, `remove`, `clear`, `get`, `flatten`, `contains`, `size`, `isEmpty`, `toCsvString` |
| `Convert` | `toInt`, `toNum` |
| `Time` | `now()`, the current UTC time in ISO 8601 |
| Groups | `isMemberOfGroupName`, `isMemberOfGroup`, `isMemberOfAnyGroup`, `isMemberOfGroupNameStartsWith`, `isMemberOfGroupNameContains`, with or without a `user.` prefix |

`toLowerCase`, `toUpperCase`, `substring`, `substringBefore` and `substringAfter` also work without the `String.` prefix.

- `String.replace` and `String.replaceFirst` match literal text, not patterns.
- `String.stringSwitch(input, default, key, value, ...)` returns the value of the first key that `input` contains.
- `Arrays` functions also accept `null` (an empty array) and comma-separated text. `Arrays.contains` and `Arrays.remove` compare single values as text, so `Arrays.contains("10, 20", 10)` is true.

Examples:

```text
String.toLowerCase(String.substringBefore(user.email, "@"))
isMemberOfGroupName("Admins") OR Arrays.contains(user.tags, "oncall")
String.stringSwitch(user.department, "Other", "Eng", "Engineering", "Sales", "Revenue")
```

## Not supported

These are rejected with a clear error:

- Regular expressions: `matches` and `isMemberOfGroupNameRegex`.
- `Time.now` with a time zone or format, and the other `Time` functions.
- `Iso3166Convert`, `getFilteredGroups`, `user.getGroups`, `user.getInternalProperty`, and the manager, assistant and linked-object functions.
- Methods on values (`user.email.toLowerCase()`), maps and projections (`.![name]`). Use the function form: `String.toLowerCase(user.email)`.

## Limits

An expression can be up to 4096 characters and nested up to 32 levels. A text result can be up to 65,536 characters; going over is an error when the expression runs. Internal names such as `__proto__`, `constructor` and `prototype` are rejected.