如何使用Postgres中的只读用户访问information_schema外键约束?

时间:2016-09-07 22:28:58

标签: postgresql privileges information-schema

简介

我一直在开发一个向导,为没有任何编程/ SQL背景的用户创建复杂的数据库Postgres查询。由于存储在information_schema视图中的外键约束,用户可以选择任意数量的表,工具将找到正确的连接设置(因此,用户不必添加ON table_a.field_1 = table_b.field_2)。

在开发过程中,我一直在使用管理数据库用户,现在想将其更改为只读用户以使其更安全。但是,此只读用户似乎无法访问外键约束。

现状

当选择了多个表时,该工具会尝试获取各个表之间的连接,以便了解如何连接它们。在此过程中,将执行以下查询:

SELECT 
  tc.constraint_name, 
  tc.table_name, 
  kcu.column_name, 
  ccu.table_name AS foreign_table_name, 
  ccu.column_name AS foreign_column_name 
FROM information_schema.table_constraints AS tc 
JOIN information_schema.key_column_usage AS kcu 
  ON tc.constraint_name = kcu.constraint_name 
JOIN information_schema.constraint_column_usage AS ccu 
  ON ccu.constraint_name = tc.constraint_name 
WHERE constraint_type = 'FOREIGN KEY' 
  AND ccu.table_name = 'TableB' 
  AND tc.table_name IN ('TableA');

(注意:最后一个WHERE子句使用IN,因为可以有多个基表可用.PableA是基表,每个成功连接/连接的表都可用于其他联接,例如第三个表可以使用AND ccu.table_name = 'TableC' AND tc.table_name IN ('TableA', 'TableB');等等。)

当使用admin db用户(具有GRANT,SELECT,INSERT,UPDATE,DELETE,TRUNCATE等最常见的权限)执行查询时,结果如下所示:

constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
constraint1     | TableA     | field_1     | TableB             | field_2
(1 row) 

但是当只读数据库用户运行该查询时,它会返回:

constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
(0 rows)

由于现有但未返回的外键约束条目,连接无法正确写入SQL,并且用户生成的查询(使用向导)失败。

我尝试了什么

首先,我认为只读用户(ro_user)可能没有权限访问数据库information_schema中的表和视图。所以我跑了

GRANT SELECT ON ALL TABLES IN SCHEMA information_schema TO ro_user;

作为管理员但无济于事。深入了解文档的深度,我发现information_schema中的所有表格和视图都可以在任何用户可以访问,默认情况下也可以在postgres中访问。因此授予select权限甚至不应该改变任何东西。

为了确保,我也跑了

GRANT REFERENCES ON ALL TABLES IN SCHEMA actual_database TO ro_user;

但当然,这并没有改变任何东西,因为REFERENCES只需要创建新的外键,我只需要阅读它们。

接下来,我想,也许这个工具的sql由于某些信息不可用而失败,所以我通过运行分别查询了三个视图:

SELECT * FROM information_schema.table_constraints AS tc WHERE constraint_type = 'FOREIGN KEY';
SELECT * FROM information_schema.key_column_usage AS kcu;
SELECT * FROM information_schema.constraint_column_usage AS ccu;

果然,最后一行不会返回ro_user的任何一行:

psql=> SELECT * FROM information_schema.constraint_column_usage AS ccu;
 table_catalog | table_schema | table_name | column_name | constraint_catalog | constraint_schema | constraint_name
---------------+--------------+------------+-------------+--------------------+-------------------+-----------------
(0 rows)

而管理员用户获得了大量结果。所以,它归结为那一个观点information_schema.constraint_column_usage

1 个答案:

答案 0 :(得分:3)

当我在一个小时的时间里输入那个问题,回忆起我在最后几天尝试过的所有想法时,我终于找到了原因。

  

视图 constraint_column_usage 标识当前数据库中某些约束使用的所有列。 仅显示包含在当前启用的角色所拥有的表中的那些列。

documentation通过此SO answer

通过我找到了解决方案

SELECT 
  conrelid::regclass AS table_from,
  conname,
  pg_get_constraintdef(c.oid) AS cdef 
FROM pg_constraint c 
JOIN pg_namespace n 
  ON n.oid = c.connamespace 
WHERE contype IN ('f') 
AND n.nspname = 'public' 
AND pg_get_constraintdef(c.oid) LIKE '%"TableB"%' 
AND conrelid::regclass::text IN ('"TableA"') 
ORDER BY conrelid::regclass::text, contype DESC;

它不会输出与旧查询相同的格式,但它包含相同的信息,并且 - 最重要的是 - 可供ro_user使用。