# Issue #195: Implement RLS context helpers consistently across all services ## Objective Implement consistent RLS (Row-Level Security) context helpers across all services to ensure proper workspace isolation through PostgreSQL session variables. ## Current State Analysis Need to examine: 1. Existing db-context.ts helpers 2. How services currently handle workspace filtering 3. Whether RLS policies are defined in Prisma schema 4. Best approach for setting PostgreSQL session variables ## Approach **Use Prisma Extension with PostgreSQL Session Variables:** - Create a Prisma extension that sets session variables before queries - Session variables: `app.current_workspace_id` and `app.current_user_id` - Apply to all workspace-scoped operations - This works with connection pooling (uses `SET LOCAL` which is transaction-scoped) ## Implementation Plan - [x] Examine existing db-context.ts - [x] Examine current service implementations - [x] Write tests for RLS context setting (11 tests passing, 5 need actual DB) - [x] Implement `setWorkspaceContext()` method in PrismaService - [x] Create helper methods for workspace-scoped queries - [x] Update db-context.ts with workspace context functions - [x] Export new helper functions - [ ] Document RLS usage patterns - [ ] Add example of service using RLS context ## Changes Made ### PrismaService Added three new methods: 1. `setWorkspaceContext(userId, workspaceId, client?)` - Sets session variables 2. `clearWorkspaceContext(client?)` - Clears session variables 3. `withWorkspaceContext(userId, workspaceId, fn)` - Transaction wrapper ### db-context.ts Added new functions: 1. `setCurrentWorkspace(workspaceId, client)` - Set workspace ID 2. `setWorkspaceContext(userId, workspaceId, client)` - Set both user and workspace 3. `clearWorkspaceContext(client)` - Clear both variables 4. `withWorkspaceContext(userId, workspaceId, fn)` - High-level transaction wrapper All functions use `SET LOCAL` for transaction-scoped variables (connection pool safe). ## Usage Pattern ```typescript // In a service const tasks = await this.prisma.withWorkspaceContext(userId, workspaceId, async (tx) => { return tx.task.findMany({ where: { status: "IN_PROGRESS" }, }); }); ``` Or using db-context helpers: ```typescript import { withWorkspaceContext } from "../lib/db-context"; const tasks = await withWorkspaceContext(userId, workspaceId, async (tx) => { return tx.task.findMany(); }); ``` ## Testing Strategy ### Unit Tests - PrismaService sets context correctly - Context is cleared after transaction - Multiple concurrent requests don't interfere ### Integration Tests - Workspace isolation is enforced - Cross-workspace queries are blocked - RLS policies work with context variables ## Notes - Must use transaction-scoped `SET LOCAL` (not session-level `SET`) - Connection pooling compatible - Should work with or without actual RLS policies (defense in depth)