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

Nei*_*gan 32 postgresql role access-control users postgresql-9.3

假设我有两个 Postgresql 数据库组,“authors”和“editors”,以及两个用户,“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
Run Code Online (Sandbox Code Playgroud)

我想编写一个高性能函数,它返回 maxwell 所属的角色列表(最好是他们的 oid),如下所示:

create or replace function get_all_roles() returns oid[] ...
Run Code Online (Sandbox Code Playgroud)

它应该返回 maxwell、authors 和 editors(但不是 ernest)的 oid。

但是当有继承时,我不知道该怎么做。

Erw*_*ter 32

您可以使用递归查询来查询系统目录,特别是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, oid::regrole::text AS rolename FROM cte;  -- oid & name
Run Code Online (Sandbox Code Playgroud)

关于强制转换为对象标识符类型的手册regrole

BTW 1:INHERIT是默认行为,CREATE ROLE不必详细说明。

BTW 2:循环依赖是不可能的。Postgres 不允许这样做。所以我们不必检查。


Dan*_*ité 23

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

 SELECT oid, rolname FROM pg_roles WHERE
   pg_has_role( 'maxwell', oid, 'member');
Run Code Online (Sandbox Code Playgroud)

pg_roles本质上是一种pg_authid公众可访问的观点,因为它不透露密码,与pg_authid. 基地oid甚至被导出到视图中。当不需要密码时,创建专用的超级用户拥有的功能是没有意义的。


Cra*_*ger 20

精简版:

SELECT a.oid 
FROM pg_authid a 
WHERE pg_has_role('maxwell', a.oid, 'member');
Run Code Online (Sandbox Code Playgroud)

这里我们使用一个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...;
Run Code Online (Sandbox Code Playgroud)

您可以使用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');
Run Code Online (Sandbox Code Playgroud)

  • +1 无论如何,因为`pg_has_role()` 可能比我的递归查询快一点,即使这无关紧要。最后一件事:它为超级用户返回*所有*角色,这可能是也可能不是一个受欢迎的副作用。这就是结果与我的查询不同的地方。 (2认同)

小智 5

这是我的看法。它适用于一个特定用户或所有用户。

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'
Run Code Online (Sandbox Code Playgroud)

  • 如果您解释了与以前的答案有何不同以及如何改进,那么这将会有很大的改善。您可以将附加信息直接编辑到答案中。 (2认同)