diff --git a/products/02-iac-drift-detection/saas/migrations/004_auth_rls_fix.sql b/products/02-iac-drift-detection/saas/migrations/004_auth_rls_fix.sql new file mode 100644 index 0000000..3fdcb23 --- /dev/null +++ b/products/02-iac-drift-detection/saas/migrations/004_auth_rls_fix.sql @@ -0,0 +1,18 @@ +-- Fix RLS for auth operations (login/signup) +-- Login needs to SELECT users by email before tenant context is known. +-- Signup needs to check for duplicate emails and INSERT new users. + +-- Allow SELECT on users without tenant context (for login email lookup + signup dupe check) +CREATE POLICY auth_select_users ON users + FOR SELECT + USING (true); + +-- Allow INSERT on users when tenant context is set (signup sets it via set_config) +CREATE POLICY auth_insert_users ON users + FOR INSERT + WITH CHECK (tenant_id::text = current_setting('app.tenant_id', true)); + +-- Allow SELECT on api_keys without tenant context (for API key auth lookup) +CREATE POLICY auth_select_api_keys ON api_keys + FOR SELECT + USING (true); diff --git a/products/02-iac-drift-detection/saas/src/auth/middleware.ts b/products/02-iac-drift-detection/saas/src/auth/middleware.ts index f9ea7cc..07f32ce 100644 --- a/products/02-iac-drift-detection/saas/src/auth/middleware.ts +++ b/products/02-iac-drift-detection/saas/src/auth/middleware.ts @@ -222,6 +222,9 @@ export function registerAuthRoutes(app: FastifyInstance, jwtSecret: string, pool role = 'owner'; } + // Set tenant context for RLS before inserting user + await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]); + const user = await client.query( `INSERT INTO users (tenant_id, email, password_hash, role) VALUES ($1, $2, $3, $4) RETURNING id`, [tenantId, body.email, passwordHash, role], diff --git a/products/03-alert-intelligence/migrations/004_auth_rls_fix.sql b/products/03-alert-intelligence/migrations/004_auth_rls_fix.sql new file mode 100644 index 0000000..3fdcb23 --- /dev/null +++ b/products/03-alert-intelligence/migrations/004_auth_rls_fix.sql @@ -0,0 +1,18 @@ +-- Fix RLS for auth operations (login/signup) +-- Login needs to SELECT users by email before tenant context is known. +-- Signup needs to check for duplicate emails and INSERT new users. + +-- Allow SELECT on users without tenant context (for login email lookup + signup dupe check) +CREATE POLICY auth_select_users ON users + FOR SELECT + USING (true); + +-- Allow INSERT on users when tenant context is set (signup sets it via set_config) +CREATE POLICY auth_insert_users ON users + FOR INSERT + WITH CHECK (tenant_id::text = current_setting('app.tenant_id', true)); + +-- Allow SELECT on api_keys without tenant context (for API key auth lookup) +CREATE POLICY auth_select_api_keys ON api_keys + FOR SELECT + USING (true); diff --git a/products/03-alert-intelligence/src/auth/middleware.ts b/products/03-alert-intelligence/src/auth/middleware.ts index b2a8ca4..20f492d 100644 --- a/products/03-alert-intelligence/src/auth/middleware.ts +++ b/products/03-alert-intelligence/src/auth/middleware.ts @@ -222,6 +222,9 @@ export function registerAuthRoutes(app: FastifyInstance, jwtSecret: string, pool role = 'owner'; } + // Set tenant context for RLS before inserting user + await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]); + const user = await client.query( `INSERT INTO users (tenant_id, email, password_hash, role) VALUES ($1, $2, $3, $4) RETURNING id`, [tenantId, body.email, passwordHash, role], diff --git a/products/04-lightweight-idp/migrations/004_auth_rls_fix.sql b/products/04-lightweight-idp/migrations/004_auth_rls_fix.sql new file mode 100644 index 0000000..3fdcb23 --- /dev/null +++ b/products/04-lightweight-idp/migrations/004_auth_rls_fix.sql @@ -0,0 +1,18 @@ +-- Fix RLS for auth operations (login/signup) +-- Login needs to SELECT users by email before tenant context is known. +-- Signup needs to check for duplicate emails and INSERT new users. + +-- Allow SELECT on users without tenant context (for login email lookup + signup dupe check) +CREATE POLICY auth_select_users ON users + FOR SELECT + USING (true); + +-- Allow INSERT on users when tenant context is set (signup sets it via set_config) +CREATE POLICY auth_insert_users ON users + FOR INSERT + WITH CHECK (tenant_id::text = current_setting('app.tenant_id', true)); + +-- Allow SELECT on api_keys without tenant context (for API key auth lookup) +CREATE POLICY auth_select_api_keys ON api_keys + FOR SELECT + USING (true); diff --git a/products/04-lightweight-idp/src/auth/middleware.ts b/products/04-lightweight-idp/src/auth/middleware.ts index 7958603..ad0256a 100644 --- a/products/04-lightweight-idp/src/auth/middleware.ts +++ b/products/04-lightweight-idp/src/auth/middleware.ts @@ -223,6 +223,9 @@ export function registerAuthRoutes(app: FastifyInstance, jwtSecret: string, pool role = 'owner'; } + // Set tenant context for RLS before inserting user + await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]); + const user = await client.query( `INSERT INTO users (tenant_id, email, password_hash, role) VALUES ($1, $2, $3, $4) RETURNING id`, [tenantId, body.email, passwordHash, role], diff --git a/products/05-aws-cost-anomaly/migrations/004_auth_rls_fix.sql b/products/05-aws-cost-anomaly/migrations/004_auth_rls_fix.sql new file mode 100644 index 0000000..3fdcb23 --- /dev/null +++ b/products/05-aws-cost-anomaly/migrations/004_auth_rls_fix.sql @@ -0,0 +1,18 @@ +-- Fix RLS for auth operations (login/signup) +-- Login needs to SELECT users by email before tenant context is known. +-- Signup needs to check for duplicate emails and INSERT new users. + +-- Allow SELECT on users without tenant context (for login email lookup + signup dupe check) +CREATE POLICY auth_select_users ON users + FOR SELECT + USING (true); + +-- Allow INSERT on users when tenant context is set (signup sets it via set_config) +CREATE POLICY auth_insert_users ON users + FOR INSERT + WITH CHECK (tenant_id::text = current_setting('app.tenant_id', true)); + +-- Allow SELECT on api_keys without tenant context (for API key auth lookup) +CREATE POLICY auth_select_api_keys ON api_keys + FOR SELECT + USING (true); diff --git a/products/05-aws-cost-anomaly/src/auth/middleware.ts b/products/05-aws-cost-anomaly/src/auth/middleware.ts index f9ea7cc..07f32ce 100644 --- a/products/05-aws-cost-anomaly/src/auth/middleware.ts +++ b/products/05-aws-cost-anomaly/src/auth/middleware.ts @@ -222,6 +222,9 @@ export function registerAuthRoutes(app: FastifyInstance, jwtSecret: string, pool role = 'owner'; } + // Set tenant context for RLS before inserting user + await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]); + const user = await client.query( `INSERT INTO users (tenant_id, email, password_hash, role) VALUES ($1, $2, $3, $4) RETURNING id`, [tenantId, body.email, passwordHash, role], diff --git a/products/06-runbook-automation/saas/migrations/004_auth_rls_fix.sql b/products/06-runbook-automation/saas/migrations/004_auth_rls_fix.sql new file mode 100644 index 0000000..3fdcb23 --- /dev/null +++ b/products/06-runbook-automation/saas/migrations/004_auth_rls_fix.sql @@ -0,0 +1,18 @@ +-- Fix RLS for auth operations (login/signup) +-- Login needs to SELECT users by email before tenant context is known. +-- Signup needs to check for duplicate emails and INSERT new users. + +-- Allow SELECT on users without tenant context (for login email lookup + signup dupe check) +CREATE POLICY auth_select_users ON users + FOR SELECT + USING (true); + +-- Allow INSERT on users when tenant context is set (signup sets it via set_config) +CREATE POLICY auth_insert_users ON users + FOR INSERT + WITH CHECK (tenant_id::text = current_setting('app.tenant_id', true)); + +-- Allow SELECT on api_keys without tenant context (for API key auth lookup) +CREATE POLICY auth_select_api_keys ON api_keys + FOR SELECT + USING (true); diff --git a/products/06-runbook-automation/saas/src/auth/middleware.ts b/products/06-runbook-automation/saas/src/auth/middleware.ts index f9ea7cc..07f32ce 100644 --- a/products/06-runbook-automation/saas/src/auth/middleware.ts +++ b/products/06-runbook-automation/saas/src/auth/middleware.ts @@ -222,6 +222,9 @@ export function registerAuthRoutes(app: FastifyInstance, jwtSecret: string, pool role = 'owner'; } + // Set tenant context for RLS before inserting user + await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]); + const user = await client.query( `INSERT INTO users (tenant_id, email, password_hash, role) VALUES ($1, $2, $3, $4) RETURNING id`, [tenantId, body.email, passwordHash, role], diff --git a/products/docker-compose.yml b/products/docker-compose.yml index 2479e71..cf7d22b 100644 --- a/products/docker-compose.yml +++ b/products/docker-compose.yml @@ -59,6 +59,12 @@ services: restart: "no" depends_on: postgres: { condition: service_healthy } + volumes: + - ./02-iac-drift-detection/saas/migrations:/migrations/02-drift:ro + - ./03-alert-intelligence/migrations:/migrations/03-alert:ro + - ./04-lightweight-idp/migrations:/migrations/04-portal:ro + - ./05-aws-cost-anomaly/migrations:/migrations/05-cost:ro + - ./06-runbook-automation/saas/migrations:/migrations/06-run:ro environment: PGHOST: postgres PGUSER: ${POSTGRES_USER:-dd0c} @@ -88,6 +94,22 @@ services: create_user dd0c_portal dd0c_portal "$$DB_PORTAL_PASSWORD" create_user dd0c_cost dd0c_cost "$$DB_COST_PASSWORD" create_user dd0c_run dd0c_run "$$DB_RUN_PASSWORD" + # Run any new migrations (idempotent — CREATE IF NOT EXISTS / DO blocks) + run_migrations() { + local db=$$1 dir=$$2 + if [ -d "$$dir" ]; then + for sql in "$$dir"/*.sql; do + [ -f "$$sql" ] || continue + echo " $$db ← $$(basename $$sql)" + psql -d "$$db" -f "$$sql" 2>/dev/null || true + done + fi + } + run_migrations dd0c_drift /migrations/02-drift + run_migrations dd0c_alert /migrations/03-alert + run_migrations dd0c_portal /migrations/04-portal + run_migrations dd0c_cost /migrations/05-cost + run_migrations dd0c_run /migrations/06-run echo "db-setup complete" # --- dd0c Products ---