2017-06-29 1 views
1

J'essaie d'écrire une requête qui (avec une liste de rôles et une liste de bases de données) liste les autorisations effectives pour les objets de type database, schema et table (pour commencer)PostgreSQL - obtenir des autorisations effectives pour les rôles spécifiés sur chaque type d'objet

J'ai essayé d'utiliser les fonctions has_XXX_privilege() mais la sortie se sent maladroit ...

3 rôles Étant donné, par exemple, (app_rwc, app_rw, app_r) et un seul db test_db I » d aime obtenir une sortie comme celle-ci

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

Jusqu'à présent, ce que je suis et ce genre de travaux, sauf qu'il est bavard ... Si quelqu'un a une meilleure approche s'il vous plaît conseiller - merci.

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 

Mise à jour # 1 - requête Voici élargi (types, fait séquences et fonctions) avec l'agrégation (Merci à @filiprem pour la pointe!) Encore assez grand, mais il fait ce que je veux qu'il faire.

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 

Répondre

0

Votre requête est bonne, il vous suffit d'ajouter une agrégation. Ceci est un début:

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 
+0

Merci pour le conseil! J'ai mis à jour ma question avec une requête étendue en utilisant vos idées (avec quelques changements pour utiliser 'string_agg' pour éviter les' NULL' dans les tableaux). – zam6ak