skip to Main Content

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:

  1. admin will be the owner of the database and all the objects contained into it
  2. manager will be used for data manipulation across all schemas but public (only user admin can modify data in public schema)
  3. 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


  1. Chosen as BEST ANSWER

    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.


  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search