Is there a way for a function in Postgres (using 9.4) to find out the user that invoked it if the function is set to SECURITY DEFINER
?
The design problem that I have is that I want to do user authentication via my web app (so that I can share a connection pool) but still maintain audit records within the database that reference the authenticated end user from the web app.
The current flow is:
SET ROLE [username]
, then the relevant query, then a RESET ROLE
before returning the connection to the connection pool.CURRENT_USER
and CURRENT_TIMESTAMP
.The problem that I'm running into is that the CURRENT_USER
always returns the function owner, and SESSION_USER
always returns the web app account, whereas what I really want is the name of the account that invoked the function.
Is this possible? I'm not seeing anything particularly promising in the documentation. Conversely, how have other people solved this design problem?
SECURITY DEFINER specifies that the function is to be executed with the privileges of the user that owns it. The key word EXTERNAL is allowed for SQL conformance, but it is optional since, unlike in SQL, this feature applies to all functions not only external ones.
It can be used to replace single quotes enclosing string literals (constants) anywhere in SQL scripts. The body of a function happens to be such a string literal. Dollar-quoting is a PostgreSQL-specific substitute for single quotes to avoid escaping of nested single quotes (recursively).
To start the function editor, navigate to the desired function in the schema browser, select the 'Source Code' tab and click on the 'Edit In Function Editor' button. Alternatively, you can right-click on the function in the schema browser and select 'Edit In Function Editor'.
You can define a DOMAIN
which constrains NAME
values to CURRENT_USER
:
CREATE DOMAIN whoami AS NAME
CHECK( VALUE = CURRENT_USER )
;
then apply this DOMAIN
to a parameter of your function. The parameter can be defined as optional, with a default value of CURRENT_USER
(the default value is computed in the context of the caller):
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()
;
which gives the expected result:
caller | owner
----------+-----------------------
web_user | restricted_area_owner
(1 row)
the CURRENT_USER
can be also be provided explicitly:
SELECT *
FROM restricted_area('web_user')
caller | owner
----------+-----------------------
web_user | restricted_area_owner
(1 row)
but no other value is accepted:
SELECT *
FROM restricted_area('postgres')
;
ERROR: value for domain whoami violates check constraint "whoami_check"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With