Access Rules
This document describes Access Rules in tSM — an additional layer of data-level security that filters which specific records a user can view or edit, beyond the base privileges granted by their roles.
Overview
While Roles and Privileges control what actions a user can perform (e.g., "view tickets", "edit customers"), Access Rules control which specific records they can access based on dynamic conditions.
Example Use Cases:
- A user can only view tickets assigned to their department
- Sales representatives can only edit customers in their region
- Managers can see all orders, but agents can only see their own
- Support agents can view tickets in status "Open" or "In Progress", but not "Closed"
Key Concepts
Privileges vs Access Rules
| Aspect | Privileges (from Roles) | Access Rules |
|---|---|---|
| Scope | Action-based (what you can do) | Data-based (which records you can access) |
| Example | "Can edit customers" | "Can only edit customers in region X" |
| Evaluation | At authentication time | At query time (runtime) |
| Performance | Fast (cached) | Slower (evaluated per query) |
Access Rules complement privileges — a user needs both the privilege to perform an action AND must pass the access rule filter to access specific records.
1. Access Rule Structure
Core Attributes
| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | – | Unique identifier / primary key in UUID format (auto-generated or UUIDv4). Not displayed to end users. |
code | String | Yes | Unique code for the rule. ASCII characters without spaces, 1-255 characters. |
name | String | Yes | Display name, 1-255 characters. |
description | String | – | Description of the value (used as tooltip). |
entityName | String | – | Entity type this rule applies to (e.g., Ticket, Customer). |
expression | String | – | Filter expression (SpEL or SQL-like). |
accessRule | Enum | – | Access type: READ or READ_WRITE. Default: READ. |
fullAccess | Boolean | – | If true, grants full access without expression evaluation. Default: false. |
Configuration Attributes
| Field | Type | Description |
|---|---|---|
dataTags | List<String> | Additional data tags (aka labels) for categorization. |
config | Map | Additional configuration key-value pairs. |
localizationData | LocalizationData | Translations for attributes (e.g., name, description). |
System/Audit Fields
| Field | Type | Read-only | Description |
|---|---|---|---|
validityFrom | Date | – | Start of the validity of the rule. |
validityTo | Date | – | End of the validity of the rule. |
auditInfo | AuditInfo | Yes | Standard audit fields (createdBy, createdAt, updatedBy, updatedAt). |
Access Rule Enum
| Value | Description |
|---|---|
READ | Filters which records a user can view (list, detail, search) |
READ_WRITE | Filters which records a user can edit, update, or delete |
Expression Language
Access Rules support SpEL (Spring Expression Language) for complex logic and SQL-like syntax for filtering.
2. Access Types
READ Access
Filters which records a user can view (list, detail, search).
Example:
entityName: Ticket
accessRule: READ
expression: "chars.assignedGroupId in (#user.groupIds)"
Effect: User can only see tickets assigned to groups they belong to.
READ_WRITE Access
Filters which records a user can edit, update, or delete.
Example:
entityName: Customer
accessRule: READ_WRITE
expression: "chars.regionCode == #user.regionCode"
Effect: User can edit only customers in their region.
Full Access Mode
When fullAccess is set to true, the rule grants access without evaluating the expression.
Example:
code: ADMIN_FULL_ACCESS
entityName: "*"
accessRule: READ_WRITE
fullAccess: true
Effect: User has unrestricted access to all records of all entity types.
Combined Rules
A user can have multiple rules for the same entity:
- One READ rule (defines what they can see)
- One READ_WRITE rule (defines what they can edit)
Example Scenario:
# Rule 1: Can view all tickets in their department
entityName: Ticket
accessRule: READ
expression: "chars.departmentCode == #user.chars['departmentCode']"
# Rule 2: Can only edit tickets assigned to them
entityName: Ticket
accessRule: READ_WRITE
expression: "chars.assignedUserId == #user.id"
Result: User sees all department tickets but can only edit their own.
3. Evaluation Logic
When multiple access rules apply to the same entity for a user:
- For READ access:
- Apply all READ rules with OR logic
- User sees records matching ANY rule
- For READ_WRITE access:
- Apply all READ_WRITE rules with OR logic
- User can edit records matching ANY rule
Example:
# Rule 1
expression: "chars.assignedGroupId in myUserGroup()"
# Rule 2
expression: "chars.createdBy = currentUser()"
Result: User sees records from BOTH rules (group assignments OR own created records).
5. Configuration Examples
Example 1: Regional Sales Access
Scenario: Sales reps can only access customers and orders in their assigned region.
# Access Rule: Regional Customer Access
code: REGIONAL_CUSTOMER_ACCESS
entityName: Customer
accessRule: READ_WRITE
expression: "chars.regionCode = 'SOUTH_BOHEMIA'"
Example 2: Support Tier System
Scenario:
- Tier 1 agents see tickets assigned to them
- Tier 2 agents see all tickets in their group
- Managers see all tickets in their department
# Tier 1: Own tickets only
code: TIER1_TICKET_ACCESS
entityName: Ticket
accessRule: READ_WRITE
expression: "chars.assignedUserId == currentUser()"
# Tier 2: Group tickets
code: TIER2_TICKET_ACCESS
entityName: Ticket
accessRule: READ_WRITE
expression: "chars.assignedGroupId in myUserGroup()"
# Managers: Department tickets
code: MANAGER_TICKET_ACCESS
entityName: Ticket
accessRule: READ_WRITE
expression: "chars.departmentCode == 'L2_SUPPORT'"
Example 4: Full Access for Admins
Scenario: Admin users have unrestricted access to all entities.
code: ADMIN_FULL_ACCESS
entityName: "*"
accessRule: READ_WRITE
fullAccess: true
5. Performance Considerations
5.1 Impact on Queries
Access Rules are translated to WHERE clauses and applied at query time. The performance impact varies based on the underlying data store.
Without Access Rule:
SELECT * FROM tickets WHERE status = 'Open'
With Access Rule:
SELECT * FROM tickets
WHERE status = 'Open'
AND chars.assignedGroupId IN myUserGroup()
The filter is automatically translated based on the entity's storage backend:
- Elasticsearch — Converted to Elasticsearch Query DSL
- SQL Database — Applied as SQL WHERE clause
5.2 Performance Characteristics by Storage Type
Elasticsearch Queries
Advantages:
- Full-text search capabilities
- Fast filtering on indexed fields
- Efficient aggregations
- Scales horizontally
Performance Tips:
- Use indexed fields in expressions
- Avoid script-based filters when possible
- Leverage term queries over wildcard queries
- Use filters (cached) instead of queries when possible
Example - Efficient:
expression: "chars.status == 'OPEN'" # Term query, fast
Example - Less Efficient:
expression: "chars.description.contains('urgent')" # Full-text search, slower
SQL Database Queries
Advantages:
- Strong consistency guarantees
- Complex joins supported
- ACID transactions
Performance Challenges:
- Can be slow on large datasets without proper indexes
- Complex expressions may prevent index usage
- JOIN operations can be expensive
Critical: Always add database indexes for fields used in access rule expressions.