fix: permissoes + RLS pro admin conseguir escrever nas tabelas de catalogo
Antes: 'permission denied for table marcas' ao tentar criar/editar qualquer entidade. Authenticated so tinha SELECT. Agora: - Grants de INSERT/UPDATE/DELETE pra authenticated nas 9 tabelas - RLS enabled em todas - Helper function is_tenant_member(bigint) via security definer - Policies members_write_* permitem escrita apenas se user estiver no tenant_members do tenant_id da row - public_read_* mantem SELECT livre pro anon da pagina publica - Reservas/reserva_extras continuam service_role-only (backend Chatwoot) Aplicada via MCP. Testada com curl + JWT do admin (INSERT marcas = 201).
This commit is contained in:
parent
25abaa807c
commit
09c7903a9c
137
supabase/migrations/20260414000005_admin_write_policies.sql
Normal file
137
supabase/migrations/20260414000005_admin_write_policies.sql
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
-- Admin write permissions + tenant-scoped RLS
|
||||||
|
-- Já aplicada via MCP.
|
||||||
|
--
|
||||||
|
-- Antes desta migration, o authenticated (admin logado) só tinha SELECT nas
|
||||||
|
-- tabelas de catalogo. Qualquer CREATE/UPDATE/DELETE via Supabase JS retornava
|
||||||
|
-- "permission denied for table X". Agora:
|
||||||
|
-- 1. authenticated ganha INSERT/UPDATE/DELETE nas tabelas de catalogo
|
||||||
|
-- 2. RLS + policy filtra por tenant_members: so escreve se o user for
|
||||||
|
-- membro do tenant dono da row
|
||||||
|
-- 3. Reservas e reserva_extras continuam service_role-only (escritas pelo
|
||||||
|
-- backend Chatwoot controller)
|
||||||
|
|
||||||
|
-- Helper: verifica se o usuario logado eh membro de um tenant
|
||||||
|
create or replace function reserva_hotel.is_tenant_member(check_tenant_id bigint)
|
||||||
|
returns boolean
|
||||||
|
language sql
|
||||||
|
security definer
|
||||||
|
stable
|
||||||
|
as $$
|
||||||
|
select exists (
|
||||||
|
select 1 from reserva_hotel.tenant_members tm
|
||||||
|
where tm.user_id = auth.uid()
|
||||||
|
and tm.tenant_id = check_tenant_id
|
||||||
|
);
|
||||||
|
$$;
|
||||||
|
|
||||||
|
grant execute on function reserva_hotel.is_tenant_member(bigint) to authenticated, anon;
|
||||||
|
|
||||||
|
-- Pattern aplicado em cada tabela de catalogo:
|
||||||
|
-- alter table <t> enable row level security
|
||||||
|
-- grant insert/update/delete to authenticated
|
||||||
|
-- policy public_read: select true
|
||||||
|
-- policy members_write: for all to authenticated using/with check is_tenant_member(tenant_id)
|
||||||
|
|
||||||
|
-- MARCAS
|
||||||
|
alter table reserva_hotel.marcas enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.marcas to authenticated;
|
||||||
|
drop policy if exists "public_read_marcas" on reserva_hotel.marcas;
|
||||||
|
create policy "public_read_marcas" on reserva_hotel.marcas for select using (true);
|
||||||
|
drop policy if exists "members_write_marcas" on reserva_hotel.marcas;
|
||||||
|
create policy "members_write_marcas" on reserva_hotel.marcas
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- UNIDADES
|
||||||
|
alter table reserva_hotel.unidades enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.unidades to authenticated;
|
||||||
|
drop policy if exists "public_read_unidades" on reserva_hotel.unidades;
|
||||||
|
create policy "public_read_unidades" on reserva_hotel.unidades for select using (true);
|
||||||
|
drop policy if exists "members_write_unidades" on reserva_hotel.unidades;
|
||||||
|
create policy "members_write_unidades" on reserva_hotel.unidades
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- SUITES
|
||||||
|
alter table reserva_hotel.suites enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.suites to authenticated;
|
||||||
|
drop policy if exists "public_read_suites" on reserva_hotel.suites;
|
||||||
|
create policy "public_read_suites" on reserva_hotel.suites for select using (true);
|
||||||
|
drop policy if exists "members_write_suites" on reserva_hotel.suites;
|
||||||
|
create policy "members_write_suites" on reserva_hotel.suites
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- SUITES_UNIDADES (junction — sem tenant_id direto, resolve via unidade)
|
||||||
|
alter table reserva_hotel.suites_unidades enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.suites_unidades to authenticated;
|
||||||
|
drop policy if exists "public_read_suites_unidades" on reserva_hotel.suites_unidades;
|
||||||
|
create policy "public_read_suites_unidades" on reserva_hotel.suites_unidades for select using (true);
|
||||||
|
drop policy if exists "members_write_suites_unidades" on reserva_hotel.suites_unidades;
|
||||||
|
create policy "members_write_suites_unidades" on reserva_hotel.suites_unidades
|
||||||
|
for all to authenticated
|
||||||
|
using (exists (
|
||||||
|
select 1 from reserva_hotel.unidades u
|
||||||
|
where u.id = suites_unidades.id_unidade
|
||||||
|
and reserva_hotel.is_tenant_member(u.tenant_id)
|
||||||
|
))
|
||||||
|
with check (exists (
|
||||||
|
select 1 from reserva_hotel.unidades u
|
||||||
|
where u.id = suites_unidades.id_unidade
|
||||||
|
and reserva_hotel.is_tenant_member(u.tenant_id)
|
||||||
|
));
|
||||||
|
|
||||||
|
-- CONTAS_PAGAMENTO
|
||||||
|
alter table reserva_hotel.contas_pagamento enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.contas_pagamento to authenticated;
|
||||||
|
drop policy if exists "public_read_contas_pagamento" on reserva_hotel.contas_pagamento;
|
||||||
|
create policy "public_read_contas_pagamento" on reserva_hotel.contas_pagamento for select using (true);
|
||||||
|
drop policy if exists "members_write_contas_pagamento" on reserva_hotel.contas_pagamento;
|
||||||
|
create policy "members_write_contas_pagamento" on reserva_hotel.contas_pagamento
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- PRECOS
|
||||||
|
alter table reserva_hotel.precos enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.precos to authenticated;
|
||||||
|
drop policy if exists "public_read_precos" on reserva_hotel.precos;
|
||||||
|
create policy "public_read_precos" on reserva_hotel.precos for select using (true);
|
||||||
|
drop policy if exists "members_write_precos" on reserva_hotel.precos;
|
||||||
|
create policy "members_write_precos" on reserva_hotel.precos
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- FOTOS_CATEGORIA
|
||||||
|
alter table reserva_hotel.fotos_categoria enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.fotos_categoria to authenticated;
|
||||||
|
drop policy if exists "public_read_fotos" on reserva_hotel.fotos_categoria;
|
||||||
|
create policy "public_read_fotos" on reserva_hotel.fotos_categoria for select using (true);
|
||||||
|
drop policy if exists "members_write_fotos" on reserva_hotel.fotos_categoria;
|
||||||
|
create policy "members_write_fotos" on reserva_hotel.fotos_categoria
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- EXTRAS
|
||||||
|
alter table reserva_hotel.extras enable row level security;
|
||||||
|
grant insert, update, delete on reserva_hotel.extras to authenticated;
|
||||||
|
drop policy if exists "public_read_extras" on reserva_hotel.extras;
|
||||||
|
create policy "public_read_extras" on reserva_hotel.extras for select using (true);
|
||||||
|
drop policy if exists "members_write_extras" on reserva_hotel.extras;
|
||||||
|
create policy "members_write_extras" on reserva_hotel.extras
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
|
|
||||||
|
-- APP_CONFIG (public SELECT ja existia via migration anterior)
|
||||||
|
grant insert, update, delete on reserva_hotel.app_config to authenticated;
|
||||||
|
drop policy if exists "members_write_app_config" on reserva_hotel.app_config;
|
||||||
|
create policy "members_write_app_config" on reserva_hotel.app_config
|
||||||
|
for all to authenticated
|
||||||
|
using (reserva_hotel.is_tenant_member(tenant_id))
|
||||||
|
with check (reserva_hotel.is_tenant_member(tenant_id));
|
||||||
Loading…
Reference in New Issue
Block a user