aboutsummaryrefslogtreecommitdiff
path: root/src/backend/replication/logical/worker.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2023-04-04 12:03:03 -0400
committerRobert Haas <rhaas@postgresql.org>2023-04-04 12:03:03 -0400
commit482675987bcdffb390ae735cfd5f34b485ae97c6 (patch)
tree889d1ea562d20bf291620e8df982d09f1852aa64 /src/backend/replication/logical/worker.c
parent1e10d49b65d6c26c61fee07999e4cd59eab2b765 (diff)
downloadpostgresql-482675987bcdffb390ae735cfd5f34b485ae97c6.tar.gz
postgresql-482675987bcdffb390ae735cfd5f34b485ae97c6.zip
Add a run_as_owner option to subscriptions.
This option is normally false, but can be set to true to obtain the legacy behavior where the subscription runs with the permissions of the subscription owner rather than the permissions of the table owner. The advantages of this mode are (1) it doesn't require that the subscription owner have permission to SET ROLE to each table owner and (2) since no role switching occurs, the SECURITY_RESTRICTED_OPERATION restrictions do not apply. On the downside, it allows any table owner to easily usurp the privileges of the subscription owner - basically, to take over their account. Because that's generally quite undesirable, we don't make this mode the default, but we do make it available, just in case the new behavior causes too many problems for someone. Discussion: http://postgr.es/m/CA+TgmoZ-WEeG6Z14AfH7KhmpX2eFh+tZ0z+vf0=eMDdbda269g@mail.gmail.com
Diffstat (limited to 'src/backend/replication/logical/worker.c')
-rw-r--r--src/backend/replication/logical/worker.c46
1 files changed, 36 insertions, 10 deletions
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 61009fa8cda..3d58910c145 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -2401,6 +2401,7 @@ apply_handle_insert(StringInfo s)
EState *estate;
TupleTableSlot *remoteslot;
MemoryContext oldctx;
+ bool run_as_owner;
/*
* Quick return if we are skipping data modification changes or handling
@@ -2425,8 +2426,13 @@ apply_handle_insert(StringInfo s)
return;
}
- /* Make sure that any user-supplied code runs as the table owner. */
- SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
+ /*
+ * Make sure that any user-supplied code runs as the table owner, unless
+ * the user has opted out of that behavior.
+ */
+ run_as_owner = MySubscription->runasowner;
+ if (!run_as_owner)
+ SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
/* Set relation for error callback */
apply_error_callback_arg.rel = rel;
@@ -2457,7 +2463,8 @@ apply_handle_insert(StringInfo s)
/* Reset relation for error callback */
apply_error_callback_arg.rel = NULL;
- RestoreUserContext(&ucxt);
+ if (!run_as_owner)
+ RestoreUserContext(&ucxt);
logicalrep_rel_close(rel, NoLock);
@@ -2546,6 +2553,7 @@ apply_handle_update(StringInfo s)
TupleTableSlot *remoteslot;
RTEPermissionInfo *target_perminfo;
MemoryContext oldctx;
+ bool run_as_owner;
/*
* Quick return if we are skipping data modification changes or handling
@@ -2577,8 +2585,13 @@ apply_handle_update(StringInfo s)
/* Check if we can do the update. */
check_relation_updatable(rel);
- /* Make sure that any user-supplied code runs as the table owner. */
- SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
+ /*
+ * Make sure that any user-supplied code runs as the table owner, unless
+ * the user has opted out of that behavior.
+ */
+ run_as_owner = MySubscription->runasowner;
+ if (!run_as_owner)
+ SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
/* Initialize the executor state. */
edata = create_edata_for_relation(rel);
@@ -2630,7 +2643,8 @@ apply_handle_update(StringInfo s)
/* Reset relation for error callback */
apply_error_callback_arg.rel = NULL;
- RestoreUserContext(&ucxt);
+ if (!run_as_owner)
+ RestoreUserContext(&ucxt);
logicalrep_rel_close(rel, NoLock);
@@ -2720,6 +2734,7 @@ apply_handle_delete(StringInfo s)
EState *estate;
TupleTableSlot *remoteslot;
MemoryContext oldctx;
+ bool run_as_owner;
/*
* Quick return if we are skipping data modification changes or handling
@@ -2750,8 +2765,13 @@ apply_handle_delete(StringInfo s)
/* Check if we can do the delete. */
check_relation_updatable(rel);
- /* Make sure that any user-supplied code runs as the table owner. */
- SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
+ /*
+ * Make sure that any user-supplied code runs as the table owner, unless
+ * the user has opted out of that behavior.
+ */
+ run_as_owner = MySubscription->runasowner;
+ if (!run_as_owner)
+ SwitchToUntrustedUser(rel->localrel->rd_rel->relowner, &ucxt);
/* Initialize the executor state. */
edata = create_edata_for_relation(rel);
@@ -2778,7 +2798,8 @@ apply_handle_delete(StringInfo s)
/* Reset relation for error callback */
apply_error_callback_arg.rel = NULL;
- RestoreUserContext(&ucxt);
+ if (!run_as_owner)
+ RestoreUserContext(&ucxt);
logicalrep_rel_close(rel, NoLock);
@@ -3225,13 +3246,18 @@ apply_handle_truncate(StringInfo s)
* Even if we used CASCADE on the upstream primary we explicitly default
* to replaying changes without further cascading. This might be later
* changeable with a user specified option.
+ *
+ * MySubscription->runasowner tells us whether we want to execute
+ * replication actions as the subscription owner; the last argument to
+ * TruncateGuts tells it whether we want to switch to the table owner.
+ * Those are exactly opposite conditions.
*/
ExecuteTruncateGuts(rels,
relids,
relids_logged,
DROP_RESTRICT,
restart_seqs,
- true);
+ !MySubscription->runasowner);
foreach(lc, remote_rels)
{
LogicalRepRelMapEntry *rel = lfirst(lc);