web-dev-qa-db-fra.com

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

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.

26
Neil McGuigan

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, oid::regrole::text AS rolename FROM cte;  -- oid & name

Le manuel sur le cast de l'identifiant d'objet de type regrole.

BTW 1: INHERIT est le comportement par défaut de CREATE ROLE et ne doit pas être précisé.

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

26
Erwin Brandstetter

Version courte:

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

Ici, nous utilisons ne version de pg_has_role Qui prend un nom de rôle comme sujet et oid de rôle pour tester l'appartenance , en passant le mode member pour tester les appartenances héritées.

L'avantage d'utiliser pg_has_role Est 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 fonction SECURITY DEFINER, Car pg_authid A un accès 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');
19
Craig Ringer

Il s'agit d'une version simplifiée de 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_roles est essentiellement une vue sur pg_authid accessible au public, car il ne révèle pas les mots de passe, contrairement à pg_authid. La base oid est 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.

16
Daniel Vérité

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'
4
Alexis.Rolland

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 oid par rolname.

1
SureShotUK

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;
0
Mihail Gershkovich