ユーザーがメンバーになっているすべてのロール(継承されたロールを含む)を取得する方法は?


23

2つのPostgresqlデータベースグループ、「authors」と「editors」、および2人のユーザー、「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、authors、editorのoidを返す必要があります(ただし、ernestではありません)。

しかし、継承がある場合にどうすればよいかわかりません。

回答:


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 DEFINERpg_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()がほとんど問題ではない場合でも、おそらく私の再帰クエリよりも少し速いためです。最後になりますが、スーパーユーザーのすべてのロールを返しますが、これは歓迎すべき副作用である場合とそうでない場合があります。ここで、クエリと結果が異なります。
アーウィンブランドステッター14年

16

これは、非スーパーユーザーが直接使用できるCraig Ringerの回答の簡略版です。

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

pg_rolespg_authidに反して、パスワードを公開しないため、本質的には一般公開されているビューpg_authidです。ベースoidはビューにエクスポートされます。パスワードを必要としない場合、専用のスーパーユーザー所有機能を作成しても意味がありません。


3

ここに私の見解があります。特定の1人のユーザーまたはすべてのユーザーに対して機能します。

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
これは、以前の回答とどのように異なり、改善されたかを説明すれば、大幅に改善されます。追加情報を直接回答に編集できます。
マイケルグリーン

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

ロール名を取得したい場合は、最初のものoidをに置き換えrolnameます。


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.