Breaks the bound of grants with DDL

granted_by_propagation
gurkanindibay 2024-03-04 21:07:56 +03:00
parent 684f4a5386
commit 3c16bb69d7
6 changed files with 197 additions and 90 deletions

View File

@ -44,7 +44,7 @@ static int ObjectAddressComparator(const void *a, const void *b);
static void EnsureDependenciesExistOnAllNodes(const ObjectAddress *target); static void EnsureDependenciesExistOnAllNodes(const ObjectAddress *target);
static void EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target, static void EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
RequiredObjectSet requiredObjectSet); RequiredObjectSet requiredObjectSet);
static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency); static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency,bool fetchGrantStatements);
static bool ShouldPropagateObject(const ObjectAddress *address); static bool ShouldPropagateObject(const ObjectAddress *address);
static char * DropTableIfExistsCommand(Oid relationId); static char * DropTableIfExistsCommand(Oid relationId);
@ -164,7 +164,7 @@ EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
ObjectAddress *object = NULL; ObjectAddress *object = NULL;
foreach_ptr(object, objectsToBeCreated) foreach_ptr(object, objectsToBeCreated)
{ {
List *dependencyCommands = GetDependencyCreateDDLCommands(object); List *dependencyCommands = GetDependencyCreateDDLCommands(object,true);
ddlCommands = list_concat(ddlCommands, dependencyCommands); ddlCommands = list_concat(ddlCommands, dependencyCommands);
/* create a new list with objects that actually created commands */ /* create a new list with objects that actually created commands */
@ -432,7 +432,7 @@ GetDistributableDependenciesForObject(const ObjectAddress *target)
* in nodes, but we utilize logic it follows to choose the objects that could * in nodes, but we utilize logic it follows to choose the objects that could
* be distributed * be distributed
*/ */
List *dependencyCommands = GetDependencyCreateDDLCommands(dependency); List *dependencyCommands = GetDependencyCreateDDLCommands(dependency,true);
/* create a new list with dependencies that actually created commands */ /* create a new list with dependencies that actually created commands */
if (list_length(dependencyCommands) > 0) if (list_length(dependencyCommands) > 0)
@ -465,7 +465,7 @@ DropTableIfExistsCommand(Oid relationId)
* commands to execute on a worker to create the object. * commands to execute on a worker to create the object.
*/ */
static List * static List *
GetDependencyCreateDDLCommands(const ObjectAddress *dependency) GetDependencyCreateDDLCommands(const ObjectAddress *dependency, bool fetchGrantStatements)
{ {
switch (getObjectClass(dependency)) switch (getObjectClass(dependency))
{ {
@ -605,7 +605,7 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
case OCLASS_ROLE: case OCLASS_ROLE:
{ {
return GenerateCreateOrAlterRoleCommand(dependency->objectId); return GenerateCreateOrAlterRoleCommand(dependency->objectId,fetchGrantStatements);
} }
case OCLASS_SCHEMA: case OCLASS_SCHEMA:
@ -680,15 +680,15 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
List * List *
GetAllDependencyCreateDDLCommands(const List *dependencies) GetAllDependencyCreateDDLCommands(const List *dependencies)
{ {
List *commands = NIL; List *ddlCommands = NIL;
ObjectAddress *dependency = NULL; ObjectAddress *dependency = NULL;
foreach_ptr(dependency, dependencies) foreach_ptr(dependency, dependencies)
{ {
commands = list_concat(commands, GetDependencyCreateDDLCommands(dependency)); ddlCommands = list_concat(ddlCommands, GetDependencyCreateDDLCommands(dependency,false));
} }
return commands; return ddlCommands;
} }

View File

@ -56,6 +56,12 @@
#include "distributed/version_compat.h" #include "distributed/version_compat.h"
#include "distributed/worker_transaction.h" #include "distributed/worker_transaction.h"
typedef struct GrantRoleStmts
{
List *adminStmts;
List *otherStmts;
} GrantRoleStmts;
static const char * ExtractEncryptedPassword(Oid roleOid); static const char * ExtractEncryptedPassword(Oid roleOid);
static const char * CreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt); static const char * CreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt);
static const char * CreateAlterRoleSetIfExistsCommand(AlterRoleSetStmt *stmt); static const char * CreateAlterRoleSetIfExistsCommand(AlterRoleSetStmt *stmt);
@ -66,7 +72,7 @@ static DefElem * makeDefElemInt(char *name, int value);
static DefElem * makeDefElemBool(char *name, bool value); static DefElem * makeDefElemBool(char *name, bool value);
static List * GenerateRoleOptionsList(HeapTuple tuple); static List * GenerateRoleOptionsList(HeapTuple tuple);
static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options); static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options);
static List * GenerateGrantRoleStmtsOfRole(Oid roleid); static GrantRoleStmts GenerateGrantRoleStmtsOfRole(Oid roleid);
static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename); static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename);
static void EnsureSequentialModeForRoleDDL(void); static void EnsureSequentialModeForRoleDDL(void);
@ -513,8 +519,10 @@ GenerateRoleOptionsList(HeapTuple tuple)
* the pg_authid table. * the pg_authid table.
*/ */
List * List *
GenerateCreateOrAlterRoleCommand(Oid roleOid) GenerateCreateOrAlterRoleCommand(Oid roleOid, bool fetchGrantStmts)
{ {
elog(NOTICE, "Generating create or alter role command for role %u with fetchGrantStmts %s", roleOid,fetchGrantStmts ? "true" : "false");
HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid));
Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple));
char *rolename = pstrdup(NameStr(role->rolname)); char *rolename = pstrdup(NameStr(role->rolname));
@ -563,12 +571,19 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
if (EnableCreateRolePropagation) if (EnableCreateRolePropagation)
{ {
List *grantRoleStmts = GenerateGrantRoleStmtsOfRole(roleOid); if(fetchGrantStmts){
elog(NOTICE, "Fetching grant statements for role %s", rolename);
List *grantRoleStmtList = NIL;
GrantRoleStmts grantRoleStmts= GenerateGrantRoleStmtsOfRole(roleOid);
grantRoleStmtList = list_concat(grantRoleStmts.adminStmts, grantRoleStmts.otherStmts);
Node *stmt = NULL; Node *stmt = NULL;
foreach_ptr(stmt, grantRoleStmts) foreach_ptr(stmt, grantRoleStmtList)
{ {
completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));
} }
}
/* /*
* append SECURITY LABEL ON ROLE commands for this specific user * append SECURITY LABEL ON ROLE commands for this specific user
@ -578,7 +593,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
* SecLabel stmts to be run in the new node. * SecLabel stmts to be run in the new node.
*/ */
List *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename); List *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename);
stmt = NULL; Node *stmt = NULL;
foreach_ptr(stmt, secLabelOnRoleStmts) foreach_ptr(stmt, secLabelOnRoleStmts)
{ {
completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));
@ -868,12 +883,14 @@ GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options)
* GenerateGrantRoleStmtsOfRole generates the GrantRoleStmts for the memberships * GenerateGrantRoleStmtsOfRole generates the GrantRoleStmts for the memberships
* of the role whose oid is roleid. * of the role whose oid is roleid.
*/ */
static List * static GrantRoleStmts
GenerateGrantRoleStmtsOfRole(Oid roleid) GenerateGrantRoleStmtsOfRole(Oid roleid)
{ {
Relation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock); Relation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock);
HeapTuple tuple = NULL; HeapTuple tuple = NULL;
List *stmts = NIL;
List *adminStmts = NIL;
List *otherStmts = NIL;
ScanKeyData skey[1]; ScanKeyData skey[1];
@ -916,7 +933,6 @@ GenerateGrantRoleStmtsOfRole(Oid roleid)
grantRoleStmt->grantor = grantorRole; grantRoleStmt->grantor = grantorRole;
#if PG_VERSION_NUM >= PG_VERSION_16 #if PG_VERSION_NUM >= PG_VERSION_16
/* inherit option is always included */ /* inherit option is always included */
DefElem *inherit_opt; DefElem *inherit_opt;
if (membership->inherit_option) if (membership->inherit_option)
@ -945,14 +961,73 @@ GenerateGrantRoleStmtsOfRole(Oid roleid)
#else #else
grantRoleStmt->admin_opt = membership->admin_option; grantRoleStmt->admin_opt = membership->admin_option;
#endif #endif
if (membership->admin_option)
stmts = lappend(stmts, grantRoleStmt); {
adminStmts = lappend(adminStmts, grantRoleStmt);
}
else
{
otherStmts = lappend(otherStmts, grantRoleStmt);
}
} }
systable_endscan(scan); systable_endscan(scan);
table_close(pgAuthMembers, AccessShareLock); table_close(pgAuthMembers, AccessShareLock);
return stmts; GrantRoleStmts result;
result.adminStmts = adminStmts;
result.otherStmts = otherStmts;
return result;
}
/**/
List * GenerateGrantRoleStmts()
{
Relation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock);
HeapTuple tuple = NULL;
List *adminStmts = NIL;
List *otherStmts = NIL;
SysScanDesc scan = systable_beginscan(pgAuthMembers, InvalidOid, false,
NULL, 0, NULL);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
Form_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple);
ObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress));
ObjectAddressSet(*roleAddress, AuthIdRelationId, membership->grantor);
elog(NOTICE, "Role name: %s", GetUserNameFromId(membership->roleid, true));
elog(NOTICE, "Member name: %s", GetUserNameFromId(membership->member, true));
if (!IsAnyObjectDistributed(list_make1(roleAddress)))
{
/* we only need to propagate the grant if the grantor is distributed */
continue;
}
//log role name
elog(NOTICE, "Role name fetched: %s", GetUserNameFromId(membership->roleid, true));
elog(NOTICE, "Member name fetched: %s", GetUserNameFromId(membership->member, true));
GrantRoleStmts grantRoleStmts = GenerateGrantRoleStmtsOfRole(membership->roleid);
adminStmts = list_concat(adminStmts, grantRoleStmts.adminStmts);
otherStmts = list_concat(otherStmts, grantRoleStmts.otherStmts);
}
systable_endscan(scan);
table_close(pgAuthMembers, AccessShareLock);
List *allGrantStatements = list_concat(adminStmts, otherStmts);
Node *stmt = NULL;
List *grantStatements = NIL;
foreach_ptr(stmt, allGrantStatements)
{
grantStatements = lappend(grantStatements, DeparseTreeNode(stmt));
}
return grantStatements;
} }

