摘要:我是管理员账号,怎么还没有权限?当小伙伴询问的时候,我第一时间就会想到都是用户同名Schema惹的祸
本文分享自华为云社区《你应该知道的数仓安全——都是同名Schema惹的祸》,作者: zhangkunhn 。
典型场景
经常遇到小伙伴问到:
- 我是管理员账号,怎么还没有权限?
- 管理员给我赋权了啊,怎么还没有权限?
当小伙伴询问的时候,我第一时间就会想到都是用户同名Schema惹的祸。
同名Schema是私有Schema
我们知道,CREATE USER语法在创建用户的同时会在当前数据库中创建一个与用户同名的SCHEMA。这个Schema很特殊,只有两种用户能在这个Schema下面创建表、视图、函数等对象:
- 用户自己
- 管理员
然而,不管谁创建的,对象的所有者(Owner)都是用户自己。基于这个事实,我们可以称用户同名Schema为私有Schema。私有表明了当前Schema的特殊性,在这个Schema下面的所有对象都是这个用户自己的,不管是谁创建的。
我们来看一个例子。数据库中有三个用户,如表所示。
使用管理员dbadmin执行以下SQL:
-
gaussdb
=#
create
table ua.ta (c1
int);
-
CREATE
TABLE
-
gaussdb
=#
select relname, relowner, rolname
from pg_class c, pg_authid a
where relname
=
'ta'
and c.relowner
= a.oid;
-
relname
| relowner
| rolname
-
---------+----------+---------
-
t1
|
16546
| ua(不是dbadmin)
-
(
1
row)
可以看到:系统管理员在普通用户同名schema下创建的对象,所有者为schema的同名用户
让我们来总结第一点:同名Schema是私有Schema,这个Schema下面的所有对象的所有者都是用户自己,不管是谁创建的。管理员在私有Schema下创建的表等对象会发生Owner切换。
视图规则:按照view的owner做权限检查
再来谈视图和视图封装的基表的权限。视图对基表的权限检查是按照视图的Owner做权限检查。例如
create view v1 as select * from t1;
用户执行select * from v1时做权限检查分为两步:
- 首先检查当前用户对视图v1的SELECT权限;
- 然后检查视图v1的owner对基表t1的SELECT权限。
而不是直接检查当前用户对基表t1的SELECT权限。
总结第二点:视图会按照视图的Owner对基表做权限检查。
私有Schema与视图规则导致莫名其妙的权限报错
由于私有Schema会造成Owner切换,而视图规则要求对基表按照视图Owner做权限检查。那么在私有Schema下面创建视图就会导致莫名其妙的现象:
私有Schema + view规则 --> 管理员无权限访问自己创建的视图。
-
gaussdb
=#
set role dbadmin password ‘
*
*
*
*
*
*
*’;
-- 切换到管理员用户
-
SET
-
gaussdb
=
>
create
table ua.ta (c1
int);
-- 表ta的owner是???
-
CREATE
TABLE
-
gaussdb
=
>
create
view ub.vb
as
select
*
from ua.ta;
-- 视图vb的owner是???
-
CREATE
VIEW
-
gaussdb
=
>
select
*
from ub.vb;
-- 管理员创建的view,他竟然无权限!!!
-
ERROR:
SELECT permission denied
to
user “ub”
for relation “ua.ta“
我们以管理员用户在用户ua的私有schema下创建表ta, 之后在用户ub的schema下创建了视图vb, 视图vb的基表是ua.ta。管理员执行对视图vb的查询,报错无权限。
对于这个莫名其妙的现象,我们仔细捋一捋其中的来龙去脉。
- 根据私有Schema切换Owner的法则,尽管是管理员创建的,ta的owner切换到ua, 同样vb的owner应该是u2.
- 结合view规则,对基表按照视图Owner做权限检查,视图vb的owner对基表ua.ta是否具有select权限。视图vb的owner是ub,而ub对ua.ta无select权限,因此查询报错。
权限报错消除
如何解决这种权限报错呢?从上述梳理中,其实已经明白了如何赋权来消除这种报错。那就是给视图的owner用户ub赋予基表ua.ta的SELCT权限:管理员或者用户ua执行下面的赋权语句即可。
GRANT SELECT on ua.ta to ub;
有小伙伴 问了,我每次都这么仔细捋一捋,感觉很浪费时间,有没有简单的方法。答案是有的,只需从查询的权限报错着手,不需要每次都捋一捋。
我们当前的权限报错有着非常完备的提醒,会给用户显示如下提示:
SELECT permission denied to user “user_name” for relation “ schema_name.table_name“
可以看到,权限报错包括哪个权限、哪个用户、哪个schema的哪个对象。那么看到这个之后,可以直接找管理员或者Owner来执行授予操作就可以了。
对于上述报错,直接就对应到赋权语句:
grant SELECT on schema_name.table_name to user_name;
当然这个赋权跟我们前面的分析是殊途同归的。
现在来回想下,是不是一切都清晰了。那么我们再来看一遍示例。以管理员dbadmin执行以下SQL语句。
-
gaussdb
=
>
create
table ua.ta (c1
int);
-- ta的owner是ua
-
CREATE
TABLE
-
gaussdb
=
>
create
view ub.vb
as
select
*
from ua.ta;
-- vb的owner是ub
-
CREATE
VIEW
-
postgres
=#
select
*
from ub.vb;
-- 按照报错的指引来
-
ERROR:
SELECT permission denied
to
user “ub”
for relation “ua.ta“
-
gaussdb
=#
grant usage
on schema ua
to ub;
-- 将schema ua和基表ta的权限给ub
-
GRANT
-
gaussdb
=#
grant
select
on ua.ta
to ub;
-
GRANT
-
postgres
=#
select
*
from ub.vb;
-- 权限检查通过,可以正常查询
-
c1
-
----
-
(
0
rows)
补充知识:
- CREATE USER语法在创建用户的同时会在当前数据库中,为该用户创建一个同名的SCHEMA;其他数据库中,则不会创建同名的SCHEMA;如果需要,可使用create schema authorization user_name语法,该语法会根据用户名来创建同名schema。
- 为什么用户同名Schema这么特殊,别的Schema没有这些特点?因为:a) 在创建用户时同时创建了与用户同名的Schema,并将Schema的owner设置为同名用户;b) 在创建对象时,如果创建对象的schema是用户同名Schema,就会将对象的Owner切换为同名用户,而不是执行SQL语句的当前用户。
总结
遇到权限报错第一时间想到是否涉及同名schema。同名Schema是用户的私有Schema。私有Schema中所有的对象Owner都是用户自己,不管是谁创建的。在私有Schema中创建对象,对象Owner会切换到同名用户。视图规则是按照视图Owner来检查对基表的权限。由于私有Schema的Owner切换机制和视图规则导致了同名Schema的权限报错。根据报错的提示,授予用户相应的权限就可以解决权限报错问题。
转载:https://blog.csdn.net/devcloud/article/details/127488053