Working on postgres 10.4 (on RDS if it makes a difference) I am trying to enforce application user permissions using Row Level Security.
I have a permissions table which looks something like
user_group_id | entity1 | entity2 | entity3 | permission
==============|=========|=========|=========|============
1 |1 |null |null | write
1 |1 |1 |null | read
entity1(root)-entity2-entity3(leaf) is a hierarchy of items stored in different tables. entity1 contains entity2 items, entity2 items contains entity3 items.
permission are inherited meaning entity3 inherits the permissions from entity2, entity2 inherits the permissions from entity1, unless:
Rows with matching entity3 override rows with matching entity2 which override rows with matching entity1.
In the above example user group 1 has write on all entity2/entity3 under entity1=1 (1st row) except for entity2=1 and all entity3 under entity2=1 on which it has read (2nd row) etc.
I already wrote the logic (i think it's not relevant to the actual question hence not pasting it) as a single query using dense_rank - which is a window function
.
When I use this logic with an UPDATE directly - it works flawlessly.
When I embed the same exact logic as the WITH CHECK
of a row level security policy - update refuses to update the rows (I enabled ROW LEVEL SECURITY on the relevant tables - so that's not it).
I tried to embed my logic in a function instead of directly in the policy - but got the same result, meaning this works:
--not as table owner
update entity1
set col1=1
where entity1_id=10
and check_access(entity1_id); --updates 1 row
but this doesn't:
--as table owner
alter table entity1 enable row level security;
CREATE POLICY entity1_update
ON entity1
AS permissive
FOR UPDATE
TO some_role
WITH CHECK ( check_access(entity1_id) );
--not as table owner
update entity1
set col1=1
where entity1_id=10; --updates no rows
Reading the documentation:
The conditional expression cannot contain any aggregate or window functions
- is that the reason? If so - I would have expected an error to be thrown when trying to create the policy or when actually running the update - but nothing happens.
I also tried re-writing my access check logic using a subquery with ORDER BY and wrapping it with a LIMIT - did not help.
Am I missing something? Any way around this?