有没有办法让 Postgres 中的函数(使用 9.4)找出调用它的用户,如果该函数设置为 SECURITY DEFINER?
我遇到的设计问题是我想通过我的 Web 应用程序进行用户身份验证(以便我可以共享连接池),但仍然在数据库中维护引用来自 Web 应用程序的经过身份验证的最终用户的审计记录。
目前的流程是:
SET ROLE [username],然后是相关查询,然后是RESET ROLE将连接返回到连接池。CURRENT_USER和插入/更新适当的值来维护“创建者”、“上次更新”等概念的列CURRENT_TIMESTAMP。我遇到的问题是CURRENT_USER始终返回函数所有者,并SESSION_USER始终返回 Web 应用程序帐户,而我真正想要的是调用该函数的帐户的名称。
这可能吗?我在文档中没有看到任何特别有希望的东西。相反,其他人是如何解决这个设计问题的?
您可以定义一个DOMAIN将NAME值约束为CURRENT_USER:
CREATE DOMAIN whoami AS NAME
CHECK( VALUE = CURRENT_USER )
;
Run Code Online (Sandbox Code Playgroud)
然后将其应用于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()
;
Run Code Online (Sandbox Code Playgroud)
这给出了预期的结果:
caller | owner
----------+-----------------------
web_user | restricted_area_owner
(1 row)
Run Code Online (Sandbox Code Playgroud)
的CURRENT_USER也可以明确地规定:
SELECT *
FROM restricted_area('web_user')
caller | owner
----------+-----------------------
web_user | restricted_area_owner
(1 row)
Run Code Online (Sandbox Code Playgroud)
但不接受其他值:
SELECT *
FROM restricted_area('postgres')
;
ERROR: value for domain whoami violates check constraint "whoami_check"
Run Code Online (Sandbox Code Playgroud)
与数据库的每个连接都在会话的上下文中。池中的每个连接都有一个与其关联的会话。根据您描述业务逻辑的方式,每当您使用池中的连接时,它都是针对应用程序的单个用户的。您可以做的是创建一个临时表(该表的存在时间与会话一样长)并将用户详细信息存储在其中,以便在需要时进行检索。
因此,每当您从池中获取连接时,请执行以下命令(也许该连接是新的,因此表最初可能不存在):
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 (...);
Run Code Online (Sandbox Code Playgroud)
您还可以将其包装到一个函数中并调用它。
然后,在会话期间,您只需将表包含user_details在FROM查询子句中或直接包含在函数主体中,然后提取所需的信息。