erp-suite/apps/shared-libs/core/database/policies/usage-example.ts

406 lines
12 KiB
TypeScript

/**
* RLS Policies - Usage Examples
*
* This file demonstrates how to use the centralized RLS policies
* in various scenarios across the ERP-Suite.
*/
import { Pool } from 'pg';
import {
applyTenantIsolationPolicy,
applyAdminBypassPolicy,
applyUserDataPolicy,
applyCompleteRlsPolicies,
applyCompletePoliciesForSchema,
batchApplyRlsPolicies,
isRlsEnabled,
listRlsPolicies,
getSchemaRlsStatus,
setRlsContext,
clearRlsContext,
withRlsContext,
RlsPolicyOptions,
} from '@erp-suite/core';
// Database connection pool
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'erp_suite',
user: 'postgres',
password: 'password',
});
/**
* EXAMPLE 1: Apply tenant isolation to a single table
*/
async function example1_singleTable() {
console.log('Example 1: Apply tenant isolation to core.partners');
// Apply tenant isolation policy
await applyTenantIsolationPolicy(pool, 'core', 'partners');
// Verify it was applied
const enabled = await isRlsEnabled(pool, 'core', 'partners');
console.log(`RLS enabled: ${enabled}`);
// List policies
const status = await listRlsPolicies(pool, 'core', 'partners');
console.log(`Policies: ${status.policies.map(p => p.name).join(', ')}`);
}
/**
* EXAMPLE 2: Apply complete policies (tenant + admin) to a table
*/
async function example2_completePolicies() {
console.log('Example 2: Apply complete policies to inventory.products');
// Apply tenant isolation + admin bypass
await applyCompleteRlsPolicies(pool, 'inventory', 'products');
// Check status
const status = await listRlsPolicies(pool, 'inventory', 'products');
console.log(`Applied ${status.policies.length} policies:`);
status.policies.forEach(p => {
console.log(` - ${p.name} (${p.command})`);
});
}
/**
* EXAMPLE 3: Apply policies to multiple tables in a schema
*/
async function example3_multipleTablesInSchema() {
console.log('Example 3: Apply policies to multiple inventory tables');
const tables = ['products', 'warehouses', 'locations', 'lots', 'pickings'];
await applyCompletePoliciesForSchema(pool, 'inventory', tables);
console.log(`Applied RLS to ${tables.length} tables in inventory schema`);
}
/**
* EXAMPLE 4: Batch apply with different configurations
*/
async function example4_batchApply() {
console.log('Example 4: Batch apply with custom configurations');
const configs: RlsPolicyOptions[] = [
// Standard tables
{ schema: 'core', table: 'partners' },
{ schema: 'core', table: 'addresses' },
{ schema: 'core', table: 'notes' },
// Table without admin bypass
{ schema: 'financial', table: 'audit_logs', includeAdminBypass: false },
// Table with custom tenant column
{ schema: 'projects', table: 'tasks', tenantColumn: 'company_id' },
];
await batchApplyRlsPolicies(pool, configs);
console.log(`Applied RLS to ${configs.length} tables`);
}
/**
* EXAMPLE 5: Apply user data policy for user-specific tables
*/
async function example5_userDataPolicy() {
console.log('Example 5: Apply user data policy to projects.tasks');
// Apply user data policy (users can only see their own tasks)
await applyUserDataPolicy(pool, 'projects', 'tasks', ['created_by', 'assigned_to']);
const status = await listRlsPolicies(pool, 'projects', 'tasks');
console.log(`User data policy applied: ${status.policies[0]?.name}`);
}
/**
* EXAMPLE 6: Get RLS status for entire schema
*/
async function example6_schemaStatus() {
console.log('Example 6: Get RLS status for core schema');
const statuses = await getSchemaRlsStatus(pool, 'core');
console.log(`RLS Status for core schema:`);
statuses.forEach(status => {
const policyCount = status.policies.length;
const rlsStatus = status.rlsEnabled ? 'enabled' : 'disabled';
console.log(` ${status.table}: RLS ${rlsStatus}, ${policyCount} policies`);
});
}
/**
* EXAMPLE 7: Query data with RLS context
*/
async function example7_queryWithContext() {
console.log('Example 7: Query data with RLS context');
const tenantId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
const userId = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
// Method 1: Manual context setting
await setRlsContext(pool, { tenantId, userId, userRole: 'user' });
const result1 = await pool.query('SELECT COUNT(*) FROM core.partners');
console.log(`Partners visible to user: ${result1.rows[0].count}`);
await clearRlsContext(pool);
// Method 2: Using withRlsContext helper
const result2 = await withRlsContext(
pool,
{ tenantId, userId, userRole: 'admin' },
async (client) => {
return await client.query('SELECT COUNT(*) FROM core.partners');
}
);
console.log(`Partners visible to admin: ${result2.rows[0].count}`);
}
/**
* EXAMPLE 8: Migration script for a vertical
*/
async function example8_verticalMigration() {
console.log('Example 8: Migration script for construccion vertical');
// Tables in construccion vertical
const constructionTables = [
{ schema: 'construction', table: 'projects' },
{ schema: 'construction', table: 'estimates' },
{ schema: 'construction', table: 'work_orders' },
{ schema: 'construction', table: 'materials' },
{ schema: 'hse', table: 'incidents' },
{ schema: 'hse', table: 'inspections' },
];
console.log('Applying RLS to construccion vertical tables...');
for (const { schema, table } of constructionTables) {
try {
await applyCompleteRlsPolicies(pool, schema, table, 'constructora_id');
console.log(`${schema}.${table}`);
} catch (error) {
console.error(`${schema}.${table}: ${error.message}`);
}
}
console.log('Migration complete!');
}
/**
* EXAMPLE 9: Verify RLS isolation (testing)
*/
async function example9_testRlsIsolation() {
console.log('Example 9: Test RLS isolation between tenants');
const tenant1 = 'tenant-1111-1111-1111-111111111111';
const tenant2 = 'tenant-2222-2222-2222-222222222222';
// Query as tenant 1
const result1 = await withRlsContext(
pool,
{ tenantId: tenant1, userRole: 'user' },
async (client) => {
return await client.query('SELECT COUNT(*) FROM core.partners');
}
);
// Query as tenant 2
const result2 = await withRlsContext(
pool,
{ tenantId: tenant2, userRole: 'user' },
async (client) => {
return await client.query('SELECT COUNT(*) FROM core.partners');
}
);
console.log(`Tenant 1 sees: ${result1.rows[0].count} partners`);
console.log(`Tenant 2 sees: ${result2.rows[0].count} partners`);
console.log('RLS isolation verified!');
}
/**
* EXAMPLE 10: Apply admin bypass for support team
*/
async function example10_adminBypass() {
console.log('Example 10: Test admin bypass policy');
const tenantId = 'tenant-1111-1111-1111-111111111111';
// Query as regular user
const userResult = await withRlsContext(
pool,
{ tenantId, userRole: 'user' },
async (client) => {
return await client.query('SELECT COUNT(*) FROM core.partners');
}
);
// Query as admin (should see all tenants)
const adminResult = await withRlsContext(
pool,
{ userRole: 'admin' }, // No tenantId set
async (client) => {
return await client.query('SELECT COUNT(*) FROM core.partners');
}
);
console.log(`User sees: ${userResult.rows[0].count} partners (own tenant only)`);
console.log(`Admin sees: ${adminResult.rows[0].count} partners (all tenants)`);
}
/**
* EXAMPLE 11: Apply RLS to ERP Core tables
*/
async function example11_erpCoreTables() {
console.log('Example 11: Apply RLS to ERP Core tables');
const erpCoreConfigs: RlsPolicyOptions[] = [
// Core schema
{ schema: 'core', table: 'partners' },
{ schema: 'core', table: 'addresses' },
{ schema: 'core', table: 'product_categories' },
{ schema: 'core', table: 'tags' },
{ schema: 'core', table: 'sequences' },
{ schema: 'core', table: 'attachments' },
{ schema: 'core', table: 'notes' },
// Inventory schema
{ schema: 'inventory', table: 'products' },
{ schema: 'inventory', table: 'warehouses' },
{ schema: 'inventory', table: 'locations' },
{ schema: 'inventory', table: 'lots' },
{ schema: 'inventory', table: 'pickings' },
{ schema: 'inventory', table: 'stock_moves' },
// Sales schema
{ schema: 'sales', table: 'sales_orders' },
{ schema: 'sales', table: 'quotations' },
{ schema: 'sales', table: 'pricelists' },
// Purchase schema
{ schema: 'purchase', table: 'purchase_orders' },
{ schema: 'purchase', table: 'rfqs' },
{ schema: 'purchase', table: 'vendor_pricelists' },
// Financial schema
{ schema: 'financial', table: 'accounts' },
{ schema: 'financial', table: 'invoices' },
{ schema: 'financial', table: 'payments' },
{ schema: 'financial', table: 'journal_entries' },
// Projects schema
{ schema: 'projects', table: 'projects' },
{ schema: 'projects', table: 'tasks' },
{ schema: 'projects', table: 'timesheets' },
];
console.log(`Applying RLS to ${erpCoreConfigs.length} ERP Core tables...`);
let successCount = 0;
let errorCount = 0;
for (const config of erpCoreConfigs) {
try {
await applyCompleteRlsPolicies(
pool,
config.schema,
config.table,
config.tenantColumn || 'tenant_id',
config.includeAdminBypass ?? true
);
console.log(`${config.schema}.${config.table}`);
successCount++;
} catch (error) {
console.error(`${config.schema}.${config.table}: ${error.message}`);
errorCount++;
}
}
console.log(`\nComplete! Success: ${successCount}, Errors: ${errorCount}`);
}
/**
* EXAMPLE 12: Cleanup - Remove RLS from a table
*/
async function example12_cleanup() {
console.log('Example 12: Remove RLS from a table');
// Import dropAllRlsPolicies
const { dropAllRlsPolicies, disableRls } = await import('@erp-suite/core');
const schema = 'test';
const table = 'temp_table';
// Drop all policies
const policyCount = await dropAllRlsPolicies(pool, schema, table);
console.log(`Dropped ${policyCount} policies from ${schema}.${table}`);
// Disable RLS
await disableRls(pool, schema, table);
console.log(`Disabled RLS on ${schema}.${table}`);
// Verify
const enabled = await isRlsEnabled(pool, schema, table);
console.log(`RLS enabled: ${enabled}`);
}
/**
* Main function - Run all examples
*/
async function main() {
try {
console.log('='.repeat(60));
console.log('RLS POLICIES - USAGE EXAMPLES');
console.log('='.repeat(60));
console.log();
// Uncomment the examples you want to run
// await example1_singleTable();
// await example2_completePolicies();
// await example3_multipleTablesInSchema();
// await example4_batchApply();
// await example5_userDataPolicy();
// await example6_schemaStatus();
// await example7_queryWithContext();
// await example8_verticalMigration();
// await example9_testRlsIsolation();
// await example10_adminBypass();
// await example11_erpCoreTables();
// await example12_cleanup();
console.log();
console.log('='.repeat(60));
console.log('Examples completed successfully!');
console.log('='.repeat(60));
} catch (error) {
console.error('Error running examples:', error);
} finally {
await pool.end();
}
}
// Run if executed directly
if (require.main === module) {
main();
}
// Export examples for individual use
export {
example1_singleTable,
example2_completePolicies,
example3_multipleTablesInSchema,
example4_batchApply,
example5_userDataPolicy,
example6_schemaStatus,
example7_queryWithContext,
example8_verticalMigration,
example9_testRlsIsolation,
example10_adminBypass,
example11_erpCoreTables,
example12_cleanup,
};