0
votes

Below are 3 tables script.

CREATE TABLE rls_permission(upn text,is_all boolean ,reference int[]);
CREATE TABLE objects(key serial primary key,status text );
CREATE TABLE object_attributes(key serial primary key,status text ,values text,reference int[], type_key int);
CREATE INDEX object_attributes_reference on object_attributes USING gin(value_reference gin__int_ops)

Web application will retrieve certain objects first and then its respective object attribute values. An object may have many attribute values in object_attributes table.

User permission settings configured in table rls_permission, if column is_all value is true then user can see all rows/objects , otherwise as references mentioned in column reference. (references values populated into rls_permission by an other interface, which has full access and gets values from object_attributes)

I have created below row level policy on objects table.


CREATE POLICY no_rls_objects ON objects AS PERMISSIVE FOR ALL TO PUBLIC USING (TRUE);

CREATE POLICY rls_on_objects ON objects AS RESTRICTIVE TO web_app_user
   USING(
         (SELECT per.is_all FROM rls_permission per
                WHERE (lower(per.upn) = lower(current_setting('db.rls_user'::text)))
          ) 
         OR (EXISTS ( SELECT 1 FROM object_attributes att
                               JOIN rls_permission per ON ((per.reference && att.reference)))
                                WHERE ((lower(per.upn) = lower(current_setting('db.rls_user'::text))) 
                                 AND (att.objects_key = objects.key) 
                      )
             ) 
        ) 

rls_on_objects RESTRICTIVE Policy has two SELECT quires separated by OR.

I can't create two RESTRICTIVE policies, with one query in each policy because having two RESTRICTIVE policies would be combined using AND. But I need combine two queries using OR.

Is there way to rewrite the query and make singe query ?

because both queries has (lower(per.upn) = lower(current_setting('db.rls_user'::text))), it has compute in both, when is_all is false or null then it check/execute 2nd query. Making into single query would improve the RLS performance as it no need to compute twice.

Thanks

2

2 Answers

0
votes

I think you are concerned about the wrong problem.

The repeated WHERE clause will not be a major performance problem. The OR and the subquery with the join that will have to executed for every table row will be a much bigger problem. Not that I have an idea how to improve that, except using a simpler permission system.

The permissive policy seems to be quite redundant, and you should get rid of it.

0
votes

Try something like this :

SELECT per.is_all
    OR EXISTS ( SELECT 1
                  FROM object_attributes att
                 WHERE att.reference && per.reference
                   AND att.objects_key = objects.key
              )
  FROM rls_permission per
 WHERE lower(per.upn) = lower(current_setting('db.rls_user'::text))