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 itmanager
will be used for data manipulation across all schemas butpublic
(only useradmin
can modify data inpublic
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?
2
Answers
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 an actual EVENT TRIGGER (This requires superuser privileges!):
The function checks whether the newly created object(s) are in the
public
schema and eventually revokes all the unwanted privileges from the usermanager
.It does not even bother to filter those objects, but rather it revokes the privileges for
ALL
TABLE
s,VIEW
s andFUNCTION
s in thepublic
schema. Of course it can be easily customised using theobject_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
:(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):But in step 7 you are revoking from the
public
schema specifically. This revocation has no effect on the global grant, so theDELETE
(and other) privileges will still be granted: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.