记录设置为SECURITY DEFINER的Postgres功能的调用者

时间:2015-07-29 22:31:14

标签: postgresql

如果函数设置为SECURITY DEFINER,有没有办法让Postgres中的函数(使用9.4)找出调用它的用户?

我遇到的设计问题是我想通过我的网络应用程序进行用户身份验证(以便我可以共享连接池)但仍然在数据库中维护审核记录,这些记录从Web应用程序引用经过身份验证的最终用户。

目前的流程是:

  • 用户连接到网络应用,并进行身份验证以验证他们是否是他们所声称的人。
  • 然后,该网络应用会通过应用用户连接到Postgres。
  • 当代表用户执行任何查询时,Web应用程序会运行SET ROLE [username],然后运行相关查询,然后运行RESET ROLE,然后再将连接返回到连接池。
  • 所有用户查询均通过功能完成。用户有权运行这些功能但没有权限直接对表进行更改(除了模拟某些用户的权限外,Web应用程序帐户没有权限)。这些函数还通过使用CURRENT_USERCURRENT_TIMESTAMP插入/更新适当的值来维护“创建者”,“上次更新”等概念的列。

我遇到的问题是CURRENT_USER总是返回功能所有者,SESSION_USER总是返回Web应用程序帐户,而我真正想要的是帐户的名称调用了这个函数。

这可能吗?我没有在文档中看到任何特别有希望的东西。相反,其他人如何解决这个设计问题?

2 个答案:

答案 0 :(得分:0)

与数据库的每个连接都在会话的上下文中。池中的每个连接都有一个与之关联的会话。根据您描述业务逻辑的方式,每当您使用池中的连接时,它都适用于您应用的单个用户。你可以做的是创建一个临时表 - 只要会话存在 - 并在那里存储用户详细信息,以便在需要时进行检索。

因此,无论何时从池中获取连接,都要执行这些命令(可能连接是新的,因此表可能最初不存在):

CREATE TEMP UNLOGGED TABLE IF NOT EXISTS user_details(
  -- whatever details you need
);
DELETE FROM user_details; -- previous session data, if any
INSERT INTO user_details VALUES (...);

你也可以把它包装成一个函数并调用它。

然后在您的会话期间,您只需在查询的user_details条款中包含表格FROM,或直接在您的函数正文中添加,并提取所需的信息。

答案 1 :(得分:0)

您可以定义DOMAIN,将NAME值约束为CURRENT_USER

CREATE DOMAIN whoami AS NAME
CHECK( VALUE = CURRENT_USER )
;

然后将此DOMAIN应用于函数的参数。该参数可以定义为可选参数,默认值为CURRENT_USER(默认值在调用者的上下文中计算):

CREATE FUNCTION restricted_area( caller whoami DEFAULT CURRENT_USER )
RETURNS TABLE ( caller NAME, owner NAME )
SECURITY DEFINER
LANGUAGE SQL
AS $SQL$
  SELECT caller, CURRENT_USER;
$SQL$
;

CREATE ROLE restricted_area_owner;
ALTER FUNCTION restricted_area( whoami )
OWNER TO restricted_area_owner
;

CREATE ROLE web_user;
GRANT EXECUTE ON FUNCTION restricted_area( whoami )
TO web_user
;

SET ROLE web_user;
SELECT *
FROM restricted_area()
;

给出了预期的结果:

  caller  |         owner         
----------+-----------------------
 web_user | restricted_area_owner
(1 row)

CURRENT_USER也可以明确提供:

SELECT *
FROM restricted_area('web_user')

  caller  |         owner         
----------+-----------------------
 web_user | restricted_area_owner
(1 row)

但没有接受其他值:

SELECT *
FROM restricted_area('postgres')
;

ERROR:  value for domain whoami violates check constraint "whoami_check"