Як отримати всі ролі, членами яких є користувач (включаючи спадкові ролі)?


23

Скажімо, у мене є дві групи баз даних Postgresql, "автори" та "редактори", і два користувачі, "maxwell" та "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

Я хотів би написати функцію виконавця, яка повертає список ролей (бажано, їх оїдів), до яких належить maxwell, приблизно так:

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

Він повинен повернути вислови для максвелл, авторів та редакторів (але не помилково).

Але я не впевнений, як це зробити, коли є спадщина.

Відповіді:


23

Ви можете запитувати системний каталог за допомогою рекурсивного запиту , зокрема 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 INHERIT- це поведінка за замовчуванням, CREATE ROLEі її не потрібно прописати.

BTW2: кругові залежності не можливі. Postgres забороняє це. Тож нам не потрібно цього перевіряти.


18

Коротка версія:

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

Тут ми використовуємо версію, pg_has_roleяка займає ім'я ролі як тему та роль ролі для перевірки на членство , проходження memberрежиму, тому ми перевіряємо наявність успадкованих членств.

Перевага використання pg_has_roleполягає в тому, що він використовує внутрішні кеші рольової інформації PostgreSQL для швидкого задоволення запитів про членство.

Ви можете зафіксувати це у SECURITY DEFINERфункції, оскільки pg_authidце обмежений доступ. Щось на зразок:

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

Ви можете використовувати pg_get_userbyid(oid)для отримання імені ролі від oid без необхідності запиту 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 у будь-якому випадку, оскільки pg_has_role()це, мабуть, трохи швидше мого рекурсивного запиту, навіть якщо це навряд чи має значення. І останнє, що все-таки: воно повертає всі ролі для суперкористувачів, що може бути, а може і не бути бажаним побічним ефектом. Ось де результат відрізняється від мого запиту.
Erwin Brandstetter

16

Це спрощена версія відповіді Крейга Рінгера, що не суперпользователь може використовувати безпосередньо:

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

pg_rolesпо суті, це погляд на pg_authidзагальнодоступний, оскільки він не виявляє паролі, всупереч pg_authid. База oidнавіть експортується у вигляд. Якщо вам не потрібні паролі, немає сенсу створювати виділену функцію, що належить суперпользователю.


3

Ось мій погляд на це. Він працює для одного конкретного користувача або всіх користувачів.

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
Це було б значно покращено, якби ви пояснили, як, якщо вони відрізнялися від попередніх відповідей та вдосконалювали їх. Ви можете редагувати додаткову інформацію безпосередньо у відповідь.
Майкл Грін

1

Я вірю, що це вдасться зробити

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

Якщо ви віддаєте перевагу отримання імен ролей, замініть перше oidна rolname.


0

якщо ви хочете знати всі ролі вашої активної ролі:

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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.