Insert causes select policy violation due to select occuring before all insert triggers are finished
See original GitHub issueBug report
Description of issue
Supabase (Or PostgREST) is performing a select operation during row insertion, which throws a select policy violation. However the conditions of the select policy should be valid if the select operation waited until all triggers on the insert were completed. Here is a more in depth explanation of what my setup is:
The setup
I have tables page
and user
where a user
has access to a page
via a join table page_user
.
create table page (
id uuid primary key
);
create table user (
id uuid primary key
);
create table page_user (
page_id uuid references page,
user_id uuid references user,
primary key (page_id, user_id)
);
I have an RLS policy that restrict select on a page
to any user
with a matching page_user
entry.
create policy "page select" on page
for select
using (
exists (
select 1 from page_user where user_id = auth.uid() and page_id = id
)
);
create policy "page insert" on page
for insert
with check (
true
);
I have a trigger on page after insert
that automatically inserts a page_user
entry for the page
and user
that is creating the page.
create function page_after_insert()
returns trigger as
$$
begin
insert into page_user (page_id, user_id) values (NEW.id, auth.uid());
return NEW;
end;
$$ language plpgsql security definer;
create trigger page_after_insert after insert on page
for each row execute procedure page_after_insert();
The problem
When I use Supabase (via PostgREST) to insert a new page I get a violation on the “page select” policy. This is because PostgREST uses a select
to send the new page
id to the client after a successful insert, which I want. However it seems like this select
query is being run before the “page_after_insert” trigger is run so the page_user
entry doesn’t exist yet. I can fix this by running the trigger before insert with a deferred reference constraint on the page_id
column, but this seems like a hack. Ideally the full insert with triggers and transactions would be fulfilled before postgrest performs the select
.
Does this make sense? Is this an expected behavior? I always thought that triggers are performed as part of a transaction so I could expect the page_user
insert to happen before a subsequent select.
Hope someone can look into this. Thank you for taking a look when you get a chance!
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:10 (6 by maintainers)
@steve-chavez So just to clarify for my understanding. There is no actual select statement. The returning is part of the insert, which has to meet select policy during the insert operation. An after trigger occurs after both the insert of data to the db and the returning part of the operation.
Have you tried using
returning=minimal
?Here’s a sample generated query. Using the below:
This is generated:
When using
returning=minimal
, the last clause changes toRETURNING 1
.