aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/user.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/user.c')
-rw-r--r--src/backend/commands/user.c262
1 files changed, 211 insertions, 51 deletions
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 511ca8d8fd1..265a48af7eb 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;
+}