Bir kullanıcının üyesi olduğu tüm roller nasıl alınır (devralınan roller dahil)?


23

Diyelim ki iki Postgresql veritabanı grubum var, "yazarlar" ve "editörler" ve iki kullanıcı "maxwell" ve "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'in sahip olduğu rollerin (tercihen onların hizmetlerinin) bir listesini döndüren bir performans işlevi yazmak istiyorum.

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

Maxwell, yazarlar ve editörler için oidleri iade etmelidir (en iyisi değil).

Miras olduğunda nasıl yapılacağından emin değilim.

Yanıtlar:


23

Sistem kataloğunu özellikle özyinelemeli bir sorgu ile sorgulayabilirsinizpg_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, INHERITvarsayılan davranıştır CREATE ROLEve hecelenmesi gerekmez.

BTW2: dairesel bağımlılıklar mümkün değil. Postgres buna izin vermez. Yani bunu kontrol etmek zorunda değiliz.


18

Kısa versiyon:

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

Burada bir pg_has_roleüyelik adı kullanıyoruz, bunun adı ve üyeliğin test edilmesinde rol üstlenilen rol adı. Geçiş membermodu, bu yüzden miras kalan üyelikleri test ediyoruz.

Kullanmanın avantajı pg_has_role üyelik sorgularını hızlı bir şekilde karşılamak için PostgreSQL'in dahili rol bilgisi önbelleklerini kullanmasıdır.

Erişimi kısıtladığından SECURITY DEFINER, bunu bir fonksiyona sarmak isteyebilirsiniz pg_authid. Gibi bir şey:

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)Sorguya gerek kalmadan oid'den rol adını almak için kullanabilirsiniz 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
Zaten +1 pg_has_role(), muhtemelen önemli olmasa da özyinelemeli sorgudan biraz daha hızlı. Yine de son bir şey: hoş bir yan etki olabilen ya da olmayabilen, süpermarketler için tüm rolleri döndürür . Sonuç benim sorgundan farklı.
Erwin Brandstetter

16

Bu, Craig Ringer'ın yanıtının , süper kullanıcı olmayan bir kullanıcı tarafından doğrudan kullanabileceği basitleştirilmiş bir sürümüdür :

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

pg_rolestemelde pg_authid, şifreleri açığa vurmadığı için kamuya açık bir bakış açısıdır pg_authid. Üs oidgörünümüne bile ihraç edilmektedir. Şifrelere ihtiyaç duymadığınızda, kullanıcılara özel adanmış işlev yaratmanın hiçbir anlamı yoktur.


3

İşte benim almam. Belirli bir kullanıcı veya tüm kullanıcılar için çalışır.

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
Önceki cevaplardan nasıl farklılaştığını ve geliştirildiğini açıklarsanız, bu çok daha iyi olur. Ek bilgileri doğrudan cevabın içine düzenleyebilirsiniz.
Michael Green

1

Bunun yapacağına inanıyorum

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

Rol adlarını almayı tercih ederseniz, ilkini oidile değiştirin rolname.


0

şu anda aktif rolünüzün tüm rollerini bilmek istiyorsanız:

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.