Access Logic (Security Definitions)
Security Definitions
Security definitions are the logic behind your security policies. They use JavaScript expressions to determine whether a user has access to a form, submission, resource, or platform feature.
You can think of security definitions as dynamic rules that say things like:
"This user can access the form if they're on the HR team, or if they're the assigned manager."
What Are Security Definitions?
A security definition is a reusable expression that evaluates to true
or false
. It's attached to a security policy at the Form, Kapp, Space, or Workflow level.
If the definition evaluates to true
→ access is allowed.
If it evaluates to false
→ access is denied and the user sees your custom message.
How Do Security Definitions Work?
Security definitions rely on dynamic data like the current user’s identity, their teams, or the submission values to determine access. That data is accessed using built-in bindings.
Bindings are functions like identity()
, values()
, or submission()
that retrieve live information about:
- The user trying to access something
- The form or submission they’re interacting with
- The space, kapp, or workflow where it’s happening
You’ll use these bindings throughout your definitions to write logic like:
identity('teams').includes('IT::Managers')
or
values('Requested For') === identity('username')
You can find a full breakdown of binding types and usage later in this documentation.
Where to Create and Use Them
You can create definitions from either:
Space > Definitions > Security
Kapp > Definitions > Security
Once created, these definitions are available in drop-downs when assigning policies to:
- Forms
- Submissions
- Kapps
- Workflows
- Teams & Users
Definitions created at the Space level can be used across all kapps.
Space vs. Kapp Security Definitions
The creation process for security definitions is the same everywhere, but the types of definitions you can create depend on where you are in the platform.
Created From | Types Available |
---|---|
Space level | Space, File Resource, Team, User |
Kapp level | Kapp, Form, Submission |
You must create the definition from the correct context in order for it to appear as an option when assigning a policy.
For example:
- If you're securing a user management action, create the definition from the Space > Definitions > Security section and choose User as the type.
- If you're securing access to a form or submission, go to the Kapp > Definitions > Security section and choose the appropriate Form or Submission type.
Selecting the Right Type
When creating a new definition, you'll select a Type that determines which objects it can secure.
Each type corresponds to a specific area of the platform:
- Space: Controls access to global features like user creation or file resources.
- File Resource: Secures access to uploaded files in the platform.
- Team / User: Controls permissions for modifying or viewing teams and users.
- Kapp: Governs access to an entire kapp or its configuration.
- Form: Secures form-level access (e.g., display or modification).
- Submission: Applies to individual submission records (e.g., view/edit logic).
If you don’t see the type you need, check whether you're in the correct level (Space vs. Kapp) when creating the definition.
Writing a Security Rule
A rule must be a single JavaScript expression that returns a boolean (true
or false
). Most rules use built-in platform functions like:
identity('username')
– returns the logged-in user's usernameidentity('teams')
– returns a list of team names the user belongs tovalues('Assigned Individual')
– pulls a value from the submissionsubmission('createdAt')
– retrieves submission metadata
Example Rules
// Only users on the HR team
identity('teams').includes('Department::HR')
// Only allow if Assigned Individual matches the logged-in user
values('Assigned Individual') === identity('username')
// Allow if user is on the assigned team OR is the submitter
hasIntersection(values('Assigned Team'), identity('teams')) ||
values('Requested By') === identity('username')
Adding a Message
Every security definition includes a Message field. This is the text the user will see when they fail the access check. In other words, when the rule evaluates to false
.
Good messages reduce confusion, prevent support tickets, and guide users to the next step.
When Is the Message Displayed?
- When a user tries to load a form they aren’t allowed to view
- When they attempt to view or update a submission they don’t have access to
- When a workflow or platform feature is restricted
Examples of Effective Messages
"This form is only available to members of the HR department. Please contact HR if you believe you need access."
"You can only view submissions you created. Contact your team lead for historical records."
"Access to this workflow is restricted to authorized reviewers."
Tips for Writing Messages
- Be specific: Tell the user what the restriction is about
- Be polite: Avoid blame or accusatory language
- Give direction: If appropriate, mention what they can do next.
If no message is provided, users will see a generic access error. Use this field to improve clarity and user trust.
Common Helper: hasIntersection
hasIntersection
When comparing two values or lists (like a user’s teams and a field value), it’s helpful to use a reusable helper function that handles edge cases and ensures type consistency.
Here’s a more robust version of the commonly used hasIntersection
helper:
(function() {
// Helper method: Checks if two lists have any shared values
var hasIntersection = function(obj1, obj2) {
// Ensure the objects are not empty
obj1 = (obj1 === null || obj1 === undefined) ? [] : obj1;
obj2 = (obj2 === null || obj2 === undefined) ? [] : obj2;
// Wrap single values in arrays if needed
var list1 = (obj1 instanceof Array) ? obj1 : [obj1];
var list2 = (obj2 instanceof Array) ? obj2 : [obj2];
// Return whether any intersecting values were found
return list1.find(function(value) {
return hasValue(list2, value);
}) !== undefined;
};
// Helper method: Verifies a value exists in a list
var hasValue = function(list, value) {
return (list instanceof Array) && list.indexOf(value) !== -1;
};
// Example: Check if the user belongs to the 'Employee' role
return hasIntersection(identity('teams'), ['Role::Employee']);
})()
What This Does:
- Converts null or undefined inputs into empty arrays
- Wraps single values into arrays so comparisons don't fail
- Uses
hasValue()
to safely check membership - Returns
true
if any value overlaps between the two lists
Why It's Useful
This pattern avoids fragile, one-off expressions and can be dropped into any security definition. You can reuse it to check:
- If the user belongs to a required team or role
- If the user matches a value in a multi-select field
- If any overlap exists between submission values and user attributes
You can copy and paste this directly into any Security Definition for a reliable list comparison
What Are Bindings?
Bindings are the dynamic values you can use inside your security definitions to evaluate who the user is, what they’re trying to access, and the context around it. They work like pre-built functions that return real-time information.
Think of bindings as your data source when writing access logic.
Common Binding Functions
Binding | What It Returns | Example Usage |
---|---|---|
identity() | Info about the current user (username, teams, attributes) | identity('username') === 'mary.manager' |
values() | Field values from the current submission | values('Department') === 'HR' |
submission() | Metadata about the submission (creator, timestamps) | submission('createdBy') |
space() | Space-level properties | space('slug') === 'prod' |
kapp() | Kapp-level properties (e.g., name or slug) | kapp('name') === 'Services' |
form() | Form-level properties (e.g., name, slug) | form('slug') === 'onboarding-request' |
team() / user() | Info about a specific team or user (Space-level only) | team('slug') === 'admins' |
Binding Scope by Definition Type
Some bindings are only available depending on the type of security definition you’re writing. For example:
Definition Type | Available Bindings |
---|---|
Space | identity() , space() , team() , user() |
Kapp | identity() , space() , kapp() |
Form | identity() , space() , kapp() , form() |
Submission | identity() , space() , kapp() , form() , values() , submission() |
Team/User | identity() , team() , user() |
Example: Using Bindings Together
// Only allow if the user is on the assigned team OR is the submitter
hasIntersection(values('Assigned Team'), identity('teams')) ||
submission('createdBy') === identity('username')
This rule combines:
values()
to pull from the formidentity()
to check the current usersubmission()
to confirm submitter meta
Optional Defaults in Bindings
Some binding functions accept a default value if the requested value is undefined. For example:
identity('attribute:Manager', ['nobody'])
This will return a nobody
if the user has no Manager
attribute.
Final Tips for Building Great Security Definitions
Crafting effective security definitions isn’t just about writing the right expression — it’s about thinking through the experience for both the builder and the end user.
Here are a few tips to bring it all together:
- Start Simple: Begin with straightforward rules like team checks or ownership comparisons. You can layer complexity as your needs grow.
- Test As a Non-Admin: Space Admins bypass security definitions. Always use a standard test account to validate your logic in real conditions.
- Reuse and Name Clearly: Create reusable definitions like
Is Submitter
orHR Only
, and use consistent naming so your team can quickly identify what each one does. - Combine with Security Policies Thoughtfully: Remember: definitions power policies — make sure you apply them at the right level (Form, Kapp, Space), and understand the fallback hierarchy.
- Don’t Skip the Message: A well-written denial message helps end users understand why they don’t have access and reduces support overhead.
- Use Helpers (like
hasIntersection
):Abstracting logic into small helpers makes complex definitions easier to read, maintain, and debug.
With the right security definitions in place, you can offer tailored, secure access throughout your platform, without relying on rigid role-based access or hardcoded logic.
When in doubt, start with what you know, test in context, and iterate from there.
Updated 7 days ago