ObjectStackObjectStack

Rls

Rls protocol schemas

Row-Level Security (RLS) Protocol

Implements fine-grained record-level access control inspired by PostgreSQL RLS

and Salesforce Criteria-Based Sharing Rules.

Overview

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.

Use Cases

  1. Multi-Tenant Data Isolation
  • Users only see records from their organization

  • using: "tenant_id = current_user.tenant_id"

  1. Ownership-Based Access
  • Users only see records they own

  • using: "owner_id = current_user.id"

  1. Department-Based Access
  • Users only see records from their department

  • using: "department = current_user.department"

  1. Regional Access Control
  • Sales reps only see accounts in their territory

  • using: "region IN (current_user.assigned_regions)"

  1. Time-Based Access
  • Users can only access active records

  • using: "status = 'active' AND expiry_date > NOW()"

PostgreSQL RLS Comparison

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: 'tenant_id = current_user.tenant_id'

\}

Salesforce Sharing Rules Comparison

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:

  • More flexible formula-based conditions

  • Direct SQL-like syntax

  • Supports complex logic with AND/OR/NOT

Best Practices

  1. Always Define SELECT Policy: Control what users can view

  2. Define INSERT/UPDATE CHECK Policies: Prevent data leakage

  3. Use Role-Based Policies: Apply different rules to different roles

  4. Test Thoroughly: RLS can have complex interactions

  5. Monitor Performance: Complex RLS policies can impact query performance

Security Considerations

  1. Defense in Depth: RLS is one layer; use with object permissions

  2. Default Deny: If no policy matches, access is denied

  3. Policy Precedence: More permissive policy wins (OR logic)

  4. 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

TypeScript Usage

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);

RLSAuditConfig

Properties

PropertyTypeRequiredDescription
enabledbooleanEnable RLS audit logging
logLevelEnum<'all' | 'denied_only' | 'granted_only' | 'none'>Which evaluations to log
destinationEnum<'system_log' | 'audit_trail' | 'external'>Audit log destination
sampleRatenumberSampling rate (0-1) for high-traffic environments
retentionDaysintegerAudit log retention period in days
includeRowDatabooleanInclude row data in audit logs (security-sensitive)
alertOnDeniedbooleanSend alerts when access is denied

RLSAuditEvent

Properties

PropertyTypeRequiredDescription
timestampstringISO 8601 timestamp of the evaluation
userIdstringUser ID whose access was evaluated
operationEnum<'select' | 'insert' | 'update' | 'delete'>Database operation being performed
objectstringTarget object name
policyNamestringName of the RLS policy evaluated
grantedbooleanWhether access was granted
evaluationDurationMsnumberPolicy evaluation duration in milliseconds
matchedConditionstringoptionalWhich USING/CHECK clause matched
rowCountnumberoptionalNumber of rows affected
metadataRecord<string, any>optionalAdditional audit event metadata

RLSConfig

Properties

PropertyTypeRequiredDescription
enabledbooleanEnable RLS enforcement globally
defaultPolicyEnum<'deny' | 'allow'>Default action when no policies match
allowSuperuserBypassbooleanAllow superusers to bypass RLS
bypassRolesstring[]optionalRoles that bypass RLS (see all data)
logEvaluationsbooleanLog RLS policy evaluations for debugging
cacheResultsbooleanCache RLS evaluation results
cacheTtlSecondsintegerCache TTL in seconds
prefetchUserContextbooleanPre-fetch user context for performance
auditObjectoptionalRLS audit logging configuration

RLSEvaluationResult

Properties

PropertyTypeRequiredDescription
policyNamestringPolicy name
grantedbooleanWhether access was granted
durationMsnumberoptionalEvaluation duration in milliseconds
errorstringoptionalError message if evaluation failed
usingResultbooleanoptionalUSING clause evaluation result
checkResultbooleanoptionalCHECK clause evaluation result

RLSOperation

Allowed Values

  • select
  • insert
  • update
  • delete
  • all

RLSUserContext

Properties

PropertyTypeRequiredDescription
idstringUser ID
emailstringoptionalUser email
tenantIdstringoptionalTenant/Organization ID
rolestring | string[]optionalUser role(s)
departmentstringoptionalUser department
attributesRecord<string, any>optionalAdditional custom user attributes

RowLevelSecurityPolicy

Properties

PropertyTypeRequiredDescription
namestringPolicy unique identifier (snake_case)
labelstringoptionalHuman-readable policy label
descriptionstringoptionalPolicy description and business justification
objectstringTarget object name
operationEnum<'select' | 'insert' | 'update' | 'delete' | 'all'>Database operation this policy applies to
usingstringoptionalFilter condition for SELECT/UPDATE/DELETE (PostgreSQL SQL WHERE clause syntax with parameterized context variables). Optional for INSERT-only policies.
checkstringoptionalValidation condition for INSERT/UPDATE (defaults to USING clause if not specified - enforced at application level)
rolesstring[]optionalRoles this policy applies to (omit for all roles)
enabledbooleanWhether this policy is active
priorityintegerPolicy evaluation priority (higher = evaluated first)
tagsstring[]optionalPolicy categorization tags

On this page