Write condition expressions
Where are condition expressions used in ConductorOne?
ConductorOne leverages condition expressions to let you build powerful custom policies and finely tuned user groups.
There are three conditional expression types used in ConductorOne:
- Policy conditionals determine what action a policy will take. They must return a Boolean.
- Policy expressions determine who will be assigned a task. They must return a list of users.
- Group expressions determine the membership of a group. They must return a list of users.
Policies
You can add condition expressions to two places in a policy:
Groups
You can also use a condition expression when configuring a custom ConductorOne group:
Forming condition expressions using CEL
ConductorOne’s condition expressions are formed using the Common Expression Language (CEL) built by Google. As you work with CEL, you might find these references useful:
Functions
These library functions let you interact with the ConductorOne system to look up whether a user has access to a certain application or entitlement, or to find the user or list of users who should review a task.
Function | Accepts | Returns |
---|---|---|
c1.user.v1.HasApp | user, app ID | Boolean |
c1.user.v1.HasEntitlement | user, app ID, and entitlement ID | Boolean |
c1.directory.users.v1.FindByEmail | user | |
c1.directory.users.v1.GetByID | user ID | user |
c1.directory.users.v1.GetManagers | user | users |
c1.directory.groups.v1.FindByName | group name | group |
Objects
Subject object
The “subject” variable refers to the ConductorOne user.
Field | Data type | Notes |
---|---|---|
subject.id | string | |
subject.department | string | |
subject.job_title | string | |
subject.profile | map[string]interface{} | Profile attributes can have any type, but are usually strings. |
subject.email | string | |
subject.email.startsWith | string | |
subject.email.endsWith | string | |
subject.status | enum | One of: USER_STATUS_ENABLED USER_STATUS_DISABLED USER_STATUS_DELETED |
subject.directory_status | enum | One of: USER_STATUS_ENABLED USER_STATUS_DISABLED USER_STATUS_DELETED |
subject.employment_type | string | |
subject.employment_status | string | |
subject.manager | string | |
subject.profile.< CUSTOM USER ATTRIBUTE > | varies | See explanation below. |
Use custom user attributes. You can write condition expressions that leverage the custom user attributes you’ve set up in ConductorOne. Any custom user attribute can be passed in to the
subject.profile.<CUSTOM USER ATTRIBUTE>
property and used in your condition expressions.
User object
Field | Data type | Notes |
---|---|---|
user.id | string | |
user.display_name | string | |
user.department | string | |
user.job_title | string | |
user.profile | JSON | Map of string -> value |
user.email | string | |
user.status | UserStatus enum | One of: UserStatus.ENABLED UserStatus.DISABLED UserStatus.DELETED |
user.directory_status | UserStatus enum | One of: UserStatus.ENABLED UserStatus.DISABLED UserStatus.DELETED |
user.employment_type | string | |
user.employment_status | string |
Task object
Field | Data type | Notes |
---|---|---|
task.id | string | |
task.numeric_id | string | |
task.display_name | string | |
task.analysis | analysis | See below |
task.origin | TaskOrigin enum | One of: TaskOrigin.PROFILE_MEMBERSHIP_AUTOMATION TaskOrigin.SLACK TaskOrigin.API TaskOrigin.JIRA TaskOrigin.COPILOT TaskOrigin.WEBAPP TaskOrigin.TIME_REVOKE |
task.is_grant_permanent | Boolean | |
task.grant_duration | duration |
Task analysis object
Field | Data type | Notes |
---|---|---|
task.analysis.id | string | |
task.analysis.has_conflict_violations | Boolean | |
task.analysis.conflict_violations | array of strings |
IP address object
Field | Data type | Notes |
---|---|---|
ip.is_4 | Boolean | |
ip.is_6 | Boolean | |
ip.is_private | Boolean | |
ip.is_loopback | Boolean | |
ip.is_global_unicast | Boolean | |
ip.is_multicast | Boolean | |
ip.is_interface_local_multicast | Boolean | |
ip.is_link_local_multicast | Boolean | |
ip.is_unspecified | Boolean |
IP CIDR object
No properties.
IP CIDR/address examples:
cidr('10.1.2.0/24').contains(ip('1.2.3.4'))
ip('10.1.2.5').within(cidr('10.1.2.0/24'))
cidr('10.1.2.0/24', '5.4.3.2/32')
Operators
CEL supports common Boolean operators, like !
, <
, >
, <=
, >=
, ||
, &&
, ==
, !=
, and in
. All operators work as they do in C, and in
functions as a “list contains” operator.
CEL allows for basic arithmetic operations, with +
, -
, *
, and \
for adding, subtracting, multiplying, and dividing.
CEL also supports ternary operators, similar to C or JavaScript. These are formed as “If this ? then check this : otherwise check this”.
Example condition expressions
Expressions that return a Boolean
Check that the subject’s email is “cheddar.crackers@company.com”:
subject.email == "cheddar.crackers@company.com"
Check that the user’s email address starts with “engineering”:
subject.email.startsWith("engineering")
Check that the user’s email address ends with “@company.com”:
subject.email.endsWith("@company.com")
Check that the user’s email address doesn’t end with “@company.com”:
!subject.email.endsWith("@company.com")
Check that the subject’s favorite food is macaroni:
subject.profile.favorite_food == "macaroni"
Check that the subject’s favorite foods include sushi:
"Sushi" in subject.profile.favorite_foods
Check if the user is enabled in the directory:
subject.directory_status == USER_STATUS_ENABLED
Check that the user status is enabled and the department is IT:
subject.status == USER_STATUS_ENABLED && subject.department == "IT"
Check that the user’s status is disabled or deleted, and their department is “ENG”:
(subject.status == USER_STATUS_DISABLED || subject.status == USER_STATUS_DELETED)
&& subject.department == "ENG"
Check that the user has access to the app with that ID:
c1.user.v1.HasApp(subject, "2SWtmlkdW0dtROVwIN0zYthXIud") <APP ID>
Check that the user doesn’t have that app, and their department is engineering:
!c1.user.v1.HasApp(subject, "2SWtmlkdW0dtROVwIN0zYthXIud")
&& subject.department == "Engineering"
Check if the subject’s employment type is full time:
subject.employment_type == "Full Time"
Check that the user has the entitlement in that app:
c1.user.v1.HasEntitlement(subject, "2SWtmlkdW0dtROVwIN0zYthXIud",
"2SWtwwe5n7AOXhRBRNK1fUakc4F")
If the subject has the director profile attribute, check that the director is Holly. Otherwise, check that their manager is Ivy:
has(subject.profile.director) ? subject.profile.director ==
"holly.berry@company.com" : subject.profile.manager == "ivy.vine@company.com"
If the subject’s employee status is ENABLED, check that their department is Engineering, otherwise check that their last date active was April 1, 2024:
subject.employee_status == EMPLOYEE_STATUS_ENABLED ? subject.profile.department ==
"Engineering" : subject.profile.last_date_active == "04/1/2024"
Check whether an entitlement is granted to a user as part of an access profile:
c1.user.v1.GrantedFromEnrollment(subject, entitlement.app_id, entitlement.id)
Expressions that return a user
Return the subject user’s director:
c1.directory.users.v1.FindByEmail(subject.profile.director)
Return the subject user’s manager:
c1.directory.users.v1.FindByEmail(subject.profile.manager)
Return the subject outright, meaning they’ll have to self approve:
subject
If the subject is in the IT department, return themselves, otherwise return their manager:
subject.profile.department == "IT" ? subject : c1.directory.users.v1.FindByEmail(subject.profile.manager)
If the user has the prop_that_only_exists_sometimes
, check its value. If the value matches, find the user by email. If it doesn’t match, find a user by ID. If the user doesn’t have that prop, return a separate user:
has(subject.profile.prop_that_only_exists_sometimes) ? subject.profile.prop_that_only_exists_sometimes == "Value That Happens" ? c1.directory.users.v1.FindByEmail("some.email@insulator.one") : c1.directory.users.v1.GetByID("012345678901234567890123456") : c1.directory.users.v1.GetByID("999999999977777777773333333")
Pre-built policy condition expressions
Here are some basic conditional policy use cases and their corresponding condition expressions. You can use these expressions as-is, or adapt them to suit your organization’s needs.
Pre-approve access based on group membership
Use case
If a user has an active account in Okta, and they currently have the Admin role in Jira, they can be automatically approved for the Admin role in Confluence.
Condition expression
c1.user.v1.HasApp(subject, "<APP ID>") &&
c1.user.v1.HasEntitlement(subject, "<APP ID>", "<ENTITLEMENT ID>")
Go to an application or entitlement’s details page to look up its ID, or use Cone.
Pre-approve access for employees who are currently on call
Use case
If a user is currently in a PagerDuty on-call rotation, they can be automatically approved for AWS S3 read access.
Condition expression
c1.user.v1.HasEntitlement(subject, "<APP ID>", "<SCHEDULE 1 ENTITLEMENT ID") ||
c1.user.v1.HasEntitlement(subject, "<APP ID>", "<SCHEDULE 2 ENTITLEMENT ID")
Auto-certify low-risk access
Use case
If a user has an active account in Google Workspace, and they are in the Engineering department, their GitHub access is automatically certified.
Condition expression
c1.user.v1.HasApp(subject, "<APP ID>") &&
subject.department == "<DEPARTMENT>"
Custom review flow for contractors
Use case
Access reviews for contractors (whose email addresses all end in @contract.company.com) are automatically assigned to their current manager, while all full-time employees (whose email addresses all end in @company.com) complete a self-evaluation.
Condition expression
!subject.email.endsWith("@company.com")
Proactively check for potential access conflicts
Use cases
Check if the requested access would create any access conflict and route the request to the security team for review if so.
Check if the requested access would create a specific access conflict and auto-deny the request if so.
Condition expressions
//would create an access conflict
task.analysis.conflict_violations
//would create a specific access conflict
`2mw8cxniKGdpndC9GJ5RVwQPnEg` in task.analysis.conflict_violations
Approve access based on grant length
Use case
Check if the access requested is for a limited duration or is permanent. Automatically approve access requests for two hours or more, but send requests for permanent access to a manager for review.
Condition expressions
//access does not expire
task.is_grant_permanent == true
//access is granted for a limited period of more than two hours
task.is_grant_permanent == false && task.grant_duration > duration("2h")
Route access requests based on where they were created
Use cases
Automatically route access requests that were not created in the ConductorOne UI (webapp) to a manager for review. Automatically deny requests for access to sensitive applications that were created in Slack.
Condition expressions
task.origin == TaskOrigin.SLACK
task.origin != TaskOrigin.WEBAPP