View File

@ -1828,13 +1828,6 @@ ExpandRolesToGroups(Oid roleid)
ObjectAddressSet(definition->data.address, AuthIdRelationId, membership->roleid); ObjectAddressSet(definition->data.address, AuthIdRelationId, membership->roleid);
roles = lappend(roles, definition); roles = lappend(roles, definition);
DependencyDefinition *definition1 = palloc0(sizeof(DependencyDefinition));
definition1->mode = DependencyObjectAddress;
ObjectAddressSet(definition1->data.address, AuthIdRelationId,
membership->grantor);
roles = lappend(roles, definition1);
} }
systable_endscan(scanDescriptor); systable_endscan(scanDescriptor);

View File

@ -5027,6 +5027,11 @@ SendDependencyCreationCommands(MetadataSyncContext *context)
List *ddlCommands = GetAllDependencyCreateDDLCommands(list_make1(dependency)); List *ddlCommands = GetAllDependencyCreateDDLCommands(list_make1(dependency));
SendOrCollectCommandListToActivatedNodes(context, ddlCommands); SendOrCollectCommandListToActivatedNodes(context, ddlCommands);
} }
List *grantRoleCommands = GenerateGrantRoleStmts();
SendOrCollectCommandListToActivatedNodes(context, grantRoleCommands);
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
if (!MetadataSyncCollectsCommands(context)) if (!MetadataSyncCollectsCommands(context))

