Skip to content

Commit 82cec50

Browse files
committed
fix: evtrigs firing SD functions
1 parent 503fad9 commit 82cec50

File tree

5 files changed

+105
-11
lines changed

5 files changed

+105
-11
lines changed

src/event_triggers.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ force_noop(FmgrInfo *finfo)
1919
finfo->fn_expr = NULL; /* no parse tree */
2020
}
2121

22-
Oid get_function_owner(func_owner_search search){
22+
func_attrs get_function_attrs(func_search search){
2323
// Lookup function name OID. Note that for event trigger functions, there's no arguments.
2424
Oid func_oid = InvalidOid;
2525

@@ -39,10 +39,11 @@ Oid get_function_owner(func_owner_search search){
3939

4040
Form_pg_proc procForm = (Form_pg_proc) GETSTRUCT(proc_tup);
4141
Oid func_owner = procForm->proowner;
42+
bool is_secdef = procForm->prosecdef;
4243

4344
ReleaseSysCache(proc_tup);
4445

45-
return func_owner;
46+
return (func_attrs){func_owner, is_secdef};
4647
}
4748

4849
bool is_event_trigger_function(Oid foid){

src/event_triggers.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@ typedef struct {
1212
List *funcname;
1313
FmgrInfo *finfo;
1414
} val;
15-
} func_owner_search;
15+
} func_search;
1616

17-
extern Oid get_function_owner(func_owner_search search);
17+
typedef struct {
18+
Oid owner;
19+
bool is_security_definer;
20+
} func_attrs;
21+
22+
extern func_attrs get_function_attrs(func_search search);
1823

1924
extern void force_noop(FmgrInfo *finfo);
2025

src/supautils.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,26 @@ static void supautils_fmgr_hook(FmgrHookEventType event, FmgrInfo *flinfo, Datum
9797
// we only need to change behavior before the function gets executed
9898
case FHET_START: {
9999
if (is_event_trigger_function(flinfo->fn_oid)){ // recheck the function is an event trigger in case another extension need_fmgr_hook passed our supautils_needs_fmgr_hook
100-
const char *current_role_name = GetUserNameFromId(GetUserId(), false);
101-
const bool role_is_super = superuser();
100+
func_attrs fattrs = get_function_attrs((func_search){ .as = FO_SEARCH_FINFO, .val.finfo = flinfo });
101+
const Oid current_role_oid =
102+
fattrs.is_security_definer?
103+
// when the function is security definer, we need to get the session user id otherwise it will fire for superusers or reserved roles.
104+
// See https://github.com/supabase/supautils/issues/140.
105+
GetOuterUserId():
106+
GetUserId();
107+
const char *current_role_name = GetUserNameFromId(current_role_oid, false);
108+
const bool role_is_super = superuser_arg(current_role_oid);
102109
const bool role_is_reserved = is_reserved_role(current_role_name, false);
103110
if (role_is_super || role_is_reserved) {
104-
Oid func_owner = get_function_owner((func_owner_search){ .as = FO_SEARCH_FINFO, .val.finfo = flinfo });
105-
bool function_is_owned_by_super = superuser_arg(func_owner);
111+
bool function_is_owned_by_super = superuser_arg(fattrs.owner);
106112
if (!function_is_owned_by_super){
107113
if (log_skipped_evtrigs){
108114
char *func_name = get_func_name(flinfo->fn_oid);
109115
ereport(
110116
NOTICE,
111117
errmsg("Skipping event trigger function \"%s\" for user \"%s\"", func_name, current_role_name),
112118
errdetail("\"%s\" %s and the function \"%s\" is not superuser-owned, it's owned by \"%s\"",
113-
current_role_name, role_is_super?"is a superuser":"is a reserved role", func_name, GetUserNameFromId(func_owner, false))
119+
current_role_name, role_is_super?"is a superuser":"is a reserved role", func_name, GetUserNameFromId(fattrs.owner, false))
114120
);
115121
}
116122
// we can't skip execution directly inside the fmgr_hook (although we can abort it with ereport)
@@ -814,8 +820,8 @@ static void supautils_hook(PROCESS_UTILITY_PARAMS) {
814820
const char *current_role_name = GetUserNameFromId(current_user_id, false);
815821

816822
bool current_user_is_super = superuser_arg(current_user_id);
817-
Oid function_owner = get_function_owner((func_owner_search){FO_SEARCH_NAME, {stmt->funcname}});
818-
bool function_is_owned_by_super = superuser_arg(function_owner);
823+
func_attrs fattrs = get_function_attrs((func_search){FO_SEARCH_NAME, {stmt->funcname}});
824+
bool function_is_owned_by_super = superuser_arg(fattrs.owner);
819825

820826
if(!current_user_is_super && function_is_owned_by_super){
821827
ereport(ERROR, (

test/expected/event_triggers.out.in

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,45 @@ ERROR: must be owner of event trigger event_trigger_1
183183
set role privileged_role;
184184
drop event trigger event_trigger_1;
185185
drop event trigger event_trigger_2;
186+
\echo
187+
188+
-- security definer function case
189+
set role privileged_role;
190+
create function secdef_show_current_user()
191+
returns event_trigger
192+
language plpgsql
193+
security definer as
194+
$$
195+
begin
196+
raise notice 'the event trigger is executed for %', current_user;
197+
end;
198+
$$;
199+
create event trigger event_trigger_3 on ddl_command_end
200+
execute procedure secdef_show_current_user();
201+
\echo
202+
203+
-- secdef won't be executed for superuser
204+
set role postgres;
205+
create table super_foo();
206+
\echo
207+
208+
-- secdef won't be executed for reserved roles
209+
set role supabase_storage_admin;
210+
create table storage_foo();
211+
\echo
212+
213+
-- secdef will be executed for other roles
214+
set role rolecreator;
215+
create table rolecreator_foo();
216+
NOTICE: the event trigger is executed for privileged_role
217+
\echo
218+
219+
-- secdef will be executed for privileged_role
220+
set role privileged_role;
221+
create table privileged_role_foo();
222+
NOTICE: the event trigger is executed for privileged_role
223+
\echo
224+
225+
-- cleanup
226+
set role privileged_role;
227+
drop event trigger event_trigger_3;

test/sql/event_triggers.sql

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,43 @@ drop event trigger event_trigger_1;
144144
set role privileged_role;
145145
drop event trigger event_trigger_1;
146146
drop event trigger event_trigger_2;
147+
\echo
148+
149+
-- security definer function case
150+
set role privileged_role;
151+
create function secdef_show_current_user()
152+
returns event_trigger
153+
language plpgsql
154+
security definer as
155+
$$
156+
begin
157+
raise notice 'the event trigger is executed for %', current_user;
158+
end;
159+
$$;
160+
create event trigger event_trigger_3 on ddl_command_end
161+
execute procedure secdef_show_current_user();
162+
\echo
163+
164+
-- secdef won't be executed for superuser
165+
set role postgres;
166+
create table super_foo();
167+
\echo
168+
169+
-- secdef won't be executed for reserved roles
170+
set role supabase_storage_admin;
171+
create table storage_foo();
172+
\echo
173+
174+
-- secdef will be executed for other roles
175+
set role rolecreator;
176+
create table rolecreator_foo();
177+
\echo
178+
179+
-- secdef will be executed for privileged_role
180+
set role privileged_role;
181+
create table privileged_role_foo();
182+
\echo
183+
184+
-- cleanup
185+
set role privileged_role;
186+
drop event trigger event_trigger_3;

0 commit comments

Comments
 (0)