Определитель безопасности не работает внутри вместо триггера? - PullRequest
0 голосов
/ 24 апреля 2018

У меня есть база данных PostgreSQL 9.5 с:

  • Таблица test.domain (int id, текстовое значение), в которой хранятся возможные значения для поля.Эти данные являются динамическими.
  • Таблица test.table (id int, domainn text), в которой поле domainn ссылается на таблицу test.domain.
  • Представление test.view_domain, являющееся представлениемtest.domain.

Я определил INSTEAD триггера с опцией определителя безопасности в представлении.Этот триггер обновляет таблицу test.domain.Проблема в том, что, несмотря на то, что этот триггер выполняется как пользовательская «система», обновление ссылки на таблицу test.table выполняется пользователем invoker.

Вот пример, где, если он выполняется как postgres,Я ожидаю получить «пользовательскую систему» ​​как ошибку вместо «пользовательский postgres»

drop schema IF EXISTS test  cascade;
create schema test;


create function test.modified() returns trigger as
$$
BEGIN
    raise exception 'user %', ' '||current_user;
END
$$
language plpgsql;

set role system;

create function test.insert_with_system() returns trigger as
$$
DECLARE
 valor text;
BEGIN

    --raise exception 'user %', ' '||current_user;
    update test.domain set value =  ''||new.value where id =  new.id;

END
$$ language plpgsql security definer;

reset role;

CREATE table test.domain(

id int primary key,
value text unique
);

create view test.domain_view as select * from test.domain;

create table test.table(
id int primary key,
domainn text
);

alter table test.table add foreign key (domainn)
   references test.domain(value) on delete restrict on update cascade;

create trigger test_trigger before insert or update or delete on test.table
   for each row execute procedure test.modified();

create trigger instead_ins INSTEAD OF update or delete on test.domain_view 
   for each row execute procedure test.insert_with_system();


insert into test.domain(id, value) values(1,'one');

alter table test.table  DISABLE TRIGGER all;
insert into test.table(id, domainn) values (0,'one');
alter table test.table enable TRIGGER all;

update test.domain_view set value = 'two';
select * from test.table;

1 Ответ

0 голосов
/ 25 апреля 2018

Каскадное обновление всегда запускается в контексте безопасности владельца ссылочной таблицы (test.table в вашем примере).

См. ri_PerformCheck в src/backend/utils/adt/ri_triggers.c:

/*
 * Use the query type code to determine whether the query is run against
 * the PK or FK table; we'll do the check as that table's owner
 */
if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
    query_rel = pk_rel;
else
    query_rel = fk_rel;

...

/* Switch to proper UID to perform check as */
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
                       save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
                       SECURITY_NOFORCE_RLS);

Я пытался следовать коду до его источника, и поведение, кажется, происходит из commit 465cf168eb6151275016486fe2d2c629fed967ca .

При поиске в архивах хакеров соответствующих обсуждений я обнаружил это .

Итак, насколько я могу сказать, поведение пытается избежать следующего:

  • Пользователь A владеет atable и предоставляет REFERENCES в этой таблице пользователю B.

  • Пользователь B владеет btable и добавляет внешний ключ к atable с помощью ON UPDATE OR DELETE CASCADE.Пользователь A не имеет разрешений на btable.

  • Пользователь A пытается обновить или удалить строку в atable, которая может каскадно перейти к btable, и завершается неудачно сОшибка «отказано в разрешении».

Лично я не уверен, является ли данное поведение хорошим или нет, но я вижу, что было бы удивительно, если бы A 'небыть в состоянии изменить таблицу, которой она владеет.

...