From d4dd44e715e337ad84edbabe3cf612c0c96c0c9d Mon Sep 17 00:00:00 2001 From: Colm Date: Wed, 30 Apr 2025 18:03:52 +0100 Subject: [PATCH] Propagate SECURITY LABEL on tables and columns. (#7956) Issue #7709 asks for security labels on columns to be propagated, to support the `anon` extension. Before, Citus supported security labels on roles (#7735) and this PR adds support for propagating security labels on tables and columns. All scenarios that involve propagating metadata for a Citus table now include the security labels on the table and on the columns of the table. These scenarios are: - When a table becomes distributed using `create_distributed_table()` or `create_reference_table()`, its security labels (if any) are propageted. - When a security label is defined on a distributed table, or one of its columns, the label is propagated. - When a node is added to a Citus cluster, all distributed tables have their security labels propagated. - When a column of a distributed table is dropped, any security labels on the column are also dropped. - When a column is added to a distributed table, security labels can be defined on the column and are propagated. - Security labels on a distributed table or its columns are not propagated when `citus.enable_metadata_sync` is enabled. Regress test `seclabel` is extended with tests to cover these scenarios. The implementation is somewhat involved because it impacts DDL propagation of Citus tables, but can be broken down as follows: - distributed_object_ops has `Role_SecLabel`, `Table_SecLabel` and `Column_SecLabel` to take care of security labels on roles, tables and columns. `Any_SecLabel` is used for all other security labels and is essentially a nop. - Deparser support - `DeparseRoleSecLabelStmt()`, `DeparseTableSecLabelStmt()` and `DeparseColumnSecLabelStmt()` take care of deparsing security label statements on roles, tables and columns respectively. - When reconstructing the DDL for a citus table, security labels on the table or its columns are included by having `GetPreLoadTableCreationCommands()` call a new function `CreateSecurityLabelCommands()` to take care of any security labels on the table or its columns. - When changing a distributed table name to a shard name before running a command locally on a worker, function `RelayEventExtendNames()` checks for security labels on a table or its columns. --- .../commands/distribute_object_ops.c | 53 +- src/backend/distributed/commands/seclabel.c | 98 +++- .../deparser/deparse_seclabel_stmts.c | 134 +++-- src/backend/distributed/metadata/distobject.c | 32 ++ .../distributed/operations/node_protocol.c | 109 ++++ .../distributed/relay/relay_event_utility.c | 53 +- src/include/distributed/commands.h | 4 +- src/include/distributed/deparser.h | 4 +- src/include/distributed/metadata/distobject.h | 1 + src/test/regress/expected/seclabel.out | 475 ++++++++++++++++-- src/test/regress/sql/seclabel.sql | 154 +++++- 11 files changed, 999 insertions(+), 118 deletions(-) diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index 0e8887905..8d1c6bc23 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -399,10 +399,37 @@ static DistributeObjectOps Any_Rename = { .markDistributed = false, }; static DistributeObjectOps Any_SecLabel = { - .deparse = DeparseSecLabelStmt, + .deparse = NULL, .qualify = NULL, .preprocess = NULL, - .postprocess = PostprocessSecLabelStmt, + .postprocess = PostprocessAnySecLabelStmt, + .operationType = DIST_OPS_ALTER, + .address = SecLabelStmtObjectAddress, + .markDistributed = false, +}; +static DistributeObjectOps Role_SecLabel = { + .deparse = DeparseRoleSecLabelStmt, + .qualify = NULL, + .preprocess = NULL, + .postprocess = PostprocessRoleSecLabelStmt, + .operationType = DIST_OPS_ALTER, + .address = SecLabelStmtObjectAddress, + .markDistributed = false, +}; +static DistributeObjectOps Table_SecLabel = { + .deparse = DeparseTableSecLabelStmt, + .qualify = NULL, + .preprocess = NULL, + .postprocess = PostprocessTableOrColumnSecLabelStmt, + .operationType = DIST_OPS_ALTER, + .address = SecLabelStmtObjectAddress, + .markDistributed = false, +}; +static DistributeObjectOps Column_SecLabel = { + .deparse = DeparseColumnSecLabelStmt, + .qualify = NULL, + .preprocess = NULL, + .postprocess = PostprocessTableOrColumnSecLabelStmt, .operationType = DIST_OPS_ALTER, .address = SecLabelStmtObjectAddress, .markDistributed = false, @@ -2119,7 +2146,27 @@ GetDistributeObjectOps(Node *node) case T_SecLabelStmt: { - return &Any_SecLabel; + SecLabelStmt *stmt = castNode(SecLabelStmt, node); + switch (stmt->objtype) + { + case OBJECT_ROLE: + { + return &Role_SecLabel; + } + + case OBJECT_TABLE: + { + return &Table_SecLabel; + } + + case OBJECT_COLUMN: + { + return &Column_SecLabel; + } + + default: + return &Any_SecLabel; + } } case T_RenameStmt: diff --git a/src/backend/distributed/commands/seclabel.c b/src/backend/distributed/commands/seclabel.c index 1d274a056..2d4f31d4a 100644 --- a/src/backend/distributed/commands/seclabel.c +++ b/src/backend/distributed/commands/seclabel.c @@ -15,19 +15,18 @@ #include "distributed/commands/utility_hook.h" #include "distributed/coordinator_protocol.h" #include "distributed/deparser.h" +#include "distributed/listutils.h" #include "distributed/log_utils.h" #include "distributed/metadata/distobject.h" #include "distributed/metadata_sync.h" - /* - * PostprocessSecLabelStmt prepares the commands that need to be run on all workers to assign - * security labels on distributed objects, currently supporting just Role objects. - * It also ensures that all object dependencies exist on all - * nodes for the object in the SecLabelStmt. + * PostprocessRoleSecLabelStmt prepares the commands that need to be run on all workers to assign + * security labels on distributed roles. It also ensures that all object dependencies exist on all + * nodes for the role in the SecLabelStmt. */ List * -PostprocessSecLabelStmt(Node *node, const char *queryString) +PostprocessRoleSecLabelStmt(Node *node, const char *queryString) { if (!EnableAlterRolePropagation || !ShouldPropagate()) { @@ -42,34 +41,91 @@ PostprocessSecLabelStmt(Node *node, const char *queryString) return NIL; } - if (secLabelStmt->objtype != OBJECT_ROLE) + EnsurePropagationToCoordinator(); + EnsureAllObjectDependenciesExistOnAllNodes(objectAddresses); + + const char *secLabelCommands = DeparseTreeNode((Node *) secLabelStmt); + List *commandList = list_make3(DISABLE_DDL_PROPAGATION, + (void *) secLabelCommands, + ENABLE_DDL_PROPAGATION); + return NodeDDLTaskList(REMOTE_NODES, commandList); +} + + +/* + * PostprocessTableOrColumnSecLabelStmt prepares the commands that need to be run on all + * workers to assign security labels on distributed tables or the columns of a distributed + * table. It also ensures that all object dependencies exist on all nodes for the table in + * the SecLabelStmt. + */ +List * +PostprocessTableOrColumnSecLabelStmt(Node *node, const char *queryString) +{ + if (!EnableAlterRolePropagation || !ShouldPropagate()) { - /* - * If we are not in the coordinator, we don't want to interrupt the security - * label command with notices, the user expects that from the worker node - * the command will not be propagated - */ - if (EnableUnsupportedFeatureMessages && IsCoordinator()) - { - ereport(NOTICE, (errmsg("not propagating SECURITY LABEL commands whose " - "object type is not role"), - errhint("Connect to worker nodes directly to manually " - "run the same SECURITY LABEL command."))); - } return NIL; } + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + + List *objectAddresses = GetObjectAddressListFromParseTree(node, false, true); + if (!IsAnyParentObjectDistributed(objectAddresses)) + { + return NIL; + } EnsurePropagationToCoordinator(); EnsureAllObjectDependenciesExistOnAllNodes(objectAddresses); const char *secLabelCommands = DeparseTreeNode((Node *) secLabelStmt); - List *commandList = list_make3(DISABLE_DDL_PROPAGATION, (void *) secLabelCommands, ENABLE_DDL_PROPAGATION); + List *DDLJobs = NodeDDLTaskList(REMOTE_NODES, commandList); + ListCell *lc = NULL; - return NodeDDLTaskList(REMOTE_NODES, commandList); + /* + * The label is for a table or a column, so we need to set the targetObjectAddress + * of the DDLJob to the relationId of the table. This is needed to ensure that + * the search path is correctly set for the remote security label command; it + * needs to be able to resolve the table that the label is being defined on. + */ + Assert(list_length(objectAddresses) == 1); + ObjectAddress *target = linitial(objectAddresses); + Oid relationId = target->objectId; + Assert(relationId != InvalidOid); + + foreach(lc, DDLJobs) + { + DDLJob *ddlJob = (DDLJob *) lfirst(lc); + ObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId); + } + + return DDLJobs; +} + + +/* + * PostprocessAnySecLabelStmt is used for any other object types + * that are not supported by Citus. It issues a notice to the client + * if appropriate. Is effectively a nop. + */ +List * +PostprocessAnySecLabelStmt(Node *node, const char *queryString) +{ + /* + * If we are not in the coordinator, we don't want to interrupt the security + * label command with notices, the user expects that from the worker node + * the command will not be propagated + */ + if (EnableUnsupportedFeatureMessages && IsCoordinator()) + { + ereport(NOTICE, (errmsg("not propagating SECURITY LABEL commands whose " + "object type is not role or table or column"), + errhint("Connect to worker nodes directly to manually " + "run the same SECURITY LABEL command."))); + } + return NIL; } diff --git a/src/backend/distributed/deparser/deparse_seclabel_stmts.c b/src/backend/distributed/deparser/deparse_seclabel_stmts.c index ffe775b76..761aa7acf 100644 --- a/src/backend/distributed/deparser/deparse_seclabel_stmts.c +++ b/src/backend/distributed/deparser/deparse_seclabel_stmts.c @@ -10,37 +10,16 @@ #include "postgres.h" +#include "catalog/namespace.h" #include "nodes/parsenodes.h" #include "utils/builtins.h" #include "distributed/deparser.h" -static void AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt); - -/* - * DeparseSecLabelStmt builds and returns a string representing of the - * SecLabelStmt for application on a remote server. - */ -char * -DeparseSecLabelStmt(Node *node) -{ - SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); - StringInfoData buf = { 0 }; - initStringInfo(&buf); - - AppendSecLabelStmt(&buf, secLabelStmt); - - return buf.data; -} - - -/* - * AppendSecLabelStmt generates the string representation of the - * SecLabelStmt and appends it to the buffer. - */ static void -AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt) +BeginSecLabel(StringInfo buf, SecLabelStmt *stmt) { + initStringInfo(buf); appendStringInfoString(buf, "SECURITY LABEL "); if (stmt->provider != NULL) @@ -49,31 +28,84 @@ AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt) } appendStringInfoString(buf, "ON "); - - switch (stmt->objtype) - { - case OBJECT_ROLE: - { - appendStringInfo(buf, "ROLE %s ", quote_identifier(strVal(stmt->object))); - break; - } - - /* normally, we shouldn't reach this */ - default: - { - ereport(ERROR, (errmsg("unsupported security label statement for" - " deparsing"))); - } - } - - appendStringInfoString(buf, "IS "); - - if (stmt->label != NULL) - { - appendStringInfo(buf, "%s", quote_literal_cstr(stmt->label)); - } - else - { - appendStringInfoString(buf, "NULL"); - } +} + + +static void +EndSecLabel(StringInfo buf, SecLabelStmt *stmt) +{ + appendStringInfo(buf, "IS %s", (stmt->label != NULL) ? + quote_literal_cstr(stmt->label) : "NULL"); +} + + +/* + * DeparseRoleSecLabelStmt builds and returns a string representation of the + * SecLabelStmt for application on a remote server. The SecLabelStmt is for + * a role object. + */ +char * +DeparseRoleSecLabelStmt(Node *node) +{ + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + char *role_name = strVal(secLabelStmt->object); + StringInfoData buf = { 0 }; + + BeginSecLabel(&buf, secLabelStmt); + appendStringInfo(&buf, "ROLE %s ", quote_identifier(role_name)); + EndSecLabel(&buf, secLabelStmt); + + return buf.data; +} + + +/* + * DeparseTableSecLabelStmt builds and returns a string representation of the + * SecLabelStmt for application on a remote server. The SecLabelStmt is for a + * table. + */ +char * +DeparseTableSecLabelStmt(Node *node) +{ + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + List *names = (List *) secLabelStmt->object; + StringInfoData buf = { 0 }; + + BeginSecLabel(&buf, secLabelStmt); + appendStringInfo(&buf, "TABLE %s", quote_identifier(strVal(linitial(names)))); + if (list_length(names) > 1) + { + appendStringInfo(&buf, ".%s", quote_identifier(strVal(lsecond(names)))); + } + appendStringInfoString(&buf, " "); + EndSecLabel(&buf, secLabelStmt); + + return buf.data; +} + + +/* + * DeparseColumnSecLabelStmt builds and returns a string representation of the + * SecLabelStmt for application on a remote server. The SecLabelStmt is for a + * column of a distributed table. + */ +char * +DeparseColumnSecLabelStmt(Node *node) +{ + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + List *names = (List *) secLabelStmt->object; + StringInfoData buf = { 0 }; + + BeginSecLabel(&buf, secLabelStmt); + appendStringInfo(&buf, "COLUMN %s.%s", + quote_identifier(strVal(linitial(names))), + quote_identifier(strVal(lsecond(names)))); + if (list_length(names) > 2) + { + appendStringInfo(&buf, ".%s", quote_identifier(strVal(lthird(names)))); + } + appendStringInfoString(&buf, " "); + EndSecLabel(&buf, secLabelStmt); + + return buf.data; } diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index daa51eb75..43bd3877a 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -566,6 +566,38 @@ IsAnyObjectDistributed(const List *addresses) } +/* + * IsAnyParentObjectDistributed - true if at least one of the + * given addresses is distributed. If an address has a non-zero + * objectSubId, it checks the parent object (the object with + * the same classId and objid, but with objectSubId = 0). For + * example, a column address will check the table address. + * If the address has a zero objectSubId, it checks the address + * itself. + */ +bool +IsAnyParentObjectDistributed(const List *addresses) +{ + bool isDistributed = false; + ListCell *lc = NULL; + foreach(lc, addresses) + { + ObjectAddress *address = (ObjectAddress *) lfirst(lc); + int32 savedObjectSubId = address->objectSubId; + address->objectSubId = 0; + isDistributed = IsObjectDistributed(address); + address->objectSubId = savedObjectSubId; + + if (isDistributed) + { + break; + } + } + + return isDistributed; +} + + /* * GetDistributedObjectAddressList returns a list of ObjectAddresses that contains all * distributed objects as marked in pg_dist_object diff --git a/src/backend/distributed/operations/node_protocol.c b/src/backend/distributed/operations/node_protocol.c index 8a633e3dc..8e253de41 100644 --- a/src/backend/distributed/operations/node_protocol.c +++ b/src/backend/distributed/operations/node_protocol.c @@ -36,6 +36,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_index.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_type.h" #include "commands/sequence.h" #include "foreign/foreign.h" @@ -57,6 +58,7 @@ #include "distributed/citus_ruleutils.h" #include "distributed/commands.h" #include "distributed/coordinator_protocol.h" +#include "distributed/deparser.h" #include "distributed/listutils.h" #include "distributed/metadata_cache.h" #include "distributed/metadata_sync.h" @@ -83,6 +85,7 @@ static char * CitusCreateAlterColumnarTableSet(char *qualifiedRelationName, const ColumnarOptions *options); static char * GetTableDDLCommandColumnar(void *context); static TableDDLCommand * ColumnarGetTableOptionsDDL(Oid relationId); +static List * CreateSecurityLabelCommands(Oid relationId); /* exports for SQL callable functions */ PG_FUNCTION_INFO_V1(master_get_table_metadata); @@ -665,6 +668,9 @@ GetPreLoadTableCreationCommands(Oid relationId, List *policyCommands = CreatePolicyCommands(relationId); tableDDLEventList = list_concat(tableDDLEventList, policyCommands); + List *securityLabelCommands = CreateSecurityLabelCommands(relationId); + tableDDLEventList = list_concat(tableDDLEventList, securityLabelCommands); + /* revert back to original search_path */ PopEmptySearchPath(saveNestLevel); @@ -833,6 +839,109 @@ GetTableRowLevelSecurityCommands(Oid relationId) } +/* + * CreateSecurityLabelCommands - return the SECURITY LABEL commands on + * the table identified by relationId. It is used by GetPreLoadTableCreationCommands() + * to reconstruct the security labels on the table and its columns. + */ +static List * +CreateSecurityLabelCommands(Oid relationId) +{ + List *securityLabelCommands = NIL; + + if (!RegularTable(relationId)) /* should be an Assert ? */ + { + return securityLabelCommands; + } + + Relation pg_seclabel = table_open(SecLabelRelationId, AccessShareLock); + ScanKeyData skey[1]; + ScanKeyInit(&skey[0], Anum_pg_seclabel_objoid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relationId)); + SysScanDesc scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, + true, NULL, 1, &skey[0]); + HeapTuple tuple = NULL; + List *table_name = NIL; + Relation relation = NULL; + TupleDesc tupleDescriptor = NULL; + List *securityLabelStmts = NULL; + ListCell *lc; + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + SecLabelStmt *secLabelStmt = makeNode(SecLabelStmt); + + if (relation == NULL) + { + relation = relation_open(relationId, AccessShareLock); + if (!RelationIsVisible(relationId)) + { + char *nsname = get_namespace_name(RelationGetNamespace(relation)); + table_name = lappend(table_name, makeString(nsname)); + } + char *relname = get_rel_name(relationId); + table_name = lappend(table_name, makeString(relname)); + } + + Datum datumArray[Natts_pg_seclabel]; + bool isNullArray[Natts_pg_seclabel]; + + heap_deform_tuple(tuple, RelationGetDescr(pg_seclabel), datumArray, + isNullArray); + int subObjectId = DatumGetInt32( + datumArray[Anum_pg_seclabel_objsubid - 1]); + secLabelStmt->provider = TextDatumGetCString( + datumArray[Anum_pg_seclabel_provider - 1]); + secLabelStmt->label = TextDatumGetCString( + datumArray[Anum_pg_seclabel_label - 1]); + + if (subObjectId > 0) + { + /* Its a column; construct the name */ + secLabelStmt->objtype = OBJECT_COLUMN; + List *col_name = list_copy(table_name); + + if (tupleDescriptor == NULL) + { + tupleDescriptor = RelationGetDescr(relation); + } + + Form_pg_attribute attrForm = TupleDescAttr(tupleDescriptor, subObjectId - 1); + char *attributeName = NameStr(attrForm->attname); + col_name = lappend(col_name, makeString(attributeName)); + + secLabelStmt->object = (Node *) col_name; + } + else + { + Assert(subObjectId == 0); + secLabelStmt->objtype = OBJECT_TABLE; + secLabelStmt->object = (Node *) table_name; + } + + securityLabelStmts = lappend(securityLabelStmts, secLabelStmt); + } + + foreach(lc, securityLabelStmts) + { + Node *stmt = (Node *) lfirst(lc); + char *secLabelStmtString = DeparseTreeNode(stmt); + TableDDLCommand *secLabelCommand = makeTableDDLCommandString(secLabelStmtString); + securityLabelCommands = lappend(securityLabelCommands, secLabelCommand); + } + + systable_endscan(scan); + table_close(pg_seclabel, AccessShareLock); + + if (relation != NULL) + { + relation_close(relation, AccessShareLock); + } + + return securityLabelCommands; +} + + /* * IndexImpliedByAConstraint is a helper function to be used while scanning * pg_index. It returns true if the index identified by the given indexForm is diff --git a/src/backend/distributed/relay/relay_event_utility.c b/src/backend/distributed/relay/relay_event_utility.c index 630c783e5..f89dba138 100644 --- a/src/backend/distributed/relay/relay_event_utility.c +++ b/src/backend/distributed/relay/relay_event_utility.c @@ -591,6 +591,58 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId) break; } + case T_SecLabelStmt: + { + SecLabelStmt *secLabelStmt = (SecLabelStmt *) parseTree; + + /* Should be looking at a security label for a table or column */ + if (secLabelStmt->objtype == OBJECT_TABLE || secLabelStmt->objtype == + OBJECT_COLUMN) + { + List *qualified_name = (List *) secLabelStmt->object; + String *table_name = NULL; + + switch (list_length(qualified_name)) + { + case 1: + { + table_name = castNode(String, linitial(qualified_name)); + break; + } + + case 2: + case 3: + { + table_name = castNode(String, lsecond(qualified_name)); + break; + } + + default: + { + /* Unlikely, but just in case */ + ereport(ERROR, (errmsg( + "unhandled name type in security label; name is: \"%s\"", + NameListToString(qualified_name)))); + break; + } + } + + /* Now change the table name: -> */ + char *relationName = strVal(table_name); + AppendShardIdToName(&relationName, shardId); + strVal(table_name) = relationName; + } + else + { + ereport(WARNING, (errmsg( + "unsafe object type in security label statement"), + errdetail("Object type: %u", + (uint32) secLabelStmt->objtype))); + } + + break; /* End of handling Security Label */ + } + default: { ereport(WARNING, (errmsg("unsafe statement type in name extension"), @@ -846,7 +898,6 @@ AppendShardIdToName(char **name, uint64 shardId) { SafeSnprintf(extendedName, NAMEDATALEN, "%s%s", (*name), shardIdAndSeparator); } - /* * Otherwise, we need to truncate the name further to accommodate * a sufficient hash value. The resulting name will avoid collision diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index a6e6bf6ec..19919e32c 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -546,7 +546,9 @@ extern List * AlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bo isPostprocess); /* seclabel.c - forward declarations*/ -extern List * PostprocessSecLabelStmt(Node *node, const char *queryString); +extern List * PostprocessAnySecLabelStmt(Node *node, const char *queryString); +extern List * PostprocessRoleSecLabelStmt(Node *node, const char *queryString); +extern List * PostprocessTableOrColumnSecLabelStmt(Node *node, const char *queryString); extern List * SecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); extern void citus_test_object_relabel(const ObjectAddress *object, const char *seclabel); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 66c697f03..362f0d59e 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -292,7 +292,9 @@ extern void QualifyTextSearchConfigurationCommentStmt(Node *node); extern void QualifyTextSearchDictionaryCommentStmt(Node *node); /* forward declarations for deparse_seclabel_stmts.c */ -extern char * DeparseSecLabelStmt(Node *node); +extern char * DeparseRoleSecLabelStmt(Node *node); +extern char * DeparseTableSecLabelStmt(Node *node); +extern char * DeparseColumnSecLabelStmt(Node *node); /* forward declarations for deparse_sequence_stmts.c */ extern char * DeparseDropSequenceStmt(Node *node); diff --git a/src/include/distributed/metadata/distobject.h b/src/include/distributed/metadata/distobject.h index e98e6ee86..9348f9e8c 100644 --- a/src/include/distributed/metadata/distobject.h +++ b/src/include/distributed/metadata/distobject.h @@ -21,6 +21,7 @@ extern bool ObjectExists(const ObjectAddress *address); extern bool CitusExtensionObject(const ObjectAddress *objectAddress); extern bool IsAnyObjectDistributed(const List *addresses); +extern bool IsAnyParentObjectDistributed(const List *addresses); extern bool ClusterHasDistributedFunctionWithDistArgument(void); extern void MarkObjectDistributed(const ObjectAddress *distAddress); extern void MarkObjectDistributedWithName(const ObjectAddress *distAddress, char *name, diff --git a/src/test/regress/expected/seclabel.out b/src/test/regress/expected/seclabel.out index ca6c6f984..bf85b3b89 100644 --- a/src/test/regress/expected/seclabel.out +++ b/src/test/regress/expected/seclabel.out @@ -1,8 +1,10 @@ -- -- SECLABEL -- --- Test suite for SECURITY LABEL ON ROLE statements +-- Test suite for SECURITY LABEL statements: +-- SECURITY LABEL ON IS -- +-- Citus can propagate ROLE, TABLE and COLUMN objects -- first we remove one of the worker nodes to be able to test -- citus_add_node later SELECT citus_remove_node('localhost', :worker_2_port); @@ -28,7 +30,8 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD (2 rows) RESET citus.enable_metadata_sync; --- check that we only support propagating for roles +-- check that we only support propagating for roles, tables and columns; +-- support for VIEW and FUNCTION is not there (yet) SET citus.shard_replication_factor to 1; -- distributed table CREATE TABLE a (a int); @@ -43,22 +46,12 @@ CREATE VIEW v_dist AS SELECT * FROM a; -- distributed function CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE '%', $1; END; $$; -SECURITY LABEL ON TABLE a IS 'citus_classified'; -NOTICE: not propagating SECURITY LABEL commands whose object type is not role -HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; -NOTICE: not propagating SECURITY LABEL commands whose object type is not role +NOTICE: not propagating SECURITY LABEL commands whose object type is not role or table or column HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; -NOTICE: not propagating SECURITY LABEL commands whose object type is not role +NOTICE: not propagating SECURITY LABEL commands whose object type is not role or table or column HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. -SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; - node_type | result ---------------------------------------------------------------------- - coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} - worker_1 | -(2 rows) - SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; node_type | result --------------------------------------------------------------------- @@ -74,17 +67,9 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') OR (2 rows) \c - - - :worker_1_port -SECURITY LABEL ON TABLE a IS 'citus_classified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; \c - - - :master_port -SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; - node_type | result ---------------------------------------------------------------------- - coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} - worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} -(2 rows) - SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; node_type | result --------------------------------------------------------------------- @@ -99,10 +84,8 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') OR worker_1 | {"label": "citus_classified", "objtype": "view", "provider": "citus '!tests_label_provider"} (2 rows) -DROP TABLE a CASCADE; -NOTICE: drop cascades to view v_dist DROP FUNCTION notice; --- test that SECURITY LABEL statement is actually propagated for ROLES +-- test that SECURITY LABEL statement is actually propagated for ROLES, TABLES and COLUMNS SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; -- we have exactly one provider loaded, so we may not include the provider in the command @@ -118,6 +101,41 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus_classified'; NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus_classified' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SECURITY LABEL ON TABLE a IS 'citus_classified'; +NOTICE: issuing SECURITY LABEL ON TABLE a IS 'citus_classified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SECURITY LABEL for "citus '!tests_label_provider" ON COLUMN a.a IS 'citus_classified'; +NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN a.a IS 'citus_classified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +-- ROLE, TABLE and COLUMN should be propagated to the worker +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + \c - - - :worker_1_port SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; @@ -139,6 +157,23 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} (2 rows) +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +SECURITY LABEL for "citus '!tests_label_provider" ON COLUMN a.a IS 'citus ''!unclassified'; +NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN a.a IS 'citus ''!unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + RESET citus.log_remote_commands; SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; @@ -150,19 +185,229 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') \c - - - :master_port SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- coordinator | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} (2 rows) -SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; - node_type | result +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - coordinator | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} - worker_1 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} (2 rows) +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +SET citus.shard_replication_factor to 1; +-- Distributed table with delimited identifiers +CREATE TABLE "Dist T" ("col.1" int); +SELECT create_distributed_table('"Dist T"', 'col.1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SECURITY LABEL ON TABLE "Dist T" IS 'citus_classified'; +SECURITY LABEL ON COLUMN "Dist T"."col.1" IS 'citus_classified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T".col.1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Add and Drop column +CREATE TABLE tddl (a1 int, b1 int, c1 int); +SELECT create_distributed_table('tddl', 'c1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE tddl ADD COLUMN d1 varchar(128); +-- Security label on tddl.d1 is propagated to all nodes +SECURITY LABEL ON COLUMN tddl.d1 IS 'citus_classified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tddl.d1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Drop column d1, security label should be removed from all nodes +ALTER TABLE tddl DROP COLUMN d1; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tddl.d1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | + worker_1 | +(2 rows) + +-- Define security labels before distributed table creation +CREATE TABLE tb (a1 int, b1 int, c1 int); +SECURITY LABEL ON TABLE tb IS 'citus_classified'; +SECURITY LABEL ON COLUMN tb.a1 IS 'citus_classified'; +SELECT create_distributed_table('tb', 'a1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb.a1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Similar test with reference table; security labels should be propagated to the worker. +CREATE TABLE tref (a1 int, b1 int, c1 int); +SECURITY LABEL ON TABLE tref IS 'citus_classified'; +SECURITY LABEL ON COLUMN tref.b1 IS 'citus_classified'; +SELECT create_reference_table('tref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tref') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tref.b1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Distributed table with delimited identifiers - 2 +CREATE TABLE "Dist T2" ("col one" int); +SELECT create_distributed_table('"Dist T2"', 'col one'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SECURITY LABEL ON TABLE "Dist T2" IS 'citus_classified'; +SECURITY LABEL ON COLUMN "Dist T2"."col one" IS 'citus_classified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2".col one') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Repeat the table and column tests using an explicit schema +CREATE SCHEMA label_test; +SET search_path TO label_test; +CREATE TABLE dist_test1 (a int); +SELECT create_distributed_table('dist_test1', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Define security labels on a distributed table +SECURITY LABEL ON TABLE dist_test1 IS 'citus_classified'; +SECURITY LABEL ON COLUMN dist_test1.a IS 'citus ''!unclassified'; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +CREATE TABLE dist_test2 (a int); +SECURITY LABEL on TABLE dist_test2 IS 'citus_unclassified'; +SECURITY LABEL on COLUMN dist_test2.a IS 'citus ''!unclassified'; +-- Distributing a table means security labels on the table and its columns are propagated +SELECT create_distributed_table('dist_test2', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Add and Drop column +CREATE TABLE tddl (a1 int, b1 int, c1 int); +SELECT create_distributed_table('tddl', 'c1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE tddl ADD COLUMN d1 varchar(128); +-- Security label on tddl.d1 is propagated to all nodes +SECURITY LABEL ON COLUMN tddl.d1 IS 'citus ''!unclassified'; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.tddl.d1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- Drop column d1, security label should be removed from all nodes +ALTER TABLE tddl DROP COLUMN d1; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.tddl.d1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | + worker_1 | +(2 rows) + +RESET search_path; -- add a new node and check that it also propagates the SECURITY LABEL statement to the new node SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; @@ -170,6 +415,20 @@ SELECT 1 FROM citus_add_node('localhost', :worker_2_port); NOTICE: issuing SELECT worker_create_or_alter_role('user1', 'CREATE ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL', 'ALTER ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL');SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'citus_classified' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SELECT worker_create_or_alter_role('user 2', 'CREATE ROLE "user 2" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL', 'ALTER ROLE "user 2" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL');SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('public.a');;DROP TABLE IF EXISTS public.a CASCADE;CREATE TABLE public.a (a integer) USING heap;ALTER TABLE public.a OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE public.a IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN public.a.a IS 'citus ''!unclassified';SELECT worker_create_truncate_trigger('public.a') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('public."Dist T"');;DROP TABLE IF EXISTS public."Dist T" CASCADE;CREATE TABLE public."Dist T" ("col.1" integer) USING heap;ALTER TABLE public."Dist T" OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE public."Dist T" IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN public."Dist T"."col.1" IS 'citus_classified';SELECT worker_create_truncate_trigger('public."Dist T"') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('public.tb');;DROP TABLE IF EXISTS public.tb CASCADE;CREATE TABLE public.tb (a1 integer, b1 integer, c1 integer) USING heap;ALTER TABLE public.tb OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE public.tb IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN public.tb.a1 IS 'citus_classified';SELECT worker_create_truncate_trigger('public.tb') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('public.tref');;DROP TABLE IF EXISTS public.tref CASCADE;CREATE TABLE public.tref (a1 integer, b1 integer, c1 integer) USING heap;ALTER TABLE public.tref OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE public.tref IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN public.tref.b1 IS 'citus_classified';SELECT worker_create_truncate_trigger('public.tref') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('public."Dist T2"');;DROP TABLE IF EXISTS public."Dist T2" CASCADE;CREATE TABLE public."Dist T2" ("col one" integer) USING heap;ALTER TABLE public."Dist T2" OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE public."Dist T2" IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN public."Dist T2"."col one" IS 'citus_classified';SELECT worker_create_truncate_trigger('public."Dist T2"') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('label_test.dist_test1');;DROP TABLE IF EXISTS label_test.dist_test1 CASCADE;CREATE TABLE label_test.dist_test1 (a integer) USING heap;ALTER TABLE label_test.dist_test1 OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE label_test.dist_test1 IS 'citus_classified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN label_test.dist_test1.a IS 'citus ''!unclassified';SELECT worker_create_truncate_trigger('label_test.dist_test1') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT pg_catalog.worker_drop_sequence_dependency('label_test.dist_test2');;DROP TABLE IF EXISTS label_test.dist_test2 CASCADE;CREATE TABLE label_test.dist_test2 (a integer) USING heap;ALTER TABLE label_test.dist_test2 OWNER TO postgres;SECURITY LABEL FOR "citus '!tests_label_provider" ON TABLE label_test.dist_test2 IS 'citus_unclassified';SECURITY LABEL FOR "citus '!tests_label_provider" ON COLUMN label_test.dist_test2.a IS 'citus ''!unclassified';SELECT worker_create_truncate_trigger('label_test.dist_test2') DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx ?column? --------------------------------------------------------------------- @@ -177,7 +436,7 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx (1 row) SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- coordinator | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} @@ -192,11 +451,114 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') worker_2 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} (3 rows) +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T".col.1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"."col one"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | + worker_1 | + worker_2 | +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb.a1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + +-- Check that security labels in the label_test schema are propagated to the newly added node +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + -- disable the GUC and check that the command is not propagated SET citus.enable_alter_role_propagation TO off; SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; NOTICE: not propagating SECURITY LABEL commands to other nodes HINT: Connect to other nodes directly to manually assign necessary labels. +SECURITY LABEL ON TABLE a IS 'citus_unclassified'; +NOTICE: not propagating SECURITY LABEL commands to other nodes +HINT: Connect to other nodes directly to manually assign necessary labels. +SECURITY LABEL ON COLUMN a.a IS 'citus_classified'; +NOTICE: not propagating SECURITY LABEL commands to other nodes +HINT: Connect to other nodes directly to manually assign necessary labels. SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; node_type | result --------------------------------------------------------------------- @@ -205,6 +567,22 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD worker_2 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"} (3 rows) +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + \c - - - :worker_2_port SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; @@ -212,6 +590,12 @@ SET citus.enable_alter_role_propagation TO off; SECURITY LABEL ON ROLE user1 IS 'citus ''!unclassified'; NOTICE: not propagating SECURITY LABEL commands to other nodes HINT: Connect to other nodes directly to manually assign necessary labels. +SECURITY LABEL ON TABLE a IS 'citus ''!unclassified'; +NOTICE: not propagating SECURITY LABEL commands to other nodes +HINT: Connect to other nodes directly to manually assign necessary labels. +SECURITY LABEL ON COLUMN a.a IS 'citus_unclassified'; +NOTICE: not propagating SECURITY LABEL commands to other nodes +HINT: Connect to other nodes directly to manually assign necessary labels. SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; node_type | result --------------------------------------------------------------------- @@ -220,7 +604,36 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD worker_2 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} (3 rows) +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_unclassified", "objtype": "column", "provider": "citus '!tests_label_provider"} +(3 rows) + RESET citus.enable_alter_role_propagation; +\c - - - :master_port -- cleanup +DROP TABLE a CASCADE; +NOTICE: drop cascades to view v_dist +DROP TABLE "Dist T" CASCADE; +DROP TABLE "Dist T2" CASCADE; +DROP TABLE tb CASCADE; +DROP TABLE tref CASCADE; +DROP TABLE tddl CASCADE; RESET citus.log_remote_commands; DROP ROLE user1, "user 2"; +DROP SCHEMA label_test CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table label_test.dist_test1 +drop cascades to table label_test.dist_test2 +drop cascades to table label_test.tddl diff --git a/src/test/regress/sql/seclabel.sql b/src/test/regress/sql/seclabel.sql index d39e01183..366579edd 100644 --- a/src/test/regress/sql/seclabel.sql +++ b/src/test/regress/sql/seclabel.sql @@ -1,8 +1,10 @@ -- -- SECLABEL -- --- Test suite for SECURITY LABEL ON ROLE statements +-- Test suite for SECURITY LABEL statements: +-- SECURITY LABEL ON IS -- +-- Citus can propagate ROLE, TABLE and COLUMN objects -- first we remove one of the worker nodes to be able to test -- citus_add_node later @@ -22,7 +24,8 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD RESET citus.enable_metadata_sync; --- check that we only support propagating for roles +-- check that we only support propagating for roles, tables and columns; +-- support for VIEW and FUNCTION is not there (yet) SET citus.shard_replication_factor to 1; -- distributed table CREATE TABLE a (a int); @@ -33,28 +36,23 @@ CREATE VIEW v_dist AS SELECT * FROM a; CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE '%', $1; END; $$; -SECURITY LABEL ON TABLE a IS 'citus_classified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; -SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; \c - - - :worker_1_port -SECURITY LABEL ON TABLE a IS 'citus_classified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; \c - - - :master_port -SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; -DROP TABLE a CASCADE; DROP FUNCTION notice; --- test that SECURITY LABEL statement is actually propagated for ROLES +-- test that SECURITY LABEL statement is actually propagated for ROLES, TABLES and COLUMNS SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; @@ -64,6 +62,15 @@ SECURITY LABEL ON ROLE user1 IS NULL; SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus_classified'; +SECURITY LABEL ON TABLE a IS 'citus_classified'; +SECURITY LABEL for "citus '!tests_label_provider" ON COLUMN a.a IS 'citus_classified'; + +-- ROLE, TABLE and COLUMN should be propagated to the worker +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + \c - - - :worker_1_port SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; @@ -72,13 +79,110 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORD SECURITY LABEL for "citus '!tests_label_provider" ON ROLE user1 IS 'citus_classified'; SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; +SECURITY LABEL for "citus '!tests_label_provider" ON COLUMN a.a IS 'citus ''!unclassified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + RESET citus.log_remote_commands; SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; \c - - - :master_port SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; -SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + +SET citus.shard_replication_factor to 1; + +-- Distributed table with delimited identifiers +CREATE TABLE "Dist T" ("col.1" int); +SELECT create_distributed_table('"Dist T"', 'col.1'); + +SECURITY LABEL ON TABLE "Dist T" IS 'citus_classified'; +SECURITY LABEL ON COLUMN "Dist T"."col.1" IS 'citus_classified'; + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T".col.1') ORDER BY node_type; + +-- Add and Drop column +CREATE TABLE tddl (a1 int, b1 int, c1 int); +SELECT create_distributed_table('tddl', 'c1'); + +ALTER TABLE tddl ADD COLUMN d1 varchar(128); + +-- Security label on tddl.d1 is propagated to all nodes +SECURITY LABEL ON COLUMN tddl.d1 IS 'citus_classified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tddl.d1') ORDER BY node_type; + +-- Drop column d1, security label should be removed from all nodes +ALTER TABLE tddl DROP COLUMN d1; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tddl.d1') ORDER BY node_type; + +-- Define security labels before distributed table creation +CREATE TABLE tb (a1 int, b1 int, c1 int); +SECURITY LABEL ON TABLE tb IS 'citus_classified'; +SECURITY LABEL ON COLUMN tb.a1 IS 'citus_classified'; + +SELECT create_distributed_table('tb', 'a1'); + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb.a1') ORDER BY node_type; + +-- Similar test with reference table; security labels should be propagated to the worker. +CREATE TABLE tref (a1 int, b1 int, c1 int); +SECURITY LABEL ON TABLE tref IS 'citus_classified'; +SECURITY LABEL ON COLUMN tref.b1 IS 'citus_classified'; + +SELECT create_reference_table('tref'); + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tref') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tref.b1') ORDER BY node_type; + +-- Distributed table with delimited identifiers - 2 +CREATE TABLE "Dist T2" ("col one" int); +SELECT create_distributed_table('"Dist T2"', 'col one'); + +SECURITY LABEL ON TABLE "Dist T2" IS 'citus_classified'; +SECURITY LABEL ON COLUMN "Dist T2"."col one" IS 'citus_classified'; + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2".col one') ORDER BY node_type; + +-- Repeat the table and column tests using an explicit schema +CREATE SCHEMA label_test; +SET search_path TO label_test; + +CREATE TABLE dist_test1 (a int); +SELECT create_distributed_table('dist_test1', 'a'); +-- Define security labels on a distributed table +SECURITY LABEL ON TABLE dist_test1 IS 'citus_classified'; +SECURITY LABEL ON COLUMN dist_test1.a IS 'citus ''!unclassified'; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1') ORDER BY node_type; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1.a') ORDER BY node_type; + +CREATE TABLE dist_test2 (a int); +SECURITY LABEL on TABLE dist_test2 IS 'citus_unclassified'; +SECURITY LABEL on COLUMN dist_test2.a IS 'citus ''!unclassified'; +-- Distributing a table means security labels on the table and its columns are propagated +SELECT create_distributed_table('dist_test2', 'a'); +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2') ORDER BY node_type; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2.a') ORDER BY node_type; + +-- Add and Drop column +CREATE TABLE tddl (a1 int, b1 int, c1 int); +SELECT create_distributed_table('tddl', 'c1'); + +ALTER TABLE tddl ADD COLUMN d1 varchar(128); + +-- Security label on tddl.d1 is propagated to all nodes +SECURITY LABEL ON COLUMN tddl.d1 IS 'citus ''!unclassified'; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.tddl.d1') ORDER BY node_type; + +-- Drop column d1, security label should be removed from all nodes +ALTER TABLE tddl DROP COLUMN d1; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.tddl.d1') ORDER BY node_type; + +RESET search_path; -- add a new node and check that it also propagates the SECURITY LABEL statement to the new node SET citus.log_remote_commands TO on; @@ -87,20 +191,52 @@ SELECT 1 FROM citus_add_node('localhost', :worker_2_port); SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T".col.1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"Dist T2"."col one"') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('tb.a1') ORDER BY node_type; +-- Check that security labels in the label_test schema are propagated to the newly added node +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1') ORDER BY node_type; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test1.a') ORDER BY node_type; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2') ORDER BY node_type; +SELECT node_type, result FROM public.get_citus_tests_label_provider_labels('label_test.dist_test2.a') ORDER BY node_type; -- disable the GUC and check that the command is not propagated SET citus.enable_alter_role_propagation TO off; SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; +SECURITY LABEL ON TABLE a IS 'citus_unclassified'; +SECURITY LABEL ON COLUMN a.a IS 'citus_classified'; + SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; \c - - - :worker_2_port SET citus.log_remote_commands TO on; SET citus.grep_remote_commands = '%SECURITY LABEL%'; SET citus.enable_alter_role_propagation TO off; SECURITY LABEL ON ROLE user1 IS 'citus ''!unclassified'; +SECURITY LABEL ON TABLE a IS 'citus ''!unclassified'; +SECURITY LABEL ON COLUMN a.a IS 'citus_unclassified'; + SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a.a') ORDER BY node_type; + RESET citus.enable_alter_role_propagation; +\c - - - :master_port -- cleanup +DROP TABLE a CASCADE; +DROP TABLE "Dist T" CASCADE; +DROP TABLE "Dist T2" CASCADE; +DROP TABLE tb CASCADE; +DROP TABLE tref CASCADE; +DROP TABLE tddl CASCADE; RESET citus.log_remote_commands; DROP ROLE user1, "user 2"; +DROP SCHEMA label_test CASCADE;