PostgreSQL - 获得每种对象类型上指定角色的有效权限

时间:2017-06-29 21:08:31

标签: postgresql permissions roles

我正在尝试编写一个查询(给定一个角色列表和数据库列表),列出databaseschematable类型对象的有效权限(开头)

我一直在尝试使用has_XXX_privilege()函数,但输出感觉很尴尬......

有3个角色,例如,(app_rwcapp_rwapp_r)和单个数据库test_db我希望得到这样的输出< / p>

role, obj_type, obj_name, has_permissions, missing_premissions app_rwc, DATABASE, test_db, CREATE+CONNECT+TEMPORARY", NULL app_rw, DATABASE, test_db, CONNECT+TEMPORARY, CREATE app_r, DATABASE, test_db, CONNECT+TEMPORARY, CREATE app_rwc, SCHEMA, audit, CREATE+USAGE, NULL app_rwc, SCHEMA, shared, CREATE+USAGE, NULL app_rw, SCHEMA, audit, USAGE, CREATE app_rw, SCHEMA, shared, USAGE, CREATE app_r, SCHEMA, audit, USAGE, CREATE app_r, SCHEMA, audit, USAGE, CREATE app_rwc, TABLE, audit.trail, SELECT+INSERT+UPDATE+DELETE+REFERENCES+TRIGGERS, TRUNCATE etc etc

到目前为止,这是我得到的,它有点工作,除了它是冗长的... 如果有人有更好的方法请指教 - 谢谢。

WITH 
databases AS (
  SELECT * FROM (VALUES ('app_prod')) AS t(database_name) 
),
roles AS (
  SELECT * FROM (VALUES ('app_rwc'), ('app_rw'), ('app_r')) AS t(role_name)   
),
db_permissions AS (
  SELECT * FROM (VALUES ('CREATE'), ('CONNECT'), ('TEMPORARY')) AS t(permission_name) 
),
schemas AS (
  SELECT 
    schema_name 
  FROM 
    information_schema.schemata
  WHERE 
        catalog_name IN (SELECT database_name FROM databases)
    AND schema_owner IN (SELECT role_name FROM roles)
),
schema_permissions AS (
  SELECT * FROM (VALUES ('CREATE'), ('USAGE')) AS t(permission_name)
), 
tables AS (
  SELECT table_schema, table_name
  FROM information_schema.tables
  WHERE
        table_catalog IN (SELECT database_name FROM databases)
    AND table_schema IN (SELECT schema_name FROM schemas)
    AND table_type IN ('BASE TABLE') -- , 'VIEW' 
),
table_permissions AS (
  SELECT * FROM (VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) AS t(permission_name)
)
-- ----------------------------------------------------------------------------
SELECT
    'DATABASE'                                         AS obj_type
  , databases.database_name                             AS obj_name
  , roles.role_name
  , db_permissions.permission_name
  , has_database_privilege(roles.role_name, databases.database_name, db_permissions.permission_name) AS has_permission
FROM 
  databases
  CROSS JOIN roles 
  CROSS JOIN db_permissions
-- ----------------------------------------------------------------------------
UNION ALL
-- ----------------------------------------------------------------------------
SELECT
    'SCHEMA'                                         AS obj_type
  , schemas.schema_name                              AS obj_name
  , roles.role_name
  , schema_permissions.permission_name 
  , has_schema_privilege(roles.role_name, schemas.schema_name, schema_permissions.permission_name) AS has_permission
FROM
  schemas
  CROSS JOIN roles
  CROSS JOIN schema_permissions 
-- ----------------------------------------------------------------------------
UNION ALL
-- ----------------------------------------------------------------------------
SELECT
    'TABLE'                                         AS obj_type
  , tables.table_schema || '.' || tables.table_name AS obj_name
  , roles.role_name
  , table_permissions.permission_name 
  , has_table_privilege(roles.role_name, (tables.table_schema || '.' || tables.table_name),table_permissions.permission_name) AS has_permission
FROM
  tables
  CROSS JOIN roles
  CROSS JOIN table_permissions

UPDATE#1 - 这是扩展查询(类型,序列和函数)与聚合(感谢@filiprem的提示!)仍然相当大,但它做我想要的要做。

