summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2022-08-25 10:06:02 -0400
committerRobert Haas <rhaas@postgresql.org>2022-08-25 10:06:02 -0400
commite3ce2de09d814f8770b2e3b3c152b7671bcdb83f (patch)
tree1ca5cf6da0ce86056ad4e573231a44ff651e252d /src/backend
parent2059c5e3b06545e7d0650dba9c665332374c3c21 (diff)
downloadpostgresql-e3ce2de09d814f8770b2e3b3c152b7671bcdb83f.tar.gz
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
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/user.c262
-rw-r--r--src/backend/parser/gram.y49
-rw-r--r--src/backend/tcop/utility.c2
-rw-r--r--src/backend/utils/adt/acl.c47
4 files changed, 267 insertions, 93 deletions
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 511ca8d8fd..265a48af7e 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -51,6 +51,9 @@
* RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
* 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
* by the operation.
*/
@@ -58,12 +61,22 @@ typedef enum
{
RRG_NOOP,
RRG_REMOVE_ADMIN_OPTION,
+ RRG_REMOVE_INHERIT_OPTION,
RRG_DELETE_GRANT
} RevokeRoleGrantAction;
/* Potentially set by pg_upgrade_support functions */
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 */
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,
List *memberSpecs, List *memberIds,
- Oid grantorId, bool admin_opt);
+ Oid grantorId, GrantRoleOptions *popt);
static void DelRoleMems(const char *rolename, Oid roleid,
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,
bool is_grant);
static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
static bool plan_single_revoke(CatCList *memlist,
RevokeRoleGrantAction *actions,
Oid member, Oid grantor,
- bool revoke_admin_option_only,
+ GrantRoleOptions *popt,
DropBehavior behavior);
static void plan_member_revoke(CatCList *memlist,
RevokeRoleGrantAction *actions, Oid member);
@@ -92,6 +106,7 @@ static void plan_recursive_revoke(CatCList *memlist,
int index,
bool revoke_admin_option_only,
DropBehavior behavior);
+static void InitGrantRoleOptions(GrantRoleOptions *popt);
/* Check if current user has createrole privileges */
@@ -144,6 +159,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
DefElem *dadminmembers = NULL;
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
+ GrantRoleOptions popt;
/* The defaults can vary depending on the original statement type */
switch (stmt->stmt_type)
@@ -462,6 +478,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
if (addroleto || adminmembers || rolemembers)
CommandCounterIncrement();
+ /* Default grant. */
+ InitGrantRoleOptions(&popt);
+
/*
* Add the new role to the specified existing roles.
*/
@@ -486,7 +505,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
AddRoleMems(oldrolename, oldroleid,
thisrole_list,
thisrole_oidlist,
- InvalidOid, false);
+ InvalidOid, &popt);
ReleaseSysCache(oldroletup);
}
@@ -497,11 +516,13 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
* option, rolemembers don't.
*/
AddRoleMems(stmt->role, roleid,
- adminmembers, roleSpecsToIds(adminmembers),
- InvalidOid, true);
- AddRoleMems(stmt->role, roleid,
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 */
InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
@@ -552,6 +573,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
Oid roleid;
+ GrantRoleOptions popt;
check_rolespec_name(stmt->role,
"Cannot alter reserved roles.");
@@ -843,6 +865,8 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
ReleaseSysCache(tuple);
heap_freetuple(new_tuple);
+ InitGrantRoleOptions(&popt);
+
/*
* Advance command counter so we can see new record; else tests in
* AddRoleMems may fail.
@@ -856,11 +880,11 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
if (stmt->action == +1) /* add members to role */
AddRoleMems(rolename, roleid,
rolemembers, roleSpecsToIds(rolemembers),
- InvalidOid, false);
+ InvalidOid, &popt);
else if (stmt->action == -1) /* drop members from role */
DelRoleMems(rolename, roleid,
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
*/
void
-GrantRole(GrantRoleStmt *stmt)
+GrantRole(ParseState *pstate, GrantRoleStmt *stmt)
{
Relation pg_authid_rel;
Oid grantor;
List *grantee_ids;
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)
grantor = get_rolespec_oid(stmt->grantor, false);
else
@@ -1355,11 +1414,11 @@ GrantRole(GrantRoleStmt *stmt)
pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
/*
- * Step through all of the granted roles and add/remove entries for the
- * grantees, or, if admin_opt is set, then just add/remove the admin
- * option.
- *
- * Note: Permissions checking is done by AddRoleMems/DelRoleMems
+ * Step through all of the granted roles and add, update, or remove
+ * entries in pg_auth_members as appropriate. If stmt->is_grant is true,
+ * 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
+ * removing options from them.
*/
foreach(item, stmt->granted_roles)
{
@@ -1377,11 +1436,11 @@ GrantRole(GrantRoleStmt *stmt)
if (stmt->is_grant)
AddRoleMems(rolename, roleid,
stmt->grantee_roles, grantee_ids,
- grantor, stmt->admin_opt);
+ grantor, &popt);
else
DelRoleMems(rolename, roleid,
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)
* memberIds: OIDs of roles to add
* grantorId: who is granting the membership (InvalidOid if not set explicitly)
- * admin_opt: granting admin option?
+ * popt: information about grant options
*/
static void
AddRoleMems(const char *rolename, Oid roleid,
List *memberSpecs, List *memberIds,
- Oid grantorId, bool admin_opt)
+ Oid grantorId, GrantRoleOptions *popt)
{
Relation pg_authmem_rel;
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
* X back to A).
*/
- if (admin_opt && grantorId != BOOTSTRAP_SUPERUSERID)
+ if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID)
{
CatCList *memlist;
RevokeRoleGrantAction *actions;
@@ -1669,25 +1728,55 @@ AddRoleMems(const char *rolename, Oid roleid,
Datum new_record[Natts_pg_auth_members] = {0};
bool new_record_nulls[Natts_pg_auth_members] = {0};
bool new_record_repl[Natts_pg_auth_members] = {0};
- Form_pg_auth_members authmem_form;
- /*
- * Check if entry for this role/member already exists; if so, give
- * warning unless we are adding admin option.
- */
+ /* Common initialization for possible 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);
+
+ /* Find any existing tuple */
authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
ObjectIdGetDatum(roleid),
ObjectIdGetDatum(memberid),
ObjectIdGetDatum(grantorId));
- if (!HeapTupleIsValid(authmem_tuple))
- {
- authmem_form = NULL;
- }
- else
+
+ /*
+ * If we found a tuple, update it with new option values, unless
+ * there are no changes, in which case issue a WARNING.
+ *
+ * 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);
- 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,
(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);
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,
new_record,
new_record_nulls, new_record_repl);
@@ -1719,6 +1798,33 @@ AddRoleMems(const char *rolename, Oid roleid,
Oid objectId;
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();
new_record[Anum_pg_auth_members_oid - 1] = objectId;
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)
* memberIds: OIDs of roles to del
* 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
*/
static void
DelRoleMems(const char *rolename, Oid roleid,
List *memberSpecs, List *memberIds,
- Oid grantorId, bool admin_opt, DropBehavior behavior)
+ Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior)
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
@@ -1824,7 +1930,7 @@ DelRoleMems(const char *rolename, Oid roleid,
Oid memberid = lfirst_oid(iditem);
if (!plan_single_revoke(memlist, actions, memberid, grantorId,
- admin_opt, behavior))
+ popt, behavior))
{
ereport(WARNING,
(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
{
- /* Just turn off the admin option */
+ /* Just turn off the specified option */
HeapTuple tuple;
Datum new_record[Natts_pg_auth_members] = {0};
bool new_record_nulls[Natts_pg_auth_members] = {0};
bool new_record_repl[Natts_pg_auth_members] = {0};
/* Build a tuple to update with */
- new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
- new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
+ if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
+ {
+ 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,
new_record,
@@ -2028,11 +2148,21 @@ initialize_revoke_actions(CatCList *memlist)
*/
static bool
plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
- Oid member, Oid grantor, bool revoke_admin_option_only,
+ Oid member, Oid grantor, GrantRoleOptions *popt,
DropBehavior behavior)
{
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)
{
HeapTuple authmem_tuple;
@@ -2044,8 +2174,27 @@ plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
if (authmem_form->member == member &&
authmem_form->grantor == grantor)
{
- plan_recursive_revoke(memlist, actions, i,
- revoke_admin_option_only, behavior);
+ if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
+ {
+ /*
+ * 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;
}
}
@@ -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;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c8bd66dd54..b5ab9d9c9a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -362,9 +362,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> utility_option_arg
%type <defelt> drop_option
%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_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 <list> OptRoleList AlterOptRoleList
@@ -7848,15 +7851,26 @@ opt_grant_grant_option:
*****************************************************************************/
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);
n->is_grant = true;
n->granted_roles = $2;
n->grantee_roles = $4;
- n->admin_opt = $5;
- n->grantor = $6;
+ n->opt = NIL;
+ 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;
}
;
@@ -7867,19 +7881,22 @@ RevokeRoleStmt:
GrantRoleStmt *n = makeNode(GrantRoleStmt);
n->is_grant = false;
- n->admin_opt = false;
+ n->opt = NIL;
n->granted_roles = $2;
n->grantee_roles = $4;
n->grantor = $5;
n->behavior = $6;
$$ = (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);
+ DefElem *opt;
+ opt = makeDefElem(pstrdup($2),
+ (Node *) makeBoolean(false), @2);
n->is_grant = false;
- n->admin_opt = true;
+ n->opt = list_make1(opt);
n->granted_roles = $5;
n->grantee_roles = $7;
n->grantor = $8;
@@ -7888,8 +7905,22 @@ RevokeRoleStmt:
}
;
-opt_grant_admin_option: WITH ADMIN OPTION { $$ = true; }
- | /*EMPTY*/ { $$ = false; }
+grant_role_opt_list:
+ 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; }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6b0a865262..aa00815787 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -767,7 +767,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_GrantRoleStmt:
/* no event triggers for global objects */
- GrantRole((GrantRoleStmt *) parsetree);
+ GrantRole(pstate, (GrantRoleStmt *) parsetree);
break;
case T_CreatedbStmt:
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3e045da31f..ea28da26a8 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -66,7 +66,7 @@ typedef struct
*/
enum RoleRecurseType
{
- ROLERECURSE_PRIVS = 0, /* recurse if rolinherit */
+ ROLERECURSE_PRIVS = 0, /* recurse through inheritable grants */
ROLERECURSE_MEMBERS = 1 /* recurse unconditionally */
};
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
- * of pg_auth_members (for roles_is_member_of()), pg_authid (for
- * has_rolinherit()), or pg_database (for roles_is_member_of())
+ * of pg_auth_members (for roles_is_member_of()) pg_database (for
+ * roles_is_member_of())
*/
CacheRegisterSyscacheCallback(AUTHMEMROLEMEM,
RoleMembershipCacheCallback,
@@ -4769,29 +4769,11 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
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
*
- * Type ROLERECURSE_PRIVS recurses only through roles that have rolinherit
- * set, while ROLERECURSE_MEMBERS recurses through all roles.
+ * Type ROLERECURSE_PRIVS recurses only through inheritable grants,
+ * while ROLERECURSE_MEMBERS recurses through all grants.
*
* Since indirect membership testing is relatively expensive, we cache
* 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;
int i;
- if (type == ROLERECURSE_PRIVS && !has_rolinherit(memberid))
- continue; /* ignore non-inheriting roles */
-
/* Find roles that memberid is directly a member of */
memlist = SearchSysCacheList1(AUTHMEMMEMROLE,
ObjectIdGetDatum(memberid));
for (i = 0; i < memlist->n_members; i++)
{
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
* OidIsValid() avoids crashing if that arises.
*/
- if (otherid == admin_of &&
- ((Form_pg_auth_members) GETSTRUCT(tup))->admin_option &&
+ if (otherid == admin_of && form->admin_option &&
OidIsValid(admin_of) && !OidIsValid(*admin_role))
*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)?
*
- * This is defined not to recurse through roles that don't have rolinherit
- * set; for such roles, membership implies the ability to do SET ROLE, but
+ * This is defined not to recurse through grants that are not inherited;
+ * in such cases, membership implies the ability to do SET ROLE, but
* the privileges are not available until you've done so.
*/
bool
@@ -4948,7 +4931,7 @@ has_privs_of_role(Oid member, Oid role)
/*
* 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()
*/