-- Allow n8n to insert order_groups with the anon key, but only with a private -- integration secret passed in the x-n8n-secret HTTP header. -- -- n8n REST headers: -- apikey: -- Authorization: Bearer -- x-n8n-secret: -- Content-Type: application/json -- Prefer: resolution=merge-duplicates,return=representation -- -- Endpoint: -- POST https://supa.supersamsev.ru/rest/v1/order_groups -- -- Important: -- - Keep this secret only in n8n credentials/environment. -- - Do not put it in the frontend. -- - Replace CHANGE_ME_LONG_RANDOM_SECRET before running this SQL. create extension if not exists pgcrypto; create table if not exists public.integration_api_secrets ( name text primary key, secret_hash text not null, created_at timestamptz not null default now() ); alter table public.integration_api_secrets enable row level security; drop policy if exists "integration api secrets admin only" on public.integration_api_secrets; create policy "integration api secrets admin only" on public.integration_api_secrets for all using (public.current_role_name() = 'admin') with check (public.current_role_name() = 'admin'); insert into public.integration_api_secrets (name, secret_hash) values ( 'n8n_order_groups_insert', crypt('CHANGE_ME_LONG_RANDOM_SECRET', gen_salt('bf')) ) on conflict (name) do update set secret_hash = excluded.secret_hash; create or replace function public.is_valid_n8n_order_groups_secret() returns boolean language sql stable security definer set search_path = public as $$ select coalesce( exists ( select 1 from public.integration_api_secrets s where s.name = 'n8n_order_groups_insert' and crypt( nullif(current_setting('request.headers', true)::jsonb ->> 'x-n8n-secret', ''), s.secret_hash ) = s.secret_hash ), false ); $$; revoke all on function public.is_valid_n8n_order_groups_secret() from public; grant execute on function public.is_valid_n8n_order_groups_secret() to anon, authenticated; alter table public.order_groups enable row level security; drop policy if exists "order groups insert service roles" on public.order_groups; drop policy if exists "order groups insert coordination and integration roles" on public.order_groups; drop policy if exists "order groups insert n8n anon secret" on public.order_groups; create policy "order groups insert n8n anon secret" on public.order_groups for insert to anon with check (public.is_valid_n8n_order_groups_secret()); create policy "order groups insert coordination and integration roles" on public.order_groups for insert to authenticated with check ( public.current_role_name() in ('manager', 'logistician', 'admin', 'integration') ); -- If n8n uses upsert, update must also be allowed for the same anon secret. drop policy if exists "order groups update n8n anon secret" on public.order_groups; drop policy if exists "order groups update coordination roles" on public.order_groups; drop policy if exists "order groups update coordination and integration roles" on public.order_groups; create policy "order groups update n8n anon secret" on public.order_groups for update to anon using (public.is_valid_n8n_order_groups_secret()) with check (public.is_valid_n8n_order_groups_secret()); create policy "order groups update coordination and integration roles" on public.order_groups for update to authenticated using ( public.current_role_name() in ('manager', 'logistician', 'admin', 'integration') ) with check ( public.current_role_name() in ('manager', 'logistician', 'admin', 'integration') ); -- Diagnostics: -- select policyname, cmd, roles, qual, with_check -- from pg_policies -- where schemaname = 'public' and tablename = 'order_groups' -- order by policyname;