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.
pull/7976/head
Colm 2025-04-30 18:03:52 +01:00 committed by GitHub
parent ea7aa6712d
commit d4dd44e715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 999 additions and 118 deletions

View File

@ -399,10 +399,37 @@ static DistributeObjectOps Any_Rename = {
.markDistributed = false, .markDistributed = false,
}; };
static DistributeObjectOps Any_SecLabel = { static DistributeObjectOps Any_SecLabel = {
.deparse = DeparseSecLabelStmt, .deparse = NULL,
.qualify = NULL, .qualify = NULL,
.preprocess = 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, .operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress, .address = SecLabelStmtObjectAddress,
.markDistributed = false, .markDistributed = false,
@ -2119,8 +2146,28 @@ GetDistributeObjectOps(Node *node)
case T_SecLabelStmt: case T_SecLabelStmt:
{ {
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; return &Any_SecLabel;
} }
}
case T_RenameStmt: case T_RenameStmt:
{ {

View File

@ -15,19 +15,18 @@
#include "distributed/commands/utility_hook.h" #include "distributed/commands/utility_hook.h"
#include "distributed/coordinator_protocol.h" #include "distributed/coordinator_protocol.h"
#include "distributed/deparser.h" #include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/log_utils.h" #include "distributed/log_utils.h"
#include "distributed/metadata/distobject.h" #include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h" #include "distributed/metadata_sync.h"
/* /*
* PostprocessSecLabelStmt prepares the commands that need to be run on all workers to assign * PostprocessRoleSecLabelStmt prepares the commands that need to be run on all workers to assign
* security labels on distributed objects, currently supporting just Role objects. * security labels on distributed roles. It also ensures that all object dependencies exist on all
* It also ensures that all object dependencies exist on all * nodes for the role in the SecLabelStmt.
* nodes for the object in the SecLabelStmt.
*/ */
List * List *
PostprocessSecLabelStmt(Node *node, const char *queryString) PostprocessRoleSecLabelStmt(Node *node, const char *queryString)
{ {
if (!EnableAlterRolePropagation || !ShouldPropagate()) if (!EnableAlterRolePropagation || !ShouldPropagate())
{ {
@ -42,7 +41,77 @@ PostprocessSecLabelStmt(Node *node, const char *queryString)
return NIL; 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())
{
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;
/*
* 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 * If we are not in the coordinator, we don't want to interrupt the security
@ -52,7 +121,7 @@ PostprocessSecLabelStmt(Node *node, const char *queryString)
if (EnableUnsupportedFeatureMessages && IsCoordinator()) if (EnableUnsupportedFeatureMessages && IsCoordinator())
{ {
ereport(NOTICE, (errmsg("not propagating SECURITY LABEL commands whose " ereport(NOTICE, (errmsg("not propagating SECURITY LABEL commands whose "
"object type is not role"), "object type is not role or table or column"),
errhint("Connect to worker nodes directly to manually " errhint("Connect to worker nodes directly to manually "
"run the same SECURITY LABEL command."))); "run the same SECURITY LABEL command.")));
} }
@ -60,19 +129,6 @@ PostprocessSecLabelStmt(Node *node, const char *queryString)
} }
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);
}
/* /*
* SecLabelStmtObjectAddress returns the object address of the object on * SecLabelStmtObjectAddress returns the object address of the object on
* which this statement operates (secLabelStmt->object). Note that it has no limitation * which this statement operates (secLabelStmt->object). Note that it has no limitation

View File

@ -10,37 +10,16 @@
#include "postgres.h" #include "postgres.h"
#include "catalog/namespace.h"
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "distributed/deparser.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 static void
AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt) BeginSecLabel(StringInfo buf, SecLabelStmt *stmt)
{ {
initStringInfo(buf);
appendStringInfoString(buf, "SECURITY LABEL "); appendStringInfoString(buf, "SECURITY LABEL ");
if (stmt->provider != NULL) if (stmt->provider != NULL)
@ -49,31 +28,84 @@ AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt)
} }
appendStringInfoString(buf, "ON "); 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: static void
EndSecLabel(StringInfo buf, SecLabelStmt *stmt)
{ {
ereport(ERROR, (errmsg("unsupported security label statement for" appendStringInfo(buf, "IS %s", (stmt->label != NULL) ?
" deparsing"))); quote_literal_cstr(stmt->label) : "NULL");
}
} }
appendStringInfoString(buf, "IS ");
if (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)
{ {
appendStringInfo(buf, "%s", quote_literal_cstr(stmt->label)); 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;
} }
else
/*
* 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)
{ {
appendStringInfoString(buf, "NULL"); 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;
} }

View File

@ -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 * GetDistributedObjectAddressList returns a list of ObjectAddresses that contains all
* distributed objects as marked in pg_dist_object * distributed objects as marked in pg_dist_object

View File

@ -36,6 +36,7 @@
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_index.h" #include "catalog/pg_index.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_seclabel.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/sequence.h" #include "commands/sequence.h"
#include "foreign/foreign.h" #include "foreign/foreign.h"
@ -57,6 +58,7 @@
#include "distributed/citus_ruleutils.h" #include "distributed/citus_ruleutils.h"
#include "distributed/commands.h" #include "distributed/commands.h"
#include "distributed/coordinator_protocol.h" #include "distributed/coordinator_protocol.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h" #include "distributed/listutils.h"
#include "distributed/metadata_cache.h" #include "distributed/metadata_cache.h"
#include "distributed/metadata_sync.h" #include "distributed/metadata_sync.h"
@ -83,6 +85,7 @@ static char * CitusCreateAlterColumnarTableSet(char *qualifiedRelationName,
const ColumnarOptions *options); const ColumnarOptions *options);
static char * GetTableDDLCommandColumnar(void *context); static char * GetTableDDLCommandColumnar(void *context);
static TableDDLCommand * ColumnarGetTableOptionsDDL(Oid relationId); static TableDDLCommand * ColumnarGetTableOptionsDDL(Oid relationId);
static List * CreateSecurityLabelCommands(Oid relationId);
/* exports for SQL callable functions */ /* exports for SQL callable functions */
PG_FUNCTION_INFO_V1(master_get_table_metadata); PG_FUNCTION_INFO_V1(master_get_table_metadata);
@ -665,6 +668,9 @@ GetPreLoadTableCreationCommands(Oid relationId,
List *policyCommands = CreatePolicyCommands(relationId); List *policyCommands = CreatePolicyCommands(relationId);
tableDDLEventList = list_concat(tableDDLEventList, policyCommands); tableDDLEventList = list_concat(tableDDLEventList, policyCommands);
List *securityLabelCommands = CreateSecurityLabelCommands(relationId);
tableDDLEventList = list_concat(tableDDLEventList, securityLabelCommands);
/* revert back to original search_path */ /* revert back to original search_path */
PopEmptySearchPath(saveNestLevel); 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 * IndexImpliedByAConstraint is a helper function to be used while scanning
* pg_index. It returns true if the index identified by the given indexForm is * pg_index. It returns true if the index identified by the given indexForm is

View File

@ -591,6 +591,58 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId)
break; 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: <dist table> -> <shard table> */
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: default:
{ {
ereport(WARNING, (errmsg("unsafe statement type in name extension"), 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); SafeSnprintf(extendedName, NAMEDATALEN, "%s%s", (*name), shardIdAndSeparator);
} }
/* /*
* Otherwise, we need to truncate the name further to accommodate * Otherwise, we need to truncate the name further to accommodate
* a sufficient hash value. The resulting name will avoid collision * a sufficient hash value. The resulting name will avoid collision

View File

@ -546,7 +546,9 @@ extern List * AlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bo
isPostprocess); isPostprocess);
/* seclabel.c - forward declarations*/ /* 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 List * SecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);
extern void citus_test_object_relabel(const ObjectAddress *object, const char *seclabel); extern void citus_test_object_relabel(const ObjectAddress *object, const char *seclabel);

View File

@ -292,7 +292,9 @@ extern void QualifyTextSearchConfigurationCommentStmt(Node *node);
extern void QualifyTextSearchDictionaryCommentStmt(Node *node); extern void QualifyTextSearchDictionaryCommentStmt(Node *node);
/* forward declarations for deparse_seclabel_stmts.c */ /* 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 */ /* forward declarations for deparse_sequence_stmts.c */
extern char * DeparseDropSequenceStmt(Node *node); extern char * DeparseDropSequenceStmt(Node *node);

View File

@ -21,6 +21,7 @@
extern bool ObjectExists(const ObjectAddress *address); extern bool ObjectExists(const ObjectAddress *address);
extern bool CitusExtensionObject(const ObjectAddress *objectAddress); extern bool CitusExtensionObject(const ObjectAddress *objectAddress);
extern bool IsAnyObjectDistributed(const List *addresses); extern bool IsAnyObjectDistributed(const List *addresses);
extern bool IsAnyParentObjectDistributed(const List *addresses);
extern bool ClusterHasDistributedFunctionWithDistArgument(void); extern bool ClusterHasDistributedFunctionWithDistArgument(void);
extern void MarkObjectDistributed(const ObjectAddress *distAddress); extern void MarkObjectDistributed(const ObjectAddress *distAddress);
extern void MarkObjectDistributedWithName(const ObjectAddress *distAddress, char *name, extern void MarkObjectDistributedWithName(const ObjectAddress *distAddress, char *name,

View File

@ -1,8 +1,10 @@
-- --
-- SECLABEL -- SECLABEL
-- --
-- Test suite for SECURITY LABEL ON ROLE statements -- Test suite for SECURITY LABEL statements:
-- SECURITY LABEL ON <object> IS <definition>
-- --
-- Citus can propagate ROLE, TABLE and COLUMN objects
-- first we remove one of the worker nodes to be able to test -- first we remove one of the worker nodes to be able to test
-- citus_add_node later -- citus_add_node later
SELECT citus_remove_node('localhost', :worker_2_port); 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) (2 rows)
RESET citus.enable_metadata_sync; 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; SET citus.shard_replication_factor to 1;
-- distributed table -- distributed table
CREATE TABLE a (a int); CREATE TABLE a (a int);
@ -43,22 +46,12 @@ CREATE VIEW v_dist AS SELECT * FROM a;
-- distributed function -- distributed function
CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$ CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$
BEGIN RAISE NOTICE '%', $1; END; $$; 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'; 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. HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command.
SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; 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. 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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type;
node_type | result node_type | result
--------------------------------------------------------------------- ---------------------------------------------------------------------
@ -74,17 +67,9 @@ SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') OR
(2 rows) (2 rows)
\c - - - :worker_1_port \c - - - :worker_1_port
SECURITY LABEL ON TABLE a IS 'citus_classified';
SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified';
SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; SECURITY LABEL ON VIEW v_dist IS 'citus_classified';
\c - - - :master_port \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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type;
node_type | result 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"} worker_1 | {"label": "citus_classified", "objtype": "view", "provider": "citus '!tests_label_provider"}
(2 rows) (2 rows)
DROP TABLE a CASCADE;
NOTICE: drop cascades to view v_dist
DROP FUNCTION notice; 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.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; SET citus.grep_remote_commands = '%SECURITY LABEL%';
-- we have exactly one provider loaded, so we may not include the provider in the command -- 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'; 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' NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus_classified'
DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 \c - - - :worker_1_port
SET citus.log_remote_commands TO on; SET citus.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; 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"} worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"}
(2 rows) (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; RESET citus.log_remote_commands;
SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; 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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type;
@ -156,13 +191,223 @@ 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"} worker_1 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"}
(2 rows) (2 rows)
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;
node_type | result node_type | result
--------------------------------------------------------------------- ---------------------------------------------------------------------
coordinator | {"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 '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"}
(2 rows) (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 -- 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.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; 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' 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 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' 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 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx
?column? ?column?
--------------------------------------------------------------------- ---------------------------------------------------------------------
@ -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"} worker_2 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"}
(3 rows) (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 -- disable the GUC and check that the command is not propagated
SET citus.enable_alter_role_propagation TO off; SET citus.enable_alter_role_propagation TO off;
SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; SECURITY LABEL ON ROLE user1 IS 'citus_unclassified';
NOTICE: not propagating SECURITY LABEL commands to other nodes NOTICE: not propagating SECURITY LABEL commands to other nodes
HINT: Connect to other nodes directly to manually assign necessary labels. 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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type;
node_type | result 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"} worker_2 | {"label": "citus_classified", "objtype": "role", "provider": "citus '!tests_label_provider"}
(3 rows) (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 \c - - - :worker_2_port
SET citus.log_remote_commands TO on; SET citus.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; 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'; SECURITY LABEL ON ROLE user1 IS 'citus ''!unclassified';
NOTICE: not propagating SECURITY LABEL commands to other nodes NOTICE: not propagating SECURITY LABEL commands to other nodes
HINT: Connect to other nodes directly to manually assign necessary labels. 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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type;
node_type | result 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"} worker_2 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"}
(3 rows) (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; RESET citus.enable_alter_role_propagation;
\c - - - :master_port
-- cleanup -- 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; RESET citus.log_remote_commands;
DROP ROLE user1, "user 2"; 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

View File

@ -1,8 +1,10 @@
-- --
-- SECLABEL -- SECLABEL
-- --
-- Test suite for SECURITY LABEL ON ROLE statements -- Test suite for SECURITY LABEL statements:
-- SECURITY LABEL ON <object> IS <definition>
-- --
-- Citus can propagate ROLE, TABLE and COLUMN objects
-- first we remove one of the worker nodes to be able to test -- first we remove one of the worker nodes to be able to test
-- citus_add_node later -- 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; 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; SET citus.shard_replication_factor to 1;
-- distributed table -- distributed table
CREATE TABLE a (a int); 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 $$ CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$
BEGIN RAISE NOTICE '%', $1; END; $$; BEGIN RAISE NOTICE '%', $1; END; $$;
SECURITY LABEL ON TABLE a IS 'citus_classified';
SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified';
SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; 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('notice(text)') ORDER BY node_type;
SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') 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 \c - - - :worker_1_port
SECURITY LABEL ON TABLE a IS 'citus_classified';
SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified';
SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; SECURITY LABEL ON VIEW v_dist IS 'citus_classified';
\c - - - :master_port \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('notice(text)') ORDER BY node_type;
SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') 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; 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.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; 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 ON ROLE user1 IS 'citus_unclassified';
SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus_classified'; 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 \c - - - :worker_1_port
SET citus.log_remote_commands TO on; SET citus.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; 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'; 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('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; RESET citus.log_remote_commands;
SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; 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; SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type;
\c - - - :master_port \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('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 -- 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.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('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('"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 -- disable the GUC and check that the command is not propagated
SET citus.enable_alter_role_propagation TO off; SET citus.enable_alter_role_propagation TO off;
SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; 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('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 \c - - - :worker_2_port
SET citus.log_remote_commands TO on; SET citus.log_remote_commands TO on;
SET citus.grep_remote_commands = '%SECURITY LABEL%'; SET citus.grep_remote_commands = '%SECURITY LABEL%';
SET citus.enable_alter_role_propagation TO off; SET citus.enable_alter_role_propagation TO off;
SECURITY LABEL ON ROLE user1 IS 'citus ''!unclassified'; 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('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; RESET citus.enable_alter_role_propagation;
\c - - - :master_port
-- cleanup -- 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; RESET citus.log_remote_commands;
DROP ROLE user1, "user 2"; DROP ROLE user1, "user 2";
DROP SCHEMA label_test CASCADE;