# 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 ... # 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: ```javascript identity('teams').includes('IT::Managers') ``` or ```coffeescript JavaScript 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 username - `identity('teams')` – returns a list of team names the user belongs to - `values('Assigned Individual')` – pulls a value from the submission - `submission('createdAt')` – retrieves submission metadata ### Example Rules ```javascript // Only users on the HR team identity('teams').includes('Department::HR') ``` ```coffeescript JavaScript // Only allow if Assigned Individual matches the logged-in user values('Assigned Individual') === identity('username') ``` ```coffeescript JavaScript // 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 ```text "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` 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: ```javascript (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 ```javascript // 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 form - `identity()` to check the current user - `submission()` to confirm submitter meta ### Optional Defaults in Bindings Some binding functions accept a default value if the requested value is undefined. For example: ```Text JavaScript 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` or `HR 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.