From 02e212557971a4638fa551bd0928c68f2e56b2a4 Mon Sep 17 00:00:00 2001 From: Haoyu Huang Date: Wed, 7 Jan 2026 01:26:51 +0000 Subject: [PATCH 1/2] c --- src/backend/commands/user.c | 39 +++++++++++++++++++++++++++++++++---- src/backend/utils/adt/acl.c | 10 ++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index aed01cf4cae..66453411c55 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -98,7 +98,7 @@ static void DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid, Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior); static void check_role_membership_authorization(Oid currentUserId, Oid roleid, - bool is_grant); + bool is_grant, GrantRoleOptions * popt); static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant); static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist); @@ -517,7 +517,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) char *oldrolename = NameStr(oldroleform->rolname); /* can only add this role to roles for which you have rights */ - check_role_membership_authorization(currentUserId, oldroleid, true); + check_role_membership_authorization(currentUserId, oldroleid, true, &popt); AddRoleMems(currentUserId, oldrolename, oldroleid, thisrole_list, thisrole_oidlist, @@ -1557,7 +1557,7 @@ GrantRole(ParseState *pstate, GrantRoleStmt *stmt) roleid = get_role_oid(rolename, false); check_role_membership_authorization(currentUserId, - roleid, stmt->is_grant); + roleid, stmt->is_grant, &popt); if (stmt->is_grant) AddRoleMems(currentUserId, rolename, roleid, stmt->grantee_roles, grantee_ids, @@ -2108,7 +2108,7 @@ DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid, */ static void check_role_membership_authorization(Oid currentUserId, Oid roleid, - bool is_grant) + bool is_grant, GrantRoleOptions * popt) { /* * The charter of pg_database_owner is to have exactly one, implicit, @@ -2147,6 +2147,37 @@ check_role_membership_authorization(Oid currentUserId, Oid roleid, } else { + + // if currentUserId is a member of privileged role and roleid is the privilegd role, allow it. + Oid privileged_role_oid = get_role_oid("databricks_superuser", true); + if (is_member_of_role(currentUserId, privileged_role_oid) && roleid == privileged_role_oid) + { + if (is_grant) + { + if (!popt->admin && !popt->set) + { + return; + } + } + else + { + // revoke is allowed. + return; + } + // if privilege role has only one member with LOGIN but it's a revoke, deny it. + // MemList *memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid)); + // if (memlist->n_members == 1 && memlist->members[0]->tuple.t_data[Anum_pg_auth_members_login - 1] == BoolGetDatum(true)) + // { + // ereport(ERROR, + // (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + // errmsg("permission denied to revoke role \"%s\"", + // GetUserNameFromId(roleid, false)), + // errdetail("Only roles with the %s option on role \"%s\" may revoke this role.", + // "ADMIN", GetUserNameFromId(roleid, false)))); + // } + // return; + } + /* * Otherwise, must have admin option on the role to be changed. */ diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 5e648e6a17c..f1de9a9a04e 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -5336,6 +5336,16 @@ select_best_admin(Oid member, Oid role) return InvalidOid; (void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role); + + if (!OidIsValid(admin_role)) + { + // if member is a member of privileged role and role is the privilegd role, return the member. + Oid privileged_role_oid = get_role_oid("databricks_superuser", true); + if (is_member_of_role(member, privileged_role_oid) && role == privileged_role_oid) + { + return BOOTSTRAP_SUPERUSERID; + } + } return admin_role; } From 9b26be4f9025172bfb5a883feb0a2bc0d197a65d Mon Sep 17 00:00:00 2001 From: Haoyu Huang Date: Thu, 8 Jan 2026 01:32:11 +0000 Subject: [PATCH 2/2] c --- src/backend/commands/user.c | 49 ++++++------------------------------- src/backend/utils/adt/acl.c | 10 +++----- src/include/commands/user.h | 11 +++++++++ src/include/utils/acl.h | 3 +++ 4 files changed, 24 insertions(+), 49 deletions(-) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 66453411c55..2f0e6c09b0f 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -69,14 +69,6 @@ typedef enum /* Potentially set by pg_upgrade_support functions */ Oid binary_upgrade_next_pg_authid_oid = InvalidOid; -typedef struct -{ - unsigned specified; - bool admin; - bool inherit; - bool set; -} GrantRoleOptions; - #define GRANT_ROLE_SPECIFIED_ADMIN 0x0001 #define GRANT_ROLE_SPECIFIED_INHERIT 0x0002 #define GRANT_ROLE_SPECIFIED_SET 0x0004 @@ -89,7 +81,7 @@ GrantRoleOptions createrole_self_grant_options; /* Hook to check passwords in CreateRole() and AlterRole() */ check_password_hook_type check_password_hook = NULL; - +CheckRoleMembershipAuthorization_hook_type CheckRoleMembershipAuthorization_hook = NULL; static void AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid, List *memberSpecs, List *memberIds, Oid grantorId, GrantRoleOptions *popt); @@ -98,7 +90,7 @@ static void DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid, Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior); static void check_role_membership_authorization(Oid currentUserId, Oid roleid, - bool is_grant, GrantRoleOptions * popt); + bool is_grant, List* memberIds, GrantRoleOptions * popt); static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant); static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist); @@ -517,7 +509,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) char *oldrolename = NameStr(oldroleform->rolname); /* can only add this role to roles for which you have rights */ - check_role_membership_authorization(currentUserId, oldroleid, true, &popt); + check_role_membership_authorization(currentUserId, oldroleid, true, thisrole_oidlist, &popt); AddRoleMems(currentUserId, oldrolename, oldroleid, thisrole_list, thisrole_oidlist, @@ -1557,7 +1549,7 @@ GrantRole(ParseState *pstate, GrantRoleStmt *stmt) roleid = get_role_oid(rolename, false); check_role_membership_authorization(currentUserId, - roleid, stmt->is_grant, &popt); + roleid, stmt->is_grant, grantee_ids, &popt); if (stmt->is_grant) AddRoleMems(currentUserId, rolename, roleid, stmt->grantee_roles, grantee_ids, @@ -2108,7 +2100,7 @@ DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid, */ static void check_role_membership_authorization(Oid currentUserId, Oid roleid, - bool is_grant, GrantRoleOptions * popt) + bool is_grant, List *memberIds, GrantRoleOptions * popt) { /* * The charter of pg_database_owner is to have exactly one, implicit, @@ -2147,37 +2139,10 @@ check_role_membership_authorization(Oid currentUserId, Oid roleid, } else { - - // if currentUserId is a member of privileged role and roleid is the privilegd role, allow it. - Oid privileged_role_oid = get_role_oid("databricks_superuser", true); - if (is_member_of_role(currentUserId, privileged_role_oid) && roleid == privileged_role_oid) + if (CheckRoleMembershipAuthorization_hook && CheckRoleMembershipAuthorization_hook(currentUserId, roleid, is_grant, memberIds, popt)) { - if (is_grant) - { - if (!popt->admin && !popt->set) - { - return; - } - } - else - { - // revoke is allowed. - return; - } - // if privilege role has only one member with LOGIN but it's a revoke, deny it. - // MemList *memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid)); - // if (memlist->n_members == 1 && memlist->members[0]->tuple.t_data[Anum_pg_auth_members_login - 1] == BoolGetDatum(true)) - // { - // ereport(ERROR, - // (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - // errmsg("permission denied to revoke role \"%s\"", - // GetUserNameFromId(roleid, false)), - // errdetail("Only roles with the %s option on role \"%s\" may revoke this role.", - // "ADMIN", GetUserNameFromId(roleid, false)))); - // } - // return; + return; } - /* * Otherwise, must have admin option on the role to be changed. */ diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index f1de9a9a04e..d488fff2830 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -134,6 +134,7 @@ static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue * Generally neon_superuser on neon.com */ char *privileged_role_name = NULL; +SelectBestAdmin_hook_type SelectBestAdmin_hook = NULL; bool is_privileged_role(void) @@ -5337,14 +5338,9 @@ select_best_admin(Oid member, Oid role) (void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role); - if (!OidIsValid(admin_role)) + if (SelectBestAdmin_hook) { - // if member is a member of privileged role and role is the privilegd role, return the member. - Oid privileged_role_oid = get_role_oid("databricks_superuser", true); - if (is_member_of_role(member, privileged_role_oid) && role == privileged_role_oid) - { - return BOOTSTRAP_SUPERUSERID; - } + SelectBestAdmin_hook(&admin_role, member, role); } return admin_role; } diff --git a/src/include/commands/user.h b/src/include/commands/user.h index 97dcb93791b..9a79402b8d4 100644 --- a/src/include/commands/user.h +++ b/src/include/commands/user.h @@ -17,6 +17,14 @@ #include "parser/parse_node.h" #include "utils/guc.h" +typedef struct +{ + unsigned specified; + bool admin; + bool inherit; + bool set; +} GrantRoleOptions; + /* GUCs */ extern PGDLLIMPORT int Password_encryption; /* values from enum PasswordType */ extern PGDLLIMPORT char *createrole_self_grant; @@ -26,6 +34,9 @@ typedef void (*check_password_hook_type) (const char *username, const char *shad extern PGDLLIMPORT check_password_hook_type check_password_hook; +typedef bool (*CheckRoleMembershipAuthorization_hook_type) (Oid currentUserId, Oid roleid, bool is_grant, List *memberIds, GrantRoleOptions * popt); +extern PGDLLIMPORT CheckRoleMembershipAuthorization_hook_type CheckRoleMembershipAuthorization_hook; + extern Oid CreateRole(ParseState *pstate, CreateRoleStmt *stmt); extern Oid AlterRole(ParseState *pstate, AlterRoleStmt *stmt); extern Oid AlterRoleSet(AlterRoleSetStmt *stmt); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 731d84b2a93..7861a947e1f 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -287,4 +287,7 @@ extern bool object_ownercheck(Oid classid, Oid objectid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); extern bool has_bypassrls_privilege(Oid roleid); +typedef void (*SelectBestAdmin_hook_type) (Oid* admin_role, Oid member, Oid role); +extern PGDLLIMPORT SelectBestAdmin_hook_type SelectBestAdmin_hook; + #endif /* ACL_H */