-
Notifications
You must be signed in to change notification settings - Fork 164
Make the api_owner
role first class
#400
Comments
Also, as it is on the docs now: CREATE ROLE api_views_owner NOINHERIT;
ALTER VIEW sample_view OWNER TO api_views_owner; The Perhaps we could even make this role the target of RLS policies that are meant to work for views. |
I agree with it in general.
But not with this. Assume In theory, there are 2 "pure" ways to do access control, imho - both of which are currently not possible with postgres:
You can do the view-level-access-control with So looking at those, the |
Yes, I think that makes a lot of sense - especially in the shared-role case, where those policies will depend on some of our GUCs. Whenever that's the case, the policies should really only affect access through PostgREST - they won't work through psql anyway. Restricting those to the view owner seems to be exactly the right thing to do. |
Yes, agree. The whole thing about RLS on views, I think the solution shouldn't be to enable SECURITY INVOKER on them. but to add the conditions(implicit WHEREs) to their rules. Not sure if it's possible in pg, but that is what most users would expect, as that wouldn't change the privilege model.
That reminds me that there's a 3rd way - only at the function level. There was a related discussion here(supabase repo), and it makes sense as a more strict model. More simple in terms of privileges, but probably more work if you have many tables you want to expose. Perhaps that stricter model could be mentioned on schema isolation. Some users might want an only RPC API(reddit discussion), and seems we're not mentioning our RPC capabilities enough(also the landing page should mention it somewhere). Edit: new request for this explanation on stackoverflow. |
Really liked the analogy of views being security definer by default on PostgREST/postgrest#2087 (reply in thread). We should add that when doing the |
We really need to be careful to word this correctly. I noticed some quirks:
So views are not really I ended up needing to give a strange mix of permissions to the base tables to both my view owner and my api users... |
Hm, lots of gotchas 😕 So updatable views are cool, queries and mutations will work, only one db object. But non-updatable views require the extra work(INSTEAD OF triggers plus the privilege gotchas) for mutations to work, so more than one db object, one function/trigger per operation IINM. Perhaps we should recommend exposing functions instead of non-updatable views? Their behavior is more consistent and the db objects required for enabling full CRUD should be the same. |
Ah, no. I don't think that should be our recommendation. I think we should find a way to make working with views easier. I just tested a bit more.. and we can use Here's an example using all three methods - and highlighting the problems with those: create role alice;
create role bob;
create role charly;
grant create on database postgres to alice, bob;
set role alice;
create schema a;
grant usage on schema a to bob;
create table a.t (c text);
alter table a.t enable row level security;
grant all privileges on table a.t to bob;
create policy current_user_only on a.t
for all to public
using (c = current_user)
with check (c = current_user);
set role bob;
create schema b;
grant usage on schema b to charly;
-- auto-updateable view
create view b.v_auto as select * from a.t;
grant all privileges on table b.v_auto to charly;
-- view with instead of insert trigger (security invoker)
create view b.v_trigger as select * from a.t;
grant all privileges on table b.v_trigger to charly;
create function b.insert_t () returns trigger
language plpgsql as $$
begin
insert into a.t values (new.*);
return new;
end
$$;
create trigger ins instead of insert on b.v_trigger
for each row execute function b.insert_t();
-- view with instead of insert trigger (security definer)
create view b.v_trigger_def as select * from a.t;
grant all privileges on table b.v_trigger_def to charly;
create function b.insert_t_def () returns trigger
security definer
language plpgsql as $$
begin
insert into a.t values (new.*);
return new;
end
$$;
create trigger ins instead of insert on b.v_trigger_def
for each row execute function b.insert_t_def();
-- view with rules
create or replace view b.v_rule as select * from a.t;
grant all privileges on table b.v_rule to charly;
create rule ins as on insert to b.v_rule
do instead insert into a.t values (new.*);
set role charly;
-- ok
insert into b.v_auto values ('charly');
-- permission denied for schema a
insert into b.v_trigger values ('charly');
-- violates row-level security
insert into b.v_trigger_def values ('charly');
-- works... but is not what we want. charly should not insert bob.
insert into b.v_trigger_def values ('bob');
-- ok
insert into b.v_rule values ('charly'); Rules are also one db object less than trigger+trigger function ;) Of course, working with Maybe we should try to make another push for PostgreSQL to allow |
In fact, this has happened in December already: https://www.postgresql.org/message-id/flat/b66dd6d6-ad3e-c6f2-8b90-47be773da240%40cybertec.at I'll try to build this patch for myself and try to test and review it to push it further. Hopefully this "nightmare" of how to use views with RLS properly is over soon. |
One thing I noticed is that we can't use
|
I remember a similar limitation for rules: |
I gave the patch a test and it works.. kind of. I gave it a review, but I am not sure whether the underlying problem of the current implementation of views might stop that patch in the end... we'll see. |
The patch has been committed in postgres/postgres@7faa5fc and will be available in PG15. I backported the patch to PG14 and built a custom docker image (alpine based) with it: Using security invoker views now... :) |
🚀 Awesome! Not too much code required on PostgreSQL to make that work as well. |
To make views work as expected for RLS, we recommend creating an
api_owner
role on:https://postgrest.org/en/stable/schema_structure.html#views
But this role is thought as a workaround, that will be avoided once views support something similar to
SECURITY INVOKER
.If views were to support that, then the INVOKER role would need privileges on the source tables of the views, i.e. the
anon
role would not only need privileges on theapi
view, but also on theprivate
table. That would be incorrect, as the api roles should only need privileges on the exposed schemas.So I think the
api_owner
role is here to stay. It should be as integral as our auth roles.The text was updated successfully, but these errors were encountered: