查询每个 table 的 return 用户权限,每个结果行一个权限

Query to return user rights for each table with one right per result row

我想编写查询,以转储每个用户的权限以防止触及 table。进行此搜索的原因是我可以快照 table 权限,然后跨服务器比较它们,或者在我 运行 一个大的 GRANTS 重置脚本之前和之后进行比较。我正在寻找易于比较的输出,所以像这样:

schema_name table_name   qualified_name  owner_name privilege   setting
api         base4        api.base4       postgres   delete      TRUE
api         bucket_test  api.bucket_test postgres   delete      TRUE

我编写了一个查询,它让我完成了其中的一部分,但权限名称和设置作为列对。我可以通过在特权之后命名它们来将列切成两半,但我使用的是上面的窄行格式。这使得我将 运行 与 table+用户+权限进行比较。

schema_name   table_name   qualified_name   owner_name   privilege   delete   privilege   insert   privilege    references  privilege setting   privilege   trigger   privilege   truncate   privilege   update
api           base4        api.base4        postgres     delete      TRUE     insert      TRUE     references   TRUE        select    TRUE      trigger     TRUE      truncate    TRUE       update      TRUE
api          bucket_test   api.bucket_test  postgres     delete      TRUE     insert      TRUE     references   TRUE        select    TRUE      trigger     TRUE      truncate    TRUE       update      TRUE

有人可以建议正确的 join 或 unnest+join 来修改我现在的查询吗?

是的,这个查询会生成很多结果行。没关系,这就是我追求的。

with 
table_list as
(   select schemaname as schema_name,
            tablename as table_name,
        quote_ident(schemaname) || '.' || quote_ident(tablename) as qualified_name,
             tableowner as owner_name

      from pg_tables

     where schemaname in ('data','api')  

  order by 3),

user_list as 
(   select usename as user_name
      from pg_user
  order by 1)

     select table_list.schema_name,
            table_list.table_name,
            table_list.qualified_name,
            table_list.owner_name,
         'delete' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'delete') as delete,
         'insert' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'insert') as insert,
         'references' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'references') as references,
         'select' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'select') as select,
         'trigger' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'trigger') as trigger,
         'truncate' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'truncate') as truncate,
         'update' as privilege, has_table_privilege(user_list.user_name, concat(table_list.qualified_name), 'update') as update

       from table_list
 cross join user_list

我在 RDS 上使用 Postgres 11.4。

跟进

对于后来发现此问题的任何人,这里是作为视图的最终查询版本:

DROP VIEW IF EXISTS data.table_grants;
CREATE OR REPLACE VIEW data.table_grants AS

with 
table_list as
(   select schemaname as schema_name,
            tablename as table_name,
             schemaname::text || '.' || tablename::text as qualified_name,
             tableowner as owner_name

      from pg_tables

     where schemaname in ('data','api')  

  order by 3),

user_list as 
(   select usename as user_name
      from pg_user
  order by 1)

select
  table_list.*,
  user_list.user_name,
  privilege,
  has_table_privilege(user_name, qualified_name, privilege) as setting
from
  table_list
  cross join user_list
  cross join (values
    ('delete'), ('insert'), ('references'), ('select'), ('trigger'), ('truncate'), ('update')
  ) as p(privilege);

ALTER TABLE data.table_grants
    OWNER TO user_change_structure;

这使得在 table 授权上的搜索变得更简单一些,就像这个可以看到在名为 item

的 table 上授予的权利
  select *
    from table_grants 
   where table_name = 'item'

order by user_name,
         privilege;

或此查询以获取特定 table 的用户权限的汇总视图:

  select qualified_name,
         owner_name,
         user_name,
         array_agg(privilege) as rights

    from table_grants 

   where table_name = 'item' and
         setting = true

group by qualified_name,
         owner_name,
         user_name;

上面的查询不一定是最有效的,视图的乘积是 tables * users * 8...但对我来说这都是即时的,不到 100 tables 和大约 15 个角色。

就像 Islingre 已经在评论中建议的那样,你可以使用

select
  table_list.*,
  privilege,
  has_table_privilege(user_name, qualified_name, privilege) as setting
from
  table_list
  cross join user_list
  cross join (values
    ('delete'), ('insert'), ('references'), ('select'), ('trigger'), ('truncate'), ('update')
  ) as p(privilege)