feat(db): consolidated PostgreSQL schema replacing Supabase (Phase 1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:43:09 +08:00
co-authored by Claude Sonnet 4.6
parent ba3d345531
commit e5fd2436fa
2 changed files with 648 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
-- =============================================================================
-- IMS HSE Incident Management System
-- Row Level Security Policies (Phase 1)
-- Run AFTER schema.sql (functions used in policies must exist).
-- All auth.uid() replaced with app_current_user_id() (GUC-based).
-- =============================================================================
-- ---------------------------------------------------------------------------
-- Enable RLS + FORCE ROW LEVEL SECURITY on all 13 tables
-- ---------------------------------------------------------------------------
ALTER TABLE sites ENABLE ROW LEVEL SECURITY;
ALTER TABLE zones ENABLE ROW LEVEL SECURITY;
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE trucks ENABLE ROW LEVEL SECURITY;
ALTER TABLE incidents ENABLE ROW LEVEL SECURITY;
ALTER TABLE evidence_files ENABLE ROW LEVEL SECURITY;
ALTER TABLE investigations ENABLE ROW LEVEL SECURITY;
ALTER TABLE capa_actions ENABLE ROW LEVEL SECURITY;
ALTER TABLE dosh_reports ENABLE ROW LEVEL SECURITY;
ALTER TABLE notifications_log ENABLE ROW LEVEL SECURITY;
ALTER TABLE audit_log ENABLE ROW LEVEL SECURITY;
ALTER TABLE app_settings ENABLE ROW LEVEL SECURITY;
ALTER TABLE incident_addenda ENABLE ROW LEVEL SECURITY;
-- FORCE RLS so that even the table owner (postgres superuser running as app_user) cannot bypass
ALTER TABLE sites FORCE ROW LEVEL SECURITY;
ALTER TABLE zones FORCE ROW LEVEL SECURITY;
ALTER TABLE users FORCE ROW LEVEL SECURITY;
ALTER TABLE trucks FORCE ROW LEVEL SECURITY;
ALTER TABLE incidents FORCE ROW LEVEL SECURITY;
ALTER TABLE evidence_files FORCE ROW LEVEL SECURITY;
ALTER TABLE investigations FORCE ROW LEVEL SECURITY;
ALTER TABLE capa_actions FORCE ROW LEVEL SECURITY;
ALTER TABLE dosh_reports FORCE ROW LEVEL SECURITY;
ALTER TABLE notifications_log FORCE ROW LEVEL SECURITY;
ALTER TABLE audit_log FORCE ROW LEVEL SECURITY;
ALTER TABLE app_settings FORCE ROW LEVEL SECURITY;
ALTER TABLE incident_addenda FORCE ROW LEVEL SECURITY;
-- ---------------------------------------------------------------------------
-- Policies — final state (all auth.uid() replaced with app_current_user_id())
-- ---------------------------------------------------------------------------
-- sites
CREATE POLICY "sites_read" ON sites FOR SELECT USING (active = true);
CREATE POLICY "sites_admin_all" ON sites FOR ALL USING (auth_user_role() = 'admin');
-- zones
CREATE POLICY "zones_read" ON zones FOR SELECT USING (true);
CREATE POLICY "zones_admin_all" ON zones FOR ALL USING (auth_user_role() = 'admin');
-- trucks
CREATE POLICY "trucks_read" ON trucks FOR SELECT USING (true);
CREATE POLICY "trucks_admin_all" ON trucks FOR ALL USING (auth_user_role() = 'admin');
-- users
CREATE POLICY "users_read_own" ON users FOR SELECT USING (id = app_current_user_id());
CREATE POLICY "users_read_elevated" ON users FOR SELECT
USING (auth_user_role() IN ('hse', 'admin', 'management', 'supervisor'));
CREATE POLICY "users_update_own" ON users FOR UPDATE
USING (id = app_current_user_id())
WITH CHECK (
id = app_current_user_id()
AND role = (SELECT role FROM public.users WHERE id = app_current_user_id())
);
CREATE POLICY "users_admin_all" ON users FOR ALL USING (auth_user_role() = 'admin');
-- incidents
CREATE POLICY "incidents_insert" ON incidents FOR INSERT
WITH CHECK (reported_by = app_current_user_id());
CREATE POLICY "incidents_read_reporter" ON incidents FOR SELECT
USING (reported_by = app_current_user_id());
CREATE POLICY "incidents_read_supervisor" ON incidents FOR SELECT
USING (auth_user_role() = 'supervisor' AND site_id = auth_user_site_id());
CREATE POLICY "incidents_read_elevated" ON incidents FOR SELECT
USING (auth_user_role() IN ('hse', 'admin', 'management'));
CREATE POLICY "incidents_update_elevated" ON incidents FOR UPDATE
USING (auth_user_role() IN ('hse', 'admin', 'supervisor'));
-- evidence_files
CREATE POLICY "evidence_insert" ON evidence_files FOR INSERT
WITH CHECK (uploaded_by = app_current_user_id());
CREATE POLICY "evidence_read_uploader" ON evidence_files FOR SELECT
USING (uploaded_by = app_current_user_id());
CREATE POLICY "evidence_read_elevated" ON evidence_files FOR SELECT
USING (auth_user_role() IN ('hse', 'admin', 'management', 'supervisor'));
-- No DELETE policy: files are never deleted (soft-delete only via `deleted` flag)
-- investigations
CREATE POLICY "investigations_hse_admin" ON investigations FOR ALL
USING (auth_user_role() IN ('hse', 'admin'));
CREATE POLICY "investigations_read_supervisor" ON investigations FOR SELECT
USING (auth_user_role() = 'supervisor');
-- capa_actions (capa_update_owner is tightened — no department clause)
CREATE POLICY "capa_read_owner" ON capa_actions FOR SELECT
USING (owner_user_id = app_current_user_id() OR department = auth_user_department());
CREATE POLICY "capa_update_owner" ON capa_actions FOR UPDATE
USING (owner_user_id = app_current_user_id());
CREATE POLICY "capa_read_elevated" ON capa_actions FOR SELECT
USING (auth_user_role() IN ('hse', 'admin', 'management', 'supervisor'));
CREATE POLICY "capa_hse_admin_all" ON capa_actions FOR ALL
USING (auth_user_role() IN ('hse', 'admin'));
-- dosh_reports
CREATE POLICY "dosh_hse_admin" ON dosh_reports FOR ALL
USING (auth_user_role() IN ('hse', 'admin'));
-- notifications_log (final state after migration 19 fix)
CREATE POLICY "notifications_insert_elevated" ON notifications_log FOR INSERT
WITH CHECK (auth_user_role() IN ('hse', 'admin', 'supervisor'));
CREATE POLICY "notifications_read_own" ON notifications_log FOR SELECT
USING (recipient_user_id = app_current_user_id());
CREATE POLICY "notifications_read_admin" ON notifications_log FOR SELECT
USING (auth_user_role() = 'admin');
CREATE POLICY "notifications_update_own" ON notifications_log FOR UPDATE
USING (recipient_user_id = app_current_user_id())
WITH CHECK (recipient_user_id = app_current_user_id());
-- audit_log
CREATE POLICY "audit_read_elevated" ON audit_log FOR SELECT
USING (auth_user_role() IN ('hse', 'admin'));
-- app_settings
CREATE POLICY "admin_select_settings" ON app_settings FOR SELECT
USING (auth_user_role() = 'admin');
CREATE POLICY "admin_update_settings" ON app_settings FOR ALL
USING (auth_user_role() = 'admin');
-- incident_addenda
CREATE POLICY "addenda_read" ON incident_addenda FOR SELECT
USING (
auth_user_role() IN ('hse', 'admin', 'supervisor', 'management')
OR EXISTS (
SELECT 1 FROM incidents i
WHERE i.id = incident_id AND i.reported_by = app_current_user_id()
)
);
CREATE POLICY "addenda_insert" ON incident_addenda FOR INSERT
WITH CHECK (
author = app_current_user_id()
AND auth_user_role() IN ('hse', 'admin', 'supervisor')
);