Skip to content

Rules and Conditions

Access Control has a powerful rule and condition engine that uses database objects and relationships to improve employee access lifecycle and support more features than just string matching.

Access Control has one or more Directory Sources that have been configured by your system administrator. During recurring sync jobs, each of the user accounts in that vendor are imported into the Access Control database as a Directory Identity. We specifically look at the profile metadata and encrypt and cache the key value array for easy lookup in the metadata column.

We can use the values of this metadata in Identity Conditions.

/api/v1/directory/identities/01JRGBTDA8QZ69VYV7G32TYHH8
// Security Notice: This is a fake user and does not contain any PII.
{
"id": "01JRGBTDA8QZ69VYV7G32TYHH8",
"vendor_id": "00uddepsuetFvYabw5d7",
"email": "abahringer@example.com",
"state": "active",
"metadata": {
"costCenter": "Marketing",
"countryCode": "US",
"department": "Sales Development",
"displayName": "Ashton Bahringer",
"division": "Marketing",
"email": "abahringer@example.com",
"employeeNumber": "c034642a",
"firstName": "Ashton",
"hire_date": "2022-10-01",
"lastName": "Bahringer",
"login": "abahringer@example.com",
"managerId": "de07edee",
"mobilePhone": null,
"secondEmail": null,
"slack_id": "UXXXXXXXX",
"title": "Business Development Representative",
"userType": "Full Time",
"workday_managementLevel": "Individual Contributor",
"workday_region": "Americas"
},
"source": {
"id": "01JRGBS8MQX4RJR27SJKQGXZD6",
"vendor": "okta",
"slug": "okta-prod",
"domain": "example.okta.com"
},
"user": {
"id": "01JRGBVFDWMJ8FRH80EXZ6XA9Y",
"manager_id": "01JRGBVH93RM0QK6KJFK9GMSN7",
"is_manager": false,
"first_name": "Ashton",
"last_name": "Bahringer",
"full_name": "Ashton Bahringer",
"email": "abahringer@example.com",
"username": "abahringer",
"created_at": "2025-04-10T17:27:07.000000Z",
"updated_at": "2025-04-16T18:27:54.000000Z",
"provisioned_at": "2023-11-20T21:41:39.000000Z",
"expires_at": null,
"deprovisioned_at": null,
"state": "active"
}
}

For Access Control to automate the provisioning and deprovisioning of users for a Resource (ex. Google Group or Okta Group), we need to have a pre-calculated list of users that have been pre-approved based on a policy with definitions of which user metadata qualifies a user to be added to the list.

Our term for a membership criteria policy is a Ruleset. Each Directory Attribute and Resource has a single Ruleset (one-to-one relationship) that is created automatically.

You can add one or more Rules to a ruleset. Each Rule can have one or more conditions.

Users are attached to a Ruleset automatically based on a recurring background job that evalutes the Rule(s) to determine which users match all of the Condition(s) (this and that) for at least one Rule (this or that) in the Ruleset.

Diagram

An Attribute is a database record that has a pre-configured ruleset that can be adjusted over time as strings in profile data are renamed, or you need to add additional rules and conditions for corner cases.

You can think of an Attribute Ruleset as a reusable string matching rule that allows you to define your string matching in one place and use the database object and relationships everywhere else downstream. You can create additional Dimension(s) and Attributes that each have a Ruleset that you can add Rules and Conditions to that is reusable for attaching to various groups and resources.

Access Control automatically imports Dimensions for each Identity profile metadata key and creates Attributes for each of the unique values that exist across all users. Each Attribute’s Ruleset has an automatically created Rule with a Identity condition for the string value. You can edit a dimension to enable attributes if they are not listed already.

This allows us to have a database record for every value that has a unique ID and we can choose it from a dropdown menu everywhere else in Access Control rather than relying on raw string matching in other Rules.

For most Attribute Rulesets that are based on imported Identity metadata, you will only have one Rule with one Condition. Any user that matches that Condition will be added as a Ruleset User.

Diagram

You can see above how we use one or more of each users’ Identity Source profile attributes (ex. their department, their manager, or a custom equation). We use condition types to determine how the user profile attributes are used and calculated for rule matching.

