วิธีรับบทบาททั้งหมดที่ผู้ใช้เป็นสมาชิก (รวมถึงบทบาทที่สืบทอด)


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

ฉันต้องการเขียนฟังก์ชั่นนักแสดงที่ส่งกลับรายการของบทบาท (โดยเฉพาะอย่างยิ่ง oid ของพวกเขา) ที่ Maxwell เป็นของบางสิ่งเช่นนี้:

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

มันควรส่งคืน oids สำหรับ maxwell, ผู้แต่งและบรรณาธิการ

แต่ฉันไม่แน่ใจว่าจะทำอย่างไรเมื่อมีการสืบทอด

คำตอบ:


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ที่ใช้ชื่อบทบาทเป็นหัวเรื่องและบทบาท oid เพื่อทดสอบการเป็นสมาชิกผ่าน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()อาจเป็นเร็วกว่าแบบสอบถามแบบเรียกซ้ำของฉันเล็กน้อยแม้ว่าจะไม่ค่อยสำคัญก็ตาม สิ่งสุดท้ายที่: มันส่งกลับบทบาททั้งหมดสำหรับ supersusers ซึ่งอาจหรือไม่อาจมีผลข้างเคียงต้อนรับ นั่นคือสิ่งที่ผลลัพธ์แตกต่างจากแบบสอบถามของฉัน
Erwin Brandstetter

16

นี่เป็นคำตอบที่ง่ายของCraig Ringerที่ผู้ใช้ที่ไม่ใช่ superuser สามารถใช้งานได้โดยตรง:

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

pg_rolesเป็นหลักในมุมมองที่สามารถเข้าถึงประชาชนในขณะที่มันไม่ได้เปิดเผยรหัสผ่านที่ตรงกันข้ามกับpg_authid pg_authidฐานoidจะถูกส่งออกไปยังมุมมอง เมื่อไม่ต้องการรหัสผ่านจะไม่มีประโยชน์ในการสร้างฟังก์ชั่นที่เป็นเจ้าของ superuser โดยเฉพาะ


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
สิ่งนี้จะได้รับการปรับปรุงให้ดีขึ้นมากหากคุณอธิบายว่าหากคำตอบก่อนหน้านั้นแตกต่างจากและปรับปรุงอย่างไร คุณสามารถแก้ไขข้อมูลเพิ่มเติมได้โดยตรงในคำตอบ
Michael Green

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

หากคุณต้องการที่จะได้รับชื่อบทบาทแล้วแทนที่คนแรกด้วยoidrolname


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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.