fix_run_as_owner.patch
application/octet-stream
Filename: fix_run_as_owner.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/replication/logical/tablesync.c | 15 | 0 |
| src/test/subscription/t/033_run_as_table_owner.pl | 38 | 2 |
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index c56d42dcd2..6874e8c2ce 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -120,6 +120,7 @@
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
+#include "utils/usercontext.h"
static bool table_states_valid = false;
static List *table_states_not_ready = NIL;
@@ -1253,6 +1254,8 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
char originname[NAMEDATALEN];
RepOriginId originid;
bool must_use_password;
+ UserContext ucxt;
+ bool run_as_owner;
/* Check the state of the table synchronization. */
StartTransactionCommand();
@@ -1374,6 +1377,15 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
*/
rel = table_open(MyLogicalRepWorker->relid, RowExclusiveLock);
+ /*
+ * Make sure that the copy command runs as the table owner, unless
+ * the user has opted out of that behaviour.
+ */
+ run_as_owner = MySubscription->runasowner;
+
+ if (!run_as_owner)
+ SwitchToUntrustedUser(rel->rd_rel->relowner, &ucxt);
+
/*
* Check that our table sync worker has permission to insert into the
* target table.
@@ -1469,6 +1481,9 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos)
res->err)));
walrcv_clear_result(res);
+ if(!run_as_owner)
+ RestoreUserContext(&ucxt);
+
table_close(rel, NoLock);
/* Make the copy visible. */
diff --git a/src/test/subscription/t/033_run_as_table_owner.pl b/src/test/subscription/t/033_run_as_table_owner.pl
index 0aa8a093ef..6d4051072c 100644
--- a/src/test/subscription/t/033_run_as_table_owner.pl
+++ b/src/test/subscription/t/033_run_as_table_owner.pl
@@ -70,8 +70,8 @@ sub revoke_superuser
# Create publisher and subscriber nodes with schemas owned and published by
# "regress_alice" but subscribed and replicated by different role
-# "regress_admin". For partitioned tables, layout the partitions differently
-# on the publisher than on the subscriber.
+# "regress_admin" and "regress_admin2". For partitioned tables, layout the
+# partitions differently on the publisher than on the subscriber.
#
$node_publisher = PostgreSQL::Test::Cluster->new('publisher');
$node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
@@ -86,15 +86,18 @@ for my $node ($node_publisher, $node_subscriber)
$node->safe_psql(
'postgres', qq(
CREATE ROLE regress_admin SUPERUSER LOGIN;
+ CREATE ROLE regress_admin2 SUPERUSER LOGIN;
CREATE ROLE regress_alice NOSUPERUSER LOGIN;
GRANT CREATE ON DATABASE postgres TO regress_alice;
SET SESSION AUTHORIZATION regress_alice;
CREATE SCHEMA alice;
GRANT USAGE ON SCHEMA alice TO regress_admin;
+ GRANT USAGE ON SCHEMA alice TO regress_admin2;
CREATE TABLE alice.unpartitioned (i INTEGER);
ALTER TABLE alice.unpartitioned REPLICA IDENTITY FULL;
GRANT SELECT ON TABLE alice.unpartitioned TO regress_admin;
+ GRANT SELECT ON TABLE alice.unpartitioned TO regress_admin2;
));
}
$node_publisher->safe_psql(
@@ -192,4 +195,37 @@ GRANT regress_alice TO regress_admin WITH INHERIT TRUE, SET FALSE;
expect_replication("alice.unpartitioned", 3, 7, 13,
"with INHERIT but not SET ROLE can replicate");
+# Remove the subscrition and truncate the table for the initial data sync
+# tests.
+$node_subscriber->safe_psql(
+ 'postgres', qq(
+DROP SUBSCRIPTION admin_sub;
+TRUNCATE alice.unpartitioned;
+));
+
+# Create a new subscription "admin_sub" owned by regress_admin2. It's
+# disabled so that we revoke superuser privilege after creation.
+$node_subscriber->safe_psql(
+ 'postgres', qq(
+SET SESSION AUTHORIZATION regress_admin2;
+CREATE SUBSCRIPTION admin_sub CONNECTION '$publisher_connstr' PUBLICATION alice
+WITH (run_as_owner = false, password_required = false, copy_data = true, enabled = false);
+));
+
+# Revoke superuser privilege for "regress_admin2", and give it the
+# ability to SET ROLE. Then enable the subscription "admin_sub".
+revoke_superuser("regress_admin2");
+$node_subscriber->safe_psql(
+ 'postgres', qq(
+GRANT regress_alice TO regress_admin2 WITH INHERIT FALSE, SET TRUE;
+ALTER SUBSCRIPTION admin_sub ENABLE;
+));
+
+# Because the initial data sync is working as the table owner, all
+# dat should be copied.
+$node_subscriber->wait_for_subscription_sync($node_publisher,
+ 'admin_sub');
+expect_replication("alice.unpartitioned", 3, 7, 13,
+ "table owner can do the initial data copy");
+
done_testing();