Each user will match one or more rulesets based on their directory profile metadata and be added to the list of ruleset manifest users for each respective ruleset.

In the examples above, you saw how an Attribute ruleset works.

A group (resource) is functionally identical. Each Google Group, Okta Group, Slack Group, etc has a Ruleset that you can create Rules and Conditions for.

All of these mappings are automated during recurring scheduled sync jobs that fetch user metadata from your Directory Sources and the ruleset users and resource users relationships are automatically updated. This means that as user metadata changes upstream, all of the role-based access control logic defined in rulesets automatically grants users access to the respective resources for downstream access grants.

Diagram

A Ruleset has list of Ruleset Users that is a many-to-many mapping to a User. This manifest is recalculated on a recurring schedule so new users with matching conditions are added and users that no longer match are removed.

When a User is attached to a Ruleset, the relationship is stored in the Ruleset Users database table. The benefit of having a Ruleset Users relationship table is that we have lifecycle tracking that provides a lot of valuable data for auditability:

  1. We can see when the user was added with created_at.
  2. When a user’s metadata changes that disqualifies them from being a member, we can deprecate them and set an expires_at value for a graceful transition.
  3. We can see when the user is scheduled to be removed with expires_at.
  4. We can see when the user was removed with deleted_at.
  5. We can see the current relationship state.
  6. We can see which rule that they qualified for with rule_id.

A condition is similar to an equation where we evaluate the left and right side with an operator and determine if the result is true (matched) or false (not matched).

If a user matches every condition in a rule, they are considered a qualified user. If a user used to match but no longer matches, they are considered a disqualified user. Any user that doesn’t match all conditions is ignored.

The benefit of multiple conditions is that you can improve least privilege and get better granularity with a smaller set of users that qualify.

An Identity condition is used for string matching values in the user profile data from a vendor’s API response.

When you create a Condition with the Identity type, you will choose a Directory Source and specify the key that you want to evaluate (ex. division). You will choose which operator to use (ex. equals) and provide the string value that you are looking for (ex. Sales).

The Sync Rule background job will look at all Identities (user data from specific Source) and return all users that have a metadata key with the corresponding value based on the operator evaluation logic.

The disadvantage to Identity string matching conditions is that if the value is renamed in the Directory Source, you have less visibility to breaking changes and long term policy management becomes harder that does not have as easy of an admin and end user experience. In other words, it’s simply a string evaluation during sync jobs and has no lifecycle management or relationship benefits.

If you will use the same custom logic multiple times, you should use an Attribute condition so that you can simply choose it from a menu of available choices and have a single source of truth (SSOT) upstream so you don’t repeat yourself (DRY).

We support several condition operators for string evaluation.

  • equals
  • not (equal to)
  • empty (null)
  • exists (not null)
  • greater (than or equal to)
  • less (than)
  • prefix (starts with)
  • suffix (ends with)
  • contains (keyword)

You’re probably familiar with using equals and several others.

Here are a few other useful ways you can use these operators for calculations.

  • You may find greater or less helpful for dates (ex. created_at {greater} 2023-01-01).
  • You can use suffix for email domains (email {suffix} @example.com) or types of emails (email {suffix} -admin@example.com).
  • If you don’t have a management level attribute, you can extract from job titles with title {contains} Vice President or title {prefix} VP.

The benefit to using Attribute Conditions is that any changes detected as your organization renames or restructures your teams automatically handle policy updates gracefully. Each attribute has a database record with an ID and the lifecycle of the attribute and its relationships are managed. Attributes have successor and predecessor relationships so we can easily handle remappings as the names of your attributes change over time.

