UNPKG

rls-guard

Version:

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

89 lines (72 loc) 3.7 kB
import { Command } from 'commander'; import { resolve } from 'path'; import { writeFileSync } from 'fs'; import chalk from 'chalk'; import { DatabaseManager } from '../lib/database.js'; import { PolicyIntrospector } from '../lib/introspector.js'; import { ConfigGenerator } from '../lib/generator.js'; import { loadConfig } from '../lib/config.js'; const pullCommand = new Command('pull'); pullCommand .description('Extract existing RLS policies from database and generate rls.config.ts') .option('-o, --output <file>', 'Output file path', 'rls.config.ts') .option('-t, --tables <tables>', 'Comma-separated list of tables to extract') .option('-f, --format <format>', 'Output format: typescript|json', 'typescript') .option('-c, --comments', 'Add explanatory comments', false) .option('--config <url>', 'Configuration file path', 'rls.config.ts') .option('--no-mask', 'Don\'t mask sensitive connection info') .action(async (options) => { try { const config = await loadConfig('rls.config.ts'); console.log(chalk.blue('🔍') + ' Starting RLS policy extraction...\n'); // Get database connection const connectionString = config.database.url || options.connection || process.env.DATABASE_URL; if (!connectionString) { throw new Error('Database connection required. Use --connection or set DATABASE_URL environment variable'); } console.log(chalk.blue('🔌') + ' Connecting to PostgreSQL database...'); const dbManager = new DatabaseManager({ url: connectionString }); try { await dbManager.connect(); await dbManager.validateConnection(); console.log(chalk.green('✅') + ' Successfully connected to database\n'); // Extract policies console.log(chalk.blue('📋') + ' Extracting RLS policies...'); const introspector = new PolicyIntrospector({ client: dbManager.getClient() }); const tableFilter = options.tables ? options.tables.split(',').map((t: string) => t.trim()) : null; const policies = await introspector.extractPolicies(tableFilter); if (policies.length === 0) { console.log(chalk.yellow('⚠️ ') + ' No RLS policies found in the database'); return; } console.log(chalk.green('✅') + ` Found ${policies.length} policies across ${getUniqueTableCount(policies)} tables\n`); // Generate config file console.log(chalk.blue('📝') + ' Generating configuration file...'); const generator = new ConfigGenerator({ format: options.format, addComments: options.comments, maskConnection: options.mask, connectionString: connectionString }); const configContent = generator.generateConfig(policies); // Write to file const outputPath = resolve(options.output); writeFileSync(outputPath, configContent); console.log(chalk.green('🎉') + ` Successfully generated ${options.output}`); console.log(''); console.log('Next steps:'); console.log('1. Review the generated configuration'); console.log('2. Test with ' + chalk.cyan('rls-guard deploy --dry-run')); console.log('3. Make any necessary adjustments'); } finally { await dbManager.close(); } } catch (error) { console.error(chalk.red('❌') + ' Pull failed:', error instanceof Error ? error.message : error); process.exit(1); } }); function getUniqueTableCount(policies: any[]) { return new Set(policies.map(p => p.table)).size; } export { pullCommand };