web-dev-qa-db-fra.com

postgresql - afficher les privilèges du schéma

Existe-t-il une requête que je peux exécuter pour afficher les privilèges actuellement attribués sur un schéma particulier?

c'est-à-dire des privilèges qui ont été attribués comme suit:

GRANT USAGE ON SCHEMA dbo TO MyUser

J'ai essayé

SELECT *
FROM information_schema.usage_privileges;

mais cela ne renvoie que des subventions au rôle PUBLIC intégré. Au lieu de cela, je veux voir quels utilisateurs ont obtenu des privilèges sur les différents schémas.

Remarque: j'utilise en fait Amazon Redshift plutôt que PostgreSQL pur, bien que j'accepterai une réponse PostgreSQL pure si ce n'est pas possible dans Amazon Redshift. (Bien que je pense que oui)

27
mwrichardson

Les privilèges sont stockés dans le champ nspacl de pg_namespace. Comme il s'agit d'un champ de tableau, vous devez effectuer un petit codage sophistiqué pour l'analyser. Cette requête vous donnera les instructions de subvention utilisées pour les utilisateurs et les groupes:

select 
'grant ' || substring(
          case when charindex('U',split_part(split_part(array_to_string(nspacl, '|'),pu.usename,2 ) ,'/',1)) > 0 then ',usage ' else '' end 
          ||case when charindex('C',split_part(split_part(array_to_string(nspacl, '|'),pu.usename,2 ) ,'/',1)) > 0 then ',create ' else '' end 
       , 2,10000)
|| ' on schema '||nspname||' to "'||pu.usename||'";' 
from pg_namespace pn,pg_user pu
 where  array_to_string(nspacl,',') like '%'||pu.usename||'%' --and pu.usename='<username>' 
and nspowner > 1 
union
select 
'grant ' || substring(
          case when charindex('U',split_part(split_part(array_to_string(nspacl, '|'),pg.groname,2 ) ,'/',1)) > 0 then ',usage ' else '' end 
          ||case when charindex('C',split_part(split_part(array_to_string(nspacl, '|'),pg.groname,2 ) ,'/',1)) > 0 then ',create ' else '' end 
       , 2,10000)
|| ' on schema '||nspname||' to group "'||pg.groname||'";' 
from pg_namespace pn,pg_group pg
 where array_to_string(nspacl,',') like '%'||pg.groname||'%' --and pg.groname='<username>' 
 and nspowner > 1 
14
mike_pdb

dans la console, utilisez psql:

\dn+

vous montrera

     Name      |  Owner   |   Access privileges   |      Description   
34
senz

Liste tous les schémas avec leurs privilèges pour l'utilisateur actuel:

WITH "names"("name") AS (
  SELECT n.nspname AS "name"
    FROM pg_catalog.pg_namespace n
      WHERE n.nspname !~ '^pg_'
        AND n.nspname <> 'information_schema'
) SELECT "name",
  pg_catalog.has_schema_privilege(current_user, "name", 'CREATE') AS "create",
  pg_catalog.has_schema_privilege(current_user, "name", 'USAGE') AS "usage"
    FROM "names";

La réponse sera par exemple:

  name   | create | usage 
---------+--------+-------
 public  | t      | t
 test    | t      | t
 awesome | f      | f
(3 rows)

Dans cet exemple, l'utilisateur actuel n'est pas propriétaire du schéma awesome.

Comme vous pouvez le deviner, demande similaire pour un schéma particulier:

SELECT
  pg_catalog.has_schema_privilege(
    current_user, 'awesome', 'CREATE') AS "create",
  pg_catalog.has_schema_privilege(
    current_user, 'awesome', 'USAGE') AS "usage";

et réponse:

 create | usage 
--------+-------
 f      | f

Comme vous le savez, il est possible d'utiliser pg_catalog.current_schema() pour le schéma actuel.

De tous les privilèges possibles