View File

@ -515,7 +515,7 @@ extern List * PreprocessDropRoleStmt(Node *stmt, const char *queryString,
extern List * PreprocessGrantRoleStmt(Node *stmt, const char *queryString, extern List * PreprocessGrantRoleStmt(Node *stmt, const char *queryString,
ProcessUtilityContext processUtilityContext); ProcessUtilityContext processUtilityContext);
extern List * PostprocessGrantRoleStmt(Node *stmt, const char *queryString); extern List * PostprocessGrantRoleStmt(Node *stmt, const char *queryString);
extern List * GenerateCreateOrAlterRoleCommand(Oid roleOid); extern List * GenerateCreateOrAlterRoleCommand(Oid roleOid,bool fetchGrantStatements);
extern List * CreateRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool extern List * CreateRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool
isPostprocess); isPostprocess);
@ -524,6 +524,7 @@ extern List * RenameRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool
extern void UnmarkRolesDistributed(List *roles); extern void UnmarkRolesDistributed(List *roles);
extern List * FilterDistributedRoles(List *roles); extern List * FilterDistributedRoles(List *roles);
extern List * GenerateGrantRoleStmts(void);
/* schema.c - forward declarations */ /* schema.c - forward declarations */
extern List * PostprocessCreateSchemaStmt(Node *node, const char *queryString); extern List * PostprocessCreateSchemaStmt(Node *node, const char *queryString);

