如何获得用户所属的所有角色(包括继承的角色)?


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所属角色(最好是其oid)的列表,如下所示:

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

它应返回maxwell,作者和编辑者的oid(而不是ernest)。

但是我不确定有继承时该怎么做。

Answers:


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;

顺便说一句,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()它可能比我的递归查询快一点,即使这并不重要。但是,最后一件事:它返回了超级管理员的所有角色,这可能是也可能不是受欢迎的副作用。那就是结果与我的查询不同的地方。
Erwin Brandstetter 2014年

16

这是Craig Ringer答案的简化版本,非超级用户可以直接使用:

 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
如果您解释了与先前答案的区别和改进之处,将会大大改善。您可以将其他信息直接编辑到答案中。
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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.