PostgreSQL - 获取指定角色对每个对象类型的有效权限
PostgreSQL - get effective permissions for specified roles on each object type
我正在尝试编写一个查询(给定角色列表和数据库列表),列出类型为 database
、schema
和 [=14 的对象的有效权限=](开始)
我一直在尝试使用 has_XXX_privilege()
函数,但输出感觉很尴尬...
给定 3 个角色,例如,(app_rwc
、app_rw
、app_r
) 和一个数据库 test_db
我想得到这样的输出
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
更新 #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
你的查询很好,你只需要添加一些聚合。这是一个开始:
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
我正在尝试编写一个查询(给定角色列表和数据库列表),列出类型为 database
、schema
和 [=14 的对象的有效权限=](开始)
我一直在尝试使用 has_XXX_privilege()
函数,但输出感觉很尴尬...
给定 3 个角色,例如,(app_rwc
、app_rw
、app_r
) 和一个数据库 test_db
我想得到这样的输出
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
更新 #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
你的查询很好,你只需要添加一些聚合。这是一个开始:
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