diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 4e6410f6d..797a63a02 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -56,6 +56,13 @@ #include "distributed/version_compat.h" #include "distributed/worker_transaction.h" +typedef struct DistributedRolesInGrantRoleStmt +{ + List *distributedGrantees; + List *distributedGrantedRoles; + RoleSpec *grantor; + bool isGrantRoleStmtValid; +} DistributedRolesInGrantRoleStmt; static const char * ExtractEncryptedPassword(Oid roleOid); static const char * CreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt); @@ -68,6 +75,7 @@ static DefElem * makeDefElemBool(char *name, bool value); static List * GenerateRoleOptionsList(HeapTuple tuple); static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options); static List * GenerateGrantRoleStmtsOfRole(Oid roleid); +static ObjectAddress * GetRoleObjectAddressFromOid(Oid roleOid); static GrantRoleStmt * GetGrantRoleStmtFromAuthMemberRecord(Form_pg_auth_members membership); static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename); @@ -937,6 +945,29 @@ GenerateGrantRoleStmts() { Form_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple); + /* we only propagate the grant if the grantor is + * member and role are distributed since all are required + * to be distributed for the grant to be propagated + */ + + + bool isAuthMemberDistributed = IsAnyObjectDistributed(list_make1( + GetRoleObjectAddressFromOid( + membership->grantor))) + && + IsAnyObjectDistributed(list_make1( + GetRoleObjectAddressFromOid( + membership->member))) + && + IsAnyObjectDistributed(list_make1( + GetRoleObjectAddressFromOid( + membership->roleid))); + if (!isAuthMemberDistributed) + { + /* we only need to propagate the grant if the grantor is distributed */ + continue; + } + GrantRoleStmt *grantRoleStmt = GetGrantRoleStmtFromAuthMemberRecord(membership); if (grantRoleStmt == NULL) { @@ -972,6 +1003,15 @@ GenerateGrantRoleStmts() } +static ObjectAddress * +GetRoleObjectAddressFromOid(Oid roleOid) +{ + ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid); + return roleAddress; +} + + static GrantRoleStmt * GetGrantRoleStmtFromAuthMemberRecord(Form_pg_auth_members membership) { @@ -1310,6 +1350,38 @@ FilterDistributedRoles(List *roles) } +/* + * FilterDistributedRoles filters the list of AccessPrivs and returns the ones + * that are distributed. + */ +List * +FilterDistributedGrantedRoles(List *roles) +{ + List *distributedRoles = NIL; + Node *roleNode = NULL; + foreach_ptr(roleNode, roles) + { + AccessPriv *role = castNode(AccessPriv, roleNode); + Oid roleOid = get_role_oid(role->priv_name, false); + if (roleOid == InvalidOid) + { + /* + * Non-existing roles are ignored silently here. Postgres will + * handle to give an error or not for these roles. + */ + continue; + } + ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid); + if (IsAnyObjectDistributed(list_make1(roleAddress))) + { + distributedRoles = lappend(distributedRoles, role); + } + } + return distributedRoles; +} + + /* * PreprocessGrantRoleStmt finds the distributed grantee roles and creates the * query to run on the workers. @@ -1327,17 +1399,22 @@ PreprocessGrantRoleStmt(Node *node, const char *queryString, GrantRoleStmt *stmt = castNode(GrantRoleStmt, node); List *allGranteeRoles = stmt->grantee_roles; + List *allGrantedRoles = stmt->granted_roles; RoleSpec *grantor = stmt->grantor; - List *distributedGranteeRoles = FilterDistributedRoles(allGranteeRoles); - if (list_length(distributedGranteeRoles) <= 0) + DistributedRolesInGrantRoleStmt *distributedRolesInGrantStmt = + ExtractDistributedRolesInGrantRoleStmt(stmt); + + if (!distributedRolesInGrantStmt->isGrantRoleStmtValid) { return NIL; } - stmt->grantee_roles = distributedGranteeRoles; + stmt->grantee_roles = distributedRolesInGrantStmt->distributedGrantees; + stmt->granted_roles = distributedRolesInGrantStmt->distributedGrantedRoles; char *sql = DeparseTreeNode((Node *) stmt); stmt->grantee_roles = allGranteeRoles; + stmt->granted_roles = allGrantedRoles; stmt->grantor = grantor; List *commands = list_make3(DISABLE_DDL_PROPAGATION, @@ -1348,6 +1425,55 @@ PreprocessGrantRoleStmt(Node *node, const char *queryString, } +DistributedRolesInGrantRoleStmt * +ExtractDistributedRolesInGrantRoleStmt(GrantRoleStmt *stmt) +{ + DistributedRolesInGrantRoleStmt *distributedRolesInGrantRoleStmt = palloc0( + sizeof(DistributedRolesInGrantRoleStmt)); + distributedRolesInGrantRoleStmt->distributedGrantees = FilterDistributedRoles( + stmt->grantee_roles); + distributedRolesInGrantRoleStmt->distributedGrantedRoles = + FilterDistributedGrantedRoles(stmt->granted_roles); + distributedRolesInGrantRoleStmt->grantor = stmt->grantor; + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = true; + + bool grantorMissingOk = false; + bool isGrantorDefined = distributedRolesInGrantRoleStmt->grantor != NULL && + get_rolespec_oid(distributedRolesInGrantRoleStmt->grantor, + grantorMissingOk) != + InvalidOid; + bool isGrantorDistributed = IsAnyObjectDistributed(RoleSpecToObjectAddress( + distributedRolesInGrantRoleStmt + ->grantor, grantorMissingOk)); + bool skipDueToGrantor = isGrantorDefined && !isGrantorDistributed; + if (list_length(distributedRolesInGrantRoleStmt->distributedGrantees) <= 0) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since no grantees are distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + if (list_length(distributedRolesInGrantRoleStmt->distributedGrantedRoles) <= 0) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since no granted roles are distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + if (skipDueToGrantor) + { + ereport(NOTICE, (errmsg("not propagating GRANT command to other nodes"), + errhint("Since grantor is not distributed, " + "the GRANT command will not be propagated to other nodes."))); + distributedRolesInGrantRoleStmt->isGrantRoleStmtValid = false; + } + + return distributedRolesInGrantRoleStmt; +} + + /* * PostprocessGrantRoleStmt actually creates the plan we need to execute for grant * role statement. diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 0b005d5d2..e0b45a8a2 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -523,7 +523,10 @@ extern List * RenameRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess); extern void UnmarkRolesDistributed(List *roles); +extern List * FilterDistributedGrantedRoles(List *roles); extern List * FilterDistributedRoles(List *roles); +extern DistributedRolesInGrantRoleStmt * ExtractDistributedRolesInGrantRoleStmt( + GrantRoleStmt *stmt); extern List * GenerateGrantRoleStmts(void); /* schema.c - forward declarations */ diff --git a/src/test/regress/expected/granted_by_support.out b/src/test/regress/expected/granted_by_support.out index 976f7c7a0..3a956bd52 100644 --- a/src/test/regress/expected/granted_by_support.out +++ b/src/test/regress/expected/granted_by_support.out @@ -1,47 +1,68 @@ -- Active: 1700033167033@@localhost@9700@gurkanindibay@public --In below tests, complex role hierarchy is created and then granted by support is tested. +--- Test 1: Tests from main database select 1 from citus_remove_node ('localhost',:worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) -create role role1; -create role role2; -create role role3; -create role role4; -create role "role5'_test"; -grant role2 to role1 with admin option; -grant role2 to role3 with admin option granted by role1; -grant role3 to role4 with admin option; -grant role3 to "role5'_test" granted by role4; -grant role2 to "role5'_test" granted by role3; -grant role4 to "role5'_test" with admin option; -grant role4 to role1 with admin option GRANTED BY "role5'_test"; -grant role4 to role3 with admin option GRANTED BY role1; -ERROR: role "role4" is a member of role "role3" -grant role3 to role1 with admin option GRANTED BY role4; -grant "role5'_test" to role1 with admin option; -grant "role5'_test" to role3 with admin option GRANTED BY role1; -ERROR: role "role5'_test" is a member of role "role3" -SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option - FROM pg_auth_members - WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') - order by member::regrole::text, roleid::regrole::text; - member | role | grantor | admin_option +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +reset citus.enable_create_role_propagation; +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname --------------------------------------------------------------------- - "role5'_test" | role2 | role3 | f - "role5'_test" | role3 | role4 | f - "role5'_test" | role4 | postgres | t - role1 | "role5'_test" | postgres | t - role1 | role2 | postgres | t - role1 | role3 | role4 | t - role1 | role4 | "role5'_test" | t - role3 | role2 | role1 | t - role4 | role3 | postgres | t -(9 rows) +(0 rows) +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +reset citus.enable_create_role_propagation; +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +grant dist_role2 to non_dist_role1 with admin option; +NOTICE: not propagating GRANT command to other nodes +HINT: Since no grantees are distributed, the GRANT command will not be propagated to other nodes. +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +grant dist_role2 to dist_role4 granted by non_dist_role1 ;--will not be propagated since grantor is non-distributed +NOTICE: not propagating GRANT command to other nodes +HINT: Since grantor is not distributed, the GRANT command will not be propagated to other nodes. +grant dist_role4 to "dist_role5'_test" with admin option; +--below command propagates the non_dist_role1 since non_dist_role1 is already granted to dist_role1 +--and citus sees granted roles as a dependency and citus propagates the dependent roles +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- + non_dist_role1 +(1 row) + +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 +ERROR: role "dist_role4" is a member of role "dist_role3" +grant non_dist_role1 to dist_role4 granted by dist_role1; +ERROR: permission denied to grant privileges as role "dist_role1" +DETAIL: The grantor must have the ADMIN option on role "non_dist_role1". +CONTEXT: while executing command on localhost:xxxxx +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" +ERROR: role "dist_role5'_test" is a member of role "dist_role3" select result FROM run_command_on_all_nodes( $$ SELECT array_to_json(array_agg(row_to_json(t))) @@ -49,15 +70,15 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ ); - result + result --------------------------------------------------------------------- - [{"member":"\"role5'_test\"","role":"role2","grantor":"role3","admin_option":false},{"member":"\"role5'_test\"","role":"role3","grantor":"role4","admin_option":false},{"member":"\"role5'_test\"","role":"role4","grantor":"postgres","admin_option":true},{"member":"role1","role":"\"role5'_test\"","grantor":"postgres","admin_option":true},{"member":"role1","role":"role2","grantor":"postgres","admin_option":true},{"member":"role1","role":"role3","grantor":"role4","admin_option":true},{"member":"role1","role":"role4","grantor":"\"role5'_test\"","admin_option":true},{"member":"role3","role":"role2","grantor":"role1","admin_option":true},{"member":"role4","role":"role3","grantor":"postgres","admin_option":true}] - [{"member":"\"role5'_test\"","role":"role2","grantor":"role3","admin_option":false},{"member":"\"role5'_test\"","role":"role3","grantor":"role4","admin_option":false},{"member":"\"role5'_test\"","role":"role4","grantor":"postgres","admin_option":true},{"member":"role1","role":"\"role5'_test\"","grantor":"postgres","admin_option":true},{"member":"role1","role":"role2","grantor":"postgres","admin_option":true},{"member":"role1","role":"role3","grantor":"role4","admin_option":true},{"member":"role1","role":"role4","grantor":"\"role5'_test\"","admin_option":true},{"member":"role3","role":"role2","grantor":"role1","admin_option":true},{"member":"role4","role":"role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] (2 rows) select 1 from citus_add_node ('localhost',:worker_2_port); @@ -73,20 +94,27 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ ); - result + result --------------------------------------------------------------------- - [{"member":"\"role5'_test\"","role":"role2","grantor":"role3","admin_option":false},{"member":"\"role5'_test\"","role":"role3","grantor":"role4","admin_option":false},{"member":"\"role5'_test\"","role":"role4","grantor":"postgres","admin_option":true},{"member":"role1","role":"\"role5'_test\"","grantor":"postgres","admin_option":true},{"member":"role1","role":"role2","grantor":"postgres","admin_option":true},{"member":"role1","role":"role3","grantor":"role4","admin_option":true},{"member":"role1","role":"role4","grantor":"\"role5'_test\"","admin_option":true},{"member":"role3","role":"role2","grantor":"role1","admin_option":true},{"member":"role4","role":"role3","grantor":"postgres","admin_option":true}] - [{"member":"\"role5'_test\"","role":"role2","grantor":"role3","admin_option":false},{"member":"\"role5'_test\"","role":"role3","grantor":"role4","admin_option":false},{"member":"\"role5'_test\"","role":"role4","grantor":"postgres","admin_option":true},{"member":"role1","role":"\"role5'_test\"","grantor":"postgres","admin_option":true},{"member":"role1","role":"role2","grantor":"postgres","admin_option":true},{"member":"role1","role":"role3","grantor":"role4","admin_option":true},{"member":"role1","role":"role4","grantor":"\"role5'_test\"","admin_option":true},{"member":"role3","role":"role2","grantor":"role1","admin_option":true},{"member":"role4","role":"role3","grantor":"postgres","admin_option":true}] - [{"member":"\"role5'_test\"","role":"role2","grantor":"role3","admin_option":false},{"member":"\"role5'_test\"","role":"role3","grantor":"role4","admin_option":false},{"member":"\"role5'_test\"","role":"role4","grantor":"postgres","admin_option":true},{"member":"role1","role":"\"role5'_test\"","grantor":"postgres","admin_option":true},{"member":"role1","role":"role2","grantor":"postgres","admin_option":true},{"member":"role1","role":"role3","grantor":"role4","admin_option":true},{"member":"role1","role":"role4","grantor":"\"role5'_test\"","admin_option":true},{"member":"role3","role":"role2","grantor":"role1","admin_option":true},{"member":"role4","role":"role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role2","grantor":"non_dist_role1","admin_option":false},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] (3 rows) --clean all resources -drop role role1,role2,role3,role4,"role5'_test"; +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1; +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +reset citus.enable_create_role_propagation; select result FROM run_command_on_all_nodes( $$ SELECT array_to_json(array_agg(row_to_json(t))) @@ -94,7 +122,7 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ @@ -106,3 +134,171 @@ select result FROM run_command_on_all_nodes( (3 rows) +--- Test 2: Tests from non-main database +set citus.enable_create_database_propagation to on; +create database test_granted_by_support; +select 1 from citus_remove_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c test_granted_by_support +--here in below block since 'citus.enable_create_role_propagation to off ' is not effective, +--non_dist_role1 is being propagated to dist_role1 unlike main db scenario +--non_dist_role1 will be used for the test scenarios in this section +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +reset citus.enable_create_role_propagation; +--dropping since it isn't non-distributed as intended +drop role non_dist_role1; +--creating non_dist_role1 again in main database +--This is actually non-distributed role +\c regression +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +NOTICE: not propagating CREATE ROLE/USER commands to other nodes +HINT: Connect to other nodes directly to manually create all necessary users and roles. +reset citus.enable_create_role_propagation; +\c test_granted_by_support +create role dist_role1; +create role dist_role1; +ERROR: role "dist_role1" already exists +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c test_granted_by_support +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +\c regression +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +reset citus.enable_create_role_propagation; +\c test_granted_by_support +grant dist_role2 to non_dist_role1 with admin option; +ERROR: failure on connection marked as essential: localhost:xxxxx +CONTEXT: while executing command on localhost:xxxxx +\c test_granted_by_support - - :worker_1_port +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +grant dist_role2 to dist_role4 granted by non_dist_role1 ;--will not be propagated since grantor is non-distributed +ERROR: role "non_dist_role1" does not exist +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c test_granted_by_support - - :worker_1_port +grant dist_role4 to "dist_role5'_test" with admin option; +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c test_granted_by_support +-- Unlike maindb scenario, non-maindb scenario doesn't propagate 'create non_dist_role1' to +--workers as it doesn't create dependency objects for non-distributed roles. +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + rolname +--------------------------------------------------------------------- +(0 rows) + +\c test_granted_by_support - - :worker_1_port +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 +ERROR: role "dist_role4" is a member of role "dist_role3" +grant non_dist_role1 to dist_role4 granted by dist_role1; +ERROR: role "non_dist_role1" does not exist +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" +ERROR: role "dist_role5'_test" is a member of role "dist_role3" +\c regression - - :master_port +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] +(2 rows) + +select 1 from citus_add_node ('localhost',:worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role1","role":"non_dist_role1","grantor":"postgres","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] + [{"member":"dist_role1","role":"\"dist_role5'_test\"","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role2","grantor":"postgres","admin_option":true},{"member":"dist_role1","role":"dist_role3","grantor":"dist_role4","admin_option":true},{"member":"dist_role1","role":"dist_role4","grantor":"\"dist_role5'_test\"","admin_option":true},{"member":"dist_role3","role":"dist_role2","grantor":"dist_role1","admin_option":true},{"member":"dist_role4","role":"dist_role3","grantor":"postgres","admin_option":true}] +(3 rows) + +--clean all resources +set citus.enable_create_database_propagation to on; +drop database test_granted_by_support; +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1; +drop role if exists non_dist_role1; +NOTICE: role "non_dist_role1" does not exist, skipping +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + result +--------------------------------------------------------------------- + + + +(3 rows) + +reset citus.enable_create_database_propagation; diff --git a/src/test/regress/sql/granted_by_support.sql b/src/test/regress/sql/granted_by_support.sql index 4ed7b8bd5..bfc9358f0 100644 --- a/src/test/regress/sql/granted_by_support.sql +++ b/src/test/regress/sql/granted_by_support.sql @@ -1,31 +1,56 @@ -- Active: 1700033167033@@localhost@9700@gurkanindibay@public --In below tests, complex role hierarchy is created and then granted by support is tested. +--- Test 1: Tests from main database select 1 from citus_remove_node ('localhost',:worker_2_port); +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +reset citus.enable_create_role_propagation; +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; -create role role1; -create role role2; -create role role3; -create role role4; -create role "role5'_test"; +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; -grant role2 to role1 with admin option; -grant role2 to role3 with admin option granted by role1; -grant role3 to role4 with admin option; -grant role3 to "role5'_test" granted by role4; -grant role2 to "role5'_test" granted by role3; -grant role4 to "role5'_test" with admin option; -grant role4 to role1 with admin option GRANTED BY "role5'_test"; -grant role4 to role3 with admin option GRANTED BY role1; -grant role3 to role1 with admin option GRANTED BY role4; -grant "role5'_test" to role1 with admin option; -grant "role5'_test" to role3 with admin option GRANTED BY role1; +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; -SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option - FROM pg_auth_members - WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') - order by member::regrole::text, roleid::regrole::text; +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +reset citus.enable_create_role_propagation; + +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + + + +grant dist_role2 to non_dist_role1 with admin option; + +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +grant dist_role2 to dist_role4 granted by non_dist_role1 ;--will not be propagated since grantor is non-distributed + + +grant dist_role4 to "dist_role5'_test" with admin option; + +--below command propagates the non_dist_role1 since non_dist_role1 is already granted to dist_role1 +--and citus sees granted roles as a dependency and citus propagates the dependent roles +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; + +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 + +grant non_dist_role1 to dist_role4 granted by dist_role1; + + +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" select result FROM run_command_on_all_nodes( @@ -35,7 +60,139 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + +select 1 from citus_add_node ('localhost',:worker_2_port); + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + +--clean all resources +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1; + +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; +reset citus.enable_create_role_propagation; + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') + order by member::regrole::text, roleid::regrole::text + ) t + $$ +); + +--- Test 2: Tests from non-main database +set citus.enable_create_database_propagation to on; +create database test_granted_by_support; + +select 1 from citus_remove_node ('localhost',:worker_2_port); + +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + +\c test_granted_by_support +--here in below block since 'citus.enable_create_role_propagation to off ' is not effective, +--non_dist_role1 is being propagated to dist_role1 unlike main db scenario +--non_dist_role1 will be used for the test scenarios in this section +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +reset citus.enable_create_role_propagation; + +--dropping since it isn't non-distributed as intended +drop role non_dist_role1; + +--creating non_dist_role1 again in main database +--This is actually non-distributed role +\c regression +set citus.enable_create_role_propagation to off; +create role non_dist_role1; +reset citus.enable_create_role_propagation; + +\c test_granted_by_support +create role dist_role1; +create role dist_role1; +create role dist_role2; +create role dist_role3; +create role dist_role4; +create role "dist_role5'_test"; +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + + +\c test_granted_by_support +grant dist_role2 to dist_role1 with admin option; +grant dist_role2 to dist_role3 with admin option granted by dist_role1; +grant dist_role3 to dist_role4 with admin option; + +-- With enable_create_role_propagation on, all grantees are propagated. +-- To test non-distributed grantor, set this option off for some roles. + +\c regression +set citus.enable_create_role_propagation to off; +grant non_dist_role1 to dist_role1 with admin option; +reset citus.enable_create_role_propagation; + +\c test_granted_by_support +grant dist_role2 to non_dist_role1 with admin option; + +\c test_granted_by_support - - :worker_1_port +grant dist_role3 to "dist_role5'_test" granted by dist_role4; +grant dist_role2 to "dist_role5'_test" granted by dist_role3; +grant dist_role2 to dist_role4 granted by non_dist_role1 ;--will not be propagated since grantor is non-distributed +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; +\c test_granted_by_support - - :worker_1_port +grant dist_role4 to "dist_role5'_test" with admin option; + +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + +\c test_granted_by_support + +-- Unlike maindb scenario, non-maindb scenario doesn't propagate 'create non_dist_role1' to +--workers as it doesn't create dependency objects for non-distributed roles. +grant dist_role4 to dist_role1 with admin option GRANTED BY "dist_role5'_test"; + +\c regression - - :master_port +select r.rolname from pg_roles r inner join pg_dist_object o on r.oid= objid where r.rolname = 'non_dist_role1'; + + +\c test_granted_by_support - - :worker_1_port +grant dist_role4 to dist_role3 with admin option GRANTED BY dist_role1; --fails since already dist_role3 granted to dist_role4 +grant non_dist_role1 to dist_role4 granted by dist_role1; +grant dist_role3 to dist_role1 with admin option GRANTED BY dist_role4; +grant "dist_role5'_test" to dist_role1 with admin option; +grant "dist_role5'_test" to dist_role3 with admin option GRANTED BY dist_role1;--fails since already dist_role3 granted to "dist_role5'_test" + +\c regression - - :master_port + +select result FROM run_command_on_all_nodes( + $$ + SELECT array_to_json(array_agg(row_to_json(t))) + FROM ( + SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option + FROM pg_auth_members + WHERE member::regrole::text in + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ @@ -49,14 +206,23 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ ); --clean all resources -drop role role1,role2,role3,role4,"role5'_test"; + +set citus.enable_create_database_propagation to on; +drop database test_granted_by_support; +drop role dist_role1,dist_role2,dist_role3,dist_role4,"dist_role5'_test"; +drop role non_dist_role1; +drop role if exists non_dist_role1; + + + + select result FROM run_command_on_all_nodes( $$ @@ -65,8 +231,9 @@ select result FROM run_command_on_all_nodes( SELECT member::regrole, roleid::regrole as role, grantor::regrole, admin_option FROM pg_auth_members WHERE member::regrole::text in - ('role1','role2','role3','role4','"role5''_test"') + ('dist_role1','dist_role2','dist_role3','dist_role4','"role5''_test"') order by member::regrole::text, roleid::regrole::text ) t $$ ); +reset citus.enable_create_database_propagation;