Why Postgres does not grant EXECUTE on function to PUBLIC as documented? - postgresql

Documentation says:
For other types of objects, the default privileges granted to PUBLIC are as follows: CONNECT and TEMPORARY (create temporary tables) privileges for databases; EXECUTE privilege for functions; and USAGE privilege for languages and data types (including domains).
I execute:
create user test_user password 'test_user';
grant create on database "NLP" to test_user;
Then I connect under this user and do:
create schema s;
create function s.f() returns void as $$begin null; end;$$ language plpgsql;
I expect that EXECUTE will be granted to PUBLIC on the function but this does not happen. Why?
Additionally I discovered a curious thing. If I alter default function privileges for the schema the mechanism starts to work.
create role test_role;
Under test_user:
alter default privileges in schema s grant execute on functions to test_role;
create function s.x() returns void as $$begin null; end;$$ language plpgsql;
Voila! In addition to EXECUTE on x() for test_role I got EXECUTE for PUBLIC!
My DB report version():
PostgreSQL 10.3 (Ubuntu 10.3-1.pgdg14.04+1) on x86_64-pc-linux-gnu,
compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4, 64-bit
Is something wrong with my database? I tested it on another database (same version()) and got the same results.

Answering my own question. Everything works as expected, but I misinterpreted pgAdmin's indications.
select proacl from pg_proc where proname = 'f'
Gives NULL, which according to documentation:
If the “Access privileges” column is empty for a given object, it means the object has default privileges (that is, its privileges column is null). Default privileges always include all privileges for the owner, and can include some privileges for PUBLIC depending on the object type, as explained above.
And for such object type as function, it assumes EXECUTE for PUBLIC (as I posted in initial question).
Effective permissions for a user can also be conveniently tested by (thanks Laurenz):
select has_function_privilege('username', 's.f()', 'EXECUTE');
Which gave TRUE for any user.
The fact that pgAdming generated explicit grant for PUBLIC in one case and nothing in another, was misleading. Absence of grant to PUBLIC does not necessarily mean that PUBLIC does not have permissions.

Related

PostgreSQL - Grant DEFAULT PRIVILEGES database-wide and revoke them just for a specific schema

