|
|
|
|
@@ -0,0 +1,178 @@
|
|
|
|
|
-- Notify Schema Migration 010: Row-Level Security
|
|
|
|
|
-- Sprint: SPRINT_3421_0001_0001 - RLS Expansion
|
|
|
|
|
-- Category: B (release migration, requires coordination)
|
|
|
|
|
--
|
|
|
|
|
-- Purpose: Enable Row-Level Security on all tenant-scoped tables in the notify
|
|
|
|
|
-- schema to provide database-level tenant isolation as defense-in-depth.
|
|
|
|
|
|
|
|
|
|
BEGIN;
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Step 1: Create helper schema and function for tenant context
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
CREATE SCHEMA IF NOT EXISTS notify_app;
|
|
|
|
|
|
|
|
|
|
-- Tenant context helper function
|
|
|
|
|
CREATE OR REPLACE FUNCTION notify_app.require_current_tenant()
|
|
|
|
|
RETURNS TEXT
|
|
|
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
v_tenant TEXT;
|
|
|
|
|
BEGIN
|
|
|
|
|
v_tenant := current_setting('app.tenant_id', true);
|
|
|
|
|
IF v_tenant IS NULL OR v_tenant = '' THEN
|
|
|
|
|
RAISE EXCEPTION 'app.tenant_id session variable not set'
|
|
|
|
|
USING HINT = 'Set via: SELECT set_config(''app.tenant_id'', ''<tenant>'', false)',
|
|
|
|
|
ERRCODE = 'P0001';
|
|
|
|
|
END IF;
|
|
|
|
|
RETURN v_tenant;
|
|
|
|
|
END;
|
|
|
|
|
$$;
|
|
|
|
|
|
|
|
|
|
REVOKE ALL ON FUNCTION notify_app.require_current_tenant() FROM PUBLIC;
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Step 2: Enable RLS on all tenant-scoped tables
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
-- notify.channels
|
|
|
|
|
ALTER TABLE notify.channels ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.channels FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS channels_tenant_isolation ON notify.channels;
|
|
|
|
|
CREATE POLICY channels_tenant_isolation ON notify.channels
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.rules
|
|
|
|
|
ALTER TABLE notify.rules ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.rules FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS rules_tenant_isolation ON notify.rules;
|
|
|
|
|
CREATE POLICY rules_tenant_isolation ON notify.rules
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.templates
|
|
|
|
|
ALTER TABLE notify.templates ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.templates FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS templates_tenant_isolation ON notify.templates;
|
|
|
|
|
CREATE POLICY templates_tenant_isolation ON notify.templates
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.deliveries
|
|
|
|
|
ALTER TABLE notify.deliveries ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.deliveries FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS deliveries_tenant_isolation ON notify.deliveries;
|
|
|
|
|
CREATE POLICY deliveries_tenant_isolation ON notify.deliveries
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.digests
|
|
|
|
|
ALTER TABLE notify.digests ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.digests FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS digests_tenant_isolation ON notify.digests;
|
|
|
|
|
CREATE POLICY digests_tenant_isolation ON notify.digests
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.quiet_hours
|
|
|
|
|
ALTER TABLE notify.quiet_hours ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.quiet_hours FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS quiet_hours_tenant_isolation ON notify.quiet_hours;
|
|
|
|
|
CREATE POLICY quiet_hours_tenant_isolation ON notify.quiet_hours
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.maintenance_windows
|
|
|
|
|
ALTER TABLE notify.maintenance_windows ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.maintenance_windows FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS maintenance_windows_tenant_isolation ON notify.maintenance_windows;
|
|
|
|
|
CREATE POLICY maintenance_windows_tenant_isolation ON notify.maintenance_windows
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.escalation_policies
|
|
|
|
|
ALTER TABLE notify.escalation_policies ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.escalation_policies FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS escalation_policies_tenant_isolation ON notify.escalation_policies;
|
|
|
|
|
CREATE POLICY escalation_policies_tenant_isolation ON notify.escalation_policies
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.escalation_states
|
|
|
|
|
ALTER TABLE notify.escalation_states ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.escalation_states FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS escalation_states_tenant_isolation ON notify.escalation_states;
|
|
|
|
|
CREATE POLICY escalation_states_tenant_isolation ON notify.escalation_states
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.on_call_schedules
|
|
|
|
|
ALTER TABLE notify.on_call_schedules ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.on_call_schedules FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS on_call_schedules_tenant_isolation ON notify.on_call_schedules;
|
|
|
|
|
CREATE POLICY on_call_schedules_tenant_isolation ON notify.on_call_schedules
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.inbox
|
|
|
|
|
ALTER TABLE notify.inbox ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.inbox FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS inbox_tenant_isolation ON notify.inbox;
|
|
|
|
|
CREATE POLICY inbox_tenant_isolation ON notify.inbox
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.incidents
|
|
|
|
|
ALTER TABLE notify.incidents ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.incidents FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS incidents_tenant_isolation ON notify.incidents;
|
|
|
|
|
CREATE POLICY incidents_tenant_isolation ON notify.incidents
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.audit
|
|
|
|
|
ALTER TABLE notify.audit ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.audit FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS audit_tenant_isolation ON notify.audit;
|
|
|
|
|
CREATE POLICY audit_tenant_isolation ON notify.audit
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- notify.locks
|
|
|
|
|
ALTER TABLE notify.locks ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
ALTER TABLE notify.locks FORCE ROW LEVEL SECURITY;
|
|
|
|
|
DROP POLICY IF EXISTS locks_tenant_isolation ON notify.locks;
|
|
|
|
|
CREATE POLICY locks_tenant_isolation ON notify.locks
|
|
|
|
|
FOR ALL
|
|
|
|
|
USING (tenant_id = notify_app.require_current_tenant())
|
|
|
|
|
WITH CHECK (tenant_id = notify_app.require_current_tenant());
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Step 3: Create admin bypass role
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
DO $$
|
|
|
|
|
BEGIN
|
|
|
|
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'notify_admin') THEN
|
|
|
|
|
CREATE ROLE notify_admin WITH NOLOGIN BYPASSRLS;
|
|
|
|
|
END IF;
|
|
|
|
|
END
|
|
|
|
|
$$;
|
|
|
|
|
|
|
|
|
|
COMMIT;
|