From 220d3edb69463a867afc6776161b2bdddecc5db6 Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Fri, 30 Jul 2021 08:52:01 -0700 Subject: [PATCH v6 14/19] Condition event triggers on owning role Make event triggers fire only when the trigger owner is a direct or indirect member of the role which performed the event. For superuser owners, nothing changes, but by assigning event trigger ownership to a non-superuser, this provides a way of filtering when a trigger fires. --- doc/src/sgml/event-trigger.sgml | 7 +++++++ doc/src/sgml/ref/create_event_trigger.sgml | 11 ++++++----- src/backend/commands/event_trigger.c | 9 +++++++-- src/backend/utils/cache/evtcache.c | 1 + src/include/utils/evtcache.h | 1 + src/test/regress/expected/event_trigger.out | 17 +++++++++++++++++ src/test/regress/sql/event_trigger.sql | 21 +++++++++++++++++++++ 7 files changed, 60 insertions(+), 7 deletions(-) diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml index 60366a950e..f003bedc29 100644 --- a/doc/src/sgml/event-trigger.sgml +++ b/doc/src/sgml/event-trigger.sgml @@ -119,6 +119,13 @@ to intercept. A common use of such triggers is to restrict the range of DDL operations which users may perform. + + + A trigger will only fire if the trigger owner is a direct or indirect + member of the role which performed the event. (Note that + PostgreSQL prior to version 15.0 did not + condition trigger firing on the permissions of the trigger owner.) + diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml index becd31bcad..ece05a5992 100644 --- a/doc/src/sgml/ref/create_event_trigger.sgml +++ b/doc/src/sgml/ref/create_event_trigger.sgml @@ -33,11 +33,12 @@ CREATE EVENT TRIGGER name CREATE EVENT TRIGGER creates a new event trigger. - Whenever the designated event occurs and the WHEN condition - associated with the trigger, if any, is satisfied, the trigger function - will be executed. For a general introduction to event triggers, see - . The user who creates an event trigger - becomes its owner. + Whenever the designated event occurs, the trigger function will be executed + if the WHEN condition associated with the trigger, if + any, is satisfied, and if the owner of the trigger is a direct or indirect + member of the role which performed the event. For a general introduction to + event triggers, see . The user who creates + an event trigger becomes its owner. diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 93e211cecf..13db569775 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -612,8 +612,13 @@ EventTriggerCommonSetup(Node *parsetree, if (filter_event_trigger(tag, item)) { - /* We must plan to fire this trigger. */ - runlist = lappend_oid(runlist, item->fnoid); + /* + * We must plan to fire this trigger only if the event trigger + * owner is a member of the current role, else the trigger owner + * can execute with privilege it could not do directly. + */ + if (is_member_of_role(item->fnowner, GetUserId())) + runlist = lappend_oid(runlist, item->fnoid); } } diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c index 460b720a65..ab86fcfa31 100644 --- a/src/backend/utils/cache/evtcache.c +++ b/src/backend/utils/cache/evtcache.c @@ -173,6 +173,7 @@ BuildEventTriggerCache(void) /* Allocate new cache item. */ item = palloc0(sizeof(EventTriggerCacheItem)); item->fnoid = form->evtfoid; + item->fnowner = form->evtowner; item->enabled = form->evtenabled; /* Decode and sort tags array. */ diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h index 58ddb71cb1..1e968129c6 100644 --- a/src/include/utils/evtcache.h +++ b/src/include/utils/evtcache.h @@ -28,6 +28,7 @@ typedef enum typedef struct { Oid fnoid; /* function to be called */ + Oid fnowner; /* event trigger owner */ char enabled; /* as SESSION_REPLICATION_ROLE_* */ Bitmapset *tagset; /* command tags, or NULL if empty */ } EventTriggerCacheItem; diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index d56c75de84..45215a622a 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -29,6 +29,23 @@ ERROR: unrecognized event name "elephant_bootstrap" -- OK create event trigger regress_event_trigger on ddl_command_start execute procedure test_event_trigger(); +create role test_evt_role1 nosuperuser; +create role test_evt_role2 nosuperuser; +alter event trigger regress_event_trigger owner to test_evt_role1; +set session authorization test_evt_role1; +-- OK, should fire test_event_trigger +create table test_evt_table1 (col text); +NOTICE: test_event_trigger: ddl_command_start CREATE TABLE +reset session authorization; +set session authorization test_evt_role2; +-- OK, should bypass test_event_trigger +create table test_evt_table2 (col text); +grant test_evt_role2 to test_evt_role1; +-- Ok, should fire test_event_trigger now +create table test_evt_table3 (col text); +NOTICE: test_event_trigger: ddl_command_start CREATE TABLE +reset session authorization; +alter event trigger regress_event_trigger owner to current_user; -- OK create event trigger regress_event_trigger_end on ddl_command_end execute function test_event_trigger(); diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index ef79b1059d..839dfd6db9 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -29,6 +29,27 @@ create event trigger regress_event_trigger on elephant_bootstrap create event trigger regress_event_trigger on ddl_command_start execute procedure test_event_trigger(); +create role test_evt_role1 nosuperuser; +create role test_evt_role2 nosuperuser; + +alter event trigger regress_event_trigger owner to test_evt_role1; + +set session authorization test_evt_role1; +-- OK, should fire test_event_trigger +create table test_evt_table1 (col text); +reset session authorization; + +set session authorization test_evt_role2; +-- OK, should bypass test_event_trigger +create table test_evt_table2 (col text); + +grant test_evt_role2 to test_evt_role1; +-- Ok, should fire test_event_trigger now +create table test_evt_table3 (col text); + +reset session authorization; +alter event trigger regress_event_trigger owner to current_user; + -- OK create event trigger regress_event_trigger_end on ddl_command_end execute function test_event_trigger(); -- 2.21.1 (Apple Git-122.3)