I am experiencing a weird and (to me) inexplicable behaviour related to DEFAULT PRIVILEGES. It seems default privileges cannot be revoked just for a specific schema once they have been granted database-wide.
I am currently testing this with PostgreSQL 10.5 on CentOS.
Let's say there are 3 users:
admin Owner of the database. Used to manipulate the STRUCTURE of the database (CREATE, DROP, TRUNCATE...)
manager Used for DATA manipulation (INSERT, UPDATE, DELETE)
reader Used to read DATA (basically SELECT)
The idea is that:
admin will be the owner of the database and all the objects contained into it
manager will be used for data manipulation across all schemas but public (only user admin can modify data in public schema)
reader will be able to read everything.
To make things easier, this will rely on default privileges, so that newly created objects (schemas, tables, views, functions, etc.) will all have the correct permissions.
This is the first time I am trying something like that instead of a fine-grained permissions policy based on multiple users for all different schemas and apparently this setup should be very straightforward.
It turns out I am missing something.
Here is a simple test script. User admin is the owner of db database and all those commands are issued being connected to it as admin:
-- 1. User manager inherits from user "reader"
GRANT reader TO manager;
-- 2. Allow connections to the database to our users, but not PUBLIC
REVOKE ALL ON DATABASE db FROM PUBLIC;
GRANT CONNECT ON DATABASE db TO reader;
-- 3. Revoke default privileges from PUBLIC
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SEQUENCES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON FUNCTIONS FROM PUBLIC;
-- 4. Grant default reading privileges to user "reader"
ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO reader;
ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO reader;
ALTER DEFAULT PRIVILEGES GRANT SELECT ON SEQUENCES TO reader;
ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO reader;
-- 5. Grant Defauly writing privileges to user "manager"
ALTER DEFAULT PRIVILEGES GRANT INSERT, UPDATE, DELETE ON TABLES TO manager;
ALTER DEFAULT PRIVILEGES GRANT USAGE ON SEQUENCES TO manager;
-- 6. Reinit "public" schema
DROP SCHEMA public;
CREATE SCHEMA public;
-- 7. HERE COMES THE WEIRD STUFF, the two following statements don't have any effect at all
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE INSERT, UPDATE, DELETE ON TABLES FROM manager;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE USAGE ON SEQUENCES FROM manager;
This can be easily verified like that:
-- Execute as user "admin":
CREATE TABLE public.t (id serial PRIMARY KEY, dummy integer)
-- Execute as user "manager" (it should not be allowed, but it is!)
DELETE FROM public.t;
I know I could circumvent this using some trigger functions, but the point of the question is whether this is something normal and expected, some sort of bug or am I missing something?
I have been thinking about it and the most elegant solution I could come up with relies on an Event Trigger.
Of course it does not answer my question directly, meaning that I am still wondering why default privileges cannot be used like that, but at least this meets the initial requirement of set-and-forget that default privileges would have provided.
Create a function that revokes unwanted privileges and returns an event_trigger:
CREATE FUNCTION reset_privileges() RETURNS event_trigger AS $$
BEGIN
IF EXISTS (SELECT true FROM pg_event_trigger_ddl_commands() WHERE schema_name = 'public') THEN
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM manager;
REVOKE USAGE ON ALL SEQUENCES IN SCHEMA public FROM manager;
END IF;
END;
$$ LANGUAGE plpgsql;
Create an actual EVENT TRIGGER (This requires superuser privileges!):
CREATE EVENT TRIGGER reset_public_schema_privileges
ON ddl_command_end WHEN TAG IN (
'CREATE TABLE',
'CREATE TABLE AS',
'CREATE VIEW',
'CREATE MATERIALIZED VIEW',
'CREATE FUNCTION'
) EXECUTE PROCEDURE reset_privileges();
The function checks whether the newly created object(s) are in the public schema and eventually revokes all the unwanted privileges from the user manager.
It does not even bother to filter those objects, but rather it revokes the privileges for ALL TABLEs, VIEWs and FUNCTIONs in the public schema. Of course it can be easily customised using the object_identity field provided by pg_event_trigger_ddl_commands and a more refined logic inside the function.
According to the manual for ALTER DEFAULT PRIVILEGES:
Default privileges that are specified per-schema are added to whatever the global default privileges are for the particular object type. This means you cannot revoke privileges per-schema if they are granted globally (either by default, or according to a previous ALTER DEFAULT PRIVILEGES command that did not specify a schema). Per-schema REVOKE is only useful to reverse the effects of a previous per-schema GRANT.
(This is even more explicit in the examples given on that manual page.)
So I think what is happening is that in step 5 of your script, you are setting the default privilege to grant DELETE on the tables of all schemas (as a global default):
ALTER DEFAULT PRIVILEGES GRANT INSERT, UPDATE, DELETE ON TABLES TO manager;
But in step 7 you are revoking from the public schema specifically. This revocation has no effect on the global grant, so the DELETE (and other) privileges will still be granted:
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE INSERT, UPDATE, DELETE ON TABLES FROM manager;
I think I would either (a) bite the bullet and add default privileges for each schema (which isn't "fire-and-forget" but is more explicit) or (b) challenge why I need the public schema to exist, aiming to remove it to simplify this situation.

SQL state: 42883, No function matches the given name and argument types. But that function actually exists

I have a server with PostgreSQL 8.1.23, with a function that works perfectly when it runs with postgres user, but with another user shows the SQL STATE:
SQL state: 42883
This is my function:
CREATE OR REPLACE FUNCTION fun_validatepost(integer, integer)
RETURNS integer AS
$BODY$
...
$BODY$
LANGUAGE plpgsql VOLATILE;
ALTER FUNCTION fun_validatepost(integer, integer)
OWNER TO postgres;
GRANT EXECUTE ON FUNCTION fun_validatepost(integer, integer) TO public;
GRANT EXECUTE ON FUNCTION fun_validatepost(integer, integer) TO postgres;
GRANT EXECUTE ON FUNCTION fun_validatepost(integer, integer) TO someuser;
If I run this with postgres user like this:
select fun_validatepost(1,230465);
The result is like this:
-[ RECORD 1 ]-----------+--
fun_validatepost | 1
But if I execute the same query as someuser, shows me this message:
ERROR: function fun_validatepost(integer, integer) does not exist
SQL state: 42883
HINT: No function matches the given name and argument types. You may need to add explicit type casts
Even if a do a explicit cast I get the same result:
select fun_validatepost from fun_validatepost(1::integer,230465::integer);
Same error message.
What can I do so someuser can execute the same function?
Is there something wrong with my function or cast?
Most probably a matter of schema vs. schema search_path. The function is created in the default schema of the creating user. If that's not in the search_path of the current user, it's not visible.
Details:
How does the search_path influence identifier resolution and the "current schema"
Typically, you would create public functions in the schema public and have that schema in everbody's search_path.
CREATE OR REPLACE FUNCTION public.fun_validatepost(integer, integer)
RETURNS integer AS
$BODY$
...
$BODY$ LANGUAGE plpgsql;
ALTER FUNCTION public.fun_validatepost(integer, integer) OWNER TO postgres;
Schema-qualification is only needed if public isn't the default schema anyway.
Also, your GRANT commands make no sense. The EXECUTE privilege for functions is granted to public by default. And once you grant to public, there is no need to grant to other users. Especially not to postgres, which is the OWNER anyway and a superuser, too. The manual:
PostgreSQL grants default privileges on some types of objects to
PUBLIC. [...] EXECUTE privilege for functions;
You do need to grant USAGE on the SCHEMA where the function is created. The public schema grants USAGE to public (everyone) by default.
Aside 1
Casting to integer does not change anything here because a numeric literal without decimal point is coerced to integer automatically. Details about constants in the manual.
Aside 2
Urgently consider updating to a current version of Postgres. Your software is completely outdated.

change output pane language

I have postgresql and using pg admin.
the problem is my system and everything is in English except errors and messages in output pane.
I have already set the user language to English but still the messages are in German!
You can execute the following SQL statement:
SET lc_messages="C";
That will change the messages you get and also the messages in the log file.
You can only set this parameter when you are a superuser, but you can create a superuser-owned function with SECURITY DEFINER and grant EXECUTE on it to a normal user:
CREATE OR REPLACE FUNCTION set_english() RETURNS void
LANGUAGE sql SECURITY DEFINER AS
'SET lc_messages="C"';
REVOKE EXECUTE ON FUNCTION set_english() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION set_english() TO laurenz;
Then user laurenz can call the function to change the setting.

Can't get access to streaming replication stats in PostgreSQL

I have streaming replication which I need to monitor. So there is a special user for Zabbix. I don't want to use pg_mongz and decided to set my own queries to pg_catalog schema's view pg_stat_replication to get replication state.
When I use query:
select *
from pg_stat_replication;
it returns replication state record for admin. But when I logged in as monitoring user it returns just:
pid, usesysid, usename, application_name
So such parameters as client_addr, client_hostname, client_port, backend_start, state, sent_location, write_location, etc. are empty.
First I granted rights to my user on schema and tables:
grant usage on schema pg_catalog to usrmonitor;
grant select on all tables in schema pg_catalog to usrmonitor;
but it didn't help. When I looked at view I found that query uses functions and granted execution:
grant execute on function pg_stat_get_wal_senders() to usrmonitor;
grant execute on function pg_stat_get_activity(integer) to usrmonitor;
But the select query still returns empty columns. What maybe the problem?
Since PostgreSQL 10, it is as simple as :
GRANT pg_monitor TO monitoring_user;
(Source: https://pganalyze.com/blog/whats-new-in-postgres-10-monitoring-improvements)
Yes, access to these fields is intentionally restricted to superusers.
As a workaround, you may use a function as a proxy with the SECURITY DEFINER attribute:
SECURITY DEFINER specifies that the function is to be executed with
the privileges of the user that created it.
So as a superuser (typically the postgres user), do:
CREATE FUNCTION func_stat_replication() RETURNS SETOF pg_stat_replication as
$$ select * from pg_stat_replication; $$
LANGUAGE sql SECURITY DEFINER;
Then revoke/grant the permission to use that function so that only the monitoring
user is allowed to execute it:
REVOKE EXECUTE ON FUNCTION func_stat_replication() FROM public;
GRANT EXECUTE ON FUNCTION func_stat_replication() to usrmonitor;
Then usrmonitor should execute:
SELECT * FROM func_stat_replication();
and it will have the same results as if it was superuser.

Postgresql ignores lack of EXECUTE permission if USAGE permission is granted on containing schema

From http://www.postgresql.org/docs/9.1/static/sql-grant.html:
USAGE
...
For schemas, allows access to objects contained in the specified schema (assuming that the objects' own privilege requirements are also met). Essentially this allows the grantee to "look up" objects within the schema. Without this permission, it is still possible to see the object names, e.g. by querying the system tables. ...
...
However, running the following script through psql seems to show that although the lack of a SELECT permission causes an error, lack on an EXECUTE permission does not, which contradicts the documentation as the 'own privilege requirement' is not met.
CREATE DATABASE testdb WITH OWNER postgres ENCODING 'UTF8';
\connect testdb
CREATE ROLE testrole;
CREATE SCHEMA testschema;
GRANT USAGE ON SCHEMA testschema TO testrole;
SET search_path TO testschema;
CREATE FUNCTION testfunc ()
RETURNS VOID
AS $$
BEGIN
RAISE NOTICE 'IN TESTFUNC';
RAISE NOTICE 'Current user: %', current_user;
END;
$$
LANGUAGE plpgsql;
CREATE TABLE testtable
(
testrow INT
);
INSERT INTO testtable (testrow) VALUES (1), (2), (3);
SET ROLE testrole;
SELECT testfunc();
SELECT * FROM testtable;
RESET ROLE;
Output:
$ psql -f usage.sql
CREATE DATABASE
You are now connected to database "testdb" as user "postgres".
CREATE ROLE
CREATE SCHEMA
GRANT
SET
CREATE FUNCTION
CREATE TABLE
INSERT 0 3
SET
psql:usage.sql:27: NOTICE: IN TESTFUNC
psql:usage.sql:27: NOTICE: Current user: testrole
testfunc
----------
(1 row)
psql:usage.sql:28: ERROR: permission denied for relation testtable
RESET
Have I missed something or am using the permissions incorrectly?
The schema is not what matters here, what you're seeing is the default execute permissions of functions.
Consider this excerpt from the CREATE FUNCTION documentation:
Another point to keep in mind is that by default, execute privilege is
granted to PUBLIC for newly created functions (see GRANT for more
information). Frequently you will wish to restrict use of a security
definer function to only some users. To do that, you must revoke the
default PUBLIC privileges and then grant execute privilege
selectively.