我遇到了一个奇怪的情况,让我很头疼,解决之后我也想了解为什么。
基本上,用户拥有执行“触发器”所需的所有权限(它实际上是具有关联的 ON DELETE CASCADE 的外键约束)以及触发器本身触及的所有对象的所有权限,但是当他执行触发器时,他实际上使用了对象的所有者权限,而不是他自己的权限。所有者的权限比用户的权限少,因此它给出了一个非常神秘的错误。
我知道理解我的意思很复杂,所以我创建了一个希望能够澄清的工作示例。我创建的模式是一个 Spotify 类型的玩具示例,其中有用户和艺术家,并且用户有他们关注的艺术家列表。
使用超级用户执行以下所有操作(仅为简单起见)
-- ALL DATA
create schema website;
create table public.artists (
id_artist serial primary key,
name text not null unique
);
insert into public.artists (name)
values ('Heilung'), ('Rammstain'), ('Iron Maiden');
create table website.users (
id_user serial primary key,
email text not null unique
);
insert into website.users (email)
values ('[email protected]'), ('[email protected]');
create table website.users_list (
id_user int not null,
id_artist int not null,
CONSTRAINT user_fk FOREIGN KEY (id_user) REFERENCES website.users (id_user),
CONSTRAINT artist_fk FOREIGN KEY (id_artist) REFERENCES public.artists (id_artist) ON DELETE CASCADE);
insert into website.users_list (id_user,id_artist)
values (1,1), (1,2), (1,3), (2,1), (2,3);
-- USERS AND PRIVILEGES
create user jenny with password '123';
create user tommy with password '123';
-- tommy is owner but has zero privileges
alter table website.users_list owner to tommy;
-- jenny is not owner but has all privileges
grant usage, create on schema website to jenny;
grant all on all tables in schema website,public to jenny;
grant usage on all sequences in schema website to jenny;
然后使用用户 Jenny 逐一运行这些查询。
--1)
select *
from website.users_list ul;
--2)
select *
from artists a;
--3)
delete from artists
where id_artist = 3;
第一个和第二个查询正常工作,因为 Jenny 可以在 website.users_list 和 public.artists 上执行 SELECT。她显然拥有模式网站的使用权限。但第三个她却做不到。它给出了这个看似毫无意义的错误。
ERROR: permission denied for schema website
当她对 public.artists 执行 DELETE 操作时,她实际上触发了 website.users_list 的外键约束,因此必须从 website.users_list 中删除与 id_artist = 3 的艺术家相关的所有行。但在那一刻,她正在使用汤米(website.users_list 的所有者)的权限,正如我们上面所看到的,汤米没有模式网站上的使用权限,从而出现了奇怪的错误。
有人可以向我解释为什么会发生这种情况吗?当用户拥有所有必要的权限时,表所有者与运行触发器有什么关系?我发现这非常令人困惑,坦率地说调试是一场噩梦(不幸的是,我是根据个人经验发言的)。
如果一个用户可以创建由不同用户以不明显的方式执行的代码(或者也许我应该说“一种情况”,因为 FK 不是明确的代码),那么这就是一个安全隐患。因此,为了安全起见,创建触发器的用户就是触发器运行的用户。
考虑另一种选择,汤米可以创建一个触发器,该触发器将删除他没有删除权限的表上的行,然后只需要引诱珍妮做一些看似无害的事情来触发触发器。