Comment obtenir tous les rôles dont un utilisateur est membre (y compris les rôles hérités)?


23

Disons que j'ai deux groupes de base de données Postgresql, "auteurs" et "éditeurs", et deux utilisateurs, "maxwell" et "ernest".

create role authors;

create role editors;

create user maxwell;

create user ernest;

grant authors to editors; --editors can do what authors can do

grant editors to maxwell; --maxwell is an editor

grant authors to ernest; --ernest is an author

Je voudrais écrire une fonction performante qui retourne une liste des rôles (de préférence leur oid) auxquels maxwell appartient, quelque chose comme ceci:

create or replace function get_all_roles() returns oid[] ...

Il devrait retourner les oids pour maxwell, les auteurs et les éditeurs (mais pas ernest).

Mais je ne sais pas comment le faire en cas d'héritage.

Réponses:


23

Vous pouvez interroger le catalogue système avec une requête récursive , en particulier pg_auth_members:

WITH RECURSIVE cte AS (
   SELECT oid FROM pg_roles WHERE rolname = 'maxwell'

   UNION ALL
   SELECT m.roleid
   FROM   cte
   JOIN   pg_auth_members m ON m.member = cte.oid
)
SELECT oid FROM cte;

BTW, INHERITest le comportement par défaut de CREATE ROLEet n'a pas besoin d'être précisé.

BTW2: les dépendances circulaires ne sont pas possibles. Postgres interdit cela. Nous n'avons donc pas à vérifier cela.


18

Version courte:

SELECT a.oid 
FROM pg_authid a 
WHERE pg_has_role('maxwell', a.oid, 'member');

Ici, nous utilisons une version pg_has_rolequi prend un nom de rôle comme sujet et oid de rôle pour tester l'appartenance , en passant le membermode afin que nous testions les appartenances héritées.

L'avantage de l'utilisation pg_has_roleest qu'il utilise les caches internes des informations de rôle de PostgreSQL pour répondre rapidement aux requêtes d'appartenance.

Vous voudrez peut-être envelopper cela dans une SECURITY DEFINERfonction, car l' pg_authidaccès est restreint. Quelque chose comme:

CREATE OR REPLACE FUNCTION user_role_memberships(text)
RETURNS SETOF oid
LANGUAGE sql
SECURITY DEFINER
SET search_path = pg_catalog, pg_temp
AS $$
SELECT a.oid 
FROM pg_authid a 
WHERE pg_has_role($1, a.oid, 'member');
$$;

REVOKE EXECUTE ON FUNCTION user_role_memberships(text) FROM public;

GRANT EXECUTE ON FUNCTION user_role_memberships(text) TO ...whoever...;

Vous pouvez utiliser pg_get_userbyid(oid)pour obtenir le nom du rôle à partir de l'oid sans avoir à interroger pg_authid:

SELECT a.oid AS member_oid, pg_get_userbyid(oid) AS member_name
FROM pg_authid a 
WHERE pg_has_role('maxwell', a.oid, 'member');

2
+1 de toute façon, car il pg_has_role()est probablement un peu plus rapide que ma requête récursive, même si cela n'a guère d'importance. Une dernière chose cependant: il renvoie tous les rôles pour les super-utilisateurs, ce qui peut ou non être un effet secondaire bienvenu. C'est là que le résultat diffère de ma requête.
Erwin Brandstetter

16

Il s'agit d'une version simplifiée de la réponse de Craig Ringer qu'un non-superutilisateur peut utiliser directement:

 SELECT oid, rolname FROM pg_roles WHERE
   pg_has_role( 'maxwell', oid, 'member');

pg_rolesest essentiellement une vue sur pg_authidaccessible au public, car elle ne révèle pas les mots de passe, contrairement à pg_authid. La base oidest même exportée dans la vue. Lorsque vous n'avez pas besoin de mots de passe, il est inutile de créer la fonction dédiée appartenant au superutilisateur.


3

Voici mon point de vue à ce sujet. Cela fonctionne pour un utilisateur spécifique ou tous les utilisateurs.

select a.oid as user_role_id
, a.rolname as user_role_name
, b.roleid as other_role_id
, c.rolname as other_role_name
from pg_roles a
inner join pg_auth_members b on a.oid=b.member
inner join pg_roles c on b.roleid=c.oid 
where a.rolname = 'user_1'

1
Ce serait bien mieux si vous expliquiez en quoi si elles différaient des améliorations et les amélioraient. Vous pouvez modifier les informations supplémentaires directement dans la réponse.
Michael Green

1

Je crois que ça le fera

SELECT 
    oid 
FROM 
    pg_roles 
WHERE 
    oid IN (SELECT 
                roleid 
            FROM 
                pg_auth_members 
            WHERE 
                member=(SELECT oid FROM pg_roles WHERE rolname='maxwell'));

Si vous préférez obtenir les noms de rôle, remplacez le premier oidpar rolname.


0

si vous souhaitez connaître tous les rôles de votre rôle actuellement actif:

CREATE OR REPLACE VIEW public.my_roles
AS WITH RECURSIVE cte AS (
         SELECT pg_roles.oid,
            pg_roles.rolname
           FROM pg_roles
          WHERE pg_roles.rolname = CURRENT_USER
        UNION ALL
         SELECT m.roleid,
            pgr.rolname
           FROM cte cte_1
             JOIN pg_auth_members m ON m.member = cte_1.oid
             JOIN pg_roles pgr ON pgr.oid = m.roleid
        )
 SELECT array_agg(cte.rolname) AS my_roles
   FROM cte;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.