我最终使用基于不同查询及其返回结果的多步骤方法解决了这个问题。我正在使用一些 PHP 代码执行所有查询,所以我没有 100% 的必要只有大查询来解决所有问题。
此外,我们的数据库在物理上是分开的,它们通过数据库链接链接在一起,所以我必须做一些额外的工作来确保这种权限检查跨数据库链接工作。
目前我只检查 SELECT、DELETE、UPDATE 和 INSERT 权限;这就是我现在真正需要的。
步骤
这是坚果列表中的一般程序。
- 获取用户可用的所有数据库链接的列表。
对于以下步骤,从我们登录的当前数据库开始,然后检查从步骤 1 检索到的每个数据库链接。
2a。使用数据库查询检查表是否可见。
2b。如果该表可见,请检查是否有任何权限查询返回此用户有权访问该表。
查询
以下是上述每个步骤的查询。
1 数据库链接
select db_link from all_db_links
2a 表格可见性
select * from all_tables%DB_LINK% where table_name = :table_name and owner = :owner
在适用的情况下,上面的 %DB_LINK% 将替换为 @db_link。如果我们正在检查当前连接,那么我将其完全删除。请记住,查询是由 PHP 脚本执行的,因此我可以对字符串进行一些字符串操作,以删除当前数据库的 %DB_LINK% 或将其替换为我们从步骤 1 中获得的数据库链接之一。
2b。用户、角色、表
这里总共有 4 个查询。
/*****/
/* 1 */
/*****/
select *
from user_tab_privs%DB_LINK%
where
owner = :owner
and
table_name = :table_name
and
privilege = :privilege
/*****/
/* 2 */
/*****/
select * from user_sys_privs%DB_LINK% where privilege = :privilege
/*****/
/* 3 */
/*****/
select * from
(
select distinct granted_role from
(
select null linker, granted_role
from user_role_privs%DB_LINK%
union all
select role linker, granted_role
from role_role_privs%DB_LINK%
)
start with linker is null
connect by prior granted_role = linker
) user_roles join role_tab_privs%DB_LINK% rtab on user_roles.granted_role = rtab.role
where
owner = :owner
and
table_name = :table_name
and
rtab.privilege = :privilege
/*****/
/* 4 */
/*****/
select * from
(
select distinct granted_role from
(
select null linker, granted_role
from user_role_privs%DB_LINK%
union all
select role linker, granted_role
from role_role_privs%DB_LINK%
)
start with linker is null
connect by prior granted_role = linker
) user_roles join role_sys_privs%DB_LINK% rtab on user_roles.granted_role = rtab.role
where rtab.privilege = :privilege
解释
数据库链接
在 phpunit 测试中,我传递了两件事:表名和模式名(所有者)。但是,由于存在数据库链接,我们必须在查询中使用 @db_link 显式检查其他数据库。否则,我可能会报告一个表无法访问,而实际上它可以通过数据库链接访问。
表可见性
如果用户看不到表,那么检查权限是没有意义的。检查权限还可以防止用户被赋予“SELECT ANY TABLE”权限但表本身实际上并不存在的情况导致不必要的失败。
4 个查询的带
如其他海报所示,用户可以通过多种方式访问表格。具体来说,他们可以被赋予角色,这些角色可以被赋予角色,然后这些角色可以被分配访问权。或者,用户可以通过系统权限获得显式访问或通用访问。
查询 1
四个查询中的第一个检查是否已为用户提供了对表的显式 SELECT、DELETE 等权限。这很容易理解,理想情况下就是所有必要的
查询 2
第二个检查是否已授予用户任何系统权限,如 DELETE ANY TABLE、SELECT ANY TABLE、INSERT ANY TABLE 等。这些权限并未在表上显式授予,但用户可以在任何表上执行任何引用的操作他们有知名度。
查询 3
第三个查询将查看用户拥有的任何角色,无论是直接还是间接,是否已被授予对表的显式 SELECT、DELETE 等权限。这类似于查询 1,只是它基于赋予用户的角色,而不是用户。
查询 4
第四个检查用户拥有的任何角色,无论是直接还是间接,是否已被授予任何系统权限,例如 DELETE ANY TABLE、SELECT ANY TABLE、INSERT ANY TABLE 等。这类似于查询 2。
就是这样!我将这些链接在一起,并使用从每个返回的结果来确定用户是否具有所需的权限。
值得一提的细节
如果用户拥有跨 db_link_1 的任何权限,这并不意味着他们对跨 db_link_2 访问的表拥有相同的权限。大多数人应该知道这一点,但我想确保我明确地说明了这一点。例如,通过 db_link_1 在表 1 上选择权限并不意味着通过 db_link_2 在表 2 上选择权限。
我一次检查每个 db_link。所以首先我从我直接连接的数据库开始,不需要数据库链接。然后,如果我找不到表或表上没有权限,我会转到下一个数据库链接。
在查询 2 和 4 中,我使用“SELECT ANY TABLE”、“DELETE ANY TABLE”等代替 :privilege 变量。
在查询 1 和 3 中,我使用 'SELECT'、'DELETE'、'UPDATE'、'INSERT' 代替 :privilege 变量。