| title | Rls |
|---|---|
| description | Rls protocol schemas |
{/*
Implements fine-grained record-level access control inspired by PostgreSQL RLS
and Salesforce Criteria-Based Sharing Rules.
Row-Level Security (RLS) allows you to control which rows users can access
in database tables based on their identity and role. Unlike object-level
permissions (CRUD), RLS provides record-level filtering.
- Multi-Tenant Data Isolation
-
Users only see records from their organization
-
using: "organization_id == current_user.organization_id"
- Ownership-Based Access
-
Users only see records they own
-
using: "owner_id == current_user.id"
- Organization Member Visibility
-
Users see fellow members of their active organization
-
using: "id in current_user.org_user_ids"
(org_user_ids is pre-resolved by the runtime)
- Territory / Regional Access (§7.3.1 dynamic membership)
-
Sales reps only see accounts in their assigned territories
-
using: "account_id in current_user.territory_account_ids"
(the runtime stages territory_account_ids in ExecutionContext.rlsMembership)
- Manager / Hierarchy Access (§7.3.1 dynamic membership)
-
Managers see records assigned to anyone they manage
-
using: "assigned_to_id in current_user.team_member_ids"
(the runtime pre-resolves team_member_ids, no subquery needed)
PostgreSQL RLS Example:
CREATE POLICY tenant_isolation ON accounts
FOR SELECT
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
CREATE POLICY account_insert ON accounts
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.current_tenant_id')::uuid);
ObjectStack RLS Equivalent:
\{
name: 'tenant_isolation',
object: 'account',
operation: 'select',
using: 'organization_id == current_user.organization_id'
\}Salesforce uses "Sharing Rules" and "Role Hierarchy" for record-level access.
ObjectStack RLS provides similar functionality with more flexibility.
Salesforce:
-
Criteria-Based Sharing: Share records matching criteria with users/roles
-
Owner-Based Sharing: Share records based on owner's role
-
Manual Sharing: Individual record sharing
ObjectStack RLS:
-
A small, fixed expression grammar (equality, set-membership, always-true)
-
Subquery-shaped needs are pre-resolved by the runtime (§7.3.1)
-
Multiple policies OR-combine for union (any-match-allows) semantics
-
Always Define SELECT Policy: Control what users can view
-
Define INSERT/UPDATE CHECK Policies: Prevent data leakage
-
Use Role-Based Policies: Apply different rules to different roles
-
Test Thoroughly: RLS can have complex interactions
-
Monitor Performance: Complex RLS policies can impact query performance
-
Defense in Depth: RLS is one layer; use with object permissions
-
Default Deny: If no policy matches, access is denied
-
Policy Precedence: More permissive policy wins (OR logic)
-
Context Variables: Ensure current_user context is always set
@see https://www.postgresql.org/docs/current/ddl-rowsecurity.html
@see https://help.salesforce.com/s/articleView?id=sf.security_sharing_rules.htm
**Source:** `packages/spec/src/security/rls.zod.ts`import { RLSAuditConfig, RLSAuditEvent, RLSConfig, RLSEvaluationResult, RLSOperation, RLSUserContext, RowLevelSecurityPolicy } from '@objectstack/spec/security';
import type { RLSAuditConfig, RLSAuditEvent, RLSConfig, RLSEvaluationResult, RLSOperation, RLSUserContext, RowLevelSecurityPolicy } from '@objectstack/spec/security';
// Validate data
const result = RLSAuditConfig.parse(data);| Property | Type | Required | Description |
|---|---|---|---|
| enabled | boolean |
✅ | Enable RLS audit logging |
| logLevel | Enum<'all' | 'denied_only' | 'granted_only' | 'none'> |
✅ | Which evaluations to log |
| destination | Enum<'system_log' | 'audit_trail' | 'external'> |
✅ | Audit log destination |
| sampleRate | number |
✅ | Sampling rate (0-1) for high-traffic environments |
| retentionDays | integer |
✅ | Audit log retention period in days |
| includeRowData | boolean |
✅ | Include row data in audit logs (security-sensitive) |
| alertOnDenied | boolean |
✅ | Send alerts when access is denied |
| Property | Type | Required | Description |
|---|---|---|---|
| timestamp | string |
✅ | ISO 8601 timestamp of the evaluation |
| userId | string |
✅ | User ID whose access was evaluated |
| operation | Enum<'select' | 'insert' | 'update' | 'delete'> |
✅ | Database operation being performed |
| object | string |
✅ | Target object name |
| policyName | string |
✅ | Name of the RLS policy evaluated |
| granted | boolean |
✅ | Whether access was granted |
| evaluationDurationMs | number |
✅ | Policy evaluation duration in milliseconds |
| matchedCondition | string |
optional | Which USING/CHECK clause matched |
| rowCount | number |
optional | Number of rows affected |
| metadata | Record<string, any> |
optional | Additional audit event metadata |
| Property | Type | Required | Description |
|---|---|---|---|
| enabled | boolean |
✅ | Enable RLS enforcement globally |
| defaultPolicy | Enum<'deny' | 'allow'> |
✅ | Default action when no policies match |
| allowSuperuserBypass | boolean |
✅ | Allow superusers to bypass RLS |
| bypassRoles | string[] |
optional | Roles that bypass RLS (see all data) |
| logEvaluations | boolean |
✅ | Log RLS policy evaluations for debugging |
| cacheResults | boolean |
✅ | Cache RLS evaluation results |
| cacheTtlSeconds | integer |
✅ | Cache TTL in seconds |
| prefetchUserContext | boolean |
✅ | Pre-fetch user context for performance |
| audit | Object |
optional | RLS audit logging configuration |
| Property | Type | Required | Description |
|---|---|---|---|
| policyName | string |
✅ | Policy name |
| granted | boolean |
✅ | Whether access was granted |
| durationMs | number |
optional | Evaluation duration in milliseconds |
| error | string |
optional | Error message if evaluation failed |
| usingResult | boolean |
optional | USING clause evaluation result |
| checkResult | boolean |
optional | CHECK clause evaluation result |
selectinsertupdatedeleteall
| Property | Type | Required | Description |
|---|---|---|---|
| id | string |
✅ | User ID |
string |
optional | User email | |
| tenantId | string |
optional | Tenant/Organization ID |
| role | string | string[] |
optional | User role(s) |
| department | string |
optional | User department |
| attributes | Record<string, any> |
optional | Additional custom user attributes |
| Property | Type | Required | Description |
|---|---|---|---|
| name | string |
✅ | Policy unique identifier (snake_case) |
| label | string |
optional | Human-readable policy label |
| description | string |
optional | Policy description and business justification |
| object | string |
✅ | Target object name |
| operation | Enum<'select' | 'insert' | 'update' | 'delete' | 'all'> |
✅ | Database operation this policy applies to |
| using | string |
optional | Filter condition for SELECT/UPDATE/DELETE. One of the four compiler-supported forms: field = current_user.<prop>, field = 'literal', field IN (current_user.<array>), or 1 = 1. Optional for INSERT-only policies. |
| check | string |
optional | Validation condition for INSERT/UPDATE (defaults to USING clause if not specified - enforced at application level) |
| roles | string[] |
optional | Roles this policy applies to (omit for all roles) |
| enabled | boolean |
✅ | Whether this policy is active |
| priority | integer |
✅ | Policy evaluation priority (higher = evaluated first) |
| tags | string[] |
optional | Policy categorization tags |