Files
iistwin/migrations/0011_rls_tasks_notnull.sql
Ильяс Султанов 1f5ecb6da4 fix(number-fields): избегаем потери точности длинных чисел
- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
2026-07-07 21:03:40 +03:00

103 lines
3.9 KiB
SQL

-- Task #423: tasks.organization_id NOT NULL + RLS policies
-- ============================================================
-- Step 1: backfill any NULL organization_id in tasks from parent form
UPDATE tasks
SET organization_id = forms.organization_id
FROM forms
WHERE tasks.form_id = forms.id
AND tasks.organization_id IS NULL;
-- Step 2: enforce NOT NULL (safe after backfill above)
ALTER TABLE tasks
ALTER COLUMN organization_id SET NOT NULL;
-- ============================================================
-- Step 3: Row-Level Security policies for tenant tables.
--
-- Policy expression for DIRECT organization_id tables:
-- USING (
-- current_setting('app.is_superadmin', true) = 'true'
-- OR organization_id = NULLIF(current_setting('app.current_org_id', true), '')::int
-- )
--
-- Policy expression for JOIN-based tables (no direct organization_id):
-- USING (
-- current_setting('app.is_superadmin', true) = 'true'
-- OR <fk_col> IN (SELECT id FROM <parent> WHERE organization_id = ...)
-- )
--
-- Policies are INACTIVE until `ALTER TABLE ... ENABLE ROW LEVEL SECURITY`
-- is also issued (done at startup when ENABLE_RLS=true).
-- ============================================================
-- Direct organization_id tables
DO $$
DECLARE t text;
DECLARE tbls text[] := ARRAY[
'tasks','users','forms','task_relations','user_notifications',
'message_reads','bookmarks','bookmark_folders','bots','bot_subscriptions',
'data_tables','invitations','user_custom_fields','device_tokens',
'web_push_subscriptions','organization_api_keys','automations',
'task_reminders','task_audit_log','conversations','global_fields',
'custom_tab_modules','external_services','notification_subscriptions'
];
BEGIN
FOREACH t IN ARRAY tbls LOOP
IF EXISTS (SELECT FROM pg_tables WHERE schemaname='public' AND tablename=t) THEN
EXECUTE format('DROP POLICY IF EXISTS tenant_iso ON %I', t);
EXECUTE format(
'CREATE POLICY tenant_iso ON %I USING ('
' current_setting(''app.is_superadmin'', true) = ''true'''
' OR organization_id = NULLIF(current_setting(''app.current_org_id'', true), '''')::int'
')', t);
END IF;
END LOOP;
END $$;
-- Join-based: form_fields, form_statuses, form_tabs, status_transitions
-- (no direct organization_id — join through forms)
DO $$
DECLARE t text;
DECLARE tbls text[] := ARRAY['form_fields','form_statuses','form_tabs','status_transitions'];
BEGIN
FOREACH t IN ARRAY tbls LOOP
IF EXISTS (SELECT FROM pg_tables WHERE schemaname='public' AND tablename=t) THEN
EXECUTE format('DROP POLICY IF EXISTS tenant_iso ON %I', t);
EXECUTE format(
'CREATE POLICY tenant_iso ON %I USING ('
' current_setting(''app.is_superadmin'', true) = ''true'''
' OR form_id IN ('
' SELECT id FROM forms'
' WHERE organization_id = NULLIF(current_setting(''app.current_org_id'', true), '''')::int'
' )'
')', t);
END IF;
END LOOP;
END $$;
-- Join-based: task_messages, task_field_values, field_history
-- (no direct organization_id — join through tasks, which is now NOT NULL)
DO $$
DECLARE t text;
DECLARE tbls text[] := ARRAY['task_messages','task_field_values','field_history'];
BEGIN
FOREACH t IN ARRAY tbls LOOP
IF EXISTS (SELECT FROM pg_tables WHERE schemaname='public' AND tablename=t) THEN
EXECUTE format('DROP POLICY IF EXISTS tenant_iso ON %I', t);
EXECUTE format(
'CREATE POLICY tenant_iso ON %I USING ('
' current_setting(''app.is_superadmin'', true) = ''true'''
' OR task_id IN ('
' SELECT id FROM tasks'
' WHERE organization_id = NULLIF(current_setting(''app.current_org_id'', true), '''')::int'
' )'
')', t);
END IF;
END LOOP;
END $$;
-- NOTE: ENABLE ROW LEVEL SECURITY is NOT issued here.
-- It is applied at runtime by server/index.ts when ENABLE_RLS=true.
-- This keeps the migration safe on environments not yet using withTenant.