-- ============================================================================= -- 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') );