Patrick
Patrick

Reputation: 32179

Retrieving all object privileges for specific role

Is there an easy way to enumerate all objects that a specific role has some access privilege to? I know of the set of has_*_privilege functions in pg_catalog but they don't do the job, I want to work the other way around. Effectively I want to have a view that gives oid and access privilege for anything stored in pg_class for a specific role.

Such a view would be extremely handy to check if the security of the database is correctly set up. Typically there are far fewer roles than relations so checking the roles is much less onerous IMHO. Should such an utility not be available in the standard PostgreSQL distribution?

According to the source code (acl.h) the aclitem is a struct:

typedef struct AclItem
{ Oid         ai_grantee;     /* ID that this item grants privs to */
  Oid         ai_grantor;     /* grantor of privs */
  AclMode     ai_privs;       /* privilege bits */
} AclItem;

Easy to work with. However, pg_type lists this as a user-defined, non-composite type. Why is that? The only way I see right now is to parse the aclitem[] array using string functions. Is there a better way to analyze the aclitem array?

Added information Trawling through the various PG lists, it is obvious that this issue keeps popping up in various forms at least since 1997 (did we have computers then? was tv around?), most relevant in the discussion thread "Binary in/out for aclitem" on pgsql-hackers in early 2011. As a (technically skilled) user - rather than a hacker - of PG I appreciate the concern of the developers to maintain a stable interface, but some of the concern voiced in the thread goes a little far for my tastes. What is the real reason not to have a pg_acl table in the system catalogs with definition equal to the AclItem struct in the source code? When did that struct last change? I am also aware of SE developments that will likely introduce changes to the way security is handled - when a users opts to, presumably - so I will settle for something that presents acl information in such a way that it is easy to enumerate granted privileges for a specific user, such as:

SELECT * FROM pg_privileges WHERE grantee = 16384;

Like so it can still be an abstraction of the underlying structures so any changes under the hood could then (presumably) still be translated into the exposed interface. Not too different from the information_schema approach, I would say.

Cheers, Patrick

Upvotes: 13

Views: 9115

Answers (2)

Daniel Silva
Daniel Silva

Reputation: 21

Probably not the best / efficient way, but it helps me a lot! I needed it while having problems dropping roles and having the error.

ERROR:  role ROLE_NAME cannot be dropped because some objects depend on it

You can use it as

SELECT * FROM upg_roles_privs WHERE grantee = 'testuser'

The code is below. I'm not including "system" objects (from pg_catalog and information_schema), you can take the conditions out of the query if you wish to enumerate them.

CREATE VIEW upg_roles_privs AS

    /* Databases */
    select type, objname, r1.rolname grantor, r2.rolname grantee, privilege_type
    from
    (select 
      'database'::text as type, datname as objname, datistemplate, datallowconn, 
      (aclexplode(datacl)).grantor as grantorI, 
      (aclexplode(datacl)).grantee as granteeI,
      (aclexplode(datacl)).privilege_type,
      (aclexplode(datacl)).is_grantable
    from pg_database) as db
    join pg_roles r1 on db.grantorI = r1.oid
    join pg_roles r2 on db.granteeI = r2.oid
    where r2.rolname not in ('postgres')

    union all

    /* Schemas / Namespaces */
    select type, objname, r1.rolname grantor, r2.rolname grantee, privilege_type from 
    (select
      'schema'::text as type, nspname as objname, 
      (aclexplode(nspacl)).grantor as grantorI, 
      (aclexplode(nspacl)).grantee as granteeI,
      (aclexplode(nspacl)).privilege_type,
      (aclexplode(nspacl)).is_grantable
    from pg_catalog.pg_namespace) as ns
    join pg_roles r1 on ns.grantorI = r1.oid
    join pg_roles r2 on ns.granteeI = r2.oid
    where r2.rolname not in ('postgres')

    union all

    /* Tabelas */
    select 'tables'::text as type, table_name||' ('||table_schema||')' as objname, grantor, grantee, privilege_type  
    from information_schema.role_table_grants 
    where grantee not in ('postgres')
    and table_schema not in ('information_schema', 'pg_catalog')
    and grantor <> grantee

    union all

    /* Colunas (TODO: se o revoke on table from x retirar acesso das colunas, nao precisa desse bloco) */
    select 
      'columns'::text as type, column_name||' ('||table_name||')' as objname,
      grantor, grantee, privilege_type
    from information_schema.role_column_grants
    where 
    table_schema not in ('information_schema', 'pg_catalog')
    and grantor <> grantee

    union all

    /* Funcoes / Procedures */
    select 'routine'::text as type, routine_name as objname, grantor, grantee, privilege_type
    from information_schema.role_routine_grants
    where grantor <> grantee
    and routine_schema not in ('information_schema', 'pg_catalog')

    --union all information_schema.role_udt_grants

    union all

    /* Outros objetos */
    select 'object'::text as type, object_name||'( '||object_type||')' as objname, grantor, grantee, privilege_type
    from information_schema.role_usage_grants
    where object_type <> 'COLLATION' and object_type <> 'DOMAIN'

Upvotes: 2

Denis de Bernardy
Denis de Bernardy

Reputation: 78423

There's no such view out of the box, but the data needed to create it is in the system catalogs:

http://www.postgresql.org/docs/current/static/catalogs.html

For instance, there's a relacl field in pg_class:

select oid::regclass, relacl from pg_class;

There are similar fields in other catalogs, namely typacl in pg_type and proacl in pg_proc.

You'll presumably want to use two more catalogs, namely pg_authid to know which roles are have superuser privileges, and pg_auth_members to know who has what role.

(The pg_default_acl is only used during object creation, so is not useful.)

There are a couple of aclitem-related internal functions that may come in handy in creating the view. You can list them in psql like so:

\df+ *acl*

In particular aclexplode(). The following example will hopefully be enough to get you started:

select oid::regclass,
       (aclexplode(relacl)).grantor,
       (aclexplode(relacl)).grantee,
       (aclexplode(relacl)).privilege_type,
       (aclexplode(relacl)).is_grantable
from pg_class
where relacl is not null;

It can be optimized by expanding the acl rows first, e.g.:

select oid::regclass,
       aclitem.grantee
from (select oid, aclexplode(relacl) as aclitem from pg_class) sub

It will lead you straight to the desired result.

Insofar as I'm aware, that's about as good as it'll get using the built-in tools. (Naturally, you could write your own set of operators in C if you'd like to try to optimize this further.)

With respect to your extra questions, I'm afraid they can only be answered by a handful of people in the world, aka the core devs themselves. They hang out on the pg hackers list more often than they do here.

Upvotes: 18

Related Questions