/api/v1/directory/dimensions/01JRGBWRN1X9A9JBDNJT76Z8J0
{
"id": "01JRGBWRN1X9A9JBDNJT76Z8J0",
"source_key": "division",
"name": "Division",
"slug": "division",
"attributes_enabled": true,
"rulesets_enabled": true,
"expires_after_days": 30,
"state": "active",
"source": {
"id": "01JRGBS8MQX4RJR27SJKQGXZD6",
"is_primary": true,
"vendor": "okta",
"name": "Okta Workforce Identity",
"slug": "okta-prod",
"domain": "example.okta.com",
"sync_enabled": true,
},
"count": {
"attributes": 5
},
"relationships": {
"attributes": [
{
"id": "01JRGBWTR52RZMS29V86PVQMB7",
"ruleset_id": "01JRGBWTR8KZFMDHBWJHEB4MV9",
"type": "source",
"name": "Engineering",
"slug": "eng",
"source_value": "Engineering",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
},
{
"id": "01JRGBWTRXB6GJY7KNY0EGE90W",
"ruleset_id": "01JRGBWTRZR8BBRVSQT0VEMJVF",
"type": "source",
"name": "Finance",
"slug": "fin",
"source_value": "Finance",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
},
{
"id": "01JRGBWTRY8B36PWRH4PZZPM30",
"ruleset_id": "01JRGBWTS0TCHQQC3HX82N9PKY",
"type": "source",
"name": "Legal",
"slug": "legal",
"source_value": "Legal",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
},
{
"id": "01JRGBWTW0FG762D3R3RK8KJQ2",
"ruleset_id": "01JRGBWTW3KWM37RSB9P305TAR",
"type": "source",
"name": "Marketing",
"slug": "mktg",
"source_value": "Marketing",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
},
{
"id": "01JRGBWV00W594F0DWWBRZJDQH",
"ruleset_id": "01JRGBWV03D6QVGA35GJ19699H",
"type": "source",
"name": "Sales",
"slug": "sales",
"source_value": "Sales",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
}
]
}
}
/api/v1/directory/rulesets/01JRGBWV03D6QVGA35GJ19699H
{
"id": "01JRGBWV03D6QVGA35GJ19699H",
"type": "accessctl-attribute",
"attribute": {
"id": "01JRGBWV00W594F0DWWBRZJDQH",
"source_id": "01JRGBS8MQX4RJR27SJKQGXZD6",
"dimension_id": "01JRGBWRN1X9A9JBDNJT76Z8J0",
"ruleset_id": "01JRGBWV03D6QVGA35GJ19699H",
"type": "source",
"name": "Sales",
"slug": "sales",
"source_value": "Sales",
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active",
"metadata": null
},
"count": {
"conditions": 1,
"rules": 1,
"ruleset-users": 701,
"manifest-users": 701,
"qualified-users": 701
},
"relationships": {
"rules": [
{
"id": "01JRGBWV065F701PKAMAVE4Q78",
"ruleset_id": "01JRGBWV03D6QVGA35GJ19699H",
"is_imported": true,
"description": "All users attached to the Sales division",
"count_manifest_users": 701,
"count_qualified_users": 701,
"count_staged_users": 0,
"created_at": "2025-04-10T17:27:52.000000Z",
"expires_at": null,
"state": "active"
}
],
"conditions": [
{
"id": "01JRGBWV076MAG79G0NH8Z5E9P",
"rule_id": "01JRGBWV065F701PKAMAVE4Q78",
"is_imported": true,
"type": "identity",
"description": "User Okta identities where division equals Sales",
"reference_id": "01JRGBS8MQX4RJR27SJKQGXZD6",
"metadata_key": "division",
"metadata_operator": "equals",
"metadata_value": "Sales"
}
]
}
}
// /api/v1/directory/ruleset-users?filter[rule_id]=01JRGBWV065F701PKAMAVE4Q78
[
{
"id": "01JRGYR3YW6D2QZ9HADB1V1NKE",
"state": "active",
"timestamp": {
"created_at": "2025-04-10T22:57:20.000000Z",
"updated_at": "2025-04-10T22:57:20.000000Z",
"expires_at": null
},
"ruleset": {
"id": "01JRGBWV03D6QVGA35GJ19699H",
},
"rule": {
"id": "01JRGBWV065F701PKAMAVE4Q78",
"is_imported": true,
"description": "All users attached to the Sales division",
"expires_at": null,
"state": "active"
},
"user": {
"id": "01JRGBVFDN4EJGK8NEDB79BXH2",
"manager_id": "01JRGBWJWZDFF933T4R9WE9ZB4",
"is_manager": false,
"first_name": "Alfonzo",
"last_name": "Ankunding",
"full_name": "Alfonzo Ankunding",
"email": "aankunding@example.com",
"expires_at": null,
"state": "active"
}
},
{
"id": "01JRGYR3YX2C8XSHFJGS346EXA",
"state": "active",
"timestamp": {
"created_at": "2025-04-10T22:57:20.000000Z",
"updated_at": "2025-04-10T22:57:20.000000Z",
"expires_at": null
},
"ruleset": {
"id": "01JRGBWV03D6QVGA35GJ19699H",
},
"rule": {
"id": "01JRGBWV065F701PKAMAVE4Q78",
"is_imported": true,
"description": "All users attached to the Sales division",
"state": "active"
},
"user": {
"id": "01JRGBVFR17TS5WYV46QGB5NJC",
"manager_id": "01JRGBWGB7WCMYAB7K98QXYPAJ",
"is_manager": true,
"first_name": "Alfred",
"last_name": "Bode",
"full_name": "Alfred Bode",
"email": "abode@example.com",
"expires_at": null,
"state": "active"
}
}
]