WITH 
databases AS (
  SELECT unnest('{app_prod}'::text[]) AS dbname 
),
roles AS (
  SELECT unnest('{app_rwc,app_rw,app_r}'::text[]) AS rname
),
permissions AS (
  SELECT 'DATABASE' AS ptype, unnest('{CREATE,CONNECT,TEMPORARY}'::text[])                                  AS pname
  UNION ALL
  SELECT 'SCHEMA'   AS ptype, unnest('{CREATE,USAGE}'::text[])                                              AS pname
  UNION ALL
  SELECT 'TABLE'    AS ptype, unnest('{SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER}'::text[])   AS pname
  UNION ALL
  SELECT 'SEQUENCE' AS ptype, unnest('{USAGE,SELECT,UPDATE}'::text[])                                       AS pname
  UNION ALL
  SELECT 'TYPE'     AS ptype, unnest('{USAGE}'::text[])                                                     AS pname
  UNION ALL
  SELECT 'FUNCTION' AS ptype, unnest('{EXECUTE}'::text[])                                                   AS pname      
),
schemas AS (
  SELECT schema_name    AS sname
  FROM   information_schema.schemata
  WHERE  catalog_name IN (SELECT dbname FROM databases)                -- show schemas that exist in specified DB
    AND  schema_owner IN (SELECT rname FROM roles)                     -- show schemas that are owned by specified roles
    OR   schema_name IN ('public') -- always include these
    --OR   schema_name IN ('public', 'information_schema', 'pg_catalog')
),
tables AS (
  SELECT table_schema AS tschema, table_name AS tname
  FROM   information_schema.tables
  WHERE  table_catalog IN (SELECT dbname FROM databases)
    AND  table_schema IN (SELECT sname FROM schemas)
    AND  table_type IN ('BASE TABLE') -- , 'VIEW' 
),
sequences AS (
  SELECT schemaname AS seqschema, sequencename AS seqname
  FROM   pg_sequences
  WHERE  schemaname IN (SELECT sname FROM schemas)
),
types AS (
  SELECT nspname AS typeschema, typname AS typename, CASE typtype WHEN 'c' THEN 'composite' WHEN 'd' THEN 'domain' WHEN 'e' THEN 'enum' WHEN 'r' THEN 'range' ELSE 'other' END AS typekind 
  FROM pg_type INNER JOIN pg_namespace ON pg_type.typnamespace = pg_namespace.oid
  WHERE nspname IN (SELECT sname FROM schemas)
    AND typtype NOT IN ('b','p')                       -- exclude base and pseudo types
    AND typname NOT IN (SELECT seqname FROM sequences) -- exclude sequences
),
functions AS (
  SELECT nspname AS fnschema, proname AS fnname, pg_proc.oid AS fnoid, pg_get_function_arguments(pg_proc.oid) AS fnargs
  FROM   pg_proc INNER JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid
  WHERE  nspname IN (SELECT sname FROM schemas)
),
final AS (
SELECT
    permissions.ptype
  , databases.dbname                                                         AS obj_name
  , roles.rname
  , permissions.pname
  , has_database_privilege(roles.rname, databases.dbname, permissions.pname) AS has_permission
FROM 
  databases
  CROSS JOIN roles 
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'DATABASE'
UNION ALL -- ----------------------------------------------------------------------------------------------------------
SELECT
    permissions.ptype
  , schemas.sname                                                         AS obj_name
  , roles.rname
  , permissions.pname 
  , has_schema_privilege(roles.rname, schemas.sname, permissions.pname)   AS has_permission
FROM
  schemas
  CROSS JOIN roles
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'SCHEMA'
UNION ALL -- ----------------------------------------------------------------------------------------------------------
SELECT
    permissions.ptype
  , tables.tschema || '.' || tables.tname                                                        AS obj_name
  , roles.rname
  , permissions.pname 
  , has_table_privilege(roles.rname, (tables.tschema || '.' || tables.tname), permissions.pname) AS has_permission
FROM
  tables
  CROSS JOIN roles
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'TABLE'
UNION ALL -- ----------------------------------------------------------------------------------------------------------
SELECT
    permissions.ptype
  , sequences.seqschema || '.' || sequences.seqname                                                           AS obj_name
  , roles.rname
  , permissions.pname 
  , has_sequence_privilege(roles.rname, (sequences.seqschema || '.' || sequences.seqname), permissions.pname) AS has_permission
FROM
  sequences
  CROSS JOIN roles
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'SEQUENCE'
UNION ALL -- ----------------------------------------------------------------------------------------------------------
SELECT
    permissions.ptype || ' - ' || types.typekind
  , types.typeschema || '.' || types.typename                                                       AS obj_name
  , roles.rname
  , permissions.pname 
  , has_type_privilege(roles.rname, (types.typeschema || '.' || types.typename), permissions.pname) AS has_permission
FROM
  types
  CROSS JOIN roles
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'TYPE'
UNION ALL -- ----------------------------------------------------------------------------------------------------------
SELECT
    permissions.ptype
  , functions.fnschema || '.' || functions.fnname || '(' || fnargs || ')'   AS obj_name
  , roles.rname
  , permissions.pname 
  , has_function_privilege(roles.rname, functions.fnoid, permissions.pname) AS has_permission
FROM
  functions
  CROSS JOIN roles
  CROSS JOIN permissions
WHERE 
  permissions.ptype = 'FUNCTION'                
)
-- ====================================================================================================================
SELECT
   rname                                                                  AS role_name  
 , ptype                                                                  AS object_type
 , obj_name                                                               AS object_name
 , string_agg(DISTINCT CASE WHEN     has_permission THEN pname END, ',')  AS granted_permissions
 , string_agg(DISTINCT CASE WHEN NOT has_permission THEN pname END, ',')  AS missing_premissions 
FROM 
  final
GROUP BY 1, 2, 3
ORDER BY 1, 2, 3 

1 个答案:

答案 0 :(得分:0)

您的查询很好,您只需要添加一些聚合。这是一个开始:

select  obj_type, obj_name, role_name,
 array_agg(distinct case when has_permission then permission_name end),
 array_agg(distinct case when not has_permission then permission_name end)
from ( /* your query */ ) AS q1
group by 1,2,3
order by 1,2,3