GRANT EXECUTE not granting access to related tables in function - postgresql

I have user that should have access only for 2 functions, so I granted them to him:
REVOKE ALL ON FUNCTION some_func(firstid INT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION some_func(firstid INT) TO some_user;
But I'm getting weird error:
42501: permission denied for relation some_table
when trying to access my function with:
SELECT * FROM offers_get(0);
My function looks like:
CREATE OR REPLACE FUNCTION public.some_func(firstid integer)
RETURNS TABLE(/* something */)
LANGUAGE plpgsql
AS $function$
DECLARE
/* something */
BEGIN
/* some other logic */
RETURN QUERY SELECT * FROM some_table;
END;
$function$
So, access to related tables was not granted? How to grant access only for function execute?

You want to define the function using SECURITY DEFINER (see the documentation).
By default, Postgres uses the permissions of the caller, not the definer.
CREATE OR REPLACE FUNCTION public.some_func(firstid integer)
RETURNS TABLE(/* something */)
LANGUAGE plpgsql
SECURITY DEFINER
. . .

Related

How to retrieve the search_path set for an function using a query on information_schema?

I have multiple functions for each schema that have search_path defined on the creation in this way:
CREATE OR REPLACE FUNCTION the_schema.update_complete_url(updateLang bigint) RETURNS boolean AS $$
DECLARE
tree_row record;
BEGIN
// body of function
END
$$ LANGUAGE plpgsql SET search_path=the_schema,public,pg_temp;
The search_path is defined so that I do not need to prefix each table in the function body with the_schema. which makes it easier for me to maintain those functions.
Now I have a maintaining/migration script that tests if certain functions exits and have the correct settings. Retrieving the function itself is not a problem as it would look that way:
SELECT * FROM "information_schema"."routines"
WHERE "routine_type"='FUNCTION' AND "specific_schema"='the_schema';
And querying for the parameters is also no problem using "information_schema"."parameters", but where is the SET search_path=the_schema,public,pg_temp part stored?
AFAIK it is not available in information_schema.
You can get it thru pg_catalog, but it is PostgreSQL specific:
select unnest(proconfig) from pg_proc where proname = 'function_name';

postgresql schemas conflict

I created new database named test1, new user test1_user and i granted all on database test1 to test1_user. Moreover, I created schema test1_user. Now, I also created new schema functions_package and its owner is test1_user.
my code :
CREATE OR REPLACE FUNCTION functions_package.newFunction(file_id
utl_file.file_type) RETURNS VOID AS $body$
BEGIN
functions_package.old_function1(file_id);
functions_package.old_function2(file_id);
functions_package.old_function3(file_id);
End;
--
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
STABLE;
I created few functions under schema functions_package and now I`m trying to create new function that uses the old functions but im getting eror :
ERROR: syntax error at or near "functions_package"
LINE 3: functions_package.old_function('aa');
IM trying to create the new function from user test1_user;
Im getting this error for every function in schema functions_package that i`m trying to summon in my new function. In all the old function I didnt summon any function at all so i didnt have this kind of error.
Some help ?
you lack select before function name:
try:
CREATE OR REPLACE FUNCTION functions_package.newFunction(file_id
utl_file.file_type) RETURNS VOID AS $body$
BEGIN
perform functions_package.old_function1(file_id);
perform functions_package.old_function2(file_id);
perform functions_package.old_function3(file_id);
End;
--
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
STABLE;

Calling function which has no permission from another function

Say we have two functions. First should check permissions and if all goes right update table. Here it is:
CREATE OR REPLACE FUNCTION public.clients_test(
_clientid int
,_comments varchar
)
RETURNS void AS
$BODY$
declare _result varchar;
BEGIN
if now()::time>'17:00'::time then
select public.clients_check_17_00() into _result;
end if;
update clients set comments=_comments where clientid=_clientid;
END
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 1;
ALTER FUNCTION public.clients_test(int, varchar) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_test(int, varchar) TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_test(int, varchar) TO "RestrictedAccess";
REVOKE ALL ON FUNCTION public.clients_test(int, varchar) FROM public;
Second function doing nothing with database and exists only for security reasons. I was going to call it from the fist one. Here it is:
CREATE OR REPLACE FUNCTION public.clients_check_17_00()
RETURNS void AS
$BODY$
BEGIN
END
$BODY$
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
COST 1;
ALTER FUNCTION public.clients_check_17_00()OWNER TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_check_17_00() TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_check_17_00() TO "FullAccess";
REVOKE ALL ON FUNCTION public.clients_check_17_00() FROM public;
Some users should have to update comments only before 17:00. So they have permissions on
public.clients_test
and have no permissions on
public.clients_check_17_00
I'd like to have the error 'You do not have permissions to execute public.clients_check_17_00', but this did not work.
This is an interesting problem, but your approach is all wrong.
Rather than checking for the time inside the function, you should manipulate permissions outside of the function. In your approach, the time is checked on every call, also at 08:02, 14:23, 16:34, etc, which is very inefficient for the obvious reasons. Instead, make two simple SQL script files, one to disable execute permissions on the function and another to enable those permissions again and have a scheduled job run those scripts at 17:00 and then presumably at 08:00 or so to re-enable execute permission. It could be as simple as:
psql -h localhost -d your_db -U postgres \
-c 'REVOKE EXECUTE ON FUNCTION clients_test(int, varchar) FROM "RestrictedAccess"'
And vice-versa for enabling the permission again. But see the documentation for parameters specific to accessing your server.
How exactly that works depends on your OS; in Linux you would use a cron job, on Windows you use Task Scheduler.
Incidentally, returning a message from a function to the session is done with RAISE NOTICE.
Another important point from your code: NEVER USE THE postgres ROLE AS OWNER OF OBJECTS OR TO RUN CODE. Apologies for EMPHASIS but this point can not be stressed enough. You should instead make a non-privileged role, preferably without login privilege, as owner of all objects (tables and functions, etc) and then explicitly set privileges for specific user roles.

Postgres function using DDL statement

I am trying to create function in Postgres which can automate user creation process but it doesn;t accept parameter in DDL statement.
CREATE OR REPLACE FUNCTION AUTOUSER (uname varchar(20))
RETURNS TEXT AS $$
DECLARE
nm varchar(20);
BEGIN
nm=$1;
CREATE USER nm WITH PASSWORD 'Iash12';
GRANT ALL ON DATABASE iashdb TO nm;
GRANT ALL ON ALL TABLES IN SCHEMA public TO nm;
RETURN CONCAT(nm,' Created');
END;
$$
LANGUAGE plpgsql;
Above function create user as 'nm' instead of passed parameter name however RETURN statement showing correct result. Thanks in advance,
You need to use dynamic SQL and you need to quote the parameters properly. The easiest way is to use the format() function with the appropriate placeholders:
CREATE OR REPLACE FUNCTION AUTOUSER (uname varchar(20))
RETURNS TEXT AS $$
BEGIN
execute format('CREATE USER %I WITH PASSWORD %L', uname, 'Iash12');
execute format('GRANT ALL ON DATABASE iashdb TO %I', uname);
execute format('GRANT ALL ON ALL TABLES IN SCHEMA public TO %I', uname);
RETURN CONCAT(uname,' Created');
END;
$$
LANGUAGE plpgsql;
The placeholder %I properly quotes SQL identifiers. The placeholder %L properly deals with string literals.

PL/pgSQL function name resolution with nested functions

I have two similar schemas in a single database with the same function names.
Each schema is owned by a role that matches the schema name.
I have issues about function name resolution with nested functions.
I was expecting that the outer function would call inner functions within the same schema, but it does not!
The name is resolved dynamically based on the search_path at run time which make some sens, but not as I would.
Here is a test case. Let say for example that the schemas and roles are named test and prod as follow.
Test schema:
CREATE ROLE test NOLOGIN;
CREATE SCHEMA test AUTHORIZATION test;
CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'test function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.inner_func() OWNER TO test;
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.outer_func() OWNER TO test;
Prod schema:
CREATE ROLE prod NOLOGIN;
CREATE SCHEMA prod AUTHORIZATION prod;
CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'prod function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.inner_func() OWNER TO prod;
CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.outer_func() OWNER TO prod;
Test cases:
SET search_path=test,public;
SELECT outer_func();
> test function
SELECT prod.outer_func();
> test function <<<---- was expecting prod function
SET search_path=prod,public;
SELECT prod.outer_func();
> prod function
The test shows that function names are resolved dynamically based on the search_path at run time. Is there a way to bind inner function within the scope of a schema?
I can get such a behavior by using SECURITY DEFINER functions with dynamic SQL and CURRENT_USER, but I am looking for something more straightforward.
The clean solution is to either schema-qualify the function:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT test.inner_func();
END
$func$ LANGUAGE plpgsql; -- no quotes!
Or you explicitly set the search_path per function. You can set configuration parameters this way:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT inner_func();
END
$func$ LANGUAGE plpgsql SET search_path = test, pg_temp;
Customize the search_path to your needs, possibly add public to the list. I put pg_temp at the end, so objects in the temporary schema cannot hide persisted objects. (But that's not applicable for functions.) Similar to what's explained in the manual for SECURITY DEFINER functions.
I would not advise to rely on the user setting the proper search_path. That would only make sense for "public" functions, it wouldn't be consistent with your design. Why create separate functions and then still have to rely on user settings after all? You could have a single function in the public schema to begin with, but I would not got that route in any case. Very confusing and error prone.
Also, PL/pgSQL executes statements like prepared statements internally. every time you change the search_path, all "prepared" statements from plpgsql functions have to be de-allocated, which is not helping to optimize performance.
Actually, your test case in the question only works if you set the search_path first:
SET search_path=test,public;
Else you get an error when trying to create
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
...
ERROR: function inner_func() does not exist
Syntax checks are run against the current search_path at creation time - unless you provide the search_path as suggested. That was fixed 2010 after I reported a bug.
Details for search_path:
How does the search_path influence identifier resolution and the "current schema"
How can I fake inet_client_addr() for unit tests in PostgreSQL?
And don't quote the language name. It's an identifier.