diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index fe1f422b6..a378bf2de 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -762,6 +762,18 @@ static DistributeObjectOps Index_Drop = { .address = NULL, .markDistributed = false, }; + +static DistributeObjectOps Parameter_Grant = { + .deparse = DeparseGrantOnParameterStmt, + .qualify = NULL, + .preprocess = NULL, + .postprocess = PostprocessGrantParameterStmt, + .objectType = OBJECT_PARAMETER_ACL, + .operationType = DIST_OPS_ALTER, + .address = NULL, + .markDistributed = false, +}; + static DistributeObjectOps Policy_Drop = { .deparse = NULL, .qualify = NULL, @@ -2061,6 +2073,11 @@ GetDistributeObjectOps(Node *node) return &Database_Grant; } + case OBJECT_PARAMETER_ACL: + { + return &Parameter_Grant; + } + default: { return &Any_Grant; diff --git a/src/backend/distributed/commands/parameter.c b/src/backend/distributed/commands/parameter.c new file mode 100644 index 000000000..84c6e358b --- /dev/null +++ b/src/backend/distributed/commands/parameter.c @@ -0,0 +1,27 @@ +#include "postgres.h" + +#include "catalog/namespace.h" +#include "commands/defrem.h" +#include "distributed/metadata_sync.h" +#include "distributed/deparser.h" +#include "distributed/commands.h" + + +List * +PostprocessGrantParameterStmt(Node *node, const char *queryString) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + EnsurePropagationToCoordinator(); + + char *command = DeparseTreeNode(node); + + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) command, + ENABLE_DDL_PROPAGATION); + + return NontransactionalNodeDDLTaskList(REMOTE_NODES, commands); +} diff --git a/src/backend/distributed/deparser/deparse_parameter_stmts.c b/src/backend/distributed/deparser/deparse_parameter_stmts.c new file mode 100644 index 000000000..cf462eddb --- /dev/null +++ b/src/backend/distributed/deparser/deparse_parameter_stmts.c @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * deparse_database_stmts.c + * All routines to deparse parameter statements. + * + * ------------------------------------------------------------------------- +*/ + +#include "postgres.h" + +#include "utils/builtins.h" + +#include "distributed/deparser.h" +#include "distributed/listutils.h" + +static void AppendGrantParameters(StringInfo buf, GrantStmt *stmt); +static void AppendGrantOnParameterStmt(StringInfo buf, GrantStmt *stmt); + +static void +AppendGrantParameters(StringInfo buf, GrantStmt *stmt) +{ + appendStringInfo(buf, " ON PARAMETER "); + + DefElem *def = NULL; + foreach_ptr(def, stmt->objects) + { + char *parameter = strVal(def); + appendStringInfoString(buf, quote_identifier(parameter)); + if (def != (DefElem *) lfirst(list_tail(stmt->objects))) + { + appendStringInfo(buf, ", "); + } + } +} + +static void +AppendGrantOnParameterStmt(StringInfo buf, GrantStmt *stmt) +{ + Assert(stmt->objtype == OBJECT_PARAMETER_ACL); + + AppendGrantSharedPrefix(buf, stmt); + + AppendGrantParameters(buf, stmt); + + AppendGrantSharedSuffix(buf, stmt); +} + + +char * +DeparseGrantOnParameterStmt(Node *node) +{ + GrantStmt *stmt = castNode(GrantStmt, node); + Assert(stmt->objtype == OBJECT_PARAMETER_ACL); + + StringInfoData str = { 0 }; + initStringInfo(&str); + + AppendGrantOnParameterStmt(&str, stmt); + + return str.data; +} + + diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 4eb6df8bf..789cad7da 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -447,6 +447,9 @@ extern List * PreprocessDropOwnedStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * PostprocessReassignOwnedStmt(Node *node, const char *queryString); +/* parameter.c - forward declarations */ +extern List * PostprocessGrantParameterStmt(Node *node, const char *queryString); + /* policy.c - forward declarations */ extern List * CreatePolicyCommands(Oid relationId); extern void ErrorIfUnsupportedPolicy(Relation relation); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 22636b401..5c60f1caa 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -254,6 +254,8 @@ extern char * DeparseCreateDatabaseStmt(Node *node); extern char * DeparseDropDatabaseStmt(Node *node); extern char * DeparseAlterDatabaseRenameStmt(Node *node); +/* forward declarations for deparse_parameter_stmts.c*/ +extern char * DeparseGrantOnParameterStmt(Node *node); /* forward declaration for deparse_publication_stmts.c */ extern char * DeparseCreatePublicationStmt(Node *stmt); diff --git a/src/test/regress/expected/grant_on_parameter_propagation.out b/src/test/regress/expected/grant_on_parameter_propagation.out new file mode 100644 index 000000000..091fcbf7d --- /dev/null +++ b/src/test/regress/expected/grant_on_parameter_propagation.out @@ -0,0 +1,182 @@ +create user grant_param_user1; +create user grant_param_user2; +create user grant_param_user3; +create user grant_param_user4; +create user "grant_param_user5-\!"; +--test the grant command with all options +SET citus.log_remote_commands to on; +SET citus.grep_remote_commands = '%GRANT%'; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user1,grant_param_user2,"grant_param_user5-\!" WITH GRANT OPTION GRANTED BY CURRENT_USER; +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user1, grant_param_user2, "grant_param_user5-\!" WITH GRANT OPTION GRANTED BY postgres; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user1, grant_param_user2, "grant_param_user5-\!" WITH GRANT OPTION GRANTED BY postgres; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +RESET citus.log_remote_commands; +SELECT check_parameter_privileges(ARRAY['grant_param_user1','grant_param_user2','grant_param_user5-\!'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + check_parameter_privileges +--------------------------------------------------------------------- + (t,grant_param_user1,max_connections,SET) + (t,grant_param_user1,max_connections,SET) + (t,grant_param_user1,max_connections,SET) + (t,grant_param_user1,max_connections,"ALTER SYSTEM") + (t,grant_param_user1,max_connections,"ALTER SYSTEM") + (t,grant_param_user1,max_connections,"ALTER SYSTEM") + (t,grant_param_user1,shared_buffers,SET) + (t,grant_param_user1,shared_buffers,SET) + (t,grant_param_user1,shared_buffers,SET) + (t,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user2,max_connections,SET) + (t,grant_param_user2,max_connections,SET) + (t,grant_param_user2,max_connections,SET) + (t,grant_param_user2,max_connections,"ALTER SYSTEM") + (t,grant_param_user2,max_connections,"ALTER SYSTEM") + (t,grant_param_user2,max_connections,"ALTER SYSTEM") + (t,grant_param_user2,shared_buffers,SET) + (t,grant_param_user2,shared_buffers,SET) + (t,grant_param_user2,shared_buffers,SET) + (t,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",max_connections,SET) + (t,"grant_param_user5-\\!",max_connections,SET) + (t,"grant_param_user5-\\!",max_connections,SET) + (t,"grant_param_user5-\\!",max_connections,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",max_connections,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",max_connections,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",shared_buffers,SET) + (t,"grant_param_user5-\\!",shared_buffers,SET) + (t,"grant_param_user5-\\!",shared_buffers,SET) + (t,"grant_param_user5-\\!",shared_buffers,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",shared_buffers,"ALTER SYSTEM") + (t,"grant_param_user5-\\!",shared_buffers,"ALTER SYSTEM") +(36 rows) + +--test the grant command admin option using grant_param_user1 with granted by +set role grant_param_user1; +SET citus.log_remote_commands to on; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3 GRANTED BY grant_param_user1; +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user3 GRANTED BY grant_param_user1; +DETAIL: on server grant_param_user1@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user3 GRANTED BY grant_param_user1; +DETAIL: on server grant_param_user1@localhost:xxxxx connectionId: xxxxxxx +SELECT check_parameter_privileges(ARRAY['grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + check_parameter_privileges +--------------------------------------------------------------------- + (t,grant_param_user3,max_connections,SET) + (t,grant_param_user3,max_connections,SET) + (t,grant_param_user3,max_connections,SET) + (t,grant_param_user3,max_connections,"ALTER SYSTEM") + (t,grant_param_user3,max_connections,"ALTER SYSTEM") + (t,grant_param_user3,max_connections,"ALTER SYSTEM") + (t,grant_param_user3,shared_buffers,SET) + (t,grant_param_user3,shared_buffers,SET) + (t,grant_param_user3,shared_buffers,SET) + (t,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (t,grant_param_user3,shared_buffers,"ALTER SYSTEM") +(12 rows) + +reset role; +--test the revoke command grant option with all options +REVOKE GRANT OPTION FOR SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers FROM grant_param_user1,grant_param_user2,"grant_param_user5-\!" cascade; +NOTICE: issuing REVOKE GRANT OPTION FOR set, alter system ON PARAMETER max_connections, shared_buffers FROM grant_param_user1, grant_param_user2, "grant_param_user5-\!" CASCADE; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing REVOKE GRANT OPTION FOR set, alter system ON PARAMETER max_connections, shared_buffers FROM grant_param_user1, grant_param_user2, "grant_param_user5-\!" CASCADE; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +--test if the admin option removed for the revoked user. Need to get error +SET ROLE "grant_param_user5-\!"; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3 GRANTED BY "grant_param_user5-\!"; +WARNING: no privileges were granted for "max_connections" +WARNING: no privileges were granted for "shared_buffers" +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user3 GRANTED BY "grant_param_user5-\!"; +DETAIL: on server grant_param_user5-\!@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT set, alter system ON PARAMETER max_connections, shared_buffers TO grant_param_user3 GRANTED BY "grant_param_user5-\!"; +DETAIL: on server grant_param_user5-\!@localhost:xxxxx connectionId: xxxxxxx +SELECT check_parameter_privileges(ARRAY['grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + check_parameter_privileges +--------------------------------------------------------------------- + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") +(12 rows) + +RESET ROLE; +--test the revoke command +REVOKE SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers FROM grant_param_user1,grant_param_user2,grant_param_user3,"grant_param_user5-\!"; +RESET citus.log_remote_commands; +SELECT check_parameter_privileges(ARRAY['grant_param_user1','grant_param_user2','grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + check_parameter_privileges +--------------------------------------------------------------------- + (f,grant_param_user1,max_connections,SET) + (f,grant_param_user1,max_connections,SET) + (f,grant_param_user1,max_connections,SET) + (f,grant_param_user1,max_connections,"ALTER SYSTEM") + (f,grant_param_user1,max_connections,"ALTER SYSTEM") + (f,grant_param_user1,max_connections,"ALTER SYSTEM") + (f,grant_param_user1,shared_buffers,SET) + (f,grant_param_user1,shared_buffers,SET) + (f,grant_param_user1,shared_buffers,SET) + (f,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user1,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user2,max_connections,SET) + (f,grant_param_user2,max_connections,SET) + (f,grant_param_user2,max_connections,SET) + (f,grant_param_user2,max_connections,"ALTER SYSTEM") + (f,grant_param_user2,max_connections,"ALTER SYSTEM") + (f,grant_param_user2,max_connections,"ALTER SYSTEM") + (f,grant_param_user2,shared_buffers,SET) + (f,grant_param_user2,shared_buffers,SET) + (f,grant_param_user2,shared_buffers,SET) + (f,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user2,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,SET) + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,max_connections,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,SET) + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user3,shared_buffers,"ALTER SYSTEM") +(36 rows) + +--test with single permission and single user +GRANT ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3; +SELECT check_parameter_privileges(ARRAY['grant_param_user4'],ARRAY['max_connections','shared_buffers'], ARRAY['ALTER SYSTEM']); + check_parameter_privileges +--------------------------------------------------------------------- + (f,grant_param_user4,max_connections,"ALTER SYSTEM") + (f,grant_param_user4,max_connections,"ALTER SYSTEM") + (f,grant_param_user4,max_connections,"ALTER SYSTEM") + (f,grant_param_user4,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user4,shared_buffers,"ALTER SYSTEM") + (f,grant_param_user4,shared_buffers,"ALTER SYSTEM") +(6 rows) + +--clean all resources +DROP USER grant_param_user1; +DROP USER grant_param_user2; +DROP USER grant_param_user3; +ERROR: role "grant_param_user3" cannot be dropped because some objects depend on it +DETAIL: privileges for parameter max_connections +privileges for parameter shared_buffers +DROP USER grant_param_user4; +DROP USER "grant_param_user5-\!"; +reset citus.log_remote_commands; +reset citus.grep_remote_commands; diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index 70a541d2a..8b9a833a3 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -625,4 +625,23 @@ BEGIN ) q2 JOIN pg_dist_node USING (nodeid); END; -$func$ LANGUAGE plpgsql; \ No newline at end of file +$func$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION check_parameter_privileges(users text[], parameters text[], permissions text[]) +RETURNS TABLE ( res text, usr text, param text, perms text) AS $func$ +DECLARE + u text; + p text; + perm text; +BEGIN + FOREACH u IN ARRAY users + LOOP + FOREACH p IN ARRAY parameters + LOOP + FOREACH perm IN ARRAY permissions + LOOP + RETURN QUERY EXECUTE format($inner$SELECT result ,'%1$s','%2$s','%3$s' FROM run_command_on_all_nodes($$SELECT has_parameter_privilege('%1$s','%2$s', '%3$s'); $$)$inner$, u, p, perm); + END LOOP; + END LOOP; + END LOOP; +END; +$func$ LANGUAGE plpgsql;; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 2b9fdeb2d..726c64e32 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -60,6 +60,7 @@ test: alter_database_propagation test: citus_shards test: reassign_owned +test: grant_on_parameter_propagation # ---------- # multi_citus_tools tests utility functions written for citus tools diff --git a/src/test/regress/sql/grant_on_parameter_propagation.sql b/src/test/regress/sql/grant_on_parameter_propagation.sql new file mode 100644 index 000000000..1a4a98db0 --- /dev/null +++ b/src/test/regress/sql/grant_on_parameter_propagation.sql @@ -0,0 +1,64 @@ + + +create user grant_param_user1; +create user grant_param_user2; +create user grant_param_user3; +create user grant_param_user4; +create user "grant_param_user5-\!"; + + +--test the grant command with all options +SET citus.log_remote_commands to on; +SET citus.grep_remote_commands = '%GRANT%'; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user1,grant_param_user2,"grant_param_user5-\!" WITH GRANT OPTION GRANTED BY CURRENT_USER; + +RESET citus.log_remote_commands; +SELECT check_parameter_privileges(ARRAY['grant_param_user1','grant_param_user2','grant_param_user5-\!'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + +--test the grant command admin option using grant_param_user1 with granted by +set role grant_param_user1; +SET citus.log_remote_commands to on; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3 GRANTED BY grant_param_user1; +SELECT check_parameter_privileges(ARRAY['grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + +reset role; + +--test the revoke command grant option with all options +REVOKE GRANT OPTION FOR SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers FROM grant_param_user1,grant_param_user2,"grant_param_user5-\!" cascade; + +--test if the admin option removed for the revoked user. Need to get error +SET ROLE "grant_param_user5-\!"; +GRANT SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3 GRANTED BY "grant_param_user5-\!"; + +SELECT check_parameter_privileges(ARRAY['grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + +RESET ROLE; + +--test the revoke command +REVOKE SET,ALTER SYSTEM ON PARAMETER max_connections,shared_buffers FROM grant_param_user1,grant_param_user2,grant_param_user3,"grant_param_user5-\!"; + +RESET citus.log_remote_commands; + +SELECT check_parameter_privileges(ARRAY['grant_param_user1','grant_param_user2','grant_param_user3'],ARRAY['max_connections','shared_buffers'], ARRAY['SET','ALTER SYSTEM']); + + +--test with single permission and single user +GRANT ALTER SYSTEM ON PARAMETER max_connections,shared_buffers TO grant_param_user3; + +SELECT check_parameter_privileges(ARRAY['grant_param_user4'],ARRAY['max_connections','shared_buffers'], ARRAY['ALTER SYSTEM']); + +--clean all resources +DROP USER grant_param_user1; +DROP USER grant_param_user2; +DROP USER grant_param_user3; +DROP USER grant_param_user4; +DROP USER "grant_param_user5-\!"; + +reset citus.log_remote_commands; +reset citus.grep_remote_commands; + + + + + + diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index e67b782a5..ec156e518 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -652,3 +652,25 @@ BEGIN JOIN pg_dist_node USING (nodeid); END; $func$ LANGUAGE plpgsql; + + + +CREATE OR REPLACE FUNCTION check_parameter_privileges(users text[], parameters text[], permissions text[]) +RETURNS TABLE ( res text, usr text, param text, perms text) AS $func$ +DECLARE + u text; + p text; + perm text; +BEGIN + FOREACH u IN ARRAY users + LOOP + FOREACH p IN ARRAY parameters + LOOP + FOREACH perm IN ARRAY permissions + LOOP + RETURN QUERY EXECUTE format($inner$SELECT result ,'%1$s','%2$s','%3$s' FROM run_command_on_all_nodes($$SELECT has_parameter_privilege('%1$s','%2$s', '%3$s'); $$)$inner$, u, p, perm); + END LOOP; + END LOOP; + END LOOP; +END; +$func$ LANGUAGE plpgsql;;