You can choose a manager and all users that report to this person will be added.

directory_users.manager_id={ulid}

New hires that report to this manager are automatically added. If a user’s manager changes, the ruleset that they qualified for will consider them to no longer be qualified. This is handled by the lifecycle deprecation process so their access to groups and resources will expire after X days based on the respective ruleset’s expires_after_days.

If you have a corner case where a user does not have one or more attributes that qualify them for a rule, you can select one of your Directory Users from the drop down list.

directory_users.id={ulid}

Access Control is designed to automate as much of the data import as possible.

When you add a Directory Source and the Directory Identities are synced, a Directory Dimension is automatically created for each user profile data key that is included in the API response (ex. division, department, title, etc.). If the Dimension is configured with attribute_enabled=true, a Directory Attribute is automatically created (and new ones are detected and created) when the Source is synced every 10-60 minutes (depending on Source sync schedule).

A Rule is automatically created for the Attribute’s Ruleset with a single Identity Condition that handles string matching for that attribute (ex. division {equals} Sales).

Since the Rule and Condition is automatically created, you have Ruleset Users mapped already for your frequently used attributes. This allows you to avoid using string matching rules from Day 1 and doesn’t require a significant amount of work to start using the power of Attribute rules.

You will be building rules and conditions for adding corner cases and specific ad-hoc users to existing attributes, and managing rulesets for groups and resources that are not already automated.

You can create a more granular set of users by specifying multiple conditions that need to be true for a User to qualify.

Diagram

There is no technical limit to the number of conditions that you can add to a rule. The best practice practical limit is up to 10 conditions depending on your use case.

Diagram

The examples above are based on location/territory profile metadata. If your organization has a manager that oversees the sales territory, you can easily include all Users that report to that manager.

We are starting to see the need for having a variety of Conditions that could be true.

Each Ruleset can be expanded to include multiple rules with any combination of conditions to meet your complex use cases.

Diagram

Now let’s create a group that has members from the Sales division in the EMEA region (Europe, Middle East, and Asia) and everyone in Marketing globally. This is an example of ({this} and {that}) or {that}.

We start with a Rule and create a Condition for division {equals} sales, just like we did for our single condition rule

In the same rule, we are going to add a second condition for region {equals} emea.

When evaluating a rule, all conditions must be true for each user.

Let’s add a second rule. This rule has a single condition where division equals marketing. We are not adding any additional conditions to this rule, so any user in Marketing regardless of region (aka global) would be added to the group.

If your organization has a specific attribute value for region that designates a user as global, then you could add a region {equals} global condition to not include users that have any other region values (ex. amer, emea, apac).

If you have that global region attribute and wanted to include your global users (ex. sales leadership) in addition to the emea users, you could add a third rule with two conditions for division equals sales and region equals global.

Diagram

You are hopefully seeing the power of using attribute and manager-based rulesets.

If you do not want to create a rule that allows multiple users to get assigned, you can create a rule with a single condition that lets you choose a specific user that should be added to the ruleset.

Diagram

You are seeing the power of using custom calculations for strings, more than just this {equals} that that you typically start with when using group rules. What if you want to save those calculations and reuse them?

