Thread
Commits
-
Allow makeaclitem() to accept multiple privilege names.
- b762bbde30d2 16.0 landed
-
Allow makeaclitem() to accept multiple privileges
Tharakan, Robins <tharar@amazon.com> — 2022-05-27T07:03:52Z
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 -
Re: Allow makeaclitem() to accept multiple privileges
Tom Lane <tgl@sss.pgh.pa.us> — 2022-07-03T20:50:44Z
"Tharakan, Robins" <tharar@amazon.com> writes: > Presently, makeaclitem() allows only a single privilege in a single call. > This > patch allows it to additionally accept multiple comma-separated privileges. Seems reasonable. Pushed with minor cosmetic adjustments (mostly docs changes). regards, tom lane