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.
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.
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');
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.
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'
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
.
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;