Làm cách nào để có được tất cả các vai trò mà người dùng là thành viên (bao gồm cả các vai trò được kế thừa)?


23

Giả sử tôi có hai nhóm cơ sở dữ liệu Postgresql, "tác giả" và "biên tập viên" và hai người dùng, "maxwell" và "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

Tôi muốn viết một hàm biểu diễn trả về một danh sách các vai trò (tốt nhất là oid của chúng) mà maxwell thuộc về, đại loại như thế này:

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

Nó sẽ trả về các oids cho maxwell, tác giả và biên tập viên (nhưng không phải là ernest).

Nhưng tôi không chắc làm thế nào để làm điều đó khi có sự kế thừa.

Câu trả lời:


23

Bạn có thể truy vấn danh mục hệ thống bằng một truy vấn đệ quy , cụ thể 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, INHERITlà hành vi mặc định của CREATE ROLEvà không cần phải đánh vần.

BTW2: phụ thuộc tròn là không thể. Postgres không cho phép điều đó. Vì vậy, chúng tôi không phải kiểm tra điều đó.


18

Phiên bản ngắn:

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

Ở đây, chúng tôi sử dụng một phiên bản pg_has_rolelấy tên vai trò là chủ đề và vai trò của nó để kiểm tra tư cách thành viên , qua memberchế độ để chúng tôi kiểm tra tư cách thành viên được kế thừa.

Ưu điểm của việc sử dụng pg_has_rolelà nó sử dụng bộ nhớ đệm thông tin vai trò nội bộ của PostgreQuery để đáp ứng các truy vấn thành viên một cách nhanh chóng.

Bạn có thể muốn bọc cái này trong một SECURITY DEFINERhàm, vì pg_authidđã hạn chế quyền truy cập. Cái gì đó như:

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

Bạn có thể sử dụng pg_get_userbyid(oid)để lấy tên vai trò từ oid mà không cần phải truy vấn 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
Dù sao +1, vì pg_has_role()có lẽ nhanh hơn một chút so với truy vấn đệ quy của tôi, ngay cả khi điều đó hầu như không quan trọng. Một điều cuối cùng: nó trả về tất cả các vai trò cho các supersers, có thể hoặc không thể là một tác dụng phụ đáng hoan nghênh. Đó là nơi kết quả khác với truy vấn của tôi.
Erwin Brandstetter

16

Đây là phiên bản đơn giản hóa câu trả lời của Craig Ringer mà một người không siêu người dùng có thể sử dụng trực tiếp:

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

pg_rolesvề cơ bản là một quan điểm về pg_authidkhả năng truy cập công khai, vì nó không tiết lộ mật khẩu, trái với pg_authid. Các cơ sở oidthậm chí được xuất khẩu vào xem. Khi không cần mật khẩu, sẽ không có điểm nào trong việc tạo chức năng sở hữu siêu người dùng chuyên dụng.


3

Tôi đã bắt nó ở đây. Nó hoạt động cho một người dùng cụ thể hoặc tất cả người dùng.

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
Điều này sẽ được cải thiện nhiều nếu bạn giải thích nếu khác biệt và cải thiện các câu trả lời trước đó. Bạn có thể chỉnh sửa thông tin bổ sung trực tiếp vào câu trả lời.
Michael Green

1

Tôi tin rằng điều này sẽ làm điều đó

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

Nếu bạn muốn có được tên vai trò thì thay thế đầu tiên oidbằng rolname.


0

nếu bạn muốn biết tất cả các vai trò của vai trò hiện đang hoạt động của mình:

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.