View File

@ -21,6 +21,12 @@ grant role3 to role1 with admin option GRANTED BY role4;
grant "role5'_test" to role1 with admin option; grant "role5'_test" to role1 with admin option;
grant "role5'_test" to role3 with admin option GRANTED BY role1; grant "role5'_test" to role3 with admin option GRANTED BY role1;
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;
select result FROM run_command_on_all_nodes( select result FROM run_command_on_all_nodes(
$$ $$
@ -35,8 +41,14 @@ select result FROM run_command_on_all_nodes(
$$ $$
); );
set citus.log_remote_commands = on;
--set citus.grep_remote_commands = '%GRANT%';
select 1 from citus_add_node ('localhost',:worker_2_port); select 1 from citus_add_node ('localhost',:worker_2_port);
reset citus.log_remote_commands;
reset citus.grep_remote_commands;
--clean all resources --clean all resources
drop role role1,role2,role3,role4,"role5'_test"; drop role role1,role2,role3,role4,"role5'_test";
@ -52,3 +64,24 @@ select result FROM run_command_on_all_nodes(
) t ) t
$$ $$
); );
GRANT role2 TO role3 WITH INHERIT TRUE, ADMIN OPTION GRANTED BY role1;;
GRANT role2 TO role3 WITH INHERIT TRUE, ADMIN OPTION GRANTED BY role1;;
GRANT role3 TO role4 WITH INHERIT TRUE, ADMIN OPTION GRANTED BY postgres;; x
GRANT role3 TO role4 WITH INHERIT TRUE, ADMIN OPTION GRANTED BY postgres;;
GRANT role2 TO role3 WITH INHERIT TRUE, ADMIN OPTION GRANTED BY role1;; x
GRANT role4 TO "role5'_test" WITH INHERIT TRUE, ADMIN OPTION GRANTED BY postgres;; x
GRANT role2 TO "role5'_test" WITH INHERIT TRUE GRANTED BY role3;; x
GRANT role3 TO "role5'_test" WITH INHERIT TRUE GRANTED BY role4 ; x
"role5'_test" | role2 | role3 | f x
"role5'_test" | role3 | role4 | f x
"role5'_test" | role4 | postgres | t x
role1 | "role5'_test" | postgres | t
role1 | role2 | postgres | t
role1 | role3 | role4 | t
role1 | role4 | "role5'_test" | t
role3 | role2 | role1 | t x
role4 | role3 | postgres | t x