Let’s assume that you don’t have a management level attribute in Okta, and you want to create policies based on whether the user in an executive, vice president, director, manager, or individual contributor.

Let’s use the following logic for this example:

  • Executive
    • title {equals} CEO
    • (or) title {equals} COO
    • (or) title {equals} CFO
    • (or) title {equals} CTO
    • (or) title {equals} CIO
    • (or) title {prefix} Executive Assistant
  • Vice President
    • title {contains} Vice President
    • (or) title {prefix} VP
    • (or) title {prefix} Senior VP
  • Director
    • title {prefix} Director
    • (or) title {prefix} Sr Director
    • (or) title {prefix} Senior Director
    • (or) title {suffix} Director
  • Manager
    • title {prefix} Manager
    • (or) title {prefix} Sr Manager
    • (or) title {prefix} Senior Manager
    • (or) title {suffix} Helpdesk Manager
      • Note: We use {suffix} instead of {equals} to capture Helpdesk Manager and Senior Helpdesk Manager so we don’t need conditions for each seniority level.
    • (or) title {suffix} Support Manager
    • Note: We do not use a generic suffix for Manager in this example since this would capture Account Manager on our Sales team that is an Individual Contributor.

Remember that any rule can match for a ruleset to be true (a.k.a. “or”) but all conditions must match for a rule to be true (a.k.a. “and”). We can handle the logic above by creating an Attribute for each of the management level top level bullets, and a rule with a single condition for each of the child bullets.

When you create an Attribute or Resource, the Ruleset will be “empty” and not contain any Rules or Conditions.

When you create a Rule for a Ruleset, either when the Rule is first created or after the Ruleset is already in use, it will be in a staged state. This means that it is a draft and is not in effect yet, so no users that qualify for this Rule will be in the manifest of users. This allows you to take your time to add multiple Conditions to the Rule and evaluate the preview of users.

If the Rule was prematurely activated, you could encounter a race condition with group/resource member sync. In other words, the first Condition that you add could add a lot of users unintentionally (ex. all users in a department). You want to add the second or third Condition to refine the rule (ex. users with specific job title) before activating the Rule.

When working with Rulesets, Rules, and Conditions, you will see different lists of users:

  • Staged Users: This is a “conditional” preview of all users that would be included if the Ruleset and all Rules are in an active state.
  • Manifest Users: This is the “production” actual list of users who are currently included in sync jobs with any Resources (ex. Google Group or Okta Group) that are attached to this Ruleset.

Day-to-Day Strategies for Building Rulesets

Section titled “Day-to-Day Strategies for Building Rulesets”

The prime directive is to not provision or deprovision a large number of users unexpectedly.

With staged rules, you can see the expected users that are included in the Ruleset, compare it to the existing vendor resource users (from the vendor API), and show you which users will be provisioning and deprovisioned before making changes prematurely.

This is the opportunity to refine your Rulesets, Rules, and Conditions to get the users that you want.

This also provides an opportunity for your internal change management processes to preview and test the ruleset (policy) before going live.

There are two popular strategies for building Rulesets in day-to-day operations.

You will create all Rules with their Conditions so the final list of users looks good before activating each Rule.

This is common when working with smaller number of users that need access.

You will create your first Rule with the conditions for that Rule. You can then activate the Rule. You first set of users will be provisioned during the next sync job.

You can create additional Rule(s) (that are in a staged state by default) and add Condition(s) for each.

You can activate each Rule when you are ready for that audience of users to be provisioned.

This is common when rolling out access to a large number of users and you want to have an initial set of users be beta testers, or roll out to a few teams or departments at a time. You can also use this approach for rolling out to specific groups of users on specific dates by activating the Rule during your change window that you announced to your employees.

We’ve seen this useful in the real world to grant access to beta testers during “week 1”, then engineering team members during “week 4”, and finally sales team members on the second week of the new quarter. This allows engineers to find the bugs, and avoids disrupting sales productivity with end of quarter activities if something goes wrong.

During scheduled sync jobs, the ruleset manifest users are kept in sync with Controlled Resources using vendor API calls to add/grant or remove/revoke user access.

In other words, when the Ruleset manifest for a Controlled Resource changes, Access Control will automatically provision or deprovision the user’s access during the next sync job.