Bagaimana cara mendapatkan semua peran yang menjadi anggota pengguna (termasuk peran yang diwarisi)?

23

Katakanlah saya memiliki dua grup basis data Postgresql, "penulis" dan "editor", dan dua pengguna, "maxwell" dan "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

Saya ingin menulis fungsi pemain yang mengembalikan daftar peran (terutama milik mereka) yang dimiliki maxwell, kira-kira seperti ini:

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

Ini harus mengembalikan oids untuk maxwell, penulis, dan editor (tetapi tidak ernest).

Tetapi saya tidak yakin bagaimana melakukannya ketika ada warisan.

Neil McGuigan
sumber

Jawaban:

23

Anda dapat meminta katalog sistem dengan kueri rekursif , khususnya 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, INHERITadalah perilaku default CREATE ROLEdan tidak harus dijabarkan.

BTW2: dependensi melingkar tidak dimungkinkan. Postgres melarang itu. Jadi kita tidak perlu memeriksa itu.

Erwin Brandstetter
sumber
18

Versi pendek:

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

Di sini kami menggunakan versi pg_has_roleyang mengambil nama peran sebagai subjek dan peran untuk menguji keanggotaan , melewati membermode sehingga kami menguji keanggotaan yang diwariskan.

Keuntungan menggunakan pg_has_roleadalah bahwa ia menggunakan cache informasi peran internal PostgreSQL untuk memenuhi permintaan keanggotaan dengan cepat.

Anda mungkin ingin membungkus ini dalam suatu SECURITY DEFINERfungsi, karena pg_authidmemiliki akses terbatas. Sesuatu seperti:

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

Anda dapat menggunakan pg_get_userbyid(oid)untuk mendapatkan nama peran dari oid tanpa perlu kueri 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');
Craig Ringer
sumber
2
+1 bagaimanapun, karena pg_has_role()mungkin sedikit lebih cepat daripada permintaan rekursif saya, bahkan jika itu tidak masalah. Namun satu hal lagi: ia mengembalikan semua peran untuk supersusers, yang mungkin atau mungkin bukan efek samping yang diterima. Di situlah hasilnya berbeda dari kueri saya.
Erwin Brandstetter
16

Ini adalah versi sederhana dari jawaban Craig Ringer yang bisa digunakan oleh bukan pengguna super:

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

pg_rolespada dasarnya adalah pandangan tentang pg_authiddiakses oleh publik, karena tidak mengungkapkan kata sandi, bertentangan dengan pg_authid. Basis oidbahkan diekspor ke tampilan. Saat tidak membutuhkan kata sandi, tidak ada gunanya membuat fungsi khusus yang dimiliki pengguna super.

Daniel Vérité
sumber
3

Ini pendapat saya. Ini berfungsi untuk satu pengguna tertentu atau semua pengguna.

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'
Alexis. Belanda
sumber
1
Ini akan jauh lebih baik jika Anda menjelaskan bagaimana jika berbeda dari, dan diperbaiki, pada jawaban sebelumnya. Anda dapat mengedit informasi tambahan langsung ke jawabannya.
Michael Green
1

Saya yakin ini akan berhasil

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

Jika Anda lebih suka mendapatkan nama peran, gantilah yang pertama oiddengan rolname.

SureShotUK
sumber
0

jika Anda ingin mengetahui semua peran dari peran aktif Anda saat ini:

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