UNPKG

rls-guard

Version:

A CLI tool for managing PostgreSQL Row Level Security (RLS) policies as code

205 lines (166 loc) 4.33 kB
// RLS Guard - Configuration Library (CommonJS) // This file contains the types and builders for defining RLS policies interface Policy { name: string; table: string; command: string; expression: string; roles: string[]; permissive?: boolean; } interface DatabaseConfig { url?: string; host?: string; port?: number; database?: string; username?: string; password?: string; ssl?: boolean; } export interface Config { database: DatabaseConfig; policies: Policy[]; } class PolicyBuilder { private policy: Partial<Policy>; constructor() { this.policy = {}; } name(name: string) { this.policy.name = name; return this; } onTable(table: string) { this.policy.table = table; return this; } forCommand(command: string) { this.policy.command = command; return this; } withExpression(expression: string) { this.policy.expression = expression; return this; } forRoles(...roles: string[]) { this.policy.roles = roles; return this; } asPermissive() { this.policy.permissive = true; return this; } asRestrictive() { this.policy.permissive = false; return this; } build(): Policy { if (!this.policy.name || !this.policy.table || !this.policy.command || !this.policy.expression || !this.policy.roles) { throw new Error('Policy is missing required fields'); } return this.policy as Policy; } } class DatabaseBuilder { private config: Partial<DatabaseConfig>; constructor() { this.config = {}; } connectionUrl(url: string) { this.config.url = url; return this; } host(host: string) { this.config.host = host; return this; } port(port: number) { this.config.port = port; return this; } database(database: string) { this.config.database = database; return this; } username(username: string) { this.config.username = username; return this; } password(password: string) { this.config.password = password; return this; } ssl(enabled: boolean = true) { this.config.ssl = enabled; return this; } build(): DatabaseConfig { if (!this.config.url && !this.config.host) { throw new Error('Either connection URL or host must be specified'); } return this.config as DatabaseConfig; } } class ConfigBuilder { private config: Config; constructor() { this.config = { database: '', policies: [] } as Config; } database(builder: (db: DatabaseBuilder) => DatabaseBuilder) { this.config.database = builder(new DatabaseBuilder()).build(); return this; } addPolicy(builder: (pb: PolicyBuilder) => PolicyBuilder) { const policy = builder(new PolicyBuilder()).build(); this.config.policies.push(policy); return this; } build(): Config { if (!this.config.database) { throw new Error('Database configuration is required'); } return this.config; } export(filename: string = 'rls.config.json') { const fs = require('fs'); const config = this.build(); fs.writeFileSync(filename, JSON.stringify(config, null, 2)); } } // Helper functions for common RLS patterns const currentUserId = (column: string = 'user_id') => `${column} = current_setting('app.current_user_id')::uuid`; const tenantId = (column: string = 'tenant_id') => `${column} = current_setting('app.tenant_id')::uuid`; const recentData = (column: string = 'created_at', days: number = 90) => `${column} >= current_date - interval '${days} days'`; const ownerOnly = (userColumn: string = 'user_id', ownerColumn: string = 'owner_id') => `${userColumn} = ${ownerColumn}`; const roleCheck = (role: string) => `current_setting('app.user_role') = '${role}'`; const timeWindow = (column: string, hours: number) => `${column} >= now() - interval '${hours} hours'`; const publicAccess = () => 'true'; const noAccess = () => 'false'; // Convenience functions const policy = () => new PolicyBuilder(); const database = () => new DatabaseBuilder(); const config = () => new ConfigBuilder(); // Export everything export default { PolicyBuilder, DatabaseBuilder, ConfigBuilder, currentUserId, tenantId, recentData, ownerOnly, roleCheck, timeWindow, publicAccess, noAccess, policy, database, config, };