-- SELECT
-- INSERT
-- UPDATE
-- DELETE
-- TRUNCATE
-- REFERENCES
-- TRIGGER
-- CREATE
-- CONNECT
-- TEMP
-- EXECUTE
-- USAGE

les seuls CREATE et USAGE autorisés pour les schémas.

Comme la current_schema() le current_user Peut être remplacé par un rôle particulier.


Bonus [~ # ~] [~ # ~] avec colonne current

WITH "names"("name") AS (
  SELECT n.nspname AS "name"
    FROM pg_catalog.pg_namespace n
      WHERE n.nspname !~ '^pg_'
        AND n.nspname <> 'information_schema'
) SELECT "name",
  pg_catalog.has_schema_privilege(current_user, "name", 'CREATE') AS "create",
  pg_catalog.has_schema_privilege(current_user, "name", 'USAGE')  AS "usage",
  "name" = pg_catalog.current_schema() AS "current"
    FROM "names";

--   name   | create | usage | current
-- ---------+--------+-------+---------
--  public  | t      | t     | t
--  test    | t      | t     | f
--  awesome | f      | f     | f
-- (3 rows)

AVEC | Fonctions d'informations système | GRANT (privilèges)

13
Ivan Black

Essayez celui-ci (fonctionne pour le rôle PUBLIC):

SELECT nspname,
       coalesce(nullif(role.name,''), 'PUBLIC') AS name,
       substring(
          CASE WHEN position('U' in split_part(split_part((','||array_to_string(nspacl,',')), ','||role.name||'=',2 ) ,'/',1)) > 0 THEN ', USAGE' ELSE '' END 
          || CASE WHEN position('C' in split_part(split_part((','||array_to_string(nspacl,',')), ','||role.name||'=',2 ) ,'/',1)) > 0 THEN ', CREATE' ELSE '' END 
       , 3,10000) AS privileges
FROM pg_namespace pn, (SELECT pg_roles.rolname AS name
   FROM pg_roles UNION ALL SELECT '' AS name) AS role
 WHERE (','||array_to_string(nspacl,',')) LIKE '%,'||role.name||'=%'
 AND nspowner > 1;
6
Andrei Petrovici

Version combinée (groupes, utilisateurs, PUBLIC) qui fonctionne pour AWS Redshift:

    SELECT *
FROM (SELECT CASE
               WHEN charindex ('U',SPLIT_PART(SPLIT_PART(ARRAY_TO_STRING(nspacl,'|'),pu.usename,2),'/',1)) > 0 THEN ' USAGE'
               ELSE ''
             END ||case WHEN charindex('C',SPLIT_PART(SPLIT_PART(ARRAY_TO_STRING(nspacl,'|'),pu.usename,2),'/',1)) > 0 THEN ' CREATE' ELSE '' END AS rights,
             nspname AS schema,
             '' AS role,
             pu.usename AS user
      FROM pg_namespace pn,
           pg_user pu
      WHERE ARRAY_TO_STRING(nspacl,',') LIKE '%' ||pu.usename|| '%'
      --and pu.usename='<username>' 
      AND   nspowner > 1

  UNION

      SELECT CASE
               WHEN charindex ('U',SPLIT_PART(SPLIT_PART(ARRAY_TO_STRING(nspacl,'|'),pg.groname,2),'/',1)) > 0 THEN ' USAGE '
               ELSE ''
             END ||case WHEN charindex('C',SPLIT_PART(SPLIT_PART(ARRAY_TO_STRING(nspacl,'|'),pg.groname,2),'/',1)) > 0 THEN ' CREATE' ELSE '' END as rights,
             nspname AS schema,
             pg.groname AS role,
             '' AS user
      FROM pg_namespace pn,
           pg_group pg
      WHERE ARRAY_TO_STRING(nspacl,',') LIKE '%' ||pg.groname|| '%'
      --and pg.groname='<username>' 
      AND   nspowner > 1

  UNION

      SELECT CASE
               WHEN POSITION('U' IN SPLIT_PART(SPLIT_PART((',' ||array_to_string (nspacl,',')),',' ||roles.name|| '=',2),'/',1)) > 0 THEN ' USAGE'
               ELSE ''
             END 
      || CASE
               WHEN POSITION('C' IN SPLIT_PART(SPLIT_PART((',' ||array_to_string (nspacl,',')),',' ||roles.name|| '=',2),'/',1)) > 0 THEN ' CREATE'
               ELSE ''
             END AS rights,
             nspname AS schema,
             COALESCE(NULLIF(roles.name,''),'PUBLIC') AS role,
             '' AS user
      FROM pg_namespace pn,
           (SELECT pg_group.groname AS name
            FROM pg_group
            UNION ALL
            SELECT '' AS name) AS roles
      WHERE (',' ||array_to_string (nspacl,',')) LIKE '%,' ||roles.name|| '=%'
      AND   nspowner > 1) privs

ORDER BY schema,rights
5
xvga

C'est ce que psql utilise en interne :)

SELECT n.nspname AS "Name",
  pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner",
  pg_catalog.array_to_string(n.nspacl, E'\n') AS "Access privileges",
  pg_catalog.obj_description(n.oid, 'pg_namespace') AS "Description"
FROM pg_catalog.pg_namespace n
WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
ORDER BY 1;
5
Dennis

Je sais que ce message est ancien mais j'ai fait une autre requête basée sur les différentes réponses pour en avoir une qui soit courte et facile à utiliser par la suite:

select
    nspname as schema_name
    , r.rolname as role_name
    , pg_catalog.has_schema_privilege(r.rolname, nspname, 'CREATE') as create_grant
    , pg_catalog.has_schema_privilege(r.rolname, nspname, 'USAGE') as usage_grant
from pg_namespace pn,pg_catalog.pg_roles r
where array_to_string(nspacl,',') like '%'||r.rolname||'%' 
    and nspowner > 1 

Je continue de penser qu'un jour je ferai une requête pour avoir tous les droits dans une seule vue ... Un jour. ;)

0
Jaisus

Pour la question actuelle, essayez celle-ci:

SELECT r.rolname AS role_name,
       n.nspname AS schema_name,
       p.perm AS privilege
FROM pg_catalog.pg_namespace AS n
    CROSS JOIN pg_catalog.pg_roles AS r
    CROSS JOIN (VALUES ('USAGE'), ('CREATE')) AS p(perm)
WHERE has_schema_privilege(r.oid, n.oid, p.perm)
--      AND n.nspname <> 'information_schema'
--      AND n.nspname !~~ 'pg\_%'
--      AND NOT r.rolsuper

Pourrait être assez faible dans les performances de la base de données avec beaucoup d'objets et d'utilisateurs avec lesquels je suis tombé. J'ai donc une solution de contournement possible en utilisant aclexplode() fonction par défaut comme ceci:

SELECT  oid_to_rolname(a.grantee) AS role_name,
        n.nspname AS schema_name,
        a.privilege_type AS privilege_type
FROM pg_catalog.pg_namespace AS n,
        aclexplode(nspacl) a
WHERE n.nspacl IS NOT NULL 
        AND oid_to_rolname(a.grantee) IS NOT NULL 
--      AND n.nspname <> 'information_schema'
--      AND n.nspname !~~ 'pg\_%'

Mais, soyez prudent , le dernier n'inclut pas les privilèges que les utilisateurs ont obtenus du rôle PUBLIC. Où oid_to_rolname() est une simple fonction personnalisée SELECT rolname FROM pg_roles WHERE oid = $1.

Et, comme @ Jaisus , ma tâche exigeait d'avoir tous les privilèges dont disposent tous les utilisateurs. J'ai donc des requêtes de privilèges similaires à schema pour table, views, columns, sequences, functions, database et même default privilèges.

En outre, il existe une extension utile pg_permission où je reçois la logique pour les requêtes fournies et que je viens de mettre à niveau pour mes besoins.

0