Allow makeaclitem() to accept multiple privileges - Mailing list pgsql-hackers
From | Tharakan, Robins |
---|---|
Subject | Allow makeaclitem() to accept multiple privileges |
Date | |
Msg-id | e5a05dc54ba64408b3dd260171c1abaf@EX13D05UWC001.ant.amazon.com Whole thread Raw |
Responses |
Re: Allow makeaclitem() to accept multiple privileges
|
List | pgsql-hackers |
Hi, Presently, makeaclitem() allows only a single privilege in a single call. This patch allows it to additionally accept multiple comma-separated privileges. The attached patch reuses the has_foo_privileges() infrastructure and besides a minor change to the function documentation, it also adds 3 regression tests that increase the function code-coverage to 100%. Sample usage: postgres=# SELECT makeaclitem('postgres'::regrole, 'usr1'::regrole, 'SELECT, INSERT, UPDATE, ALTER SYSTEM', FALSE); makeaclitem -------------------- postgres=arwA/usr1 (1 row) The need for this patch came up during a recent customer experience, where 'pg_init_privs.initprivs' had grantees pointing to non-existent roles. This is easy to reproduce [5] and given that this issue was blocking the customer's planned upgrade, the temporary solution was to UPDATE the initprivs column. From what I could see, there was a fix for similar issues [1], although that didn't fix this specific issue [2] and thus manually modifying initprivs was required. For this manual update though, if the proposed feature was available, it would have helped with the UPDATE SQLs. To elaborate the customer issue, in most rows aclitems[]::TEXT was 2000+ characters long and spanned 30+ missing roles and multiple databases. In trying to automate the generation of UPDATE SQLs, I tried to use aclexplode() to filter-out the missing grantee roles, however re-stitching the remaining items back into an aclitems[] array was non-trivial, since makeaclitem() doesn't yet accept multiple privileges in a single call. In particular, the unnest() + string-search approach mentioned in this thread [4] didn't scale with many missing roles where rolenames were alphanumeric. See [6] for a contrived example where the updated makeaclitem() can be used to regenerate the initprivs column, weeding out privileges related to missing grantees. Lastly, while researching, I saw a thread [3] questioning whether makeaclitem() is useful, and think that if it were to allow multiple privileges, it could have helped in the above situation, and thus I'd -1 on dropping the function. Reference: 1) https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=47088c599 cc6d6473c7b89ac029060525cf086d8 2) https://www.postgresql.org/message-id/flat/29913.1573835973%40sss.pgh.pa.us# 90b0192c126ea61266e31dbb864c9b08 3) https://www.postgresql.org/message-id/flat/48f9156d-3937-cf47-13ee-ac4e90c83 c43%40postgresfriends.org#7f5c830819bc104c222b440689d2028f 4) https://www.postgresql.org/message-id/flat/1573808483712.96817%40Optiver.com 5) Reproduction to get pg_init_privs.initprivs to point to missing roles. psql -c "CREATE DATABASE w" postgres export PGDATABASE=w; psql -c "CREATE USER usr1 PASSWORD 'usr1' SUPERUSER;" psql -U usr1 -c "CREATE EXTENSION pg_stat_statements" -c "SELECT * FROM pg_init_privs WHERE privtype = 'e'" -c "REASSIGN OWNED BY usr1 TO postgres;" psql -c "DROP USER usr1" -c "SELECT * FROM pg_init_privs WHERE privtype = 'e';" . . . This would end up with something like this: r=# select * from pg_init_privs where privtype = 'e'; objoid | classoid | objsubid | privtype | initprivs --------+----------+----------+----------+---------------------------------- ------------------------------------- 16433 | 1255 | 0 | e | {16425=X/16425} 16441 | 1259 | 0 | e | {16425=arwdDxt/16425,=r/16425,t=r/postgres} 16452 | 1259 | 0 | e | {16425=arwdDxt/16425,=r/16425,uw22341=arwdDxt/postgres,t=rw/postgres} (3 rows) 6) This feature can then be used to generate the UPDATE SQLs to fix the issue: r=# BEGIN; BEGIN r=*# WITH a AS (SELECT pg_init_privs.* ,(aclexplode(initprivs)).* FROM pg_init_privs WHERE privtype = 'e') r-*# SELECT DISTINCT 'UPDATE pg_init_privs SET initprivs = ''{' || ( r(*# WITH x AS ( r(*# SELECT (aclexplode(initprivs)).* r(*# FROM pg_init_privs r(*# WHERE objoid = a.objoid AND classoid = a.classoid AND objsubid = a.objsubid r(*# ) r(*# ,y AS ( r(*# SELECT makeaclitem(grantee, grantor, string_agg(privilege_type, ','), is_grantable) p r(*# FROM x r(*# WHERE grantee IN (SELECT oid FROM pg_roles) r(*# GROUP BY grantee,grantor,is_grantable r(*# ) r(*# SELECT string_agg(p::TEXT, ',') FROM y r(*# ) || '}'' WHERE objoid=' || a.objoid || ' AND classoid=' || a.classoid || ' AND objsubid=' || a.objsubid || ';' r-*# FROM a; ?column? ---------------------------------------------------------------------------- ---------------------------------------------------------- UPDATE pg_init_privs SET initprivs = '{t=r/postgres}' WHERE objoid=16441 AND classoid=1259 AND objsubid=0; UPDATE pg_init_privs SET initprivs = '{uw22341=arwdDxt/postgres,t=rw/postgres}' WHERE objoid=16452 AND classoid=1259 AND objsubid=0; (3 rows) r=*# \gexec UPDATE 1 UPDATE 1 r=*# select * from pg_init_privs where privtype = 'e'; objoid | classoid | objsubid | privtype | initprivs --------+----------+----------+----------+---------------------------------- -------- 16433 | 1255 | 0 | e | {16425=X/16425} 16441 | 1259 | 0 | e | {t=r/postgres} 16452 | 1259 | 0 | e | {uw22341=arwdDxt/postgres,t=rw/postgres} (3 rows) (Similarly, it should be possible to generate DELETE SQLs for rows with *all* ACL objects pointing to missing roles - for e.g. row 1 above). - Robins Tharakan Amazon Web Services
Attachment
pgsql-hackers by date: