diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index e7ade898a4..3b7b49b86a 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -131,7 +131,8 @@ have_createrole_privilege(void) Oid CreateRole(ParseState *pstate, CreateRoleStmt *stmt) { - Relation pg_authid_rel; + Relation pg_authid_rel, + pg_auth_members_rel; TupleDesc pg_authid_dsc; HeapTuple tuple; Datum new_record[Natts_pg_authid] = {0}; @@ -543,6 +544,11 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) GrantRoleOptions poptself; List *memberSpecs; List *memberIds = list_make1_oid(currentUserId); + ScanKeyData scankey; + SysScanDesc sscan; + HeapTuple tmp_tuple; + ObjectAddress depobj; + ObjectAddress refobj; current_role->roletype = ROLESPEC_CURRENT_ROLE; current_role->location = -1; @@ -565,6 +571,49 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) */ CommandCounterIncrement(); + /* + * Register the dependency of the current user's members on the current + * user. + * + * This is only done for members who have the admin option. The reason + * is that if the current user is dropped, members with the admin option + * would lose their control over the newly created role. Without this + * dependency, only superusers would be able to manage the new role, as + * the admin option would be revoked from these members. + */ + pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock); + + ScanKeyInit(&scankey, + Anum_pg_auth_members_roleid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(GetUserId())); + + sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId, + true, NULL, 1, &scankey); + + while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan))) + { + Form_pg_auth_members authmem_form; + authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple); + + if (authmem_form->admin_option) + { + depobj.classId = AuthIdRelationId; + depobj.objectId = authmem_form->member; + depobj.objectSubId = 0; + + refobj.classId = AuthIdRelationId; + refobj.objectId = authmem_form->roleid; + refobj.objectSubId = 0; + + recordSharedDependencyOn(&depobj, &refobj, + SHARED_DEPENDENCY_ACL); + } + } + + systable_endscan(sscan); + table_close(pg_auth_members_rel, NoLock); + /* * Because of the implicit grant above, a CREATEROLE user who creates * a role has the ability to grant that role back to themselves with @@ -1301,6 +1350,9 @@ DropRole(DropRoleStmt *stmt) errdetail_internal("%s", detail), errdetail_log("%s", detail_log))); + /* Remove any shared depencies of this role. */ + deleteSharedDependencyRecordsFor(AuthIdRelationId, roleid, 0); + /* * Remove the role from the pg_authid table */