using Insight.Database; using System.Data; namespace Database.Identity { public class DbInfrastructureSetup { private readonly IDbConnection _db; string _schema; public DbInfrastructureSetup(IDbConnection db) { _db = db; } public async Task CreateDatabaseWithSchema(string schema) { _schema = schema; if (_db.State != ConnectionState.Open) _db.Open(); using var transaction = _db.BeginTransaction(); try { await CreateUserTable(); await CreateTenantTable(); await CreateUserTenantTable(); await SetupRLS(); transaction.Commit(); } catch { transaction.Rollback(); throw; } } private async Task CreateUserTable() { await _db.ExecuteSqlAsync(@$" CREATE TABLE IF NOT EXISTS {_schema}.users ( id SERIAL PRIMARY KEY, email VARCHAR(256) NOT NULL UNIQUE, password_hash VARCHAR(256) NOT NULL, security_stamp VARCHAR(36) NOT NULL, email_confirmed BOOLEAN NOT NULL DEFAULT FALSE, created_date TIMESTAMP NOT NULL, last_login_date TIMESTAMP NULL );"); } private async Task CreateTenantTable() { await _db.ExecuteSqlAsync(@$" CREATE TABLE IF NOT EXISTS {_schema}.tenants ( id SERIAL PRIMARY KEY, connection_string VARCHAR(500) NOT NULL, created_date TIMESTAMP NOT NULL, created_by INTEGER REFERENCES {_schema}.users(id), is_active BOOLEAN DEFAULT true );"); } private async Task CreateUserTenantTable() { await _db.ExecuteSqlAsync(@$" CREATE TABLE IF NOT EXISTS {_schema}.user_tenants ( user_id INTEGER REFERENCES {_schema}.users(id), tenant_id INTEGER REFERENCES {_schema}.tenants(id), created_date TIMESTAMP NOT NULL, PRIMARY KEY (user_id, tenant_id) );"); } private async Task SetupRLS() { await _db.ExecuteSqlAsync(@$" ALTER TABLE {_schema}.tenants ENABLE ROW LEVEL SECURITY; ALTER TABLE {_schema}.user_tenants ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_access ON {_schema}.tenants; CREATE POLICY tenant_access ON {_schema}.tenants USING (id IN ( SELECT tenant_id FROM {_schema}.user_tenants WHERE user_id = current_setting('app.user_id', TRUE)::INTEGER )); DROP POLICY IF EXISTS user_tenant_access ON {_schema}.user_tenants; CREATE POLICY user_tenant_access ON {_schema}.user_tenants USING (user_id = current_setting('app.user_id', TRUE)::INTEGER);"); } } }