Allow grant-level control of role inheritance behavior.
The GRANT statement can now specify WITH INHERIT TRUE or WITH INHERIT FALSE to control whether the member inherits the granted role's permissions. For symmetry, you can now likewise write WITH ADMIN TRUE or WITH ADMIN FALSE to turn ADMIN OPTION on or off. If a GRANT does not specify WITH INHERIT, the behavior based on whether the member role is marked INHERIT or NOINHERIT. This means that if all roles are marked INHERIT or NOINHERIT before any role grants are performed, the behavior is identical to what we had before; otherwise, it's different, because ALTER ROLE [NO]INHERIT now only changes the default behavior of future grants, and has no effect on existing ones. Patch by me. Reviewed and testing by Nathan Bossart and Tushar Ahuja, with design-level comments from various others. Discussion: http://postgr.es/m/CA+Tgmoa5Sf4PiWrfxA=sGzDKg0Ojo3dADw=wAHOhR9dggV=RmQ@mail.gmail.com
This commit is contained in:
parent
2059c5e3b0
commit
e3ce2de09d
|
@ -1717,6 +1717,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
|
||||||
<structfield>roleid</structfield> to others
|
<structfield>roleid</structfield> to others
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
|
<structfield>inherit_option</structfield> <type>bool</type>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
True if the member automatically inherits the privileges of the
|
||||||
|
granted role
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -133,17 +133,24 @@ in sync when changing the above synopsis!
|
||||||
<term><literal>NOINHERIT</literal></term>
|
<term><literal>NOINHERIT</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
These clauses determine whether a role <quote>inherits</quote> the
|
When the <literal>GRANT</literal> statement is used to confer
|
||||||
privileges of roles it is a member of.
|
membership in one role to another role, the <literal>GRANT</literal>
|
||||||
A role with the <literal>INHERIT</literal> attribute can automatically
|
may use the <literal>WITH INHERIT</literal> clause to specify whether
|
||||||
use whatever database privileges have been granted to all roles
|
the privileges of the granted role should be <quote>inherited</quote>
|
||||||
it is directly or indirectly a member of.
|
by the new member. If the <literal>GRANT</literal> statement does not
|
||||||
Without <literal>INHERIT</literal>, membership in another role
|
specify either inheritance behavior, the new <literal>GRANT</literal>
|
||||||
only grants the ability to <command>SET ROLE</command> to that other role;
|
will be created <literal>WITH INHERIT TRUE</literal> if the member
|
||||||
the privileges of the other role are only available after having
|
role is set to <literal>INHERIT</literal> and to
|
||||||
done so.
|
<literal>WITH INHERIT FALSE</literal> if it is set to
|
||||||
If not specified,
|
<literal>NOINHERIT</literal>.
|
||||||
<literal>INHERIT</literal> is the default.
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In <productname>PostgreSQL</productname> versions before 16,
|
||||||
|
the <literal>GRANT</literal> statement did not support
|
||||||
|
<literal>WITH INHERIT</literal>. Therefore, changing this role-level
|
||||||
|
property would also change the behavior of already-existing grants.
|
||||||
|
This is no longer the case.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
|
@ -98,7 +98,7 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
|
||||||
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
||||||
|
|
||||||
GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replaceable class="parameter">role_specification</replaceable> [, ...]
|
GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replaceable class="parameter">role_specification</replaceable> [, ...]
|
||||||
[ WITH ADMIN OPTION ]
|
[ WITH { ADMIN | INHERIT } { OPTION | TRUE | FALSE } ]
|
||||||
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
||||||
|
|
||||||
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
|
<phrase>where <replaceable class="parameter">role_specification</replaceable> can be:</phrase>
|
||||||
|
@ -255,7 +255,17 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If <literal>WITH ADMIN OPTION</literal> is specified, the member can
|
The effect of membership in a role can be modified by specifying the
|
||||||
|
<literal>ADMIN</literal> or <literal>INHERIT</literal> option, each
|
||||||
|
of which can be set to either <literal>TRUE</literal> or
|
||||||
|
<literal>FALSE</literal>. The keyword <literal>OPTION</literal> is accepted
|
||||||
|
as a synonym for <literal>TRUE</literal>, so that
|
||||||
|
<literal>WITH ADMIN OPTION</literal>
|
||||||
|
is a synonym for <literal>WITH ADMIN TRUE</literal>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <literal>ADMIN</literal> option allows the member to
|
||||||
in turn grant membership in the role to others, and revoke membership
|
in turn grant membership in the role to others, and revoke membership
|
||||||
in the role as well. Without the admin option, ordinary users cannot
|
in the role as well. Without the admin option, ordinary users cannot
|
||||||
do that. A role is not considered to hold <literal>WITH ADMIN
|
do that. A role is not considered to hold <literal>WITH ADMIN
|
||||||
|
@ -265,6 +275,18 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
|
||||||
in any role that is not a superuser.
|
in any role that is not a superuser.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <literal>INHERIT</literal> option, if it is set to
|
||||||
|
<literal>TRUE</literal>, causes the member to inherit the privileges of
|
||||||
|
the granted role. That is, it can automatically use whatever database
|
||||||
|
privileges have been granted to that role. If set to
|
||||||
|
<literal>FALSE</literal>, the member does not inherit the privileges
|
||||||
|
of the granted role. If this clause is not specified, it defaults to
|
||||||
|
true if the member role is set to <literal>INHERIT</literal> and to false
|
||||||
|
if the member role is set to <literal>NOINHERIT</literal>.
|
||||||
|
See <link linkend="sql-createrole"><command>CREATE ROLE</command></link>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If <literal>GRANTED BY</literal> is specified, the grant is recorded as
|
If <literal>GRANTED BY</literal> is specified, the grant is recorded as
|
||||||
having been done by the specified role. A user can only attribute a grant
|
having been done by the specified role. A user can only attribute a grant
|
||||||
|
|
|
@ -125,7 +125,7 @@ REVOKE [ GRANT OPTION FOR ]
|
||||||
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
||||||
[ CASCADE | RESTRICT ]
|
[ CASCADE | RESTRICT ]
|
||||||
|
|
||||||
REVOKE [ ADMIN OPTION FOR ]
|
REVOKE [ { ADMIN | INHERIT } OPTION FOR ]
|
||||||
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
|
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
|
||||||
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
|
||||||
[ CASCADE | RESTRICT ]
|
[ CASCADE | RESTRICT ]
|
||||||
|
@ -206,6 +206,13 @@ REVOKE [ ADMIN OPTION FOR ]
|
||||||
allow the noise word <literal>GROUP</literal>
|
allow the noise word <literal>GROUP</literal>
|
||||||
in <replaceable class="parameter">role_specification</replaceable>.
|
in <replaceable class="parameter">role_specification</replaceable>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Just as <literal>ADMIN OPTION</literal> can be removed from an existing
|
||||||
|
role grant, it is also possible to revoke <literal>INHERIT OPTION</literal>.
|
||||||
|
This is equivalent to setting the value of that option to
|
||||||
|
<literal>FALSE</literal>.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1 id="sql-revoke-notes">
|
<refsect1 id="sql-revoke-notes">
|
||||||
|
|
|
@ -51,6 +51,9 @@
|
||||||
* RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
|
* RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
|
||||||
* admin_option set to false by the operation.
|
* admin_option set to false by the operation.
|
||||||
*
|
*
|
||||||
|
* RRG_REMOVE_INHERIT_OPTION indicates a grant that would need to have
|
||||||
|
* inherit_option set to false by the operation.
|
||||||
|
*
|
||||||
* RRG_DELETE_GRANT indicates a grant that would need to be removed entirely
|
* RRG_DELETE_GRANT indicates a grant that would need to be removed entirely
|
||||||
* by the operation.
|
* by the operation.
|
||||||
*/
|
*/
|
||||||
|
@ -58,12 +61,22 @@ typedef enum
|
||||||
{
|
{
|
||||||
RRG_NOOP,
|
RRG_NOOP,
|
||||||
RRG_REMOVE_ADMIN_OPTION,
|
RRG_REMOVE_ADMIN_OPTION,
|
||||||
|
RRG_REMOVE_INHERIT_OPTION,
|
||||||
RRG_DELETE_GRANT
|
RRG_DELETE_GRANT
|
||||||
} RevokeRoleGrantAction;
|
} RevokeRoleGrantAction;
|
||||||
|
|
||||||
/* Potentially set by pg_upgrade_support functions */
|
/* Potentially set by pg_upgrade_support functions */
|
||||||
Oid binary_upgrade_next_pg_authid_oid = InvalidOid;
|
Oid binary_upgrade_next_pg_authid_oid = InvalidOid;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned specified;
|
||||||
|
bool admin;
|
||||||
|
bool inherit;
|
||||||
|
} GrantRoleOptions;
|
||||||
|
|
||||||
|
#define GRANT_ROLE_SPECIFIED_ADMIN 0x0001
|
||||||
|
#define GRANT_ROLE_SPECIFIED_INHERIT 0x0002
|
||||||
|
|
||||||
/* GUC parameter */
|
/* GUC parameter */
|
||||||
int Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;
|
int Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;
|
||||||
|
@ -73,17 +86,18 @@ check_password_hook_type check_password_hook = NULL;
|
||||||
|
|
||||||
static void AddRoleMems(const char *rolename, Oid roleid,
|
static void AddRoleMems(const char *rolename, Oid roleid,
|
||||||
List *memberSpecs, List *memberIds,
|
List *memberSpecs, List *memberIds,
|
||||||
Oid grantorId, bool admin_opt);
|
Oid grantorId, GrantRoleOptions *popt);
|
||||||
static void DelRoleMems(const char *rolename, Oid roleid,
|
static void DelRoleMems(const char *rolename, Oid roleid,
|
||||||
List *memberSpecs, List *memberIds,
|
List *memberSpecs, List *memberIds,
|
||||||
Oid grantorId, bool admin_opt, DropBehavior behavior);
|
Oid grantorId, GrantRoleOptions *popt,
|
||||||
|
DropBehavior behavior);
|
||||||
static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId,
|
static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId,
|
||||||
bool is_grant);
|
bool is_grant);
|
||||||
static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
|
static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
|
||||||
static bool plan_single_revoke(CatCList *memlist,
|
static bool plan_single_revoke(CatCList *memlist,
|
||||||
RevokeRoleGrantAction *actions,
|
RevokeRoleGrantAction *actions,
|
||||||
Oid member, Oid grantor,
|
Oid member, Oid grantor,
|
||||||
bool revoke_admin_option_only,
|
GrantRoleOptions *popt,
|
||||||
DropBehavior behavior);
|
DropBehavior behavior);
|
||||||
static void plan_member_revoke(CatCList *memlist,
|
static void plan_member_revoke(CatCList *memlist,
|
||||||
RevokeRoleGrantAction *actions, Oid member);
|
RevokeRoleGrantAction *actions, Oid member);
|
||||||
|
@ -92,6 +106,7 @@ static void plan_recursive_revoke(CatCList *memlist,
|
||||||
int index,
|
int index,
|
||||||
bool revoke_admin_option_only,
|
bool revoke_admin_option_only,
|
||||||
DropBehavior behavior);
|
DropBehavior behavior);
|
||||||
|
static void InitGrantRoleOptions(GrantRoleOptions *popt);
|
||||||
|
|
||||||
|
|
||||||
/* Check if current user has createrole privileges */
|
/* Check if current user has createrole privileges */
|
||||||
|
@ -144,6 +159,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
|
||||||
DefElem *dadminmembers = NULL;
|
DefElem *dadminmembers = NULL;
|
||||||
DefElem *dvalidUntil = NULL;
|
DefElem *dvalidUntil = NULL;
|
||||||
DefElem *dbypassRLS = NULL;
|
DefElem *dbypassRLS = NULL;
|
||||||
|
GrantRoleOptions popt;
|
||||||
|
|
||||||
/* The defaults can vary depending on the original statement type */
|
/* The defaults can vary depending on the original statement type */
|
||||||
switch (stmt->stmt_type)
|
switch (stmt->stmt_type)
|
||||||
|
@ -462,6 +478,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
|
||||||
if (addroleto || adminmembers || rolemembers)
|
if (addroleto || adminmembers || rolemembers)
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/* Default grant. */
|
||||||
|
InitGrantRoleOptions(&popt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the new role to the specified existing roles.
|
* Add the new role to the specified existing roles.
|
||||||
*/
|
*/
|
||||||
|
@ -486,7 +505,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
|
||||||
AddRoleMems(oldrolename, oldroleid,
|
AddRoleMems(oldrolename, oldroleid,
|
||||||
thisrole_list,
|
thisrole_list,
|
||||||
thisrole_oidlist,
|
thisrole_oidlist,
|
||||||
InvalidOid, false);
|
InvalidOid, &popt);
|
||||||
|
|
||||||
ReleaseSysCache(oldroletup);
|
ReleaseSysCache(oldroletup);
|
||||||
}
|
}
|
||||||
|
@ -496,12 +515,14 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
|
||||||
* Add the specified members to this new role. adminmembers get the admin
|
* Add the specified members to this new role. adminmembers get the admin
|
||||||
* option, rolemembers don't.
|
* option, rolemembers don't.
|
||||||
*/
|
*/
|
||||||
AddRoleMems(stmt->role, roleid,
|
|
||||||
adminmembers, roleSpecsToIds(adminmembers),
|
|
||||||
InvalidOid, true);
|
|
||||||
AddRoleMems(stmt->role, roleid,
|
AddRoleMems(stmt->role, roleid,
|
||||||
rolemembers, roleSpecsToIds(rolemembers),
|
rolemembers, roleSpecsToIds(rolemembers),
|
||||||
InvalidOid, false);
|
InvalidOid, &popt);
|
||||||
|
popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
|
||||||
|
popt.admin = true;
|
||||||
|
AddRoleMems(stmt->role, roleid,
|
||||||
|
adminmembers, roleSpecsToIds(adminmembers),
|
||||||
|
InvalidOid, &popt);
|
||||||
|
|
||||||
/* Post creation hook for new role */
|
/* Post creation hook for new role */
|
||||||
InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
|
InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
|
||||||
|
@ -552,6 +573,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
|
||||||
DefElem *dvalidUntil = NULL;
|
DefElem *dvalidUntil = NULL;
|
||||||
DefElem *dbypassRLS = NULL;
|
DefElem *dbypassRLS = NULL;
|
||||||
Oid roleid;
|
Oid roleid;
|
||||||
|
GrantRoleOptions popt;
|
||||||
|
|
||||||
check_rolespec_name(stmt->role,
|
check_rolespec_name(stmt->role,
|
||||||
"Cannot alter reserved roles.");
|
"Cannot alter reserved roles.");
|
||||||
|
@ -843,6 +865,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
heap_freetuple(new_tuple);
|
heap_freetuple(new_tuple);
|
||||||
|
|
||||||
|
InitGrantRoleOptions(&popt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Advance command counter so we can see new record; else tests in
|
* Advance command counter so we can see new record; else tests in
|
||||||
* AddRoleMems may fail.
|
* AddRoleMems may fail.
|
||||||
|
@ -856,11 +880,11 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
|
||||||
if (stmt->action == +1) /* add members to role */
|
if (stmt->action == +1) /* add members to role */
|
||||||
AddRoleMems(rolename, roleid,
|
AddRoleMems(rolename, roleid,
|
||||||
rolemembers, roleSpecsToIds(rolemembers),
|
rolemembers, roleSpecsToIds(rolemembers),
|
||||||
InvalidOid, false);
|
InvalidOid, &popt);
|
||||||
else if (stmt->action == -1) /* drop members from role */
|
else if (stmt->action == -1) /* drop members from role */
|
||||||
DelRoleMems(rolename, roleid,
|
DelRoleMems(rolename, roleid,
|
||||||
rolemembers, roleSpecsToIds(rolemembers),
|
rolemembers, roleSpecsToIds(rolemembers),
|
||||||
InvalidOid, false, DROP_RESTRICT);
|
InvalidOid, &popt, DROP_RESTRICT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1337,13 +1361,48 @@ RenameRole(const char *oldname, const char *newname)
|
||||||
* Grant/Revoke roles to/from roles
|
* Grant/Revoke roles to/from roles
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
GrantRole(GrantRoleStmt *stmt)
|
GrantRole(ParseState *pstate, GrantRoleStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation pg_authid_rel;
|
Relation pg_authid_rel;
|
||||||
Oid grantor;
|
Oid grantor;
|
||||||
List *grantee_ids;
|
List *grantee_ids;
|
||||||
ListCell *item;
|
ListCell *item;
|
||||||
|
GrantRoleOptions popt;
|
||||||
|
|
||||||
|
/* Parse options list. */
|
||||||
|
InitGrantRoleOptions(&popt);
|
||||||
|
foreach(item, stmt->opt)
|
||||||
|
{
|
||||||
|
DefElem *opt = (DefElem *) lfirst(item);
|
||||||
|
char *optval = defGetString(opt);
|
||||||
|
|
||||||
|
if (strcmp(opt->defname, "admin") == 0)
|
||||||
|
{
|
||||||
|
popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
|
||||||
|
|
||||||
|
if (parse_bool(optval, &popt.admin))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (strcmp(opt->defname, "inherit") == 0)
|
||||||
|
{
|
||||||
|
popt.specified |= GRANT_ROLE_SPECIFIED_INHERIT;
|
||||||
|
if (parse_bool(optval, &popt.inherit))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("unrecognized role option \"%s\"", opt->defname),
|
||||||
|
parser_errposition(pstate, opt->location));
|
||||||
|
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("unrecognized value for role option \"%s\": \"%s\"",
|
||||||
|
opt->defname, optval),
|
||||||
|
parser_errposition(pstate, opt->location)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lookup OID of grantor, if specified. */
|
||||||
if (stmt->grantor)
|
if (stmt->grantor)
|
||||||
grantor = get_rolespec_oid(stmt->grantor, false);
|
grantor = get_rolespec_oid(stmt->grantor, false);
|
||||||
else
|
else
|
||||||
|
@ -1355,11 +1414,11 @@ GrantRole(GrantRoleStmt *stmt)
|
||||||
pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
|
pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Step through all of the granted roles and add/remove entries for the
|
* Step through all of the granted roles and add, update, or remove
|
||||||
* grantees, or, if admin_opt is set, then just add/remove the admin
|
* entries in pg_auth_members as appropriate. If stmt->is_grant is true,
|
||||||
* option.
|
* we are adding new grants or, if they already exist, updating options
|
||||||
*
|
* on those grants. If stmt->is_grant is false, we are revoking grants or
|
||||||
* Note: Permissions checking is done by AddRoleMems/DelRoleMems
|
* removing options from them.
|
||||||
*/
|
*/
|
||||||
foreach(item, stmt->granted_roles)
|
foreach(item, stmt->granted_roles)
|
||||||
{
|
{
|
||||||
|
@ -1377,11 +1436,11 @@ GrantRole(GrantRoleStmt *stmt)
|
||||||
if (stmt->is_grant)
|
if (stmt->is_grant)
|
||||||
AddRoleMems(rolename, roleid,
|
AddRoleMems(rolename, roleid,
|
||||||
stmt->grantee_roles, grantee_ids,
|
stmt->grantee_roles, grantee_ids,
|
||||||
grantor, stmt->admin_opt);
|
grantor, &popt);
|
||||||
else
|
else
|
||||||
DelRoleMems(rolename, roleid,
|
DelRoleMems(rolename, roleid,
|
||||||
stmt->grantee_roles, grantee_ids,
|
stmt->grantee_roles, grantee_ids,
|
||||||
grantor, stmt->admin_opt, stmt->behavior);
|
grantor, &popt, stmt->behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1483,12 +1542,12 @@ roleSpecsToIds(List *memberNames)
|
||||||
* memberSpecs: list of RoleSpec of roles to add (used only for error messages)
|
* memberSpecs: list of RoleSpec of roles to add (used only for error messages)
|
||||||
* memberIds: OIDs of roles to add
|
* memberIds: OIDs of roles to add
|
||||||
* grantorId: who is granting the membership (InvalidOid if not set explicitly)
|
* grantorId: who is granting the membership (InvalidOid if not set explicitly)
|
||||||
* admin_opt: granting admin option?
|
* popt: information about grant options
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
AddRoleMems(const char *rolename, Oid roleid,
|
AddRoleMems(const char *rolename, Oid roleid,
|
||||||
List *memberSpecs, List *memberIds,
|
List *memberSpecs, List *memberIds,
|
||||||
Oid grantorId, bool admin_opt)
|
Oid grantorId, GrantRoleOptions *popt)
|
||||||
{
|
{
|
||||||
Relation pg_authmem_rel;
|
Relation pg_authmem_rel;
|
||||||
TupleDesc pg_authmem_dsc;
|
TupleDesc pg_authmem_dsc;
|
||||||
|
@ -1607,7 +1666,7 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||||
* has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION on
|
* has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION on
|
||||||
* X back to A).
|
* X back to A).
|
||||||
*/
|
*/
|
||||||
if (admin_opt && grantorId != BOOTSTRAP_SUPERUSERID)
|
if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID)
|
||||||
{
|
{
|
||||||
CatCList *memlist;
|
CatCList *memlist;
|
||||||
RevokeRoleGrantAction *actions;
|
RevokeRoleGrantAction *actions;
|
||||||
|
@ -1669,25 +1728,55 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||||
Datum new_record[Natts_pg_auth_members] = {0};
|
Datum new_record[Natts_pg_auth_members] = {0};
|
||||||
bool new_record_nulls[Natts_pg_auth_members] = {0};
|
bool new_record_nulls[Natts_pg_auth_members] = {0};
|
||||||
bool new_record_repl[Natts_pg_auth_members] = {0};
|
bool new_record_repl[Natts_pg_auth_members] = {0};
|
||||||
Form_pg_auth_members authmem_form;
|
|
||||||
|
|
||||||
/*
|
/* Common initialization for possible insert or update */
|
||||||
* Check if entry for this role/member already exists; if so, give
|
new_record[Anum_pg_auth_members_roleid - 1] =
|
||||||
* warning unless we are adding admin option.
|
ObjectIdGetDatum(roleid);
|
||||||
*/
|
new_record[Anum_pg_auth_members_member - 1] =
|
||||||
|
ObjectIdGetDatum(memberid);
|
||||||
|
new_record[Anum_pg_auth_members_grantor - 1] =
|
||||||
|
ObjectIdGetDatum(grantorId);
|
||||||
|
|
||||||
|
/* Find any existing tuple */
|
||||||
authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
|
authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
|
||||||
ObjectIdGetDatum(roleid),
|
ObjectIdGetDatum(roleid),
|
||||||
ObjectIdGetDatum(memberid),
|
ObjectIdGetDatum(memberid),
|
||||||
ObjectIdGetDatum(grantorId));
|
ObjectIdGetDatum(grantorId));
|
||||||
if (!HeapTupleIsValid(authmem_tuple))
|
|
||||||
{
|
/*
|
||||||
authmem_form = NULL;
|
* If we found a tuple, update it with new option values, unless
|
||||||
}
|
* there are no changes, in which case issue a WARNING.
|
||||||
else
|
*
|
||||||
|
* If we didn't find a tuple, just insert one.
|
||||||
|
*/
|
||||||
|
if (HeapTupleIsValid(authmem_tuple))
|
||||||
{
|
{
|
||||||
|
Form_pg_auth_members authmem_form;
|
||||||
|
bool at_least_one_change = false;
|
||||||
|
|
||||||
authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
|
authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
|
||||||
|
|
||||||
if (!admin_opt || authmem_form->admin_option)
|
if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0
|
||||||
|
&& authmem_form->admin_option != popt->admin)
|
||||||
|
{
|
||||||
|
new_record[Anum_pg_auth_members_admin_option - 1] =
|
||||||
|
BoolGetDatum(popt->admin);
|
||||||
|
new_record_repl[Anum_pg_auth_members_admin_option - 1] =
|
||||||
|
true;
|
||||||
|
at_least_one_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0
|
||||||
|
&& authmem_form->inherit_option != popt->inherit)
|
||||||
|
{
|
||||||
|
new_record[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
BoolGetDatum(popt->inherit);
|
||||||
|
new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
true;
|
||||||
|
at_least_one_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!at_least_one_change)
|
||||||
{
|
{
|
||||||
ereport(NOTICE,
|
ereport(NOTICE,
|
||||||
(errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"",
|
(errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"",
|
||||||
|
@ -1696,17 +1785,7 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||||
ReleaseSysCache(authmem_tuple);
|
ReleaseSysCache(authmem_tuple);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Build a tuple to insert or update */
|
|
||||||
new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
|
|
||||||
new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
|
|
||||||
new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
|
|
||||||
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
|
|
||||||
|
|
||||||
if (HeapTupleIsValid(authmem_tuple))
|
|
||||||
{
|
|
||||||
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
|
|
||||||
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
|
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
|
||||||
new_record,
|
new_record,
|
||||||
new_record_nulls, new_record_repl);
|
new_record_nulls, new_record_repl);
|
||||||
|
@ -1719,6 +1798,33 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||||
Oid objectId;
|
Oid objectId;
|
||||||
Oid *newmembers = palloc(sizeof(Oid));
|
Oid *newmembers = palloc(sizeof(Oid));
|
||||||
|
|
||||||
|
/* Set admin option if user set it to true, otherwise not. */
|
||||||
|
new_record[Anum_pg_auth_members_admin_option - 1] =
|
||||||
|
BoolGetDatum(popt->admin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the user specified a value for the inherit option, use
|
||||||
|
* whatever was specified. Otherwise, set the default value based
|
||||||
|
* on the role-level property.
|
||||||
|
*/
|
||||||
|
if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
|
||||||
|
new_record[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
popt->inherit;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeapTuple mrtup;
|
||||||
|
Form_pg_authid mrform;
|
||||||
|
|
||||||
|
mrtup = SearchSysCache1(AUTHOID, memberid);
|
||||||
|
if (!HeapTupleIsValid(mrtup))
|
||||||
|
elog(ERROR, "cache lookup failed for role %u", memberid);
|
||||||
|
mrform = (Form_pg_authid) GETSTRUCT(mrtup);
|
||||||
|
new_record[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
mrform->rolinherit;
|
||||||
|
ReleaseSysCache(mrtup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get an OID for the new row and insert it */
|
||||||
objectId = GetNewObjectId();
|
objectId = GetNewObjectId();
|
||||||
new_record[Anum_pg_auth_members_oid - 1] = objectId;
|
new_record[Anum_pg_auth_members_oid - 1] = objectId;
|
||||||
tuple = heap_form_tuple(pg_authmem_dsc,
|
tuple = heap_form_tuple(pg_authmem_dsc,
|
||||||
|
@ -1751,13 +1857,13 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||||
* memberSpecs: list of RoleSpec of roles to del (used only for error messages)
|
* memberSpecs: list of RoleSpec of roles to del (used only for error messages)
|
||||||
* memberIds: OIDs of roles to del
|
* memberIds: OIDs of roles to del
|
||||||
* grantorId: who is revoking the membership
|
* grantorId: who is revoking the membership
|
||||||
* admin_opt: remove admin option only?
|
* popt: information about grant options
|
||||||
* behavior: RESTRICT or CASCADE behavior for recursive removal
|
* behavior: RESTRICT or CASCADE behavior for recursive removal
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
DelRoleMems(const char *rolename, Oid roleid,
|
DelRoleMems(const char *rolename, Oid roleid,
|
||||||
List *memberSpecs, List *memberIds,
|
List *memberSpecs, List *memberIds,
|
||||||
Oid grantorId, bool admin_opt, DropBehavior behavior)
|
Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior)
|
||||||
{
|
{
|
||||||
Relation pg_authmem_rel;
|
Relation pg_authmem_rel;
|
||||||
TupleDesc pg_authmem_dsc;
|
TupleDesc pg_authmem_dsc;
|
||||||
|
@ -1824,7 +1930,7 @@ DelRoleMems(const char *rolename, Oid roleid,
|
||||||
Oid memberid = lfirst_oid(iditem);
|
Oid memberid = lfirst_oid(iditem);
|
||||||
|
|
||||||
if (!plan_single_revoke(memlist, actions, memberid, grantorId,
|
if (!plan_single_revoke(memlist, actions, memberid, grantorId,
|
||||||
admin_opt, behavior))
|
popt, behavior))
|
||||||
{
|
{
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
(errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"",
|
(errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"",
|
||||||
|
@ -1862,15 +1968,29 @@ DelRoleMems(const char *rolename, Oid roleid,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Just turn off the admin option */
|
/* Just turn off the specified option */
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Datum new_record[Natts_pg_auth_members] = {0};
|
Datum new_record[Natts_pg_auth_members] = {0};
|
||||||
bool new_record_nulls[Natts_pg_auth_members] = {0};
|
bool new_record_nulls[Natts_pg_auth_members] = {0};
|
||||||
bool new_record_repl[Natts_pg_auth_members] = {0};
|
bool new_record_repl[Natts_pg_auth_members] = {0};
|
||||||
|
|
||||||
/* Build a tuple to update with */
|
/* Build a tuple to update with */
|
||||||
new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
|
if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
|
||||||
new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
|
{
|
||||||
|
new_record[Anum_pg_auth_members_admin_option - 1] =
|
||||||
|
BoolGetDatum(false);
|
||||||
|
new_record_repl[Anum_pg_auth_members_admin_option - 1] =
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
else if (actions[i] == RRG_REMOVE_INHERIT_OPTION)
|
||||||
|
{
|
||||||
|
new_record[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
BoolGetDatum(false);
|
||||||
|
new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elog(ERROR, "unknown role revoke action");
|
||||||
|
|
||||||
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
|
tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
|
||||||
new_record,
|
new_record,
|
||||||
|
@ -2028,11 +2148,21 @@ initialize_revoke_actions(CatCList *memlist)
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
|
plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
|
||||||
Oid member, Oid grantor, bool revoke_admin_option_only,
|
Oid member, Oid grantor, GrantRoleOptions *popt,
|
||||||
DropBehavior behavior)
|
DropBehavior behavior)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If popt.specified == 0, we're revoking the grant entirely; otherwise,
|
||||||
|
* we expect just one bit to be set, and we're revoking the corresponding
|
||||||
|
* option. As of this writing, there's no syntax that would allow for
|
||||||
|
* an attempt to revoke multiple options at once, and the logic below
|
||||||
|
* wouldn't work properly if such syntax were added, so assert that our
|
||||||
|
* caller isn't trying to do that.
|
||||||
|
*/
|
||||||
|
Assert(pg_popcount32(popt->specified) <= 1);
|
||||||
|
|
||||||
for (i = 0; i < memlist->n_members; ++i)
|
for (i = 0; i < memlist->n_members; ++i)
|
||||||
{
|
{
|
||||||
HeapTuple authmem_tuple;
|
HeapTuple authmem_tuple;
|
||||||
|
@ -2044,8 +2174,27 @@ plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
|
||||||
if (authmem_form->member == member &&
|
if (authmem_form->member == member &&
|
||||||
authmem_form->grantor == grantor)
|
authmem_form->grantor == grantor)
|
||||||
{
|
{
|
||||||
plan_recursive_revoke(memlist, actions, i,
|
if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
|
||||||
revoke_admin_option_only, behavior);
|
{
|
||||||
|
/*
|
||||||
|
* Revoking the INHERIT option doesn't change anything for
|
||||||
|
* dependent privileges, so we don't need to recurse.
|
||||||
|
*/
|
||||||
|
actions[i] = RRG_REMOVE_INHERIT_OPTION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool revoke_admin_option_only;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Revoking the grant entirely, or ADMIN option on a grant,
|
||||||
|
* implicates dependent privileges, so we may need to recurse.
|
||||||
|
*/
|
||||||
|
revoke_admin_option_only =
|
||||||
|
(popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0;
|
||||||
|
plan_recursive_revoke(memlist, actions, i,
|
||||||
|
revoke_admin_option_only, behavior);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2172,3 +2321,14 @@ plan_recursive_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a GrantRoleOptions object with default values.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
InitGrantRoleOptions(GrantRoleOptions *popt)
|
||||||
|
{
|
||||||
|
popt->specified = 0;
|
||||||
|
popt->admin = false;
|
||||||
|
popt->inherit = false;
|
||||||
|
}
|
||||||
|
|
|
@ -362,9 +362,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||||
%type <node> utility_option_arg
|
%type <node> utility_option_arg
|
||||||
%type <defelt> drop_option
|
%type <defelt> drop_option
|
||||||
%type <boolean> opt_or_replace opt_no
|
%type <boolean> opt_or_replace opt_no
|
||||||
opt_grant_grant_option opt_grant_admin_option
|
opt_grant_grant_option
|
||||||
opt_nowait opt_if_exists opt_with_data
|
opt_nowait opt_if_exists opt_with_data
|
||||||
opt_transaction_chain
|
opt_transaction_chain
|
||||||
|
%type <list> grant_role_opt_list
|
||||||
|
%type <defelt> grant_role_opt
|
||||||
|
%type <node> grant_role_opt_value
|
||||||
%type <ival> opt_nowait_or_skip
|
%type <ival> opt_nowait_or_skip
|
||||||
|
|
||||||
%type <list> OptRoleList AlterOptRoleList
|
%type <list> OptRoleList AlterOptRoleList
|
||||||
|
@ -7848,15 +7851,26 @@ opt_grant_grant_option:
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
GrantRoleStmt:
|
GrantRoleStmt:
|
||||||
GRANT privilege_list TO role_list opt_grant_admin_option opt_granted_by
|
GRANT privilege_list TO role_list opt_granted_by
|
||||||
{
|
{
|
||||||
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
||||||
|
|
||||||
n->is_grant = true;
|
n->is_grant = true;
|
||||||
n->granted_roles = $2;
|
n->granted_roles = $2;
|
||||||
n->grantee_roles = $4;
|
n->grantee_roles = $4;
|
||||||
n->admin_opt = $5;
|
n->opt = NIL;
|
||||||
n->grantor = $6;
|
n->grantor = $5;
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| GRANT privilege_list TO role_list WITH grant_role_opt_list opt_granted_by
|
||||||
|
{
|
||||||
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
||||||
|
|
||||||
|
n->is_grant = true;
|
||||||
|
n->granted_roles = $2;
|
||||||
|
n->grantee_roles = $4;
|
||||||
|
n->opt = $6;
|
||||||
|
n->grantor = $7;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -7867,19 +7881,22 @@ RevokeRoleStmt:
|
||||||
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
||||||
|
|
||||||
n->is_grant = false;
|
n->is_grant = false;
|
||||||
n->admin_opt = false;
|
n->opt = NIL;
|
||||||
n->granted_roles = $2;
|
n->granted_roles = $2;
|
||||||
n->grantee_roles = $4;
|
n->grantee_roles = $4;
|
||||||
n->grantor = $5;
|
n->grantor = $5;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| REVOKE ADMIN OPTION FOR privilege_list FROM role_list opt_granted_by opt_drop_behavior
|
| REVOKE ColId OPTION FOR privilege_list FROM role_list opt_granted_by opt_drop_behavior
|
||||||
{
|
{
|
||||||
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
||||||
|
DefElem *opt;
|
||||||
|
|
||||||
|
opt = makeDefElem(pstrdup($2),
|
||||||
|
(Node *) makeBoolean(false), @2);
|
||||||
n->is_grant = false;
|
n->is_grant = false;
|
||||||
n->admin_opt = true;
|
n->opt = list_make1(opt);
|
||||||
n->granted_roles = $5;
|
n->granted_roles = $5;
|
||||||
n->grantee_roles = $7;
|
n->grantee_roles = $7;
|
||||||
n->grantor = $8;
|
n->grantor = $8;
|
||||||
|
@ -7888,8 +7905,22 @@ RevokeRoleStmt:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_grant_admin_option: WITH ADMIN OPTION { $$ = true; }
|
grant_role_opt_list:
|
||||||
| /*EMPTY*/ { $$ = false; }
|
grant_role_opt_list ',' grant_role_opt { $$ = lappend($1, $3); }
|
||||||
|
| grant_role_opt { $$ = list_make1($1); }
|
||||||
|
;
|
||||||
|
|
||||||
|
grant_role_opt:
|
||||||
|
ColLabel grant_role_opt_value
|
||||||
|
{
|
||||||
|
$$ = makeDefElem(pstrdup($1), $2, @1);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
grant_role_opt_value:
|
||||||
|
OPTION { $$ = (Node *) makeBoolean(true); }
|
||||||
|
| TRUE_P { $$ = (Node *) makeBoolean(true); }
|
||||||
|
| FALSE_P { $$ = (Node *) makeBoolean(false); }
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_granted_by: GRANTED BY RoleSpec { $$ = $3; }
|
opt_granted_by: GRANTED BY RoleSpec { $$ = $3; }
|
||||||
|
|
|
@ -767,7 +767,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
|
||||||
|
|
||||||
case T_GrantRoleStmt:
|
case T_GrantRoleStmt:
|
||||||
/* no event triggers for global objects */
|
/* no event triggers for global objects */
|
||||||
GrantRole((GrantRoleStmt *) parsetree);
|
GrantRole(pstate, (GrantRoleStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreatedbStmt:
|
case T_CreatedbStmt:
|
||||||
|
|
|
@ -66,7 +66,7 @@ typedef struct
|
||||||
*/
|
*/
|
||||||
enum RoleRecurseType
|
enum RoleRecurseType
|
||||||
{
|
{
|
||||||
ROLERECURSE_PRIVS = 0, /* recurse if rolinherit */
|
ROLERECURSE_PRIVS = 0, /* recurse through inheritable grants */
|
||||||
ROLERECURSE_MEMBERS = 1 /* recurse unconditionally */
|
ROLERECURSE_MEMBERS = 1 /* recurse unconditionally */
|
||||||
};
|
};
|
||||||
static Oid cached_role[] = {InvalidOid, InvalidOid};
|
static Oid cached_role[] = {InvalidOid, InvalidOid};
|
||||||
|
@ -4735,8 +4735,8 @@ initialize_acl(void)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In normal mode, set a callback on any syscache invalidation of rows
|
* In normal mode, set a callback on any syscache invalidation of rows
|
||||||
* of pg_auth_members (for roles_is_member_of()), pg_authid (for
|
* of pg_auth_members (for roles_is_member_of()) pg_database (for
|
||||||
* has_rolinherit()), or pg_database (for roles_is_member_of())
|
* roles_is_member_of())
|
||||||
*/
|
*/
|
||||||
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
|
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
|
||||||
RoleMembershipCacheCallback,
|
RoleMembershipCacheCallback,
|
||||||
|
@ -4769,29 +4769,11 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
||||||
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
|
cached_role[ROLERECURSE_MEMBERS] = InvalidOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check if specified role has rolinherit set */
|
|
||||||
static bool
|
|
||||||
has_rolinherit(Oid roleid)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
HeapTuple utup;
|
|
||||||
|
|
||||||
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
|
|
||||||
if (HeapTupleIsValid(utup))
|
|
||||||
{
|
|
||||||
result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
|
|
||||||
ReleaseSysCache(utup);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a list of roles that the specified roleid is a member of
|
* Get a list of roles that the specified roleid is a member of
|
||||||
*
|
*
|
||||||
* Type ROLERECURSE_PRIVS recurses only through roles that have rolinherit
|
* Type ROLERECURSE_PRIVS recurses only through inheritable grants,
|
||||||
* set, while ROLERECURSE_MEMBERS recurses through all roles.
|
* while ROLERECURSE_MEMBERS recurses through all grants.
|
||||||
*
|
*
|
||||||
* Since indirect membership testing is relatively expensive, we cache
|
* Since indirect membership testing is relatively expensive, we cache
|
||||||
* a list of memberships. Hence, the result is only guaranteed good until
|
* a list of memberships. Hence, the result is only guaranteed good until
|
||||||
|
@ -4861,23 +4843,24 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
||||||
CatCList *memlist;
|
CatCList *memlist;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (type == ROLERECURSE_PRIVS && !has_rolinherit(memberid))
|
|
||||||
continue; /* ignore non-inheriting roles */
|
|
||||||
|
|
||||||
/* Find roles that memberid is directly a member of */
|
/* Find roles that memberid is directly a member of */
|
||||||
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
|
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
|
||||||
ObjectIdGetDatum(memberid));
|
ObjectIdGetDatum(memberid));
|
||||||
for (i = 0; i < memlist->n_members; i++)
|
for (i = 0; i < memlist->n_members; i++)
|
||||||
{
|
{
|
||||||
HeapTuple tup = &memlist->members[i]->tuple;
|
HeapTuple tup = &memlist->members[i]->tuple;
|
||||||
Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid;
|
Form_pg_auth_members form = (Form_pg_auth_members) GETSTRUCT(tup);
|
||||||
|
Oid otherid = form->roleid;
|
||||||
|
|
||||||
|
/* If we're supposed to ignore non-heritable grants, do so. */
|
||||||
|
if (type == ROLERECURSE_PRIVS && !form->inherit_option)
|
||||||
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* While otherid==InvalidOid shouldn't appear in the catalog, the
|
* While otherid==InvalidOid shouldn't appear in the catalog, the
|
||||||
* OidIsValid() avoids crashing if that arises.
|
* OidIsValid() avoids crashing if that arises.
|
||||||
*/
|
*/
|
||||||
if (otherid == admin_of &&
|
if (otherid == admin_of && form->admin_option &&
|
||||||
((Form_pg_auth_members) GETSTRUCT(tup))->admin_option &&
|
|
||||||
OidIsValid(admin_of) && !OidIsValid(*admin_role))
|
OidIsValid(admin_of) && !OidIsValid(*admin_role))
|
||||||
*admin_role = memberid;
|
*admin_role = memberid;
|
||||||
|
|
||||||
|
@ -4920,8 +4903,8 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
||||||
/*
|
/*
|
||||||
* Does member have the privileges of role (directly or indirectly)?
|
* Does member have the privileges of role (directly or indirectly)?
|
||||||
*
|
*
|
||||||
* This is defined not to recurse through roles that don't have rolinherit
|
* This is defined not to recurse through grants that are not inherited;
|
||||||
* set; for such roles, membership implies the ability to do SET ROLE, but
|
* in such cases, membership implies the ability to do SET ROLE, but
|
||||||
* the privileges are not available until you've done so.
|
* the privileges are not available until you've done so.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
|
@ -4948,7 +4931,7 @@ has_privs_of_role(Oid member, Oid role)
|
||||||
/*
|
/*
|
||||||
* Is member a member of role (directly or indirectly)?
|
* Is member a member of role (directly or indirectly)?
|
||||||
*
|
*
|
||||||
* This is defined to recurse through roles regardless of rolinherit.
|
* This is defined to recurse through grants whether they are inherited or not.
|
||||||
*
|
*
|
||||||
* Do not use this for privilege checking, instead use has_privs_of_role()
|
* Do not use this for privilege checking, instead use has_privs_of_role()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -947,11 +947,14 @@ static void
|
||||||
dumpRoleMembership(PGconn *conn)
|
dumpRoleMembership(PGconn *conn)
|
||||||
{
|
{
|
||||||
PQExpBuffer buf = createPQExpBuffer();
|
PQExpBuffer buf = createPQExpBuffer();
|
||||||
|
PQExpBuffer optbuf = createPQExpBuffer();
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
int start = 0,
|
int start = 0,
|
||||||
end,
|
end,
|
||||||
total;
|
total;
|
||||||
bool dump_grantors;
|
bool dump_grantors;
|
||||||
|
bool dump_inherit_option;
|
||||||
|
int i_inherit_option;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Previous versions of PostgreSQL didn't used to track the grantor very
|
* Previous versions of PostgreSQL didn't used to track the grantor very
|
||||||
|
@ -962,19 +965,28 @@ dumpRoleMembership(PGconn *conn)
|
||||||
*/
|
*/
|
||||||
dump_grantors = (PQserverVersion(conn) >= 160000);
|
dump_grantors = (PQserverVersion(conn) >= 160000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Previous versions of PostgreSQL also did not have a grant-level
|
||||||
|
* INHERIT option.
|
||||||
|
*/
|
||||||
|
dump_inherit_option = (server_version >= 160000);
|
||||||
|
|
||||||
/* Generate and execute query. */
|
/* Generate and execute query. */
|
||||||
printfPQExpBuffer(buf, "SELECT ur.rolname AS role, "
|
printfPQExpBuffer(buf, "SELECT ur.rolname AS role, "
|
||||||
"um.rolname AS member, "
|
"um.rolname AS member, "
|
||||||
"ug.oid AS grantorid, "
|
"ug.oid AS grantorid, "
|
||||||
"ug.rolname AS grantor, "
|
"ug.rolname AS grantor, "
|
||||||
"a.admin_option "
|
"a.admin_option");
|
||||||
"FROM pg_auth_members a "
|
if (dump_inherit_option)
|
||||||
|
appendPQExpBuffer(buf, ", a.inherit_option");
|
||||||
|
appendPQExpBuffer(buf, " FROM pg_auth_members a "
|
||||||
"LEFT JOIN %s ur on ur.oid = a.roleid "
|
"LEFT JOIN %s ur on ur.oid = a.roleid "
|
||||||
"LEFT JOIN %s um on um.oid = a.member "
|
"LEFT JOIN %s um on um.oid = a.member "
|
||||||
"LEFT JOIN %s ug on ug.oid = a.grantor "
|
"LEFT JOIN %s ug on ug.oid = a.grantor "
|
||||||
"WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
|
"WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
|
||||||
"ORDER BY 1,2,4", role_catalog, role_catalog, role_catalog);
|
"ORDER BY 1,2,4", role_catalog, role_catalog, role_catalog);
|
||||||
res = executeQuery(conn, buf->data);
|
res = executeQuery(conn, buf->data);
|
||||||
|
i_inherit_option = PQfnumber(res, "inherit_option");
|
||||||
|
|
||||||
if (PQntuples(res) > 0)
|
if (PQntuples(res) > 0)
|
||||||
fprintf(OPF, "--\n-- Role memberships\n--\n\n");
|
fprintf(OPF, "--\n-- Role memberships\n--\n\n");
|
||||||
|
@ -1079,10 +1091,24 @@ dumpRoleMembership(PGconn *conn)
|
||||||
rolename_insert(ht, member, &found);
|
rolename_insert(ht, member, &found);
|
||||||
|
|
||||||
/* Generate the actual GRANT statement. */
|
/* Generate the actual GRANT statement. */
|
||||||
|
resetPQExpBuffer(optbuf);
|
||||||
fprintf(OPF, "GRANT %s", fmtId(role));
|
fprintf(OPF, "GRANT %s", fmtId(role));
|
||||||
fprintf(OPF, " TO %s", fmtId(member));
|
fprintf(OPF, " TO %s", fmtId(member));
|
||||||
if (*admin_option == 't')
|
if (*admin_option == 't')
|
||||||
fprintf(OPF, " WITH ADMIN OPTION");
|
appendPQExpBufferStr(optbuf, "ADMIN OPTION");
|
||||||
|
if (dump_inherit_option)
|
||||||
|
{
|
||||||
|
char *inherit_option;
|
||||||
|
|
||||||
|
if (optbuf->data[0] != '\0')
|
||||||
|
appendPQExpBufferStr(optbuf, ", ");
|
||||||
|
inherit_option = PQgetvalue(res, i, i_inherit_option);
|
||||||
|
appendPQExpBuffer(optbuf, "INHERIT %s",
|
||||||
|
*inherit_option == 't' ?
|
||||||
|
"TRUE" : "FALSE");
|
||||||
|
}
|
||||||
|
if (optbuf->data[0] != '\0')
|
||||||
|
fprintf(OPF, " WITH %s", optbuf->data);
|
||||||
if (dump_grantors)
|
if (dump_grantors)
|
||||||
fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
|
fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
|
||||||
fprintf(OPF, ";\n");
|
fprintf(OPF, ";\n");
|
||||||
|
|
|
@ -57,6 +57,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202208221
|
#define CATALOG_VERSION_NO 202208251
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,6 +34,7 @@ CATALOG(pg_auth_members,1261,AuthMemRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_
|
||||||
Oid member BKI_LOOKUP(pg_authid); /* ID of a member of that role */
|
Oid member BKI_LOOKUP(pg_authid); /* ID of a member of that role */
|
||||||
Oid grantor BKI_LOOKUP(pg_authid); /* who granted the membership */
|
Oid grantor BKI_LOOKUP(pg_authid); /* who granted the membership */
|
||||||
bool admin_option; /* granted with admin option? */
|
bool admin_option; /* granted with admin option? */
|
||||||
|
bool inherit_option; /* exercise privileges without SET ROLE? */
|
||||||
} FormData_pg_auth_members;
|
} FormData_pg_auth_members;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
|
|
@ -28,7 +28,7 @@ extern Oid CreateRole(ParseState *pstate, CreateRoleStmt *stmt);
|
||||||
extern Oid AlterRole(ParseState *pstate, AlterRoleStmt *stmt);
|
extern Oid AlterRole(ParseState *pstate, AlterRoleStmt *stmt);
|
||||||
extern Oid AlterRoleSet(AlterRoleSetStmt *stmt);
|
extern Oid AlterRoleSet(AlterRoleSetStmt *stmt);
|
||||||
extern void DropRole(DropRoleStmt *stmt);
|
extern void DropRole(DropRoleStmt *stmt);
|
||||||
extern void GrantRole(GrantRoleStmt *stmt);
|
extern void GrantRole(ParseState *pstate, GrantRoleStmt *stmt);
|
||||||
extern ObjectAddress RenameRole(const char *oldname, const char *newname);
|
extern ObjectAddress RenameRole(const char *oldname, const char *newname);
|
||||||
extern void DropOwnedObjects(DropOwnedStmt *stmt);
|
extern void DropOwnedObjects(DropOwnedStmt *stmt);
|
||||||
extern void ReassignOwnedObjects(ReassignOwnedStmt *stmt);
|
extern void ReassignOwnedObjects(ReassignOwnedStmt *stmt);
|
||||||
|
|
|
@ -2451,7 +2451,7 @@ typedef struct GrantRoleStmt
|
||||||
List *granted_roles; /* list of roles to be granted/revoked */
|
List *granted_roles; /* list of roles to be granted/revoked */
|
||||||
List *grantee_roles; /* list of member roles to add/delete */
|
List *grantee_roles; /* list of member roles to add/delete */
|
||||||
bool is_grant; /* true = GRANT, false = REVOKE */
|
bool is_grant; /* true = GRANT, false = REVOKE */
|
||||||
bool admin_opt; /* with admin option */
|
List *opt; /* options e.g. WITH GRANT OPTION */
|
||||||
RoleSpec *grantor; /* set grantor to other than current role */
|
RoleSpec *grantor; /* set grantor to other than current role */
|
||||||
DropBehavior behavior; /* drop behavior (for REVOKE) */
|
DropBehavior behavior; /* drop behavior (for REVOKE) */
|
||||||
} GrantRoleStmt;
|
} GrantRoleStmt;
|
||||||
|
|
|
@ -517,7 +517,19 @@ BEGIN;
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
ALTER ROLE regress_priv_user1 NOINHERIT;
|
ALTER ROLE regress_priv_user1 NOINHERIT;
|
||||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
DELETE FROM atest3;
|
SAVEPOINT s1;
|
||||||
|
DELETE FROM atest3; -- ok because grant-level option is unchanged
|
||||||
|
ROLLBACK TO s1;
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
GRANT regress_priv_group2 TO regress_priv_user1 WITH INHERIT FALSE;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
DELETE FROM atest3; -- fail
|
||||||
|
ERROR: permission denied for table atest3
|
||||||
|
ROLLBACK TO s1;
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
REVOKE INHERIT OPTION FOR regress_priv_group2 FROM regress_priv_user1;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
DELETE FROM atest3; -- also fail
|
||||||
ERROR: permission denied for table atest3
|
ERROR: permission denied for table atest3
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- views
|
-- views
|
||||||
|
|
|
@ -351,7 +351,18 @@ BEGIN;
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
ALTER ROLE regress_priv_user1 NOINHERIT;
|
ALTER ROLE regress_priv_user1 NOINHERIT;
|
||||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
DELETE FROM atest3;
|
SAVEPOINT s1;
|
||||||
|
DELETE FROM atest3; -- ok because grant-level option is unchanged
|
||||||
|
ROLLBACK TO s1;
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
GRANT regress_priv_group2 TO regress_priv_user1 WITH INHERIT FALSE;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
DELETE FROM atest3; -- fail
|
||||||
|
ROLLBACK TO s1;
|
||||||
|
RESET SESSION AUTHORIZATION;
|
||||||
|
REVOKE INHERIT OPTION FOR regress_priv_group2 FROM regress_priv_user1;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
DELETE FROM atest3; -- also fail
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
-- views
|
-- views
|
||||||
|
|
Loading…
Reference in New Issue