Thread

Commits

  1. Make role grant system more consistent with other privileges.

  2. Ensure that pg_auth_members.grantor is always valid.

  3. Remove the ability of a role to administer itself.

  4. Add tests of the CREATEROLE attribute

  5. Replace explicit PIN entries in pg_depend with an OID range test.

  6. Shore up ADMIN OPTION restrictions.

  7. Add pg_has_role() family of privilege inquiry functions modeled after the

  8. Align GRANT/REVOKE behavior more closely with the SQL spec, per discussion

  1. CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-10-20T18:40:35Z

    These patches have been split off the now deprecated monolithic "Delegating superuser tasks to new security roles" thread at [1].
    
    The purpose of these patches is to fix the CREATEROLE escalation attack vector misfeature.  (Not everyone will see CREATEROLE that way, but the perceived value of the patch set likely depends on how much you see CREATEROLE in that light.)
    
    
  2. Re: CREATEROLE and role ownership hierarchies

    Nathan Bossart <bossartn@amazon.com> — 2021-10-21T23:04:34Z

    On 10/20/21, 11:46 AM, "Mark Dilger" <mark.dilger@enterprisedb.com> wrote:
    > The purpose of these patches is to fix the CREATEROLE escalation
    > attack vector misfeature.  (Not everyone will see CREATEROLE that
    > way, but the perceived value of the patch set likely depends on how
    > much you see CREATEROLE in that light.)
    
    Regarding the "attack vector misfeature" comment, I remember being
    surprised when I first learned how much roles with CREATEROLE can do.
    When I describe CREATEROLE to others, I am sure to emphasize the note
    in the docs about such roles being "almost-superuser" roles.
    CREATEROLE is a rather big hammer at the moment, so I certainly think
    there is value in reducing its almost-superuser-ness.
    
    I mentioned this in the other thread [0] already, but the first thing
    that comes to mind when I look at these patches is how upgrades might
    work.  Will we just make the bootstrap superuser the owner for all
    roles when you first upgrade to v15?  Also, are we just going to strip
    the current CREATEROLE roles of much of their powers?  Maybe it's
    worth keeping a legacy CREATEROLE role attribute for upgraded clusters
    that could eventually be removed down the road.
    
    I'd also like to bring up my note about allowing users to transfer
    role ownership.  When I tested the patches earlier, REASSIGN OWNED BY
    was failing with an "unexpected classid" ERROR.  Besides REASSIGN
    OWNED BY, perhaps there should be another mechanism for transferring
    ownership on a role-by-role basis (i.e., ALTER ROLE OWNER TO).  I
    haven't looked at this new patch set too closely, so my apologies if
    this has already been added.
    
    Nathan
    
    [0] https://postgr.es/m/53C7DF4C-8463-4647-9DFD-779B5E1861C4%40amazon.com
    
    
  3. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-10-21T23:21:09Z

    
    > On Oct 21, 2021, at 4:04 PM, Bossart, Nathan <bossartn@amazon.com> wrote:
    > 
    > Regarding the "attack vector misfeature" comment, I remember being
    > surprised when I first learned how much roles with CREATEROLE can do.
    > When I describe CREATEROLE to others, I am sure to emphasize the note
    > in the docs about such roles being "almost-superuser" roles.
    > CREATEROLE is a rather big hammer at the moment, so I certainly think
    > there is value in reducing its almost-superuser-ness.
    
    It is hard to know how many people are using CREATEROLE currently.  There isn't much reason to give it out, since if you care enough about security to not give out superuser, you probably care too much about security to give away CREATEROLE.
    
    > I mentioned this in the other thread [0] already, but the first thing
    > that comes to mind when I look at these patches is how upgrades might
    > work.  Will we just make the bootstrap superuser the owner for all
    > roles when you first upgrade to v15?
    
    Yes, that's the idea.  After upgrade, all roles will form a tree, with the bootstrap superuser at the root of the tree.  The initial tree structure isn't very interesting, with all other roles directly owned by it, but from there the superuser can rearrange the tree, and after that non-superuser roles can manage whatever subtree of roles they are the root of.
    
    >  Also, are we just going to strip
    > the current CREATEROLE roles of much of their powers?  Maybe it's
    > worth keeping a legacy CREATEROLE role attribute for upgraded clusters
    > that could eventually be removed down the road.
    
    The patch as written drastically reduces the power of the CREATEROLE attribute, in a non-backwards compatible way.  I wondered if there would be complaints about that.  If so, we could instead leave CREATEROLE alone, and create some other privileged role for the same thing, but it does start to look funny having a CREATEROLE privilege bit and also a privileged role named, perhaps, pg_can_create_roles.
    
    > I'd also like to bring up my note about allowing users to transfer
    > role ownership.  When I tested the patches earlier, REASSIGN OWNED BY
    > was failing with an "unexpected classid" ERROR.  Besides REASSIGN
    > OWNED BY, perhaps there should be another mechanism for transferring
    > ownership on a role-by-role basis (i.e., ALTER ROLE OWNER TO).  I
    > haven't looked at this new patch set too closely, so my apologies if
    > this has already been added.
    
    Yes, I completely agree with you on that.  Both REASSIGN OWNED BY and ALTER ROLE OWNER TO should work.  I'll take a look at the patches and repost with any adjustments that I find necessary to make those work.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  4. Re: CREATEROLE and role ownership hierarchies

    Shinya Kato <shinya11.kato@oss.nttdata.com> — 2021-10-26T05:09:09Z

    On 2021-10-21 03:40, Mark Dilger wrote:
    > These patches have been split off the now deprecated monolithic
    > "Delegating superuser tasks to new security roles" thread at [1].
    > 
    > The purpose of these patches is to fix the CREATEROLE escalation
    > attack vector misfeature.  (Not everyone will see CREATEROLE that way,
    > but the perceived value of the patch set likely depends on how much
    > you see CREATEROLE in that light.)
    
    Hi! Thank you for the patch.
    I too think that CREATEROLE escalation attack is problem.
    
    I have three comments.
    1. Is there a function to check the owner of a role, it would be nice to 
    be able to check with \du or pg_roles view.
    2. Is it correct that REPLICATION/BYPASSRLS can be granted even if you 
    are not a super user, but have CREATEROLE and REPLICATION/BYPASSRLS?
    3. I think it would be better to have an "DROP ROLE [ IF EXISTS ] name 
    [, ...] [CASCADE | RESTRICT]" like "DROP TABLE [ IF EXISTS ] name [, 
    ...] [ CASCADE | RESTRICT ]". What do you think?
    
    -- 
    Regards,
    
    --
    Shinya Kato
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
    
    
    
    
  5. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-10-26T15:12:47Z

    
    > On Oct 25, 2021, at 10:09 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    > 
    > On 2021-10-21 03:40, Mark Dilger wrote:
    >> These patches have been split off the now deprecated monolithic
    >> "Delegating superuser tasks to new security roles" thread at [1].
    >> The purpose of these patches is to fix the CREATEROLE escalation
    >> attack vector misfeature.  (Not everyone will see CREATEROLE that way,
    >> but the perceived value of the patch set likely depends on how much
    >> you see CREATEROLE in that light.)
    > 
    > Hi! Thank you for the patch.
    > I too think that CREATEROLE escalation attack is problem.
    > 
    > I have three comments.
    > 1. Is there a function to check the owner of a role, it would be nice to be able to check with \du or pg_roles view.
    
    No, but that is a good idea.
    
    > 2. Is it correct that REPLICATION/BYPASSRLS can be granted even if you are not a super user, but have CREATEROLE and REPLICATION/BYPASSRLS?
    
    It is intentional, yes.  Whether it is correct is up for debate, but I think it is. 
    
    > 3. I think it would be better to have an "DROP ROLE [ IF EXISTS ] name [, ...] [CASCADE | RESTRICT]" like "DROP TABLE [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]". What do you think?
    
    I agree it would be nice to have, but roles are cluster-global and there are technical difficulties in cascading into multiple databases to drop all objects owned by the role.  There was also a debate [1] about whether we would even want such behavior, leading to no real conclusion regarding how or if such a command should be implemented.
    
    The current solution is to run REASSIGN OWNED in each database where the role owns objects before running DROP ROLE.  At that point, the CASCADE option (not implemented) won't be needed.  Of course, I need to post the next revision of this patch set addressing the deficiencies that Nathan pointed out upthread to make that work. 
    
    [1] https://www.postgresql.org/message-id/flat/20211005025746.GN20998%40tamriel.snowman.net
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  6. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2021-10-26T18:50:49Z

    On 10/21/21 19:21, Mark Dilger wrote:
    >>  Also, are we just going to strip
    >> the current CREATEROLE roles of much of their powers?  Maybe it's
    >> worth keeping a legacy CREATEROLE role attribute for upgraded clusters
    >> that could eventually be removed down the road.
    > The patch as written drastically reduces the power of the CREATEROLE attribute, in a non-backwards compatible way.  I wondered if there would be complaints about that.  If so, we could instead leave CREATEROLE alone, and create some other privileged role for the same thing, but it does start to look funny having a CREATEROLE privilege bit and also a privileged role named, perhaps, pg_can_create_roles.
    
    
    Give that CREATEROLE currently just about amounts to being a superuser,
    maybe there should be a pg_upgrade option to convert CREATEROLE to
    SUPERUSER. I don't want to perpetuate the misfeature though, so let's
    just bring it to an end.
    
    
    cheers
    
    
    andrew
    
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  7. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-10-27T22:21:43Z

    >> On Oct 25, 2021, at 10:09 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    
    >> Hi! Thank you for the patch.
    >> I too think that CREATEROLE escalation attack is problem.
    >> 
    >> I have three comments.
    >> 1. Is there a function to check the owner of a role, it would be nice to be able to check with \du or pg_roles view.
    > 
    > No, but that is a good idea.
    
    These two ideas are implemented in v2.  Both \du and pg_roles show the owner information.
    
    > The current solution is to run REASSIGN OWNED in each database where the role owns objects before running DROP ROLE. At that point, the CASCADE option (not implemented) won't be needed.  Of course, I need to post the next revision of this patch set addressing the deficiencies that Nathan pointed out upthread to make that work. 
    
    REASSIGN OWNED and ALTER ROLE..OWNER TO now work in v2.
    
    
  8. Re: CREATEROLE and role ownership hierarchies

    Shinya Kato <shinya11.kato@oss.nttdata.com> — 2021-10-28T02:32:03Z

    On 2021-10-28 07:21, Mark Dilger wrote:
    >>> On Oct 25, 2021, at 10:09 PM, Shinya Kato 
    >>> <Shinya11.Kato@oss.nttdata.com> wrote:
    > 
    >>> Hi! Thank you for the patch.
    >>> I too think that CREATEROLE escalation attack is problem.
    >>> 
    >>> I have three comments.
    >>> 1. Is there a function to check the owner of a role, it would be nice 
    >>> to be able to check with \du or pg_roles view.
    >> 
    >> No, but that is a good idea.
    > 
    > These two ideas are implemented in v2.  Both \du and pg_roles show the
    > owner information.
    Thank you. It seems good to me.
    
    By the way, I got the following execution result.
    I was able to add the membership of a role with a different owner.
    In brief, "a" was able to change the membership of owner "shinya".
    Is this the correct behavior?
    ---
    postgres=# CREATE ROLE a LOGIN;
    CREATE ROLE
    postgres=# GRANT pg_execute_server_program TO a WITH ADMIN OPTION;
    GRANT ROLE
    postgres=# CREATE ROLE b;
    CREATE ROLE
    postgres=# \du a
                              List of roles
      Role name | Owner  | Attributes |          Member of
    -----------+--------+------------+-----------------------------
      a         | shinya |            | {pg_execute_server_program}
    
    postgres=# \du b
                      List of roles
      Role name | Owner  |  Attributes  | Member of
    -----------+--------+--------------+-----------
      b         | shinya | Cannot login | {}
    
    postgres=# \c - a
    You are now connected to database "postgres" as user "a".
    postgres=> GRANT pg_execute_server_program TO b;
    GRANT ROLE
    postgres=> \du b
                               List of roles
      Role name | Owner  |  Attributes  |          Member of
    -----------+--------+--------------+-----------------------------
      b         | shinya | Cannot login | {pg_execute_server_program}
    ---
    
    -- 
    Regards,
    
    --
    Shinya Kato
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
    
    
    
    
  9. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-10-28T15:24:23Z

    
    > On Oct 27, 2021, at 7:32 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    > 
    > I was able to add the membership of a role with a different owner.
    > In brief, "a" was able to change the membership of owner "shinya".
    > Is this the correct behavior?
    
    I believe it is required for backwards compatibility.  In a green field, we might consider doing things differently.
    
    The only intentional backward compatibility break in this patch set is the the behavior of CREATEROLE.  The general hope is that such a compatibility break will help far more than it hurts, as CREATEROLE does not appear to be a well adopted feature.  I would expect that breaking the behavior of the WITH ADMIN OPTION feature would cause a lot more pain.
    
    
    Trying your example on both the unpatched and the patched sources, things appear to work as they should:
    
    
    UNPATCHED
    ------------------
    mark.dilger=# CREATE ROLE a LOGIN;
    CREATE ROLE
    mark.dilger=# GRANT pg_execute_server_program TO a WITH ADMIN OPTION;
    GRANT ROLE
    mark.dilger=# CREATE ROLE b;
    CREATE ROLE
    mark.dilger=# \du+ a
                               List of roles
     Role name | Attributes |          Member of          | Description 
    -----------+------------+-----------------------------+-------------
     a         |            | {pg_execute_server_program} | 
    
    mark.dilger=# \du+ b
                       List of roles
     Role name |  Attributes  | Member of | Description 
    -----------+--------------+-----------+-------------
     b         | Cannot login | {}        | 
    
    mark.dilger=# \c - a
    You are now connected to database "mark.dilger" as user "a".
    mark.dilger=> GRANT pg_execute_server_program TO b;
    GRANT ROLE
    mark.dilger=> \du+ b
                                List of roles
     Role name |  Attributes  |          Member of          | Description 
    -----------+--------------+-----------------------------+-------------
     b         | Cannot login | {pg_execute_server_program} | 
    
    mark.dilger=> \du+ "mark.dilger"
                                               List of roles
      Role name  |                         Attributes                         | Member of | Description 
    -------------+------------------------------------------------------------+-----------+-------------
     mark.dilger | Superuser, Create role, Create DB, Replication, Bypass RLS | {}        | 
    
    
    PATCHED:
    ---------------
    mark.dilger=# CREATE ROLE a LOGIN;
    CREATE ROLE
    mark.dilger=# GRANT pg_execute_server_program TO a WITH ADMIN OPTION;
    GRANT ROLE
    mark.dilger=# CREATE ROLE b;
    CREATE ROLE
    mark.dilger=# \du+ a
                                      List of roles
     Role name |    Owner    | Attributes |          Member of          | Description 
    -----------+-------------+------------+-----------------------------+-------------
     a         | mark.dilger |            | {pg_execute_server_program} | 
    
    mark.dilger=# \du+ b
                              List of roles
     Role name |    Owner    |  Attributes  | Member of | Description 
    -----------+-------------+--------------+-----------+-------------
     b         | mark.dilger | Cannot login | {}        | 
    
    mark.dilger=# \c - a
    You are now connected to database "mark.dilger" as user "a".
    mark.dilger=> GRANT pg_execute_server_program TO b;
    GRANT ROLE
    mark.dilger=> \du+ b
                                       List of roles
     Role name |    Owner    |  Attributes  |          Member of          | Description 
    -----------+-------------+--------------+-----------------------------+-------------
     b         | mark.dilger | Cannot login | {pg_execute_server_program} | 
    
    mark.dilger=> \du+ "mark.dilger"
                                                      List of roles
      Role name  |    Owner    |                         Attributes                         | Member of | Description 
    -------------+-------------+------------------------------------------------------------+-----------+-------------
     mark.dilger | mark.dilger | Superuser, Create role, Create DB, Replication, Bypass RLS | {}        | 
    
    
    
    You should notice that the owner of role "b" is the superuser "mark.dilger", and that owner's attributes are unchanged.  But your point that role "a" can change the attributes of role "mark.dilger" is correct, as shown here:
    
    mark.dilger=> GRANT pg_execute_server_program TO "mark.dilger";
    GRANT ROLE
    mark.dilger=> \du+ "mark.dilger"
                                                               List of roles
      Role name  |    Owner    |                         Attributes                         |          Member of          | Description 
    -------------+-------------+------------------------------------------------------------+-----------------------------+-------------
     mark.dilger | mark.dilger | Superuser, Create role, Create DB, Replication, Bypass RLS | {pg_execute_server_program} | 
    
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  10. Re: CREATEROLE and role ownership hierarchies

    Tom Lane <tgl@sss.pgh.pa.us> — 2021-10-28T16:14:06Z

    Mark Dilger <mark.dilger@enterprisedb.com> writes:
    > The only intentional backward compatibility break in this patch set is the the behavior of CREATEROLE.  The general hope is that such a compatibility break will help far more than it hurts, as CREATEROLE does not appear to be a well adopted feature.  I would expect that breaking the behavior of the WITH ADMIN OPTION feature would cause a lot more pain.
    
    Even more to the point, WITH ADMIN OPTION is defined by the SQL standard.
    The only way you get to mess with that is if you can convince people we
    mis-implemented the standard.
    
    			regards, tom lane
    
    
    
    
  11. Re: CREATEROLE and role ownership hierarchies

    Shinya Kato <shinya11.kato@oss.nttdata.com> — 2021-10-29T02:05:10Z

    On 2021-10-29 01:14, Tom Lane wrote:
    > Mark Dilger <mark.dilger@enterprisedb.com> writes:
    >> The only intentional backward compatibility break in this patch set is 
    >> the the behavior of CREATEROLE.  The general hope is that such a 
    >> compatibility break will help far more than it hurts, as CREATEROLE 
    >> does not appear to be a well adopted feature.  I would expect that 
    >> breaking the behavior of the WITH ADMIN OPTION feature would cause a 
    >> lot more pain.
    > 
    > Even more to the point, WITH ADMIN OPTION is defined by the SQL 
    > standard.
    > The only way you get to mess with that is if you can convince people we
    > mis-implemented the standard.
    Thank you for the detailed explanation.
    I now understand what you said.
    
    -- 
    Regards,
    
    --
    Shinya Kato
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
    
    
    
    
  12. Re: CREATEROLE and role ownership hierarchies

    Shinya Kato <shinya11.kato@oss.nttdata.com> — 2021-11-04T07:00:06Z

    On 2021-10-28 07:21, Mark Dilger wrote:
    >>> On Oct 25, 2021, at 10:09 PM, Shinya Kato 
    >>> <Shinya11.Kato@oss.nttdata.com> wrote:
    > 
    >>> Hi! Thank you for the patch.
    >>> I too think that CREATEROLE escalation attack is problem.
    >>> 
    >>> I have three comments.
    >>> 1. Is there a function to check the owner of a role, it would be nice 
    >>> to be able to check with \du or pg_roles view.
    >> 
    >> No, but that is a good idea.
    > 
    > These two ideas are implemented in v2.  Both \du and pg_roles show the
    > owner information.
    > 
    >> The current solution is to run REASSIGN OWNED in each database where 
    >> the role owns objects before running DROP ROLE. At that point, the 
    >> CASCADE option (not implemented) won't be needed.  Of course, I need 
    >> to post the next revision of this patch set addressing the 
    >> deficiencies that Nathan pointed out upthread to make that work.
    > 
    > REASSIGN OWNED and ALTER ROLE..OWNER TO now work in v2.
    
    When ALTER ROLE with the privilege of REPLICATION, only the superuser is 
    checked.
    Therefore, we have a strange situation where we can create a role but 
    not change it.
    ---
    postgres=> SELECT current_user;
      current_user
    --------------
      test
    (1 row)
    
    postgres=> \du test
                            List of roles
      Role name | Owner  |        Attributes        | Member of
    -----------+--------+--------------------------+-----------
      test      | shinya | Create role, Replication | {}
    
    postgres=> CREATE ROLE test2 REPLICATION;
    CREATE ROLE
    postgres=> ALTER ROLE test2 NOREPLICATION;
    2021-11-04 14:24:02.687 JST [2615016] ERROR:  must be superuser to alter 
    replication roles or change replication attribute
    2021-11-04 14:24:02.687 JST [2615016] STATEMENT:  ALTER ROLE test2 
    NOREPLICATION;
    ERROR:  must be superuser to alter replication roles or change 
    replication attribute
    ---
    Wouldn't it be better to check if the role has CREATEROLE and 
    REPLICATION?
    The same is true for BYPASSRLS.
    
    By the way, is this thread registered to CommitFest?
    
    -- 
    Regards,
    
    --
    Shinya Kato
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
    
    
    
    
  13. Re: CREATEROLE and role ownership hierarchies

    Shinya Kato <shinya11.kato@oss.nttdata.com> — 2021-12-22T01:11:15Z

    On 2021-11-04 16:00, Shinya Kato wrote:
    > On 2021-10-28 07:21, Mark Dilger wrote:
    >>>> On Oct 25, 2021, at 10:09 PM, Shinya Kato 
    >>>> <Shinya11.Kato@oss.nttdata.com> wrote:
    >> 
    >>>> Hi! Thank you for the patch.
    >>>> I too think that CREATEROLE escalation attack is problem.
    >>>> 
    >>>> I have three comments.
    >>>> 1. Is there a function to check the owner of a role, it would be 
    >>>> nice to be able to check with \du or pg_roles view.
    >>> 
    >>> No, but that is a good idea.
    >> 
    >> These two ideas are implemented in v2.  Both \du and pg_roles show the
    >> owner information.
    >> 
    >>> The current solution is to run REASSIGN OWNED in each database where 
    >>> the role owns objects before running DROP ROLE. At that point, the 
    >>> CASCADE option (not implemented) won't be needed.  Of course, I need 
    >>> to post the next revision of this patch set addressing the 
    >>> deficiencies that Nathan pointed out upthread to make that work.
    >> 
    >> REASSIGN OWNED and ALTER ROLE..OWNER TO now work in v2.
    > 
    > When ALTER ROLE with the privilege of REPLICATION, only the superuser
    > is checked.
    > Therefore, we have a strange situation where we can create a role but
    > not change it.
    > ---
    > postgres=> SELECT current_user;
    >  current_user
    > --------------
    >  test
    > (1 row)
    > 
    > postgres=> \du test
    >                        List of roles
    >  Role name | Owner  |        Attributes        | Member of
    > -----------+--------+--------------------------+-----------
    >  test      | shinya | Create role, Replication | {}
    > 
    > postgres=> CREATE ROLE test2 REPLICATION;
    > CREATE ROLE
    > postgres=> ALTER ROLE test2 NOREPLICATION;
    > 2021-11-04 14:24:02.687 JST [2615016] ERROR:  must be superuser to
    > alter replication roles or change replication attribute
    > 2021-11-04 14:24:02.687 JST [2615016] STATEMENT:  ALTER ROLE test2
    > NOREPLICATION;
    > ERROR:  must be superuser to alter replication roles or change
    > replication attribute
    > ---
    > Wouldn't it be better to check if the role has CREATEROLE and 
    > REPLICATION?
    > The same is true for BYPASSRLS.
    > 
    > By the way, is this thread registered to CommitFest?
    
    I fixed the patches because they cannot be applied to HEAD.
    
    -- 
    Regards,
    
    --
    Shinya Kato
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
  14. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2021-12-22T01:25:54Z

    
    > On Dec 21, 2021, at 5:11 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    > 
    > I fixed the patches because they cannot be applied to HEAD.
    
    Thank you.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  15. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2021-12-23T21:06:45Z

    On Tue, Dec 21, 2021 at 8:26 PM Mark Dilger
    <mark.dilger@enterprisedb.com> wrote:
    >
    >
    >
    > > On Dec 21, 2021, at 5:11 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    > >
    > > I fixed the patches because they cannot be applied to HEAD.
    >
    > Thank you.
    
    I reviewed and tested these and they LGTM. FYI the rebased v3 patches
    upthread are raw diffs so git am won't apply them. I can add myself to
    the CF as a reviewer if it is helpful.
    
    
    
    
  16. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-01-03T22:08:05Z

    On 12/23/21 16:06, Joshua Brindle wrote:
    > On Tue, Dec 21, 2021 at 8:26 PM Mark Dilger
    > <mark.dilger@enterprisedb.com> wrote:
    >>
    >>
    >>> On Dec 21, 2021, at 5:11 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    >>>
    >>> I fixed the patches because they cannot be applied to HEAD.
    >> Thank you.
    > I reviewed and tested these and they LGTM. FYI the rebased v3 patches
    > upthread are raw diffs so git am won't apply them. 
    
    
    That's not at all unusual. I normally apply patches just using
    
       patch -p 1 < $patchfile
    
    > I can add myself to
    > the CF as a reviewer if it is helpful.
    
    
    Please do.
    
    
    cheers
    
    
    andrew
    
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  17. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-01-04T14:35:31Z

    On Mon, Jan 3, 2022 at 5:08 PM Andrew Dunstan <andrew@dunslane.net> wrote:
    >
    >
    > On 12/23/21 16:06, Joshua Brindle wrote:
    > > On Tue, Dec 21, 2021 at 8:26 PM Mark Dilger
    > > <mark.dilger@enterprisedb.com> wrote:
    > >>
    > >>
    > >>> On Dec 21, 2021, at 5:11 PM, Shinya Kato <Shinya11.Kato@oss.nttdata.com> wrote:
    > >>>
    > >>> I fixed the patches because they cannot be applied to HEAD.
    > >> Thank you.
    > > I reviewed and tested these and they LGTM. FYI the rebased v3 patches
    > > upthread are raw diffs so git am won't apply them.
    >
    >
    > That's not at all unusual. I normally apply patches just using
    >
    >    patch -p 1 < $patchfile
    >
    > > I can add myself to
    > > the CF as a reviewer if it is helpful.
    >
    >
    > Please do.
    
    I just ran across this and I don't know if it is intended behavior or
    not, can you tell me why this happens?
    
    postgres=> \du+
                                                    List of roles
     Role name |  Owner   |                         Attributes
                | Member of | Description
    -----------+----------+------------------------------------------------------------+-----------+-------------
     brindle   | brindle  | Password valid until 2022-01-05 00:00:00-05
                | {}        |
     joshua    | postgres | Create role
                | {}        |
     postgres  | postgres | Superuser, Create role, Create DB,
    Replication, Bypass RLS | {}        |
    
    postgres=> \password
    Enter new password for user "brindle":
    Enter it again:
    ERROR:  role "brindle" with OID 16384 owns itself
    
    
    
    
  18. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-04T17:07:31Z

    
    > On Jan 4, 2022, at 6:35 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
    > 
    > I just ran across this and I don't know if it is intended behavior or
    > not
    
    <snip>
    
    > postgres=> \password
    > Enter new password for user "brindle":
    > Enter it again:
    > ERROR:  role "brindle" with OID 16384 owns itself
    
    No, that looks like a bug.  Thanks for reviewing!
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  19. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-04T20:39:32Z

    
    > On Jan 4, 2022, at 9:07 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    > 
    > No, that looks like a bug.
    
    I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  20. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-01-04T20:47:31Z

    On Tue, Jan 4, 2022 at 3:39 PM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    >
    >
    >
    > > On Jan 4, 2022, at 9:07 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    > >
    > > No, that looks like a bug.
    >
    > I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    
    I did:
    ALTER ROLE brindle OWNER TO brindle;
    
    
    
    
  21. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-06T00:05:41Z

    
    > On Jan 4, 2022, at 12:47 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
    > 
    >> I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    > 
    > I did:
    > ALTER ROLE brindle OWNER TO brindle;
    
    Ok, thanks.  I have rebased, fixed both REASSIGN OWNED BY and ALTER ROLE .. OWNER TO cases, and added regression coverage for them.
    
    The last patch set to contain significant changes was v2, with v3 just being a rebase.  Relative to those sets:
    
    0001 -- rebased.
    0002 -- rebased; extend AlterRoleOwner_internal to disallow making a role its own immediate owner.
    0003 -- rebased; extend AlterRoleOwner_internal to disallow cycles in the role ownership graph.
    0004 -- rebased.
    0005 -- new; removes the broken pg_auth_members.grantor field.
    
    
  22. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-01-07T14:51:41Z

    On Wed, Jan 5, 2022 at 7:05 PM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    
    > > On Jan 4, 2022, at 12:47 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
    > >
    > >> I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    > >
    > > I did:
    > > ALTER ROLE brindle OWNER TO brindle;
    >
    > Ok, thanks.  I have rebased, fixed both REASSIGN OWNED BY and ALTER ROLE .. OWNER TO cases, and added regression coverage for them.
    >
    > The last patch set to contain significant changes was v2, with v3 just being a rebase.  Relative to those sets:
    >
    > 0001 -- rebased.
    > 0002 -- rebased; extend AlterRoleOwner_internal to disallow making a role its own immediate owner.
    > 0003 -- rebased; extend AlterRoleOwner_internal to disallow cycles in the role ownership graph.
    > 0004 -- rebased.
    > 0005 -- new; removes the broken pg_auth_members.grantor field.
    >
    
    LGTM +1
    
    
    
    
  23. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-01-10T22:34:56Z

    On 1/5/22 19:05, Mark Dilger wrote:
    >
    >> On Jan 4, 2022, at 12:47 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
    >>
    >>> I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    >> I did:
    >> ALTER ROLE brindle OWNER TO brindle;
    > Ok, thanks.  I have rebased, fixed both REASSIGN OWNED BY and ALTER ROLE .. OWNER TO cases, and added regression coverage for them.
    >
    > The last patch set to contain significant changes was v2, with v3 just being a rebase.  Relative to those sets:
    >
    > 0001 -- rebased.
    > 0002 -- rebased; extend AlterRoleOwner_internal to disallow making a role its own immediate owner.
    > 0003 -- rebased; extend AlterRoleOwner_internal to disallow cycles in the role ownership graph.
    > 0004 -- rebased.
    > 0005 -- new; removes the broken pg_auth_members.grantor field.
    
    
    In general this looks good. Some nitpicks:
    
    
    +/*
    + * Ownership check for a role (specified by OID)
    + */
    +bool
    +pg_role_ownercheck(Oid role_oid, Oid roleid)
    
    
    This is a bit confusing. Let's rename these params so it's clear which
    is the owner and which the owned role.
    
    
    + * Note: In versions prior to PostgreSQL version 15, roles did not have
    owners
    + * per se; instead we used this test in places where an ownership-like
    + * permissions test was needed for a role.
    
    
    No need to talk about what we used to do. People who want to know can
    look back at older branches.
    
    
    +bool
    +has_rolinherit_privilege(Oid roleid)
    +{
    
    
    This and similar functions should have header comments.
    
    
    +   /* Owners of roles have every privilge the owned role has */
    
    s/privlge/privilege/
    
    
    +CREATE ROLE regress_role_1 CREATEDB CREATEROLE REPLICATION BYPASSRLS;
    
    
    I don't really like this business of just numbering large numbers of
    roles in the tests. Let's give them more meaningful names.
    
    
    +   Role owners can change any of these settings on roles they own except
    
    
    I would say "on roles they directly or indirectly own", here and
    similarly in one or two other places.
    
    
    ...
    
    
    I will probably do one or two more passes over the patches, but as I say
    in general they look fairly good.
    
    
    cheers
    
    
    andrew
    
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  24. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-11T21:24:53Z

    
    > On Jan 10, 2022, at 2:34 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
    > 
    > In general this looks good. Some nitpicks:
    
    Thanks.  Some responses...
    
    > +/*
    > + * Ownership check for a role (specified by OID)
    > + */
    > +bool
    > +pg_role_ownercheck(Oid role_oid, Oid roleid)
    > 
    > 
    > This is a bit confusing. Let's rename these params so it's clear which
    > is the owner and which the owned role.
    
    Yeah, I wondered about that when I was writing it.  All the neighboring functions follow the pattern:
    
       (Oid <something>_oid, Oid roleid)
    
    so I followed that, but it isn't great.  I've changed that in v5-0002 to use
    
       (Oid owned_role_oid, Oid owner_roleid)
    
    I wouldn't choose this naming in a green field, but I'm trying to stay close to the naming scheme of the surrounding functions.
    
    > + * Note: In versions prior to PostgreSQL version 15, roles did not have
    > owners
    > + * per se; instead we used this test in places where an ownership-like
    > + * permissions test was needed for a role.
    > 
    > 
    > No need to talk about what we used to do. People who want to know can
    > look back at older branches.
    
    Removed in v5-0003.
    
    > +bool
    > +has_rolinherit_privilege(Oid roleid)
    > +{
    > 
    > 
    > This and similar functions should have header comments.
    
    Header comments added for this and similar functions in v5-0004.  This function was misnamed in prior patch sets; the privilege is INHERIT, not ROLINHERIT, so I also fixed the name in v5-0004.
    
    > +   /* Owners of roles have every privilge the owned role has */
    > 
    > s/privlge/privilege/
    
    Fixed in v5-0003.
    
    > +CREATE ROLE regress_role_1 CREATEDB CREATEROLE REPLICATION BYPASSRLS;
    > 
    > 
    > I don't really like this business of just numbering large numbers of
    > roles in the tests. Let's give them more meaningful names.
    
    Changed in v5-0001.
    
    > +   Role owners can change any of these settings on roles they own except
    > 
    > 
    > I would say "on roles they directly or indirectly own", here and
    > similarly in one or two other places.
    
    Changed a few sentences of doc/src/sgml/ref/alter_role.sgml in v5-0004 as you suggest.  Please advise if you have other locations in mind.  A quick grep -i 'role owner' doesn't show any other relevant locations.
    
    
  25. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-18T20:51:46Z

    Rebased:
    
    
  26. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-22T21:20:33Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 4, 2022, at 12:47 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
    > > 
    > >> I was able to reproduce that using REASSIGN OWNED BY to cause a user to own itself.  Is that how you did it, or is there yet another way to get into that state?
    > > 
    > > I did:
    > > ALTER ROLE brindle OWNER TO brindle;
    > 
    > Ok, thanks.  I have rebased, fixed both REASSIGN OWNED BY and ALTER ROLE .. OWNER TO cases, and added regression coverage for them.
    > 
    > The last patch set to contain significant changes was v2, with v3 just being a rebase.  Relative to those sets:
    > 
    > 0001 -- rebased.
    > 0002 -- rebased; extend AlterRoleOwner_internal to disallow making a role its own immediate owner.
    > 0003 -- rebased; extend AlterRoleOwner_internal to disallow cycles in the role ownership graph.
    > 0004 -- rebased.
    > 0005 -- new; removes the broken pg_auth_members.grantor field.
    
    > Subject: [PATCH v4 1/5] Add tests of the CREATEROLE attribute.
    
    No particular issue with this one.
    
    > Subject: [PATCH v4 2/5] Add owners to roles
    > 
    > All roles now have owners.  By default, roles belong to the role
    > that created them, and initdb-time roles are owned by POSTGRES.
    
    ... database superuser, not 'POSTGRES'.
    
    > +++ b/src/backend/catalog/aclchk.c
    > @@ -5430,6 +5434,57 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
    >  	return has_privs_of_role(roleid, ownerId);
    >  }
    >  
    > +/*
    > + * Ownership check for a role (specified by OID)
    > + */
    > +bool
    > +pg_role_ownercheck(Oid role_oid, Oid roleid)
    > +{
    > +	HeapTuple		tuple;
    > +	Form_pg_authid	authform;
    > +	Oid				owner_oid;
    > +
    > +	/* Superusers bypass all permission checking. */
    > +	if (superuser_arg(roleid))
    > +		return true;
    > +
    > +	/* Otherwise, look up the owner of the role */
    > +	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(role_oid));
    > +	if (!HeapTupleIsValid(tuple))
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_UNDEFINED_OBJECT),
    > +				 errmsg("role with OID %u does not exist",
    > +						role_oid)));
    > +	authform = (Form_pg_authid) GETSTRUCT(tuple);
    > +	owner_oid = authform->rolowner;
    > +
    > +	/*
    > +	 * Roles must necessarily have owners.  Even the bootstrap user has an
    > +	 * owner.  (It owns itself).  Other roles must form a proper tree.
    > +	 */
    > +	if (!OidIsValid(owner_oid))
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_DATA_CORRUPTED),
    > +				 errmsg("role \"%s\" with OID %u has invalid owner",
    > +						authform->rolname.data, authform->oid)));
    > +	if (authform->oid != BOOTSTRAP_SUPERUSERID &&
    > +		authform->rolowner == authform->oid)
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_DATA_CORRUPTED),
    > +				 errmsg("role \"%s\" with OID %u owns itself",
    > +						authform->rolname.data, authform->oid)));
    > +	if (authform->oid == BOOTSTRAP_SUPERUSERID &&
    > +		authform->rolowner != BOOTSTRAP_SUPERUSERID)
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_DATA_CORRUPTED),
    > +				 errmsg("role \"%s\" with OID %u owned by role with OID %u",
    > +						authform->rolname.data, authform->oid,
    > +						authform->rolowner)));
    > +	ReleaseSysCache(tuple);
    > +
    > +	return (owner_oid == roleid);
    > +}
    
    Do we really need all of these checks on every call of this function..?
    Also, there isn't much point in including the role OID twice in the last
    error message, is there?  Unless things have gotten quite odd, it's
    goint to be the same value both times as we just proved to ourselves
    that it is, in fact, the same value (and that it's not the
    BOOTSTRAP_SUPERUSERID).
    
    This function also doesn't actually do any kind of checking to see if
    the role ownership forms a proper tree, so it seems a bit odd to have
    the comment talking about that here where it's doing other checks.
    
    > +++ b/src/backend/commands/user.c
    > @@ -77,6 +79,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  	Datum		new_record[Natts_pg_authid];
    >  	bool		new_record_nulls[Natts_pg_authid];
    >  	Oid			roleid;
    > +	Oid			owner_uid;
    > +	Oid			saved_uid;
    > +	int			save_sec_context;
    
    Seems a bit odd to introduce 'uid' into this file, which hasn't got any
    such anywhere in it, and I'm not entirely sure that any of these are
    actually needed..?
    
    > @@ -108,6 +113,16 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  	DefElem    *dvalidUntil = NULL;
    >  	DefElem    *dbypassRLS = NULL;
    >  
    > +	GetUserIdAndSecContext(&saved_uid, &save_sec_context);
    > +
    > +	/*
    > +	 * Who is supposed to own the new role?
    > +	 */
    > +	if (stmt->authrole)
    > +		owner_uid = get_rolespec_oid(stmt->authrole, false);
    > +	else
    > +		owner_uid = saved_uid;
    > +
    >  	/* The defaults can vary depending on the original statement type */
    >  	switch (stmt->stmt_type)
    >  	{
    > @@ -254,6 +269,10 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  			ereport(ERROR,
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("must be superuser to create superusers")));
    > +		if (!superuser_arg(owner_uid))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > +					 errmsg("must be superuser to own superusers")));
    >  	}
    >  	else if (isreplication)
    >  	{
    
    So, we're telling a superuser (which is the only way you could get to
    this point...) that they aren't allowed to create a superuser role which
    is owned by a non-superuser...  Why?
    
    > @@ -310,6 +329,19 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  				 errmsg("role \"%s\" already exists",
    >  						stmt->role)));
    >  
    > +	/*
    > +	 * If the requested authorization is different from the current user,
    > +	 * temporarily set the current user so that the object(s) will be created
    > +	 * with the correct ownership.
    > +	 *
    > +	 * (The setting will be restored at the end of this routine, or in case of
    > +	 * error, transaction abort will clean things up.)
    > +	 */
    > +	if (saved_uid != owner_uid)
    > +		SetUserIdAndSecContext(owner_uid,
    > +							   save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
    
    Err, why is this needed?  This looks copied from the CreateSchemaCommand
    but, unlike with the create schema command, CreateRole doesn't actually
    allow sub-commands to be run to create other objects in the way that
    CreateSchemaCommand does.
    
    > @@ -478,6 +513,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  	 */
    >  	table_close(pg_authid_rel, NoLock);
    >  
    > +	/* Reset current user and security context */
    > +	SetUserIdAndSecContext(saved_uid, save_sec_context);
    > +
    >  	return roleid;
    >  }
    
    ... ditto with this.
    
    > @@ -1675,3 +1714,110 @@ DelRoleMems(const char *rolename, Oid roleid,
    > +static void
    > +AlterRoleOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
    > +{
    > +	Form_pg_authid authForm;
    > +
    > +	Assert(tup->t_tableOid == AuthIdRelationId);
    > +	Assert(RelationGetRelid(rel) == AuthIdRelationId);
    > +
    > +	authForm = (Form_pg_authid) GETSTRUCT(tup);
    > +
    > +	/*
    > +	 * If the new owner is the same as the existing owner, consider the
    > +	 * command to have succeeded.  This is for dump restoration purposes.
    > +	 */
    > +	if (authForm->rolowner != newOwnerId)
    > +	{
    > +		/* Otherwise, must be owner of the existing object */
    > +		if (!pg_role_ownercheck(authForm->oid, GetUserId()))
    > +			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_ROLE,
    > +						   NameStr(authForm->rolname));
    > +
    > +		/* Must be able to become new owner */
    > +		check_is_member_of_role(GetUserId(), newOwnerId);
    
    Feels like we should be saying a bit more about why we check for role
    membership vs. has_privs_of_role() here.  I'm generally of the opinion
    that membership is the right thing to check here, just feel like we
    should try to explain more why that's the right thing.
    
    > +		/*
    > +		 * must have CREATEROLE rights
    > +		 *
    > +		 * NOTE: This is different from most other alter-owner checks in that
    > +		 * the current user is checked for create privileges instead of the
    > +		 * destination owner.  This is consistent with the CREATE case for
    > +		 * roles.  Because superusers will always have this right, we need no
    > +		 * special case for them.
    > +		 */
    > +		if (!have_createrole_privilege())
    > +			aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_ROLE,
    > +						   NameStr(authForm->rolname));
    > +
    
    I would think we'd be trying to get away from the role attribute stuff.
    
    > diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
    
    > +			CREATE ROLE RoleId AUTHORIZATION RoleSpec opt_with OptRoleList
    > +				{
    > +					CreateRoleStmt *n = makeNode(CreateRoleStmt);
    > +					n->stmt_type = ROLESTMT_ROLE;
    > +					n->role = $3;
    > +					n->authrole = $5;
    > +					n->options = $7;
    > +					$$ = (Node *)n;
    > +				}
    >  		;
    
    ...
    
    > @@ -1218,6 +1229,10 @@ CreateOptRoleElem:
    >  				{
    >  					$$ = makeDefElem("addroleto", (Node *)$3, @1);
    >  				}
    > +			| OWNER RoleSpec
    > +				{
    > +					$$ = makeDefElem("owner", (Node *)$2, @1);
    > +				}
    >  		;
    
    Not sure why we'd have both AUTHORIZATION and OWNER for CREATE ROLE..?
    We don't do that for other objects.
    
    > diff --git a/src/test/regress/sql/create_role.sql b/src/test/regress/sql/create_role.sql
    
    > @@ -1,6 +1,7 @@
    >  -- ok, superuser can create users with any set of privileges
    >  CREATE ROLE regress_role_super SUPERUSER;
    >  CREATE ROLE regress_role_1 CREATEDB CREATEROLE REPLICATION BYPASSRLS;
    > +GRANT CREATE ON DATABASE regression TO regress_role_1;
    
    Seems odd to add this as part of this patch, or am I missing something?
    
    > From 1784a5b51d4dbebf99798b5832d92b0f585feb08 Mon Sep 17 00:00:00 2001
    > From: Mark Dilger <mark.dilger@enterprisedb.com>
    > Date: Tue, 4 Jan 2022 11:42:27 -0800
    > Subject: [PATCH v4 3/5] Give role owners control over owned roles
    > 
    > Create a role ownership hierarchy.  The previous commit added owners
    > to roles.  This goes further, making role ownership transitive.  If
    > role A owns role B, and role B owns role C, then role A can act as
    > the owner of role C.  Also, roles A and B can perform any action on
    > objects belonging to role C that role C could itself perform.
    > 
    > This is a preparatory patch for changing how CREATEROLE works.
    
    This feels odd to have be an independent commit.
    
    > diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
    > index ddd205d656..ef36fad700 100644
    > --- a/src/backend/catalog/aclchk.c
    > +++ b/src/backend/catalog/aclchk.c
    > @@ -5440,61 +5440,20 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
    >  bool
    >  pg_role_ownercheck(Oid role_oid, Oid roleid)
    >  {
    > -	HeapTuple		tuple;
    > -	Form_pg_authid	authform;
    > -	Oid				owner_oid;
    > -
    >  	/* Superusers bypass all permission checking. */
    >  	if (superuser_arg(roleid))
    >  		return true;
    >  
    > -	/* Otherwise, look up the owner of the role */
    > -	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(role_oid));
    > -	if (!HeapTupleIsValid(tuple))
    > -		ereport(ERROR,
    > -				(errcode(ERRCODE_UNDEFINED_OBJECT),
    > -				 errmsg("role with OID %u does not exist",
    > -						role_oid)));
    > -	authform = (Form_pg_authid) GETSTRUCT(tuple);
    > -	owner_oid = authform->rolowner;
    > -
    > -	/*
    > -	 * Roles must necessarily have owners.  Even the bootstrap user has an
    > -	 * owner.  (It owns itself).  Other roles must form a proper tree.
    > -	 */
    > -	if (!OidIsValid(owner_oid))
    > -		ereport(ERROR,
    > -				(errcode(ERRCODE_DATA_CORRUPTED),
    > -				 errmsg("role \"%s\" with OID %u has invalid owner",
    > -						authform->rolname.data, authform->oid)));
    > -	if (authform->oid != BOOTSTRAP_SUPERUSERID &&
    > -		authform->rolowner == authform->oid)
    > -		ereport(ERROR,
    > -				(errcode(ERRCODE_DATA_CORRUPTED),
    > -				 errmsg("role \"%s\" with OID %u owns itself",
    > -						authform->rolname.data, authform->oid)));
    > -	if (authform->oid == BOOTSTRAP_SUPERUSERID &&
    > -		authform->rolowner != BOOTSTRAP_SUPERUSERID)
    > -		ereport(ERROR,
    > -				(errcode(ERRCODE_DATA_CORRUPTED),
    > -				 errmsg("role \"%s\" with OID %u owned by role with OID %u",
    > -						authform->rolname.data, authform->oid,
    > -						authform->rolowner)));
    > -	ReleaseSysCache(tuple);
    > -
    > -	return (owner_oid == roleid);
    > +	/* Otherwise, check the role ownership hierarchy */
    > +	return is_owner_of_role_nosuper(roleid, role_oid);
    >  }
    
    The function being basically entirely rewritten in this patch would be
    one reason why it seems an odd split.
    
    >  /*
    >   * Check whether specified role has CREATEROLE privilege (or is a superuser)
    >   *
    > - * Note: roles do not have owners per se; instead we use this test in
    > - * places where an ownership-like permissions test is needed for a role.
    > - * Be sure to apply it to the role trying to do the operation, not the
    > - * role being operated on!	Also note that this generally should not be
    > - * considered enough privilege if the target role is a superuser.
    > - * (We don't handle that consideration here because we want to give a
    > - * separate error message for such cases, so the caller has to deal with it.)
    > + * Note: In versions prior to PostgreSQL version 15, roles did not have owners
    > + * per se; instead we used this test in places where an ownership-like
    > + * permissions test was needed for a role.
    >   */
    >  bool
    >  has_createrole_privilege(Oid roleid)
    
    Surely this should be in the prior commit, if the split is kept..
    
    > diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
    
    > @@ -363,7 +363,7 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
    >  		/*
    >  		 * must have create-schema rights
    >  		 *
    > -		 * NOTE: This is different from other alter-owner checks in that the
    > +		 * NOTE: This is different from most other alter-owner checks in that the
    >  		 * current user is checked for create privileges instead of the
    >  		 * destination owner.  This is consistent with the CREATE case for
    >  		 * schemas.  Because superusers will always have this right, we need
    
    Not a fan of just dropping 'most' in here, doesn't really help someone
    understand what is being talked about.  I'd suggest adjusting the
    comment to talk about alter-owner checks for objects which exist in
    schemas, as that's really what is being referred to.
    
    > diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
    > index 14820744bf..11d5dffc90 100644
    > --- a/src/backend/commands/user.c
    > +++ b/src/backend/commands/user.c
    > @@ -724,7 +724,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
    >  			  !rolemembers &&
    >  			  !validUntil &&
    >  			  dpassword &&
    > -			  roleid == GetUserId()))
    > +			  !pg_role_ownercheck(roleid, GetUserId())))
    >  			ereport(ERROR,
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("permission denied")));
    > @@ -925,7 +925,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
    >  		}
    >  		else
    >  		{
    > -			if (!have_createrole_privilege() && roleid != GetUserId())
    > +			if (!have_createrole_privilege() &&
    > +				!pg_role_ownercheck(roleid, GetUserId()))
    >  				ereport(ERROR,
    >  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  						 errmsg("permission denied")));
    > @@ -977,11 +978,6 @@ DropRole(DropRoleStmt *stmt)
    >  				pg_auth_members_rel;
    >  	ListCell   *item;
    >  
    > -	if (!have_createrole_privilege())
    > -		ereport(ERROR,
    > -				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -				 errmsg("permission denied to drop role")));
    > -
    >  	/*
    >  	 * Scan the pg_authid relation to find the Oid of the role(s) to be
    >  	 * deleted.
    > @@ -1053,6 +1049,12 @@ DropRole(DropRoleStmt *stmt)
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("must be superuser to drop superusers")));
    >  
    > +		if (!have_createrole_privilege() &&
    > +			!pg_role_ownercheck(roleid, GetUserId()))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > +					 errmsg("permission denied to drop role")));
    > +
    >  		/* DROP hook for the role being removed */
    >  		InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
    >  
    > @@ -1811,6 +1813,18 @@ AlterRoleOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
    >  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    >  					 errmsg("role may not own itself")));
    >  
    > +		/*
    > +		 * Must not create cycles in the role ownership hierarchy.  If this
    > +		 * role owns (directly or indirectly) the proposed new owner, disallow
    > +		 * the ownership transfer.
    > +		 */
    > +		if (is_owner_of_role_nosuper(authForm->oid, newOwnerId))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > +					 errmsg("role \"%s\" may not both own and be owned by role \"%s\"",
    > +							NameStr(authForm->rolname),
    > +							GetUserNameFromId(newOwnerId, false))));
    > +
    >  		authForm->rolowner = newOwnerId;
    >  		CatalogTupleUpdate(rel, &tup->t_self, tup);
    
    > diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
    
    > +/*
    > + * Get a list of roles which own the given role, directly or indirectly.
    > + *
    > + * Each role has only one direct owner.  The returned list contains the given
    > + * role's owner, that role's owner, etc., up to the top of the ownership
    > + * hierarchy, which is always the bootstrap superuser.
    > + *
    > + * Raises an error if any role ownership invariant is violated.  Returns NIL if
    > + * the given roleid is invalid.
    > + */
    > +static List *
    > +roles_is_owned_by(Oid roleid)
    > +{
    > +	List	   *owners_list = NIL;
    > +	Oid			role_oid = roleid;
    > +
    > +	/*
    > +	 * Start with the current role and follow the ownership chain upwards until
    > +	 * we reach the bootstrap superuser.  To defend against getting into an
    > +	 * infinite loop, we must check for ownership cycles.  We choose to perform
    > +	 * other corruption checks on the ownership structure while iterating, too.
    > +	 */
    > +	while (OidIsValid(role_oid))
    > +	{
    > +		HeapTuple		tuple;
    > +		Form_pg_authid	authform;
    > +		Oid				owner_oid;
    > +
    > +		/* Find the owner of the current iteration's role */
    > +		tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(role_oid));
    > +		if (!HeapTupleIsValid(tuple))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_UNDEFINED_OBJECT),
    > +					 errmsg("role with OID %u does not exist", role_oid)));
    > +
    > +		authform = (Form_pg_authid) GETSTRUCT(tuple);
    > +		owner_oid = authform->rolowner;
    > +
    > +		/*
    > +		 * Roles must necessarily have owners.  Even the bootstrap user has an
    > +		 * owner.  (It owns itself).
    > +		 */
    > +		if (!OidIsValid(owner_oid))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_DATA_CORRUPTED),
    > +					 errmsg("role \"%s\" with OID %u has invalid owner",
    > +							NameStr(authform->rolname), authform->oid)));
    > +
    > +		/* The bootstrap user must own itself */
    > +		if (authform->oid == BOOTSTRAP_SUPERUSERID &&
    > +			owner_oid != BOOTSTRAP_SUPERUSERID)
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_DATA_CORRUPTED),
    > +					 errmsg("role \"%s\" with OID %u owned by role with OID %u",
    > +							NameStr(authform->rolname), authform->oid,
    > +							authform->rolowner)));
    > +
    > +		/*
    > +		 * Roles other than the bootstrap user must not be their own direct
    > +		 * owners.
    > +		 */
    > +		if (authform->oid != BOOTSTRAP_SUPERUSERID &&
    > +			authform->oid == owner_oid)
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_DATA_CORRUPTED),
    > +					 errmsg("role \"%s\" with OID %u owns itself",
    > +							NameStr(authform->rolname), authform->oid)));
    > +
    > +		ReleaseSysCache(tuple);
    > +
    > +		/* If we have reached the bootstrap user, we're done. */
    > +		if (role_oid == BOOTSTRAP_SUPERUSERID)
    > +		{
    > +			if (!owners_list)
    > +				owners_list = lappend_oid(owners_list, owner_oid);
    > +			break;
    > +		}
    > +
    > +		/*
    > +		 * For all other users, check they do not own themselves indirectly
    > +		 * through an ownership cycle.
    > +		 *
    > +		 * Scanning the list each time through this loop results in overall
    > +		 * quadratic work in the depth of the ownership chain, but we're
    > +		 * not on a critical performance path, nor do we expect ownership
    > +		 * hierarchies to be deep.
    > +		 */
    > +		if (owners_list && list_member_oid(owners_list,
    > +										   ObjectIdGetDatum(owner_oid)))
    > +			ereport(ERROR,
    > +					(errcode(ERRCODE_DATA_CORRUPTED),
    > +					 errmsg("role \"%s\" with OID %u indirectly owns itself",
    > +							GetUserNameFromId(owner_oid, false),
    > +							owner_oid)));
    > +
    > +		/* Done with sanity checks.  Add this owner to the list. */
    > +		owners_list = lappend_oid(owners_list, owner_oid);
    > +
    > +		/* Otherwise, iterate on this iteration's owner_oid. */
    > +		role_oid = owner_oid;
    > +	}
    > +
    > +	return owners_list;
    > +}
    
    > @@ -4850,6 +4955,10 @@ has_privs_of_role(Oid member, Oid role)
    
    > +	/* Owners of roles have every privilge the owned role has */
    > +	if (pg_role_ownercheck(role, member))
    > +		return true;
    
    Whoah, really?  No, I don't agree with this, it's throwing away the
    entire concept around inheritance of role rights and how you can have
    roles which you can get the privileges of by doing a SET ROLE to them
    but you don't automatically have those rights.
    
    > +/*
    > + * Is owner a direct or indirect owner of the role, not considering
    > + * superuserness?
    > + */
    > +bool
    > +is_owner_of_role_nosuper(Oid owner, Oid role)
    > +{
    > +	return list_member_oid(roles_is_owned_by(role), owner);
    > +}
    
    
    Surely if you're a member of a role which owns another role, you should
    be considered to be an owner of that role too..?  Just checking if the
    current role is a member of the roles which directly own the specified
    role misses that case.
    
    That is:
    
    CREATE ROLE r1;
    CREATE ROLE r2;
    
    GRANT r2 to r1;
    
    CREATE ROLE r3 AUTHORIZATION r2;
    
    Surely, r1 is to be considered an owner of r3 in this case, but the
    above check wouldn't consider that to be the case- it would only return
    true if the current role is r2.
    
    We do need some kind of direct membership check in the list of owners to
    avoid creating loops, so maybe this function is kept as that and the
    pg_role_ownership() check is changed to address the above case, but I
    don't think we should just ignore role membership when it comes to role
    ownership- we don't do that for any other kind of ownership check.
    
    > Subject: [PATCH v4 4/5] Restrict power granted via CREATEROLE.
    
    I would think this would be done independently of the other patches and
    probably be first.
    
    > diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml
    
    > @@ -70,18 +70,18 @@ ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | A
    >     <link linkend="sql-revoke"><command>REVOKE</command></link> for that.)
    >     Attributes not mentioned in the command retain their previous settings.
    >     Database superusers can change any of these settings for any role.
    > -   Roles having <literal>CREATEROLE</literal> privilege can change any of these
    > -   settings except <literal>SUPERUSER</literal>, <literal>REPLICATION</literal>,
    > -   and <literal>BYPASSRLS</literal>; but only for non-superuser and
    > -   non-replication roles.
    > -   Ordinary roles can only change their own password.
    > +   Role owners can change any of these settings on roles they own except
    > +   <literal>SUPERUSER</literal>, <literal>REPLICATION</literal>, and
    > +   <literal>BYPASSRLS</literal>; but only for non-superuser and non-replication
    > +   roles, and only if the role owner does not alter the target role to have a
    > +   privilege which the role owner itself lacks.  Ordinary roles can only change
    > +   their own password.
    >    </para>
    
    Having contemplated this a bit more, I don't like it, and it's not how
    things work when it comes to regular privileges.
    
    Consider that I can currently GRANT someone UPDATE privileges on an
    object, but they can't GRANT that privilege to someone else unless I
    explicitly allow it.  The same could certainly be said for roles-
    perhaps I want to allow someone the privilege to create non-login roles,
    but I don't want them to be able to create new login roles, even if they
    themselves have LOGIN.
    
    As another point, I might want to have an 'admin' role that I want
    admins to SET ROLE to before they go creating other roles, because I
    don't want them to be creating roles as their regular user and so that
    those other roles are owned by the 'admin' role, but I don't want that
    role to have the 'login' attribute.
    
    In other words, we should really consider what role attributes a given
    role has to be independent of what role attributes that role is allowed
    to set on roles they create.  I appreciate that "just whatever the
    current role has" is simpler and less work but also will be difficult to
    walk back from once it's in the wild.
    
    > @@ -1457,7 +1449,7 @@ AddRoleMems(const char *rolename, Oid roleid,
    
    >  	/*
    > -	 * Check permissions: must have createrole or admin option on the role to
    > +	 * Check permissions: must be owner or have admin option on the role to
    >  	 * be changed.  To mess with a superuser role, you gotta be superuser.
    >  	 */
    >  	if (superuser_arg(roleid))
    
    ...
    
    > @@ -1467,9 +1459,9 @@ AddRoleMems(const char *rolename, Oid roleid,
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("must be superuser to alter superusers")));
    >  	}
    > -	else
    > +	else if (!superuser())
    >  	{
    > -		if (!have_createrole_privilege() &&
    > +		if (!pg_role_ownercheck(roleid, grantorId) &&
    >  			!is_admin_of_role(grantorId, roleid))
    >  			ereport(ERROR,
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    
    I'm not entirely sure about including owners here though I'm not
    completely against it either.  This conflation of what the 'admin'
    privileges on a role means vs. the 'ownership' of a role is part of what
    I dislike about having two distinct systems for saying who is allowed to
    GRANT one role to another.
    
    Also, if we're going to always consider owners to be admins of roles
    they own, why not push that into is_admin_of_role()?
    
    > Subject: [PATCH v4 5/5] Remove grantor field from pg_auth_members
    
    While I do think we should fix the issue with dangling references, I
    dislike just getting rid of this entirely.  While I don't really agree
    with the spec about running around DROP'ing objects when a user's
    privilege to create those objects has been revoked, I do think we should
    be REVOKE'ing rights when a user's right to GRANT has been revoked, and
    tracking the information about who GRANT'd what role to what other role
    is needed for that.  Further, we track who GRANT'd access to what in the
    regular ACL system and I don't like the idea of removing that for roles.
    
    > We could fix the bug, but there is no clear solution to the problem
    > that existing installations may have broken data.  Since the field
    > is not used for any purpose, removing it seems the best option.
    
    Existing broken systems will have to eventually be upgraded and the
    admin will have to deal with such cases then, so I don't really consider
    this to be that big of an issue or reason to entirely remove this.
    
    If we're going to do this, it should also be done independently of the
    role ownership stuff too.
    
    Thanks,
    
    Stephen
    
  27. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-01-24T19:19:50Z

    On 1/22/22 16:20, Stephen Frost wrote:
    >> Subject: [PATCH v4 1/5] Add tests of the CREATEROLE attribute.
    > No particular issue with this one.
    >
    >
    
    I'm going to commit this piece forthwith so we get it out of the way.
    That will presumably make the cfbot unhappy until Mark submits a new
    patch set.
    
    
    cheers
    
    
    andrew
    
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  28. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-01-24T20:33:01Z

    On Sat, Jan 22, 2022 at 4:20 PM Stephen Frost <sfrost@snowman.net> wrote:
    > Whoah, really?  No, I don't agree with this, it's throwing away the
    > entire concept around inheritance of role rights and how you can have
    > roles which you can get the privileges of by doing a SET ROLE to them
    > but you don't automatically have those rights.
    
    I see it differently. In my opinion, what that does is make the patch
    actually useful instead of largely a waste of time. If you are a
    service provider, you want to give your customers a super-user-like
    experience without actually making them superuser. You don't want to
    actually make them superuser, because then they could do things like
    change archive_command or install plperlu and shell out to the OS
    account, which you don't want. But you do want them to be able to
    administer objects within the database just as a superuser could. And
    a superuser has privileges over objects they own and objects belonging
    to other users automatically, without needing to SET ROLE.
    
    Imagine what happens if we adopt your proposal here. Everybody now has
    to understand the behavior of a regular account, the behavior of a
    superuser account, and the behavior of this third type of account
    which is sort of like a superuser but requires a lot more SET ROLE
    commands. And also every tool. So for example pg_dump and restore
    isn't going to work, not even on the set of objects this
    elevated-privilege user can access. pgAdmin isn't going to understand
    that it needs to insert a bunch of extra SET ROLE commands to
    administer objects. Ditto literally every other tool anyone has ever
    written to administer PostgreSQL. And for all of that pain, we get
    exactly zero extra security.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  29. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-01-24T21:00:28Z

    On 1/24/22 15:33, Robert Haas wrote:
    > On Sat, Jan 22, 2022 at 4:20 PM Stephen Frost <sfrost@snowman.net> wrote:
    >> Whoah, really?  No, I don't agree with this, it's throwing away the
    >> entire concept around inheritance of role rights and how you can have
    >> roles which you can get the privileges of by doing a SET ROLE to them
    >> but you don't automatically have those rights.
    > I see it differently. In my opinion, what that does is make the patch
    > actually useful instead of largely a waste of time. If you are a
    > service provider, you want to give your customers a super-user-like
    > experience without actually making them superuser. You don't want to
    > actually make them superuser, because then they could do things like
    > change archive_command or install plperlu and shell out to the OS
    > account, which you don't want. But you do want them to be able to
    > administer objects within the database just as a superuser could. And
    > a superuser has privileges over objects they own and objects belonging
    > to other users automatically, without needing to SET ROLE.
    >
    
    +many
    
    
    I encountered such issues on a cloud provider several years ago, and
    blogged about the difficulties, which would have been solved very nicely
    and cleanly by this proposal. It was when I understood properly how this
    proposal worked, precisely as Robert states, that I became more
    enthusiastic about it.
    
    
    cheers
    
    
    andrew
    
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  30. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-24T21:23:24Z

    Greetings,
    
    On Mon, Jan 24, 2022 at 15:33 Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Sat, Jan 22, 2022 at 4:20 PM Stephen Frost <sfrost@snowman.net> wrote:
    > > Whoah, really?  No, I don't agree with this, it's throwing away the
    > > entire concept around inheritance of role rights and how you can have
    > > roles which you can get the privileges of by doing a SET ROLE to them
    > > but you don't automatically have those rights.
    >
    > I see it differently. In my opinion, what that does is make the patch
    > actually useful instead of largely a waste of time.
    
    
    The idea behind this patch is to enable creation and dropping of roles,
    which isn’t possible now without being effectively a superuser.
    
    Forcing owners to also implicitly have all rights of the roles they create
    is orthogonal to that and an unnecessary change.
    
    If you are a
    > service provider, you want to give your customers a super-user-like
    > experience without actually making them superuser. You don't want to
    > actually make them superuser, because then they could do things like
    > change archive_command or install plperlu and shell out to the OS
    > account, which you don't want. But you do want them to be able to
    > administer objects within the database just as a superuser could. And
    > a superuser has privileges over objects they own and objects belonging
    > to other users automatically, without needing to SET ROLE.
    
    
    I am not saying that we would explicitly set all cases to be noninherit or
    that we would even change the default away from what it is today, only that
    we should use the existing role system and it’s concept of
    inherit-vs-noninherit rather than throwing all of that away.
    
    Everybody now has
    > to understand the behavior of a regular account, the behavior of a
    > superuser account, and the behavior of this third type of account
    > which is sort of like a superuser but requires a lot more SET ROLE
    > commands.
    
    
    Inherit vs. noninherit roles is not a new concept, it has existed since the
    role system was implemented.  Further, that system does not require a lot
    of SET ROLE commands unless and until an admin sets up a non-inherit role.
    At that time, however, it’s expected that the rights of a role which has
    inherit set to false are not automatically allowed for the role to which it
    was GRANT’d.  That’s how roles have always worked since they were
    introduced.
    
    And also every tool. So for example pg_dump and restore
    > isn't going to work, not even on the set of objects this
    > elevated-privilege user can access. pgAdmin isn't going to understand
    > that it needs to insert a bunch of extra SET ROLE commands to
    > administer objects. Ditto literally every other tool anyone has ever
    > written to administer PostgreSQL. And for all of that pain, we get
    > exactly zero extra security.
    
    
    We have an inherit system today and pg_dump works just fine, as far as I’m
    aware, and it does, indeed, issue SET ROLE at various points. Perhaps you
    could explain with PG today what the issue is that is caused?  Or what
    issue pgAdmin has with PG’s existing role inherit system?
    
    Further, being able to require a SET ROLE before running a given operation
    is certainly a benefit in much the same way that having a user have to sudo
    before running an operation is.
    
    Thanks,
    
    Stephen
    
    >
    
  31. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-01-24T21:41:50Z

    On Mon, Jan 24, 2022 at 4:23 PM Stephen Frost <sfrost@snowman.net> wrote:
    > The idea behind this patch is to enable creation and dropping of roles, which isn’t possible now without being effectively a superuser.
    >
    > Forcing owners to also implicitly have all rights of the roles they create is orthogonal to that and an unnecessary change.
    
    I just took a look at the first email on this thread and it says this:
    
    >>> These patches have been split off the now deprecated monolithic "Delegating superuser tasks to new security roles" thread at [1].
    
    Therefore I think it is pretty clear that the goals of this patch set
    include being able to delegate superuser tasks to new security roles.
    And having those tasks be delegated but *work randomly differently* is
    much less useful.
    
    > I am not saying that we would explicitly set all cases to be noninherit or that we would even change the default away from what it is today, only that we should use the existing role system and it’s concept of inherit-vs-noninherit rather than throwing all of that away.
    
    INHERIT vs. NOINHERIT is documented to control the behavior of role
    *membership*. This patch is introducing a new concept of role
    *ownership*. It's not self-evident that what applies to one case
    should apply to the other.
    
    > Further, being able to require a SET ROLE before running a given operation is certainly a benefit in much the same way that having a user have to sudo before running an operation is.
    
    That's a reasonable point of view, but having things work similarly to
    what happens for a superuser is ALSO a very big benefit. In my
    opinion, in fact, it is a far larger benefit.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  32. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-24T22:21:40Z

    Greetings,
    
    On Mon, Jan 24, 2022 at 16:42 Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Mon, Jan 24, 2022 at 4:23 PM Stephen Frost <sfrost@snowman.net> wrote:
    > > The idea behind this patch is to enable creation and dropping of roles,
    > which isn’t possible now without being effectively a superuser.
    > >
    > > Forcing owners to also implicitly have all rights of the roles they
    > create is orthogonal to that and an unnecessary change.
    >
    > I just took a look at the first email on this thread and it says this:
    >
    > >>> These patches have been split off the now deprecated monolithic
    > "Delegating superuser tasks to new security roles" thread at [1].
    >
    > Therefore I think it is pretty clear that the goals of this patch set
    > include being able to delegate superuser tasks to new security roles.
    > And having those tasks be delegated but *work randomly differently* is
    > much less useful.
    
    
    Being able to create and drop users is, in fact, effectively a
    superuser-only task today.  We could throw out the entire idea of role
    ownership, in fact, as being entirely unnecessary when talking about that
    specific task.
    
    > I am not saying that we would explicitly set all cases to be noninherit
    > or that we would even change the default away from what it is today, only
    > that we should use the existing role system and it’s concept of
    > inherit-vs-noninherit rather than throwing all of that away.
    >
    > INHERIT vs. NOINHERIT is documented to control the behavior of role
    > *membership*. This patch is introducing a new concept of role
    > *ownership*. It's not self-evident that what applies to one case
    > should apply to the other.
    
    
    This is an argument to drop the role ownership concept, as I view it.
    Privileges are driven by membership today and inventing some new
    independent way to do that is increasing confusion, not improving things.
    I disagree that adding role ownership should necessarily change how the
    regular GRANT privilege system works or throw away basic concepts of that
    system which have been in place for decades.  Increasing the number of
    independent ways to answer the question of “what users have what rights on
    object X” is an active bad thing.  Anything that cares about object access
    will now also have to address role ownership to answer that question, while
    if we don’t include this one change then they don’t need to directly have
    any concern for ownership because regular object privileges still work the
    same way they did before.
    
    > Further, being able to require a SET ROLE before running a given
    > operation is certainly a benefit in much the same way that having a user
    > have to sudo before running an operation is.
    >
    > That's a reasonable point of view, but having things work similarly to
    > what happens for a superuser is ALSO a very big benefit. In my
    > opinion, in fact, it is a far larger benefit.
    
    
    Superuser is a problem specifically because it gives people access to do
    absolutely anything, both for security and safety concerns. Disallowing a
    way to curtail that same risk when it comes to role ownership invites
    exactly those same problems.
    
    I appreciate that there’s an edge between the ownership system being
    proposed and the existing role membership system, but we’d be much better
    off trying to minimize the amount that they end up overlapping- role
    ownership should be about managing roles.
    
    To push back on the original “tenant” argument, consider that one of the
    bigger issues in cloud computing today is exactly the problem that the
    cloud managers can potentially gain access to the sensitive data of their
    tenants and that’s not generally viewed as a positive thing.  This change
    would make it so that every landlord can go and SELECT from the tables of
    their tenants without so much as a by-your-leave.  The tenants likely don’t
    like that idea, and almost as likely the landlords in many cases aren’t
    thrilled with it either.  Should the landlords be able to DROP the tenant
    due to the tenant not paying their bill?  Of course, and that should then
    eliminate the tenant’s tables and other objects which take up resources,
    but that’s not the same thing as saying that a landlord should be able to
    unlock a tenant’s old phone that they left behind (and yeah, maybe the
    analogy falls apart a bit there, but the point I’m trying to get at is that
    it’s not as simple as it’s being made out to be here and we should think
    about these things and not just implicitly grant all access to the owner
    because that’s an easy thing to do- and is exactly what viewing owners as
    “mini superusers” does and leads to many of the same issues we already have
    with superusers).
    
    Thanks,
    
    Stephen
    
    >
    
  33. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-24T22:49:34Z

    
    > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > Being able to create and drop users is, in fact, effectively a superuser-only task today.  We could throw out the entire idea of role ownership, in fact, as being entirely unnecessary when talking about that specific task.
    
    Wow, that's totally contrary to how I see this patch.  The heart and soul of this patch is to fix the fact that CREATEROLE is currently overpowered.  Everything else is gravy.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  34. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-24T23:18:03Z

    
    > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > Superuser is a problem specifically because it gives people access to do absolutely anything, both for security and safety concerns. Disallowing a way to curtail that same risk when it comes to role ownership invites exactly those same problems.
    
    Before the patch, users with CREATEROLE can do mischief.  After the patch, users with CREATEROLE can do mischief.  The difference is that the mischief that can be done after the patch is a proper subset of the mischief that can be done before the patch.  (Counter-examples highly welcome.)
    
    Specifically, I claim that before the patch, non-superuser "bob" with CREATEROLE can interfere with *any* non-superuser.  After the patch, non-superuser "bob" with CREATEROLE can interfere with *some* non-superusers; specifically, with non-superusers he created himself, or which have had ownership transferred to him.
    
    Restricting the scope of bob's mischief is a huge win, in my view.
    
    The argument about whether owners should always implicitly inherit privileges from roles they own is a bit orthogonal to my point about mischief-making.  Do we at least agree on the mischief-abatement aspect of this patch set?  
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  35. Re: CREATEROLE and role ownership hierarchies

    Fujii Masao <masao.fujii@oss.nttdata.com> — 2022-01-25T06:55:49Z

    
    On 2022/01/25 8:18, Mark Dilger wrote:
    > 
    > 
    >> On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    >>
    >> Superuser is a problem specifically because it gives people access to do absolutely anything, both for security and safety concerns. Disallowing a way to curtail that same risk when it comes to role ownership invites exactly those same problems.
    > 
    > Before the patch, users with CREATEROLE can do mischief.  After the patch, users with CREATEROLE can do mischief.  The difference is that the mischief that can be done after the patch is a proper subset of the mischief that can be done before the patch.  (Counter-examples highly welcome.)
    > 
    > Specifically, I claim that before the patch, non-superuser "bob" with CREATEROLE can interfere with *any* non-superuser.  After the patch, non-superuser "bob" with CREATEROLE can interfere with *some* non-superusers; specifically, with non-superusers he created himself, or which have had ownership transferred to him.
    > 
    > Restricting the scope of bob's mischief is a huge win, in my view.
    
    +1
    
    One of "mischiefs" I'm thinking problematic is that users with CREATEROLE can give any predefined role that they don't have, to other users including themselves. For example, users with CREATEROLE can give pg_execute_server_program to themselves and run any OS commands by COPY PROGRAM. This would be an issue when providing something like PostgreSQL cloud service that wants to prevent end users from running OS commands but allow them to create/drop roles. Does the proposed patch fix also this issue?
    
    Regards,
    
    -- 
    Fujii Masao
    Advanced Computing Technology Center
    Research and Development Headquarters
    NTT DATA CORPORATION
    
    
    
    
  36. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-25T16:21:16Z

    
    > On Jan 24, 2022, at 10:55 PM, Fujii Masao <masao.fujii@oss.nttdata.com> wrote:
    > 
    > +1
    > 
    > One of "mischiefs" I'm thinking problematic is that users with CREATEROLE can give any predefined role that they don't have, to other users including themselves. For example, users with CREATEROLE can give pg_execute_server_program to themselves and run any OS commands by COPY PROGRAM. This would be an issue when providing something like PostgreSQL cloud service that wants to prevent end users from running OS commands but allow them to create/drop roles. Does the proposed patch fix also this issue?
    
    Yes, the patch restricts CREATEROLE privilege from granting any privilege they themselves lack.  There is a regression test in the patch set which demonstrates this.  See src/test/regress/expected/create_role.out.  The diffs from v6-0004-Restrict-power-granted-via-CREATEROLE.patch are quoted here for ease of viewing:
    
    --- ok, having CREATEROLE is enough to create roles in privileged roles
    +-- fail, having CREATEROLE is not enough to create roles in privileged roles
     CREATE ROLE regress_read_all_data IN ROLE pg_read_all_data;
    +ERROR:  must have admin option on role "pg_read_all_data"
     CREATE ROLE regress_write_all_data IN ROLE pg_write_all_data;
    +ERROR:  must have admin option on role "pg_write_all_data"
     CREATE ROLE regress_monitor IN ROLE pg_monitor;
    +ERROR:  must have admin option on role "pg_monitor"
     CREATE ROLE regress_read_all_settings IN ROLE pg_read_all_settings;
    +ERROR:  must have admin option on role "pg_read_all_settings"
     CREATE ROLE regress_read_all_stats IN ROLE pg_read_all_stats;
    +ERROR:  must have admin option on role "pg_read_all_stats"
     CREATE ROLE regress_stat_scan_tables IN ROLE pg_stat_scan_tables;
    +ERROR:  must have admin option on role "pg_stat_scan_tables"
     CREATE ROLE regress_read_server_files IN ROLE pg_read_server_files;
    +ERROR:  must have admin option on role "pg_read_server_files"
     CREATE ROLE regress_write_server_files IN ROLE pg_write_server_files;
    +ERROR:  must have admin option on role "pg_write_server_files"
     CREATE ROLE regress_execute_server_program IN ROLE pg_execute_server_program;
    +ERROR:  must have admin option on role "pg_execute_server_program"
     CREATE ROLE regress_signal_backend IN ROLE pg_signal_backend;
    +ERROR:  must have admin option on role "pg_signal_backend"
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  37. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-25T16:42:14Z

    
    > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > To push back on the original “tenant” argument, consider that one of the bigger issues in cloud computing today is exactly the problem that the cloud managers can potentially gain access to the sensitive data of their tenants and that’s not generally viewed as a positive thing.
    
    +1.  This is a real problem.  I have been viewing this problem as separate from the one which role ownership is intended to fix.  Do you have a suggestion about how to tackle the problems together with less work than tackling them separately?
    
    >  This change would make it so that every landlord can go and SELECT from the tables of their tenants without so much as a by-your-leave.
    
    I would expect that is already true.  A user with CREATEROLE can do almost everything.  This patch closes some CREATEROLE related security problems, but not this one you mention.
    
    >  The tenants likely don’t like that idea
    
    +1
    
    > , and almost as likely the landlords in many cases aren’t thrilled with it either.
    
    +1
    
    >  Should the landlords be able to DROP the tenant due to the tenant not paying their bill?  Of course, and that should then eliminate the tenant’s tables and other objects which take up resources, but that’s not the same thing as saying that a landlord should be able to unlock a tenant’s old phone that they left behind (and yeah, maybe the analogy falls apart a bit there, but the point I’m trying to get at is that it’s not as simple as it’s being made out to be here and we should think about these things and not just implicitly grant all access to the owner because that’s an easy thing to do- and is exactly what viewing owners as “mini superusers” does and leads to many of the same issues we already have with superusers).
    
    This is a pretty interesting argument.  I don't believe it will work to do as you say unconditionally, as there is still a need to have CREATEROLE users who have privileges on their created roles' objects, even if for no other purpose than to be able to REASSIGN OWNED BY those objects before dropping roles.  But maybe there is also a need to have CREATEROLE users who lack that privilege?  Would that be a privilege bit akin to (but not the same as!) the INHERIT privilege?  Should I redesign for something like that?
    
    I like that the current patch restricts CREATEROLE users from granting privileges they themselves lack.  Would such a new privilege bit work the same way?  Imagine that you, "stephen", have CREATEROLE but not this new bit, and you create me, "mark" as a tenant with CREATEROLE.  Can you give me the bit?  Or does the fact that you lack the bit mean you can't give it to me, either?
    
    Other suggestions?
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  38. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-01-25T19:04:03Z

    On Mon, Jan 24, 2022 at 5:21 PM Stephen Frost <sfrost@snowman.net> wrote:
    > This is an argument to drop the role ownership concept, as I view it.  Privileges are driven by membership today and inventing some new independent way to do that is increasing confusion, not improving things.  I disagree that adding role ownership should necessarily change how the regular GRANT privilege system works or throw away basic concepts of that system which have been in place for decades.  Increasing the number of independent ways to answer the question of “what users have what rights on object X” is an active bad thing.  Anything that cares about object access will now also have to address role ownership to answer that question, while if we don’t include this one change then they don’t need to directly have any concern for ownership because regular object privileges still work the same way they did before.
    
    It really feels to me like you just keep moving the goalposts. We
    started out with a conversation where Mark said he'd like to be able
    to grant permissions on GUCs to non-superusers.[1] You argued
    repeatedly that we really needed to do something about CREATEROLE
    [2,3,4]. Mark argued that this was an unrelated problem[5] but you
    argued that unless it were addressed, users would still be able to
    break out of the sandbox[6] which must mean either the OS user, or at
    least PostgreSQL users other than the ones they were supposed to be
    able to control.
    
    That led *directly* to the patch at hand, which solves the problem by
    inventing the notion of role ownership, so that you can distinguish
    the roles you can administer from the ones you drop. You are now
    proposing that we get rid of that concept, a concept that was added
    four months ago[7] as a direct response to your previous feedback.
    It's completely unfair to make an argument that results in the
    addition of a complex piece of machinery to a body of work that was
    initially on an only marginally related topic and then turn around and
    argue, quite close to the end of the release cycle, for the removal of
    that exact same mechanism.
    
    And your argument about whether the privileges should be able to be
    exercised without SET ROLE is also just completely baffling to me
    given the previous conversation. It seems 100% clear from the previous
    discussion that we were talking about service provider environments
    and trying to deliver a good user experience to "lead tenants" in such
    environments. Regardless of the technical details of how INHERIT or
    anything else work, an actual superuser would not be subject to a
    restriction similar to the one you're talking about, so arguing that
    it ought to be present here for some technical reason is placing
    technicalities ahead of what seemed at the time to be a shared goal.
    There's a perfectly good argument to be made that the superuser role
    should not work the way it does, but it's too late to relitigate that.
    And I can't imagine why any service provider would find any value in a
    new role that requires all of the extra push-ups you're trying to
    impose on it.
    
    I just can't shake the feeling that you're trying to redesign this
    patch out of (a) getting committed and (b) solving any of the problems
    it intends to solve, problems with which you largely seemed to agree.
    I assume that is not actually your intention, but I can't think of
    anything you'd be doing differently here if it were.
    
    [1] https://www.postgresql.org/message-id/F9408A5A-B20B-42D2-9E7F-49CD3D1547BC%40enterprisedb.com
    [2] https://www.postgresql.org/message-id/20210726200542.GX20766%40tamriel.snowman.net
    [3] https://www.postgresql.org/message-id/20210726205433.GA20766%40tamriel.snowman.net
    [4] https://www.postgresql.org/message-id/20210823181351.GB17906%40tamriel.snowman.net
    [5] https://www.postgresql.org/message-id/92AA9A52-A644-42FE-B699-8ECAEE12E635%40enterprisedb.com
    [6] https://www.postgresql.org/message-id/20210823195130.GF17906%40tamriel.snowman.net
    [7] https://www.postgresql.org/message-id/67BB2F92-704B-415C-8D47-149327CA8F4B%40enterprisedb.com
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  39. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-25T20:44:29Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > Being able to create and drop users is, in fact, effectively a superuser-only task today.  We could throw out the entire idea of role ownership, in fact, as being entirely unnecessary when talking about that specific task.
    > 
    > Wow, that's totally contrary to how I see this patch.  The heart and soul of this patch is to fix the fact that CREATEROLE is currently overpowered.  Everything else is gravy.
    
    I agree that CREATEROLE is overpowered and that the goal of this should
    be to provide a way for roles to be created and dropped that doesn't
    give the user who has that power everything that CREATEROLE currently
    does.  The point I was making is that the concept of role ownership
    isn't intrinsically linked to that and is, therefore, as you say, gravy.
    That isn't to say that I'm entirely against the role ownership idea but
    I'd want it to be focused on the goal of providing ways of creating and
    dropping users and otherwise performing that kind of administration and
    that doesn't require the specific change to make owners be members of
    all roles they own and automatically have all privileges of those roles
    all the time.
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > 
    > > Superuser is a problem specifically because it gives people access to do absolutely anything, both for security and safety concerns. Disallowing a way to curtail that same risk when it comes to role ownership invites exactly those same problems.
    > 
    > Before the patch, users with CREATEROLE can do mischief.  After the patch, users with CREATEROLE can do mischief.  The difference is that the mischief that can be done after the patch is a proper subset of the mischief that can be done before the patch.  (Counter-examples highly welcome.)
    > 
    > Specifically, I claim that before the patch, non-superuser "bob" with CREATEROLE can interfere with *any* non-superuser.  After the patch, non-superuser "bob" with CREATEROLE can interfere with *some* non-superusers; specifically, with non-superusers he created himself, or which have had ownership transferred to him.
    > 
    > Restricting the scope of bob's mischief is a huge win, in my view.
    > 
    > The argument about whether owners should always implicitly inherit privileges from roles they own is a bit orthogonal to my point about mischief-making.  Do we at least agree on the mischief-abatement aspect of this patch set?  
    
    I don't know how many bites at this particular apple we're going to get,
    but I doubt folks are going to be happy if we change our minds every
    release.  Further, I suspect we'll be better off going too far in the
    direction of 'mischief reduction' than not far enough.  If we restrict
    things too far then we can provide ways to add those things back, but
    it's harder to remove things we didn't take away.
    
    This particular case is even an oddity on that spectrum though-
    CREATEROLE users, today, don't have access to all the objects created by
    roles which they create.  Yes, they can get such access if they go
    through some additional hoops, but that could then be caught by someone
    auditing the logs, a consideration that I don't think we appreciate
    enough today.
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 24, 2022, at 2:21 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > 
    > > To push back on the original “tenant” argument, consider that one of the bigger issues in cloud computing today is exactly the problem that the cloud managers can potentially gain access to the sensitive data of their tenants and that’s not generally viewed as a positive thing.
    > 
    > +1.  This is a real problem.  I have been viewing this problem as separate from the one which role ownership is intended to fix.  Do you have a suggestion about how to tackle the problems together with less work than tackling them separately?
    
    I don't know about less work or not, but in this particular case I was
    asking for a few lines to be removed from the patch.  I can believe that
    doing so would create some issues in terms of the use-cases that you
    want to solve with this and if we agree on those being sensible cases to
    address then we'd need to implement something to address those, though
    it's also possibly not the case and maybe removing those few lines
    doesn't impact anything beyond then allowing owners to not automatically
    inherit the rights of the roles they own if they don't wish to.
    
    Instead of talking about those cases concretely though, it seems like
    we've shifted to abstractly talking about ownership and landlords.
    Maybe some of that is helpful, but it seems to increasingly be an area
    that's causing more division than helping to move forward towards a
    mutually agreeable result.
    
    > >  This change would make it so that every landlord can go and SELECT from the tables of their tenants without so much as a by-your-leave.
    > 
    > I would expect that is already true.  A user with CREATEROLE can do almost everything.  This patch closes some CREATEROLE related security problems, but not this one you mention.
    
    Yes, such a role *can* do almost anything, but they can't do this today:
    
    => create role r3;
    CREATE ROLE
    =*> set role r3;
    ERROR:  permission denied to set role "r3"
    
    Nor, should 'r3' log in and create tables, can the creating role SELECT
    from r3's tables or otherwise have any effect on them.  That has
    positives and negatives- we do want the 'owning' role to be able to do
    certain things, like DROP the role, and once a role has created objects
    that isn't able to be done unless those objects are reassigned or
    dropped themselves.  How do we allow explicitly that then?  That's the
    general direction I would think we'd be wanting to go in, rather than
    just blanketly giving the owner all privileges of the roles they create
    without any further say by anyone.
    
    > >  The tenants likely don’t like that idea
    > 
    > +1
    > 
    > > , and almost as likely the landlords in many cases aren’t thrilled with it either.
    > 
    > +1
    
    Glad we agree on those.
    
    > >  Should the landlords be able to DROP the tenant due to the tenant not paying their bill?  Of course, and that should then eliminate the tenant’s tables and other objects which take up resources, but that’s not the same thing as saying that a landlord should be able to unlock a tenant’s old phone that they left behind (and yeah, maybe the analogy falls apart a bit there, but the point I’m trying to get at is that it’s not as simple as it’s being made out to be here and we should think about these things and not just implicitly grant all access to the owner because that’s an easy thing to do- and is exactly what viewing owners as “mini superusers” does and leads to many of the same issues we already have with superusers).
    > 
    > This is a pretty interesting argument.  I don't believe it will work to do as you say unconditionally, as there is still a need to have CREATEROLE users who have privileges on their created roles' objects, even if for no other purpose than to be able to REASSIGN OWNED BY those objects before dropping roles.  But maybe there is also a need to have CREATEROLE users who lack that privilege?  Would that be a privilege bit akin to (but not the same as!) the INHERIT privilege?  Should I redesign for something like that?
    
    We have INHERIT today already for roles and I'm not really thrilled with
    the idea of coming up with some new and independent way to make that
    work, or having something that works effectively the same way as role
    membership does today but is called something else (which is what this
    patch set is doing with ownership, hence my concern).
    
    There's a couple of thoughts I have about addressing things around DROP
    and REASSIGN- one is that those could perhaps just be made to work for
    owners, but another is to allow owners to manage the role memberships of
    roles they own, to include allowing the role to be granted to
    themselves, and maybe that's even the default?  With today's CREATEROLE,
    that looks like:
    
    => create role r3 admin sfrost;
    CREATE ROLE
    =*> set role r3;
    SET
    
    but we could possibly change that to be the default, or maybe we don't,
    since that isn't how it works today.
    
    Either way, we likely would need to allow owners to modify the role
    membership of roles they own, but that doesn't strike me as a terribly
    difficult thing to allow.  A more interesting question is about if a
    role can manage their *own* membership- something we allow today but, as
    I've brought up before, we should probably curtail to some extent.
    
    Ultimately, that makes it possible for this:
    
    SELECT * FROM secret_table;
    
    to fail when secret_table was created by a tenant and the query is run
    by a landlord.  A landlord would still be able to get access to
    secret_table, but they'd have to do:
    
    GRANT tenant TO landlord;
    SELECT * FROM secret_table;
    
    which may not seem like a lot to us, but it shows clear forethought and
    very likely that GRANT would be an audited statement.  If tenant also
    doesn't have 'inherit' set then a SET ROLE might also be required.
    Perhaps additional requirements could be added to the GRANT/SET ROLE to
    make those operations not be trivial to do (certainly we've been asked
    in the past for a way for SET ROLE to require a password, and, indeed,
    some other database systems support that; consider that one day a
    landlord might have to reset the PW for the role, GRANT themselves into
    the role, and then SET ROLE with the reset password...).
    
    I'd also like to share that while we talk about 'landlords' and
    'tenants' here, the real world is more complicated- I'm sure the various
    cloud providers have employees who have different levels of access,
    perhaps some of whom are able to reset passwords for users, while others
    are able to create new accounts, and yet others are able to authorize
    access to customer data, something which hopefully most of the
    organization isn't able to do and requires some additional hoops.
    
    > I like that the current patch restricts CREATEROLE users from granting privileges they themselves lack.  Would such a new privilege bit work the same way?  Imagine that you, "stephen", have CREATEROLE but not this new bit, and you create me, "mark" as a tenant with CREATEROLE.  Can you give me the bit?  Or does the fact that you lack the bit mean you can't give it to me, either?
    > 
    > Other suggestions?
    
    As I mentioned in the patch review, having a particular bit set doesn't
    necessarily mean you should be able to pass it on- the existing object
    GRANT system distinguishes those two and it seems like we should too.
    In other words, I'm saying that we should be able to explicitly say just
    what privileges a CREATEROLE user is able to grant to some other role
    rather than basing it on what that user themselves has.  This might
    already be possible with the proposed patch by creating a role with
    CREATEROLE that then has the privileges we want to be allowed to be
    passed on, and then GRANT'ing that role to the user who we want to allow
    to create roles, though they would then have to SET ROLE to that role to
    run the CREATE ROLE since role attributes aren't inherited by role
    memberships.  That doesn't seem like a terrible approach to solving that
    particular issue, but then perhaps others feel differently.
    
    Thanks,
    
    Stephen
    
  40. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-25T21:29:55Z

    
    > On Jan 25, 2022, at 12:44 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > As I mentioned in the patch review, having a particular bit set doesn't
    > necessarily mean you should be able to pass it on- the existing object
    > GRANT system distinguishes those two and it seems like we should too.
    > In other words, I'm saying that we should be able to explicitly say just
    > what privileges a CREATEROLE user is able to grant to some other role
    > rather than basing it on what that user themselves has.
    
    I like the way you are thinking, but I'm not sure I agree with the facts you are asserting.
    
    I agree that "CREATE ROLE.. ROLE .." differs from "CREATE ROLE .. ADMIN ..", and "GRANT..WITH GRANT OPTION" differs from "GRANT..", but those only cover privileges tracked in an aclitem array.  The privileges CREATEDB, CREATEROLE, REPLICATION, and BYPASSRLS don't work that way.  There isn't a with/without grant option distinction for them.  So I'm forced to say that a role without those privileges must not give them away.
    
    I'd be happier if we could get rid of all privileges of that kind, leaving only those that can be granted with/without grant option, tracked in an aclitem, and use that to determine if the user creating the role can give them away.  But that's a bigger redesign of the system.  Just touching how CREATEROLE works entails backwards compatibility problems.  I'd hate to try to change all these other things; we'd be breaking a lot more, and features that appear more commonly used.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  41. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-25T23:17:47Z

    
    > On Jan 22, 2022, at 1:20 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    >> Subject: [PATCH v4 1/5] Add tests of the CREATEROLE attribute.
    > 
    > No particular issue with this one.
    
    Andrew already committed this, forcing the remaining patches to be renumbered.  Per your comments below, I have combined what was 0002+0003 into 0001, renumbered 0004 as 0002, and abandoned 0005.  (It may come back as an independent patch.)  Also owing to the fact that 0001 has been committed, I really need to post another patch set right away, to make the cfbot happy.  I'm fixing non-controversial deficits you call out in your review, but leaving other things unchanged, in the interest of getting a patch posted sooner rather than later.
    
    >> Subject: [PATCH v4 2/5] Add owners to roles
    >> 
    >> All roles now have owners.  By default, roles belong to the role
    >> that created them, and initdb-time roles are owned by POSTGRES.
    > 
    > ... database superuser, not 'POSTGRES'.
    
    I rephrased this as "bootstrap superuser" in the commit message.
    
    >> +++ b/src/backend/catalog/aclchk.c
    >> @@ -5430,6 +5434,57 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
    >> 	return has_privs_of_role(roleid, ownerId);
    >> }
    >> 
    >> +/*
    >> + * Ownership check for a role (specified by OID)
    >> + */
    >> +bool
    >> +pg_role_ownercheck(Oid role_oid, Oid roleid)
    >> +{
    >> +	HeapTuple		tuple;
    >> +	Form_pg_authid	authform;
    >> +	Oid				owner_oid;
    >> +
    >> +	/* Superusers bypass all permission checking. */
    >> +	if (superuser_arg(roleid))
    >> +		return true;
    >> +
    >> +	/* Otherwise, look up the owner of the role */
    >> +	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(role_oid));
    >> +	if (!HeapTupleIsValid(tuple))
    >> +		ereport(ERROR,
    >> +				(errcode(ERRCODE_UNDEFINED_OBJECT),
    >> +				 errmsg("role with OID %u does not exist",
    >> +						role_oid)));
    >> +	authform = (Form_pg_authid) GETSTRUCT(tuple);
    >> +	owner_oid = authform->rolowner;
    >> +
    >> +	/*
    >> +	 * Roles must necessarily have owners.  Even the bootstrap user has an
    >> +	 * owner.  (It owns itself).  Other roles must form a proper tree.
    >> +	 */
    >> +	if (!OidIsValid(owner_oid))
    >> +		ereport(ERROR,
    >> +				(errcode(ERRCODE_DATA_CORRUPTED),
    >> +				 errmsg("role \"%s\" with OID %u has invalid owner",
    >> +						authform->rolname.data, authform->oid)));
    >> +	if (authform->oid != BOOTSTRAP_SUPERUSERID &&
    >> +		authform->rolowner == authform->oid)
    >> +		ereport(ERROR,
    >> +				(errcode(ERRCODE_DATA_CORRUPTED),
    >> +				 errmsg("role \"%s\" with OID %u owns itself",
    >> +						authform->rolname.data, authform->oid)));
    >> +	if (authform->oid == BOOTSTRAP_SUPERUSERID &&
    >> +		authform->rolowner != BOOTSTRAP_SUPERUSERID)
    >> +		ereport(ERROR,
    >> +				(errcode(ERRCODE_DATA_CORRUPTED),
    >> +				 errmsg("role \"%s\" with OID %u owned by role with OID %u",
    >> +						authform->rolname.data, authform->oid,
    >> +						authform->rolowner)));
    >> +	ReleaseSysCache(tuple);
    >> +
    >> +	return (owner_oid == roleid);
    >> +}
    > 
    > Do we really need all of these checks on every call of this function..?
    
    Since the function is following the ownership chain upwards, it seems necessary to check that the chain is wellformed, else we might get into an infinite loop or return the wrong answer.  These would only happen under corrupt conditions, but it seems sensible to check for those, since they are cheap to check.  (Actually, the check for nontrivial cycles included in the patch is not as efficient as it could be, but I'm punting the work of improving that algorithm from quadratic to linear until a later patch version, in the interest of posting the patch soon.) 
    
    > Also, there isn't much point in including the role OID twice in the last
    > error message, is there?  Unless things have gotten quite odd, it's
    > goint to be the same value both times as we just proved to ourselves
    > that it is, in fact, the same value (and that it's not the
    > BOOTSTRAP_SUPERUSERID).
    
    It is comparing the authform->oid against the authform->rolowner, which are not the same.  The first is the owned role, the second is the owning role.  We could hardcode the message to say something like "bootstrap superuser owned by role with Oid %u", but that hardcodes "bootstrap superuser" into the message, rather than something like "stephen".  I don't feel strongly about the wording.  Let me know if you still want me to change it.
    
    > This function also doesn't actually do any kind of checking to see if
    > the role ownership forms a proper tree, so it seems a bit odd to have
    > the comment talking about that here where it's doing other checks.
    
    Right.  The comment simply explains the structure we expect, not the structure we are fully validating.  The point is that each link in the hierarchy must be compatible with the expected structure.  It would be overkill to validate the whole tree in this one function.  I don't mind rewording the code comment, if you have a less confusing suggestion.
    
    >> +++ b/src/backend/commands/user.c
    >> @@ -77,6 +79,9 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >> 	Datum		new_record[Natts_pg_authid];
    >> 	bool		new_record_nulls[Natts_pg_authid];
    >> 	Oid			roleid;
    >> +	Oid			owner_uid;
    >> +	Oid			saved_uid;
    >> +	int			save_sec_context;
    > 
    > Seems a bit odd to introduce 'uid' into this file, which hasn't got any
    > such anywhere in it, and I'm not entirely sure that any of these are
    > actually needed..?
    
    
    Good catch!  The implementation in v6 was wrong.  It didn't enforce that the creating role was a member of the target owner, something this next patch set does.
    
    >> @@ -108,6 +113,16 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >> 	DefElem    *dvalidUntil = NULL;
    >> 	DefElem    *dbypassRLS = NULL;
    >> 
    >> +	GetUserIdAndSecContext(&saved_uid, &save_sec_context);
    >> +
    >> +	/*
    >> +	 * Who is supposed to own the new role?
    >> +	 */
    >> +	if (stmt->authrole)
    >> +		owner_uid = get_rolespec_oid(stmt->authrole, false);
    >> +	else
    >> +		owner_uid = saved_uid;
    >> +
    >> 	/* The defaults can vary depending on the original statement type */
    >> 	switch (stmt->stmt_type)
    >> 	{
    >> @@ -254,6 +269,10 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >> 			ereport(ERROR,
    >> 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >> 					 errmsg("must be superuser to create superusers")));
    >> +		if (!superuser_arg(owner_uid))
    >> +			ereport(ERROR,
    >> +					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >> +					 errmsg("must be superuser to own superusers")));
    >> 	}
    >> 	else if (isreplication)
    >> 	{
    > 
    > So, we're telling a superuser (which is the only way you could get to
    > this point...) that they aren't allowed to create a superuser role which
    > is owned by a non-superuser...  Why?
    
    The reason is one you won't like very much.  Given that roles have the privileges of roles they own (which you don't like), allowing a non-superuser to own a superuser effectively promotes that owner to superuser status.  That's a pretty obscure way of making someone a superuser, probably not what was intended, and quite a high-caliber foot-gun.
    
    Even if roles didn't inherit privileges from roles they own, I think it would be odd for a non-superuser to own a superuser.  The definition of "ownership" would have to be extremely restricted to prevent the owner from using their ownership to obtain superuser.
    
    >> @@ -310,6 +329,19 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >> 				 errmsg("role \"%s\" already exists",
    >> 						stmt->role)));
    >> 
    >> +	/*
    >> +	 * If the requested authorization is different from the current user,
    >> +	 * temporarily set the current user so that the object(s) will be created
    >> +	 * with the correct ownership.
    >> +	 *
    >> +	 * (The setting will be restored at the end of this routine, or in case of
    >> +	 * error, transaction abort will clean things up.)
    >> +	 */
    >> +	if (saved_uid != owner_uid)
    >> +		SetUserIdAndSecContext(owner_uid,
    >> +							   save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
    > 
    > Err, why is this needed?  This looks copied from the CreateSchemaCommand
    > but, unlike with the create schema command, CreateRole doesn't actually
    > allow sub-commands to be run to create other objects in the way that
    > CreateSchemaCommand does.
    
    Not quite.  There are still the check_password_hook and RunObjectPostCreateHook() to consider.  The check_password_hook might want to validate the validuntil_time parameter against the owner's validuntil time, or some other property of the owner.  And the RunObjectPostCreateHook (called via InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0)) may want the information, too.
    
    I'm not saying these are super strong arguments.  If people generally feel that CREATE ROLE ... AUTHORIZATION shouldn't call SetUserIdAndSecContext, feel free to argue that.
    
    >> @@ -1675,3 +1714,110 @@ DelRoleMems(const char *rolename, Oid roleid,
    >> +static void
    >> +AlterRoleOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
    >> +{
    >> +	Form_pg_authid authForm;
    >> +
    >> +	Assert(tup->t_tableOid == AuthIdRelationId);
    >> +	Assert(RelationGetRelid(rel) == AuthIdRelationId);
    >> +
    >> +	authForm = (Form_pg_authid) GETSTRUCT(tup);
    >> +
    >> +	/*
    >> +	 * If the new owner is the same as the existing owner, consider the
    >> +	 * command to have succeeded.  This is for dump restoration purposes.
    >> +	 */
    >> +	if (authForm->rolowner != newOwnerId)
    >> +	{
    >> +		/* Otherwise, must be owner of the existing object */
    >> +		if (!pg_role_ownercheck(authForm->oid, GetUserId()))
    >> +			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_ROLE,
    >> +						   NameStr(authForm->rolname));
    >> +
    >> +		/* Must be able to become new owner */
    >> +		check_is_member_of_role(GetUserId(), newOwnerId);
    > 
    > Feels like we should be saying a bit more about why we check for role
    > membership vs. has_privs_of_role() here.  I'm generally of the opinion
    > that membership is the right thing to check here, just feel like we
    > should try to explain more why that's the right thing.
    
    For orthogonality with how ALTER .. OWNER TO works for everything else?  AlterEventTriggerOwner_internal doesn't check this explicitly, but that's because it has already checked that the new owner is superuser, so the check must necessarily succeed.  I'm not aware of any ALTER .. OWNER TO commands that don't require this, at least implicitly.
    
    We could explain this in AlterRoleOwner_internal, as you suggest, but if we need it there, do we need to put the same explanation in functions which handle other object types?  I don't see why this one function would require the explanation if other equivalent functions do not.
    
    >> +		/*
    >> +		 * must have CREATEROLE rights
    >> +		 *
    >> +		 * NOTE: This is different from most other alter-owner checks in that
    >> +		 * the current user is checked for create privileges instead of the
    >> +		 * destination owner.  This is consistent with the CREATE case for
    >> +		 * roles.  Because superusers will always have this right, we need no
    >> +		 * special case for them.
    >> +		 */
    >> +		if (!have_createrole_privilege())
    >> +			aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_ROLE,
    >> +						   NameStr(authForm->rolname));
    >> +
    > 
    > I would think we'd be trying to get away from the role attribute stuff.
    
    That's not a bad idea, but I thought it was discussed months ago.  The two options were (1) keep using CREATEROLE but change it to be less powerful, and (2) add a new built-in role, say "pg_create_role", and have membership in that role be what we use.  Option (2) was generally viewed less favorably, or that was my sense of people's opinions, on the theory that we'd be better off fixing how CREATEROLE works than having two different ways of doing roughly the same thing.
    
    >> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
    > 
    >> +			CREATE ROLE RoleId AUTHORIZATION RoleSpec opt_with OptRoleList
    >> +				{
    >> +					CreateRoleStmt *n = makeNode(CreateRoleStmt);
    >> +					n->stmt_type = ROLESTMT_ROLE;
    >> +					n->role = $3;
    >> +					n->authrole = $5;
    >> +					n->options = $7;
    >> +					$$ = (Node *)n;
    >> +				}
    >> 		;
    > 
    > ...
    > 
    >> @@ -1218,6 +1229,10 @@ CreateOptRoleElem:
    >> 				{
    >> 					$$ = makeDefElem("addroleto", (Node *)$3, @1);
    >> 				}
    >> +			| OWNER RoleSpec
    >> +				{
    >> +					$$ = makeDefElem("owner", (Node *)$2, @1);
    >> +				}
    >> 		;
    > 
    > Not sure why we'd have both AUTHORIZATION and OWNER for CREATE ROLE..?
    > We don't do that for other objects.
    
    Good catch!  The "OWNER RoleSpec" here was unused.  I have removed it from the new patch set.
    
    >> diff --git a/src/test/regress/sql/create_role.sql b/src/test/regress/sql/create_role.sql
    > 
    >> @@ -1,6 +1,7 @@
    >> -- ok, superuser can create users with any set of privileges
    >> CREATE ROLE regress_role_super SUPERUSER;
    >> CREATE ROLE regress_role_1 CREATEDB CREATEROLE REPLICATION BYPASSRLS;
    >> +GRANT CREATE ON DATABASE regression TO regress_role_1;
    > 
    > Seems odd to add this as part of this patch, or am I missing something?
    
    It's not used much in patch 0001 where it gets introduced, but gets used more in patch 0002.  I put it here to reduce the number of diffs the next patch creates.
    
    >> From 1784a5b51d4dbebf99798b5832d92b0f585feb08 Mon Sep 17 00:00:00 2001
    >> From: Mark Dilger <mark.dilger@enterprisedb.com>
    >> Date: Tue, 4 Jan 2022 11:42:27 -0800
    >> Subject: [PATCH v4 3/5] Give role owners control over owned roles
    >> 
    >> Create a role ownership hierarchy.  The previous commit added owners
    >> to roles.  This goes further, making role ownership transitive.  If
    >> role A owns role B, and role B owns role C, then role A can act as
    >> the owner of role C.  Also, roles A and B can perform any action on
    >> objects belonging to role C that role C could itself perform.
    >> 
    >> This is a preparatory patch for changing how CREATEROLE works.
    > 
    > This feels odd to have be an independent commit.
    
    Reworked the v6-0002 and v6-0003 patches into just one, as discussed at the top of this email.
    
    >> diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
    > 
    >> @@ -363,7 +363,7 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
    >> 		/*
    >> 		 * must have create-schema rights
    >> 		 *
    >> -		 * NOTE: This is different from other alter-owner checks in that the
    >> +		 * NOTE: This is different from most other alter-owner checks in that the
    >> 		 * current user is checked for create privileges instead of the
    >> 		 * destination owner.  This is consistent with the CREATE case for
    >> 		 * schemas.  Because superusers will always have this right, we need
    > 
    > Not a fan of just dropping 'most' in here, doesn't really help someone
    > understand what is being talked about.  I'd suggest adjusting the
    > comment to talk about alter-owner checks for objects which exist in
    > schemas, as that's really what is being referred to.
    
    Yeah, that's a better approach.  This next patch set changes the comment in both AlterSchemaOwner_internal and AlterRoleOwner_internal to make that clear.
    
    ...<snip>...
    
    > Whoah, really?  No, I don't agree with this, it's throwing away the
    > entire concept around inheritance of role rights and how you can have
    > roles which you can get the privileges of by doing a SET ROLE to them
    > but you don't automatically have those rights.
    
    I didn't change any of this for the next patch set, not because I'm ignoring you, but because we're still arguing out what the right behavior should be.  Whatever we come up with, I think it should allow the use case that Robert has been talking about.  Doing that and also doing what you are talking about might be hard, but I'm still hoping to find some solution.
    
    Recall that upthread, months ago, we discussed that it is abnormal for any role to be a member of a login role.  You can think of "login role" as a synonym for "user", and "non-login role" as a synonym for "group", and that language makes it easier to think about how weird it is for users to be members of other users.
    
    It's perfectly sensible to have users own users, but not for users to be members of users.  If not for that, I'd be in favor of what you suggest, excepting that I'd accommodate Robert's requirements by having the owner of a role have ADMIN on that role by default, with grammar for requesting the alternative.  Maybe there is something I'm forgetting to consider just now, but I'd think that would handle Robert's "tenant" type argument while also making it easy to operate the way that you want.  But, again, it does require having users be members of users, something which was rejected in the discussion months ago.
    
    >> +/*
    >> + * Is owner a direct or indirect owner of the role, not considering
    >> + * superuserness?
    >> + */
    >> +bool
    >> +is_owner_of_role_nosuper(Oid owner, Oid role)
    >> +{
    >> +	return list_member_oid(roles_is_owned_by(role), owner);
    >> +}
    > 
    > 
    > Surely if you're a member of a role which owns another role, you should
    > be considered to be an owner of that role too..?  Just checking if the
    > current role is a member of the roles which directly own the specified
    > role misses that case.
    > 
    > That is:
    > 
    > CREATE ROLE r1;
    > CREATE ROLE r2;
    > 
    > GRANT r2 to r1;
    > 
    > CREATE ROLE r3 AUTHORIZATION r2;
    > 
    > Surely, r1 is to be considered an owner of r3 in this case, but the
    > above check wouldn't consider that to be the case- it would only return
    > true if the current role is r2.
    > 
    > We do need some kind of direct membership check in the list of owners to
    > avoid creating loops, so maybe this function is kept as that and the
    > pg_role_ownership() check is changed to address the above case, but I
    > don't think we should just ignore role membership when it comes to role
    > ownership- we don't do that for any other kind of ownership check.
    
    I like this line of reasoning, and it appears to be an argument in your favor where the larger question is concerned.  If role ownership is transitive, and role membership is transitive, it gets weird trying to work out larger relationship chains.
    
    This deserves more attention.
    
    >> Subject: [PATCH v4 4/5] Restrict power granted via CREATEROLE.
    > 
    > I would think this would be done independently of the other patches and
    > probably be first.
    
    The way I'm trying to fix CREATEROLE is first by introducing the concept of role owners, then second by restricting what roles can do based on whether they own a target role.  I don't see how I can reverse the order.
    
    >> diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml
    > 
    >> @@ -70,18 +70,18 @@ ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | A
    >>    <link linkend="sql-revoke"><command>REVOKE</command></link> for that.)
    >>    Attributes not mentioned in the command retain their previous settings.
    >>    Database superusers can change any of these settings for any role.
    >> -   Roles having <literal>CREATEROLE</literal> privilege can change any of these
    >> -   settings except <literal>SUPERUSER</literal>, <literal>REPLICATION</literal>,
    >> -   and <literal>BYPASSRLS</literal>; but only for non-superuser and
    >> -   non-replication roles.
    >> -   Ordinary roles can only change their own password.
    >> +   Role owners can change any of these settings on roles they own except
    >> +   <literal>SUPERUSER</literal>, <literal>REPLICATION</literal>, and
    >> +   <literal>BYPASSRLS</literal>; but only for non-superuser and non-replication
    >> +   roles, and only if the role owner does not alter the target role to have a
    >> +   privilege which the role owner itself lacks.  Ordinary roles can only change
    >> +   their own password.
    >>   </para>
    > 
    > Having contemplated this a bit more, I don't like it, and it's not how
    > things work when it comes to regular privileges.
    > 
    > Consider that I can currently GRANT someone UPDATE privileges on an
    > object, but they can't GRANT that privilege to someone else unless I
    > explicitly allow it.  The same could certainly be said for roles-
    > perhaps I want to allow someone the privilege to create non-login roles,
    > but I don't want them to be able to create new login roles, even if they
    > themselves have LOGIN.
    
    This comment conflates privileges like LOGIN for which there isn't any "with grant option" logic with privileges that do.  Granting someone UPDATE privileges on a relation will be tracked in an aclitem including whether the "with grant option" bit is set.  Nothing like that will exist for LOGIN.  I'm not dead-set against having that functionality for the privileges that currently lack it, but we'd have to do so in a way that doesn't gratuitously break backward compatibility, and how to do so has not been discussed.
    
    > As another point, I might want to have an 'admin' role that I want
    > admins to SET ROLE to before they go creating other roles, because I
    > don't want them to be creating roles as their regular user and so that
    > those other roles are owned by the 'admin' role, but I don't want that
    > role to have the 'login' attribute.
    
    Same problem.  We don't have aclitem bits for this.
    
    > In other words, we should really consider what role attributes a given
    > role has to be independent of what role attributes that role is allowed
    > to set on roles they create.  I appreciate that "just whatever the
    > current role has" is simpler and less work but also will be difficult to
    > walk back from once it's in the wild.
    
    I don't feel there is any fundamental disagreement here, except perhaps whether it needs to be done as part of this patch, vs. implemented in a future development cycle.  We don't currently have any syntax for "CREATE ROLE bob LOGIN WITH GRANT OPTION".  I can see some advantages in doing it all in one go, but also some advantage in being incremental.  More discussion is needed here.
    
    >> @@ -1457,7 +1449,7 @@ AddRoleMems(const char *rolename, Oid roleid,
    > 
    >> 	/*
    >> -	 * Check permissions: must have createrole or admin option on the role to
    >> +	 * Check permissions: must be owner or have admin option on the role to
    >> 	 * be changed.  To mess with a superuser role, you gotta be superuser.
    >> 	 */
    >> 	if (superuser_arg(roleid))
    > 
    > ...
    > 
    >> @@ -1467,9 +1459,9 @@ AddRoleMems(const char *rolename, Oid roleid,
    >> 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >> 					 errmsg("must be superuser to alter superusers")));
    >> 	}
    >> -	else
    >> +	else if (!superuser())
    >> 	{
    >> -		if (!have_createrole_privilege() &&
    >> +		if (!pg_role_ownercheck(roleid, grantorId) &&
    >> 			!is_admin_of_role(grantorId, roleid))
    >> 			ereport(ERROR,
    >> 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > 
    > I'm not entirely sure about including owners here though I'm not
    > completely against it either.  This conflation of what the 'admin'
    > privileges on a role means vs. the 'ownership' of a role is part of what
    > I dislike about having two distinct systems for saying who is allowed to
    > GRANT one role to another.
    > 
    > Also, if we're going to always consider owners to be admins of roles
    > they own, why not push that into is_admin_of_role()?
    
    Unchanged in this patch set, but worth further discussion and evaluation.
    
    >> Subject: [PATCH v4 5/5] Remove grantor field from pg_auth_members
    
    ...<snip>...
    
    > If we're going to do this, it should also be done independently of the
    > role ownership stuff too.
    
    I've withdrawn 0005 from this patch set, and we can come back to it separately.
    
    > Thanks,
    > 
    > Stephen
    
    Thanks for the review!  I hope we can keep pushing this forward.  Again, no offense is intended in having not addressed all your concerns in the v7 patch set:
    
    
  42. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-30T05:58:38Z

    
    > On Jan 25, 2022, at 12:44 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > I agree that CREATEROLE is overpowered and that the goal of this should
    > be to provide a way for roles to be created and dropped that doesn't
    > give the user who has that power everything that CREATEROLE currently
    > does.
    
    I'm attaching a patch that attempts to fix CREATEROLE without any connection to role ownership.
    
    >  The point I was making is that the concept of role ownership
    > isn't intrinsically linked to that and is, therefore, as you say, gravy.
    
    I agree, they aren't intrinsically linked, though the solution to one might interact in some ways with the solution to the other.
    
    > That isn't to say that I'm entirely against the role ownership idea but
    > I'd want it to be focused on the goal of providing ways of creating and
    > dropping users and otherwise performing that kind of administration and
    > that doesn't require the specific change to make owners be members of
    > all roles they own and automatically have all privileges of those roles
    > all the time.
    
    The attached WIP patch attempts to solve most of the CREATEROLE problems but not the problem of which role who can drop which other role.  That will likely require an ownership concept.
    
    The main idea here is that having CREATEROLE doesn't give you ADMIN on roles, nor on role attributes.  For role attributes, the syntax has been extended.  An excerpt from the patch's regression test illustrates some of that concept:
    
    -- ok, superuser can create a role that can create login replication users, but
    -- cannot itself login, nor perform replication
    CREATE ROLE regress_role_repladmin
        CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
        NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
        NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
        NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
        NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    
    -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    CREATE ROLE regress_role_minoradmin
        NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
        CREATEROLE WITHOUT ADMIN OPTION
        NOCREATEDB WITHOUT ADMIN OPTION
        NOLOGIN WITHOUT ADMIN OPTION
        NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
        NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
        NOINHERIT WITHOUT ADMIN OPTION
        CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
        VALID ALWAYS WITHOUT ADMIN OPTION
        PASSWORD NULL WITHOUT ADMIN OPTION;
    
    -- fail, having CREATEROLE is not enough to create roles in privileged roles
    SET SESSION AUTHORIZATION regress_role_minoradmin;
    CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    ERROR:  must have admin option on role "pg_read_all_data"
    
    -- fail, cannot change attributes without ADMIN for them
    SET SESSION AUTHORIZATION regress_role_minoradmin;
    ALTER ROLE regress_role_login LOGIN;
    ERROR:  must have admin on login to change login attribute
    ALTER ROLE regress_role_login NOLOGIN;
    ERROR:  must have admin on login to change login attribute
    
    
    Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied hinges on whether the role is given CREATEROLE.  That hackery is necessary to preserve backwards compatibility.  If we don't care about compatibility, I could change the patch to make "WITHOUT ADMIN OPTION" implied for all attributes when not specified.
    
    I'd appreciate feedback on the direction this patch is going.
    
    
    
  43. Re: CREATEROLE and role ownership hierarchies

    Michael Banck <michael.banck@credativ.de> — 2022-01-30T22:38:10Z

    Hi,
    
    On Sat, Jan 29, 2022 at 09:58:38PM -0800, Mark Dilger wrote:
    > > On Jan 25, 2022, at 12:44 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > I agree that CREATEROLE is overpowered and that the goal of this should
    > > be to provide a way for roles to be created and dropped that doesn't
    > > give the user who has that power everything that CREATEROLE currently
    > > does.
    > 
    > I'm attaching a patch that attempts to fix CREATEROLE without any
    > connection to role ownership.
    
    Sounds like a useful way forward.
     
    > >  The point I was making is that the concept of role ownership
    > > isn't intrinsically linked to that and is, therefore, as you say, gravy.
    > 
    > I agree, they aren't intrinsically linked, though the solution to one
    > might interact in some ways with the solution to the other.
    > 
    > > That isn't to say that I'm entirely against the role ownership idea but
    > > I'd want it to be focused on the goal of providing ways of creating and
    > > dropping users and otherwise performing that kind of administration and
    > > that doesn't require the specific change to make owners be members of
    > > all roles they own and automatically have all privileges of those roles
    > > all the time.
    > 
    > The attached WIP patch attempts to solve most of the CREATEROLE
    > problems but not the problem of which role who can drop which other
    > role.  That will likely require an ownership concept.
    > 
    > The main idea here is that having CREATEROLE doesn't give you ADMIN on
    > roles, nor on role attributes.  For role attributes, the syntax has
    > been extended.  An excerpt from the patch's regression test
    > illustrates some of that concept:
    > 
    > -- ok, superuser can create a role that can create login replication users, but
    > -- cannot itself login, nor perform replication
    > CREATE ROLE regress_role_repladmin
    >     CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
    >     NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
    >     NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
    >     NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
    >     NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    > 
    > -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    > CREATE ROLE regress_role_minoradmin
    >     NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
    >     CREATEROLE WITHOUT ADMIN OPTION
    >     NOCREATEDB WITHOUT ADMIN OPTION
    >     NOLOGIN WITHOUT ADMIN OPTION
    >     NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
    >     NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
    >     NOINHERIT WITHOUT ADMIN OPTION
    >     CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
    >     VALID ALWAYS WITHOUT ADMIN OPTION
    >     PASSWORD NULL WITHOUT ADMIN OPTION;
    > 
    > -- fail, having CREATEROLE is not enough to create roles in privileged roles
    > SET SESSION AUTHORIZATION regress_role_minoradmin;
    > CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    > ERROR:  must have admin option on role "pg_read_all_data"
    
    Great.
     
    > -- fail, cannot change attributes without ADMIN for them
    > SET SESSION AUTHORIZATION regress_role_minoradmin;
    > ALTER ROLE regress_role_login LOGIN;
    > ERROR:  must have admin on login to change login attribute
    >
    > ALTER ROLE regress_role_login NOLOGIN;
    > ERROR:  must have admin on login to change login attribute
    > 
    > Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied
    > hinges on whether the role is given CREATEROLE.  That hackery is
    > necessary to preserve backwards compatibility.  If we don't care about
    > compatibility, I could change the patch to make "WITHOUT ADMIN OPTION"
    > implied for all attributes when not specified.
    > 
    > I'd appreciate feedback on the direction this patch is going.
     
    One thing I noticed (and which will likely make DBAs grumpy) is that it
    seems being able to create users (as opposed to non-login roles/groups)
    depends on when you get the CREATEROLE attribute (on role creation or
    later), viz:
    
    postgres=# CREATE USER admin CREATEROLE;
    CREATE ROLE
    postgres=# SET ROLE admin;
    SET
    postgres=> CREATE USER testuser; -- this works
    CREATE ROLE
    postgres=> RESET ROLE;
    RESET
    postgres=# CREATE USER admin2;
    CREATE ROLE
    postgres=# ALTER ROLE admin2 CREATEROLE; -- we get CREATEROLE after the fact
    ALTER ROLE
    postgres=# SET ROLE admin2;
    SET
    postgres=> CREATE USER testuser2; -- bam
    ERROR:  must have grant option on LOGIN privilege to create login users
    postgres=# SELECT rolname, admcreaterole, admcanlogin FROM pg_authid
    WHERE rolname LIKE 'admin%';
     rolname | admcreaterole | admcanlogin 
    ---------+---------------+-------------
     admin   | t             | t
     admin2  | f             | f
    (2 rows)
    
    Is that intentional? If it is, I think it would be nice if this could be
    changed, unless I'm missing some serious security concerns or so. 
    
    Some light review of the patch (I haven't read all the previous ones, so
    please excuse me if I rehash old discussions):
    
    > From 82d235b39b32ca0cd0b94d47a54ee6806645a365 Mon Sep 17 00:00:00 2001
    > From: Mark Dilger <mark.dilger@enterprisedb.com>
    > Date: Fri, 28 Jan 2022 07:57:57 -0800
    > Subject: [PATCH v8] Adding admin options for role attributes
    > 
    > When creating roles, attributes such as BYPASSRLS can be optionally
    > specified WITH ADMIN OPTION or WITHOUT ADMIN OPTION.  If these
    > optional clauses are unspecified, they all default to WITHOUT
    > unless the role being created is given CREATEROLE, in which case
    > they default to WITHOUT for SUPERUSER, REPLICATION, and BYPASSRLS
    > and true for all others.  This preserves backwards compatible
    > behavior.
    > 
    > The CREATEROLE attribute no longer makes up for lacking the ADMIN
    > option on a role.  The creator of a role only has the ADMIN-like
    > right to grant other roles into the new role during the creation
    > statement itself.  After that, the creator may only do so if the
    > creator has ADMIN on the role.  Note that creators may add
    > themselves to the list of ADMINs on the new role during creation
    > time.
    > 
    > SUPERUSER can still only be granted by superusers.
    > ---
    >  doc/src/sgml/ref/create_role.sgml         |  50 ++--
    >  src/backend/catalog/aclchk.c              | 179 ++++++++++++--
    >  src/backend/commands/user.c               | 278 +++++++++++++++++-----
    >  src/backend/parser/gram.y                 | 161 ++++++++++---
    >  src/include/catalog/pg_authid.dat         |  52 +++-
    >  src/include/catalog/pg_authid.h           |  10 +
    >  src/include/nodes/nodes.h                 |   1 +
    >  src/include/nodes/parsenodes.h            |  11 +-
    >  src/include/utils/acl.h                   |  12 +
    >  src/test/regress/expected/create_role.out | 188 ++++++++++++++-
    >  src/test/regress/expected/privileges.out  |   4 +
    >  src/test/regress/sql/create_role.sql      | 153 +++++++++++-
    >  src/test/regress/sql/privileges.sql       |   3 +
    >  src/tools/pgindent/typedefs.list          |   1 +
    >  14 files changed, 936 insertions(+), 167 deletions(-)
    > 
    > diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
    > index b6a4ea1f72..7163779e0a 100644
    > --- a/doc/src/sgml/ref/create_role.sgml
    > +++ b/doc/src/sgml/ref/create_role.sgml
    > @@ -26,15 +26,22 @@ CREATE ROLE <replaceable class="parameter">name</replaceable> [ [ WITH ] <replac
    >  <phrase>where <replaceable class="parameter">option</replaceable> can be:</phrase>
    >  
    >        SUPERUSER | NOSUPERUSER
    > -    | CREATEDB | NOCREATEDB
    > -    | CREATEROLE | NOCREATEROLE
    > -    | INHERIT | NOINHERIT
    > -    | LOGIN | NOLOGIN
    > -    | REPLICATION | NOREPLICATION
    > -    | BYPASSRLS | NOBYPASSRLS
    > -    | CONNECTION LIMIT <replaceable class="parameter">connlimit</replaceable>
    > -    | [ ENCRYPTED ] PASSWORD '<replaceable class="parameter">password</replaceable>' | PASSWORD NULL
    > -    | VALID UNTIL '<replaceable class="parameter">timestamp</replaceable>'
    > +    | INHERIT [ { WITH | WITHOUT } GRANT OPTION ]
    > +	| NOINHERIT [ { WITH | WITHOUT } GRANT OPTION ]
    
    Spaces vs. tabs here...
    
    > +    | CREATEDB [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | NOCREATEDB [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | CREATEROLE [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | NOCREATEROLE [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | LOGIN [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | NOLOGIN [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | REPLICATION [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | NOREPLICATION [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | BYPASSRLS [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | NOBYPASSRLS [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | CONNECTION LIMIT [ <replaceable class="parameter">connlimit</replaceable> | NONE ] [ { WITH | WITHOUT } GRANT OPTION ]
    > +    | [ ENCRYPTED ] PASSWORD '<replaceable class="parameter">password</replaceable>' [ { WITH | WITHOUT } GRANT OPTION ]
    > +	| PASSWORD NULL [ { WITH | WITHOUT } GRANT OPTION ]
    
    ... and here, is that intentional?
    
    > @@ -356,6 +363,18 @@ in sync when changing the above synopsis!
    >     <link linkend="sql-revoke"><command>REVOKE</command></link>.
    >    </para>
    >  
    > +  <para>
    > +   Some parameters allow the <literal>WITH ADMIN OPTION</literal> or
    > +   <literal>WITHOUT ADMIN OPTION</literal> clause to be specified.  For roles
    > +   with the <literal>CREATEROLE</literal> attribute, these clauses govern
    > +   whether new roles may be created with the attribute.  If not given, for
    > +   reasons of backwards compatibility, <literal>WITHOUT ADMIN OPTION</literal>
    > +   is the default for <literal>REPLICATION</literal> and
    > +   <literal>BYPASSRLS</literal>, but <literal>WITH ADMIN OPTION</literal> is
    > +   the default for <literal>CREATEDB</literal>, <literal>CREATEROLE</literal>,
    > +   and <literal>LOGIN</literal>.
    > +  </para>
    > +
    >    <para>
    >     The <literal>VALID UNTIL</literal> clause defines an expiration time for a
    >     password only, not for the role per se.  In
    > diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
    > index 1dd03a8e51..c66f545f36 100644
    > --- a/src/backend/catalog/aclchk.c
    > +++ b/src/backend/catalog/aclchk.c
    > @@ -5430,6 +5430,91 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
    >  	return has_privs_of_role(roleid, ownerId);
    >  }
    >  
    > +typedef enum ROLPRIV
    
    I think typdefs usually go at the top of the file, not at line 5441...
    
    > +{
    > +	CREATEROLE,
    > +	CREATEDB,
    > +	CANLOGIN,
    > +	REPLICATION,
    > +	BYPASSRLS,
    > +	INHERIT,
    > +	CONNLIMIT,
    > +	VALIDUNTIL,
    > +	PASSWORD
    > +} ROLPRIV;
    > +
    
    [...]
    
    >  /*
    >   * Check whether specified role has CREATEROLE privilege (or is a superuser)
    >   *
    
    I feel this function comment needs revision; we now have a dozen similar
    functions that all do the same, but only the first one
    (has_createrole_privilege) is being explained.
    
    I guess the comment overall is still applicable, so as a minimum maybe
    just change the CREATEROLE above for a generic "has some privilege", and
    add a space in order to make it clear this applies to all of the
    following functions.
    
    Hrm, maybe also mention why there may_admin_*_privilege for all
    privileges, but has_*_privilege only for some.
    
    > diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
    > index f9d3c1246b..501613a840 100644
    > --- a/src/backend/commands/user.c
    > +++ b/src/backend/commands/user.c
    > @@ -255,27 +305,36 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("must be superuser to create superusers")));
    >  	}
    > -	else if (isreplication)
    > -	{
    > -		if (!superuser())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("must be superuser to create replication users")));
    > -	}
    > -	else if (bypassrls)
    > -	{
    > -		if (!superuser())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("must be superuser to create bypassrls users")));
    > -	}
    > -	else
    > -	{
    > -		if (!have_createrole_privilege())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("permission denied to create role")));
    > -	}
    > +
    > +	if (createrole && !may_admin_createrole_privilege(GetUserId()))
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > +				 errmsg("must have grant option on CREATEROLE privilege to create createrole users")));
    
    Shouldn't this (and the following) be "must have admin option on
    CREATEROLE"?
    
    > @@ -311,7 +370,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >  						stmt->role)));
    >  
    >  	/* Convert validuntil to internal form */
    > -	if (validUntil)
    > +	if (validUntil && strcmp(validUntil, "always") != 0)
    
    This (there are other similar hunks further down) looks like an
    independent patch/feature?
    
    > @@ -637,32 +727,57 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
    >  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >  					 errmsg("must be superuser to alter superuser roles or change superuser attribute")));
    >  	}
    > -	else if (authform->rolreplication || disreplication)
    > -	{
    > -		if (!superuser())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("must be superuser to alter replication roles or change replication attribute")));
    > -	}
    > -	else if (dbypassRLS)
    > -	{
    > -		if (!superuser())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("must be superuser to change bypassrls attribute")));
    > -	}
    > -	else if (!have_createrole_privilege())
    > -	{
    > -		/* check the rest */
    > -		if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
    > -			drolemembers || dvalidUntil || !dpassword || roleid != GetUserId())
    > -			ereport(ERROR,
    > -					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > -					 errmsg("permission denied")));
    > -	}
    > +
    > +	/* To mess with replication roles, must have admin on REPLICATION */
    > +	if ((authform->rolreplication || disreplication) &&
    > +		!may_admin_replication_privilege(GetUserId()))
    > +		ereport(ERROR,
    > +				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > +				 errmsg("must have admin on replication to alter replication roles or change replication attribute")));
    
    "have admin" sounds a bit weird, but I understand the error message is
    too long already to spell out "must have admin option"? Or am I mistaken
    and "admin" is what it's actually called (same for the ones below)?
    
    Also, I think those role options are usually capitalized like
    REPLICATION in other error messages.
    
    > diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
    > index b5966712ce..7503d3ead6 100644
    > --- a/src/backend/parser/gram.y
    > +++ b/src/backend/parser/gram.y
    > @@ -1131,67 +1140,111 @@ AlterOptRoleElem:
    [...]
    
    > +			| VALID ALWAYS opt_admin_spec
    > +				{
    > +					RoleElem *n = makeNode(RoleElem);
    > +					n->elem = makeDefElem("validUntil", (Node *)makeString("always"), @1);
    > +					n->admin_spec = $3;
    > +					$$ = (Node *)n;
    
    This one is from another patch as well I think.
    
    >  				}
    >  		/*	Supported but not documented for roles, for use by ALTER GROUP. */
    > -			| USER role_list
    > +			| USER role_list opt_admin_spec
    >  				{
    > -					$$ = makeDefElem("rolemembers", (Node *)$2, @1);
    > +					RoleElem *n = makeNode(RoleElem);
    > +					n->elem = makeDefElem("rolemembers", (Node *)$2, @1);
    > +					n->admin_spec = $3;
    > +					$$ = (Node *)n;
    >  				}
    > -			| IDENT
    > +			| IDENT opt_admin_spec
    >  				{
    >  					/*
    >  					 * We handle identifiers that aren't parser keywords with
    >  					 * the following special-case codes, to avoid bloating the
    >  					 * size of the main parser.
    >  					 */
    > +					RoleElem *n = makeNode(RoleElem);
    > +
    > +					/*
    > +					 * Record whether the user specified WITH GRANT OPTION.
    
    WITH ADMIN OPTION rather?
    
    > +					 * Note that for some privileges this is always implied,
    > +					 * such as SUPERUSER, but we don't reflect that here.
    > +					 */
    > +					n->admin_spec = $2;
    > +
    
    > diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
    > index 6c28119fa1..4829a6dbd2 100644
    > --- a/src/include/catalog/pg_authid.dat
    > +++ b/src/include/catalog/pg_authid.dat
    > @@ -22,67 +22,93 @@
    >  { oid => '10', oid_symbol => 'BOOTSTRAP_SUPERUSERID',
    >    rolname => 'POSTGRES', rolsuper => 't', rolinherit => 't',
    >    rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't',
    > -  rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1',
    > +  rolreplication => 't', rolbypassrls => 't', adminherit => 't', admcreaterole => 't',
    > +  admcreatedb => 't', admcanlogin => 't', admreplication => 't', admbypassrls => 't',
    > +  admconnlimit => 't', admpassword => 't', admvaliduntil => 't', rolconnlimit => '-1',
    >    rolpassword => '_null_', rolvaliduntil => '_null_' },
    
    Those sure are a couple of new columns in pg_authid, but oh well...
    
    > diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
    > index 4b65e39a1f..4acdcaa685 100644
    > --- a/src/include/catalog/pg_authid.h
    > +++ b/src/include/catalog/pg_authid.h
    > @@ -39,6 +39,16 @@ CATALOG(pg_authid,1260,AuthIdRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID(284
    >  	bool		rolcanlogin;	/* allowed to log in as session user? */
    >  	bool		rolreplication; /* role used for streaming replication */
    >  	bool		rolbypassrls;	/* bypasses row-level security? */
    > +
    > +	bool		adminherit;		/* allowed to administer inherit? */
    > +	bool		admcreaterole;	/* allowed to administer createrole? */
    > +	bool		admcreatedb;	/* allowed to administer createdb?? */
    > +	bool		admcanlogin;	/* allowed to administer login? */
    > +	bool		admreplication; /* allowed to administer replication? */
    > +	bool		admbypassrls;	/* allowed to administer bypassesrls? */
    > +	bool		admconnlimit;	/* allowed to administer connlimit? */
    > +	bool		admpassword;	/* allowed to administer password? */
    > +	bool		admvaliduntil;	/* allowed to administer validuntil? */
    >  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
    
    It's cosmetic, but the space between rolbypassrls and adminherit is
    maybe not needed, and I'd put rolconnlimit first (even though it has a
    different type).
    
    
    Michael
    
    -- 
    Michael Banck
    Teamleiter PostgreSQL-Team
    Projektleiter
    Tel.: +49 2166 9901-171
    Email: michael.banck@credativ.de
    
    credativ GmbH, HRB Mönchengladbach 12080
    USt-ID-Nummer: DE204566209
    Trompeterallee 108, 41189 Mönchengladbach
    Geschäftsführung: Dr. Michael Meskes, Geoff Richardson, Peter Lilley
    
    Unser Umgang mit personenbezogenen Daten unterliegt
    folgenden Bestimmungen: https://www.credativ.de/datenschutz
    
    
    
    
  44. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-31T01:11:48Z

    
    > On Jan 30, 2022, at 2:38 PM, Michael Banck <michael.banck@credativ.de> wrote:
    > 
    > Hi,
    
    Your review is greatly appreciated!
    
    >> The attached WIP patch attempts to solve most of the CREATEROLE
    
    I'm mostly looking for whether the general approach in this Work In Progress patch is acceptable, so I was a bit sloppy with whitespace and such....
    
    > One thing I noticed (and which will likely make DBAs grumpy) is that it
    > seems being able to create users (as opposed to non-login roles/groups)
    > depends on when you get the CREATEROLE attribute (on role creation or
    > later), viz:
    > 
    > postgres=# CREATE USER admin CREATEROLE;
    > CREATE ROLE
    > postgres=# SET ROLE admin;
    > SET
    > postgres=> CREATE USER testuser; -- this works
    > CREATE ROLE
    > postgres=> RESET ROLE;
    > RESET
    > postgres=# CREATE USER admin2;
    > CREATE ROLE
    > postgres=# ALTER ROLE admin2 CREATEROLE; -- we get CREATEROLE after the fact
    > ALTER ROLE
    > postgres=# SET ROLE admin2;
    > SET
    > postgres=> CREATE USER testuser2; -- bam
    > ERROR:  must have grant option on LOGIN privilege to create login users
    > postgres=# SELECT rolname, admcreaterole, admcanlogin FROM pg_authid
    > WHERE rolname LIKE 'admin%';
    > rolname | admcreaterole | admcanlogin 
    > ---------+---------------+-------------
    > admin   | t             | t
    > admin2  | f             | f
    > (2 rows)
    > 
    > Is that intentional? If it is, I think it would be nice if this could be
    > changed, unless I'm missing some serious security concerns or so. 
    
    It's intentional, but part of what I wanted review comments about.  The issue is that historically:
    
      CREATE USER michael CREATEROLE
    
    meant that you could go on to do things like create users with LOGIN privilege.  I could take that away, which would be a backwards compatibility break, or I can do the weird thing this patch does.  Or I could have your
    
      ALTER ROLE admin2 CREATEROLE;
    
    also grant the other privileges like LOGIN unless you explicitly say otherwise with a bunch of explicit WITHOUT ADMIN OPTION clauses.  Finding out which of those this is preferred was a big part of why I put this up for review.  Thanks for calling it out in under 24 hours!
    
    > Some light review of the patch (I haven't read all the previous ones, so
    > please excuse me if I rehash old discussions):
    
    Not a problem.
    
    > Spaces vs. tabs here...
    > 
    > ... and here, is that intentional?
    
    
    > I think typdefs usually go at the top of the file, not at line 5441...
    
    > I feel this function comment needs revision...
    
    > Hrm, maybe also mention ...
    
    All good comments, but I'm not doing code cleanup on this WIP patch just yet.  Forgive me.
    
    > Shouldn't this (and the following) be "must have admin option on
    > CREATEROLE"?
    
    Yes, there may be other places where I failed to replace the verbiage "grant option" with "admin option".  Earlier drafts of the patch were using that language.  I wouldn't mind review comments on which language people thinks is better.
    
    > 
    >> @@ -311,7 +370,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
    >> 						stmt->role)));
    >> 
    >> 	/* Convert validuntil to internal form */
    >> -	if (validUntil)
    >> +	if (validUntil && strcmp(validUntil, "always") != 0)
    > 
    > This (there are other similar hunks further down) looks like an
    > independent patch/feature?
    
    Part of the problem with the grammar introduced in this patch is that you are not normally required to mention attributes like VALID UNTIL, but if you want to change whether the created role gets WITH ADMIN OPTION, you have to.  That leaves the problem of what to do if you *only* want to specify the ADMIN part.  The grammar needs some sort of "dummy" value that intentionally has no effect, but sets up for the WITH/WITHOUT ADMIN OPTION clause.  I think I left a few bits of cruft around like that.  But what I'd really like to know is if people think this sort of thing is even headed in the right direction?  Are there problems with SQL spec compliance?  Does it just feel icky?  I don't have any pride-of-ownership in the grammar this WIP patch introduces.  I just needed something to put out there for people to attack/improve.
    
    >> +
    >> +	/* To mess with replication roles, must have admin on REPLICATION */
    >> +	if ((authform->rolreplication || disreplication) &&
    >> +		!may_admin_replication_privilege(GetUserId()))
    >> +		ereport(ERROR,
    >> +				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >> +				 errmsg("must have admin on replication to alter replication roles or change replication attribute")));
    > 
    > "have admin" sounds a bit weird, but I understand the error message is
    > too long already to spell out "must have admin option"? Or am I mistaken
    > and "admin" is what it's actually called (same for the ones below)?
    
    If it is the officially correct language, I arrived at it by accident.  I didn't take any time to wordsmith those error messages.  Improvements welcome!
    
    > Also, I think those role options are usually capitalized like
    > REPLICATION in other error messages.
    
    Yeah, I noticed some amount of inconsistency there.  For a brief time I was trying to make them all the same, but got a bit confused on what would be correct, and didn't waste the time.  The sort of thing I'm thinking about is the pre-existing message text, "must be superuser to change bypassrls attribute".  Note that neither "superuser" nor "bypassrls" are capitalized.  If people like where this patch is going, I'll no doubt need to clean it up.
    
    >> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
    >> index b5966712ce..7503d3ead6 100644
    >> --- a/src/backend/parser/gram.y
    >> +++ b/src/backend/parser/gram.y
    >> @@ -1131,67 +1140,111 @@ AlterOptRoleElem:
    > [...]
    > 
    >> +			| VALID ALWAYS opt_admin_spec
    >> +				{
    >> +					RoleElem *n = makeNode(RoleElem);
    >> +					n->elem = makeDefElem("validUntil", (Node *)makeString("always"), @1);
    >> +					n->admin_spec = $3;
    >> +					$$ = (Node *)n;
    > 
    > This one is from another patch as well I think.
    
    That was an attempt at a "dummy" type value.  I agree it probably doesn't belong.
    
    >> 				}
    >> 		/*	Supported but not documented for roles, for use by ALTER GROUP. */
    >> -			| USER role_list
    >> +			| USER role_list opt_admin_spec
    >> 				{
    >> -					$$ = makeDefElem("rolemembers", (Node *)$2, @1);
    >> +					RoleElem *n = makeNode(RoleElem);
    >> +					n->elem = makeDefElem("rolemembers", (Node *)$2, @1);
    >> +					n->admin_spec = $3;
    >> +					$$ = (Node *)n;
    >> 				}
    >> -			| IDENT
    >> +			| IDENT opt_admin_spec
    >> 				{
    >> 					/*
    >> 					 * We handle identifiers that aren't parser keywords with
    >> 					 * the following special-case codes, to avoid bloating the
    >> 					 * size of the main parser.
    >> 					 */
    >> +					RoleElem *n = makeNode(RoleElem);
    >> +
    >> +					/*
    >> +					 * Record whether the user specified WITH GRANT OPTION.
    > 
    > WITH ADMIN OPTION rather?
    
    Yes.
    
    >> +					 * Note that for some privileges this is always implied,
    >> +					 * such as SUPERUSER, but we don't reflect that here.
    >> +					 */
    >> +					n->admin_spec = $2;
    >> +
    > 
    >> diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
    >> index 6c28119fa1..4829a6dbd2 100644
    >> --- a/src/include/catalog/pg_authid.dat
    >> +++ b/src/include/catalog/pg_authid.dat
    >> @@ -22,67 +22,93 @@
    >> { oid => '10', oid_symbol => 'BOOTSTRAP_SUPERUSERID',
    >>   rolname => 'POSTGRES', rolsuper => 't', rolinherit => 't',
    >>   rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't',
    >> -  rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1',
    >> +  rolreplication => 't', rolbypassrls => 't', adminherit => 't', admcreaterole => 't',
    >> +  admcreatedb => 't', admcanlogin => 't', admreplication => 't', admbypassrls => 't',
    >> +  admconnlimit => 't', admpassword => 't', admvaliduntil => 't', rolconnlimit => '-1',
    >>   rolpassword => '_null_', rolvaliduntil => '_null_' },
    > 
    > Those sure are a couple of new columns in pg_authid, but oh well...
    
    Yes, that's also a big part of what people might object to.  I think it's a reasonable objection, but I don't know where else to put the information, given the lack of an aclitem[]?
    
    >> diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
    >> index 4b65e39a1f..4acdcaa685 100644
    >> --- a/src/include/catalog/pg_authid.h
    >> +++ b/src/include/catalog/pg_authid.h
    >> @@ -39,6 +39,16 @@ CATALOG(pg_authid,1260,AuthIdRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID(284
    >> 	bool		rolcanlogin;	/* allowed to log in as session user? */
    >> 	bool		rolreplication; /* role used for streaming replication */
    >> 	bool		rolbypassrls;	/* bypasses row-level security? */
    >> +
    >> +	bool		adminherit;		/* allowed to administer inherit? */
    >> +	bool		admcreaterole;	/* allowed to administer createrole? */
    >> +	bool		admcreatedb;	/* allowed to administer createdb?? */
    >> +	bool		admcanlogin;	/* allowed to administer login? */
    >> +	bool		admreplication; /* allowed to administer replication? */
    >> +	bool		admbypassrls;	/* allowed to administer bypassesrls? */
    >> +	bool		admconnlimit;	/* allowed to administer connlimit? */
    >> +	bool		admpassword;	/* allowed to administer password? */
    >> +	bool		admvaliduntil;	/* allowed to administer validuntil? */
    >> 	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
    > 
    > It's cosmetic, but the space between rolbypassrls and adminherit is
    > maybe not needed, and I'd put rolconnlimit first (even though it has a
    > different type).
    
    Oh, totally agree.  I had that blank there during development because the "rol..." and "adm..." all started to blur together.
    
    Thanks again!  If the patch stays mostly like it is, I'll incorporate all your review comments into a next version.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  45. Re: CREATEROLE and role ownership hierarchies

    Michael Banck <michael.banck@credativ.de> — 2022-01-31T08:43:27Z

    Hi,
    
    Am Sonntag, dem 30.01.2022 um 17:11 -0800 schrieb Mark Dilger:
    > > On Jan 30, 2022, at 2:38 PM, Michael Banck <   
    > > michael.banck@credativ.de> wrote:
    > > > The attached WIP patch attempts to solve most of the CREATEROLE
    > 
    > I'm mostly looking for whether the general approach in this Work In
    > Progress patch is acceptable, so I was a bit sloppy with whitespace
    > and such....
    
    Ok, sure. I think this topic is hugely important and as I read the
    patch anyway, I added some comments, but yeah, we need to figure out
    the fundamentals first.
    > 
    
    > > One thing I noticed (and which will likely make DBAs grumpy) is that it
    > > seems being able to create users (as opposed to non-login roles/groups)
    > > depends on when you get the CREATEROLE attribute (on role creation or
    > > later), viz:
    > > 
    > > postgres=# CREATE USER admin CREATEROLE;
    > > CREATE ROLE
    > > postgres=# SET ROLE admin;
    > > SET
    > > postgres=> CREATE USER testuser; -- this works
    > > CREATE ROLE
    > > postgres=> RESET ROLE;
    > > RESET
    > > postgres=# CREATE USER admin2;
    > > CREATE ROLE
    > > postgres=# ALTER ROLE admin2 CREATEROLE; -- we get CREATEROLE after the fact
    > > ALTER ROLE
    > > postgres=# SET ROLE admin2;
    > > SET
    > > postgres=> CREATE USER testuser2; -- bam
    > > ERROR:  must have grant option on LOGIN privilege to create login users
    > > postgres=# SELECT rolname, admcreaterole, admcanlogin FROM
    > > pg_authid
    > > WHERE rolname LIKE 'admin%';
    > > rolname | admcreaterole | admcanlogin 
    > > ---------+---------------+-------------
    > > admin   | t             | t
    > > admin2  | f             | f
    > > (2 rows)
    > > 
    > > Is that intentional? If it is, I think it would be nice if this
    > > could be
    > > changed, unless I'm missing some serious security concerns or so. 
    > 
    > It's intentional, but part of what I wanted review comments about. 
    > The issue is that historically:
    > 
    >   CREATE USER michael CREATEROLE
    > 
    > meant that you could go on to do things like create users with LOGIN
    > privilege.  I could take that away, which would be a backwards
    > compatibility break, or I can do the weird thing this patch does.  Or
    > I could have your
    > 
    >   ALTER ROLE admin2 CREATEROLE;
    > 
    > also grant the other privileges like LOGIN unless you explicitly say
    > otherwise with a bunch of explicit WITHOUT ADMIN OPTION clauses. 
    > Finding out which of those this is preferred was a big part of why I
    > put this up for review.  Thanks for calling it out in under 24 hours!
    
    Ok, so what I would have needed to do in the above in order to have
    "admin2" and "admin" be the same as far as creating login users is (I
    believe):
    
    ALTER ROLE admin2 CREATEROLE LOGIN WITH ADMIN OPTION;
    
    I think if possible, it would be nice to just have this part as default
    if possible. I.e. CREATEROLE and HASLOGIN are historically so much
    intertwined that I think the above should be implicit (again, if that
    is possible); I don't care and/or haven't made up my mind about any of
    the other options so far...
    
    Ok, so now that I had another look, I see we are going down Pandora's
    box: For any of the other things a role admin would like to do (change
    password, change conn limit), one would have to go with this weird
    disconnect between CREATE USER admin CREATEROLE and ALTER USER admin2
    CREATEROLE [massive list of WITH ADMIN OPTION], and then I'm not sure
    where we stop.
    
    By the way, is there now even a way to add admpassword to a role after
    it got created?
    
    postgres=# SET ROLE admin2;
    SET
    postgres=> \password test
    Enter new password for user "test": 
    Enter it again: 
    ERROR:  must have admin on password to change password attribute
    postgres=> RESET ROLE;
    RESET
    postgres=# ALTER ROLE admin2 PASSWORD WITH ADMIN OPTION;
    ERROR:  syntax error at or near "WITH"
    UPDATE pg_authid SET admpassword = 't' WHERE rolname = 'admin2';
    UPDATE 1
    postgres=# SET ROLE admin2;
    SET
    postgres=> \password test
    Enter new password for user "test": 
    Enter it again: 
    postgres=> 
    
    However, the next thing is:
    
    postgres=# SET ROLE admin;
    SET
    postgres=> CREATE GROUP testgroup;
    CREATE ROLE
    postgres=> GRANT testgroup TO test;
    ERROR:  must have admin option on role "testgroup"
    
    First off, what does "admin option" mean on a role?
    
    I then tried this:
    
    postgres=# CREATE USER admin3 CREATEROLE WITH ADMIN OPTION;
    CREATE ROLE
    postgres=# SET ROLE admin3;
    SET
    postgres=> CREATE USER test3;
    CREATE ROLE
    postgres=> CREATE GROUP testgroup3;
    CREATE ROLE
    postgres=> GRANT testgroup3 TO test3;
    ERROR:  must have admin option on role "testgroup3"
    
    So I created both user and group, I have the CREATEROLE priv (with or
    without admin option), but I still can't assign the group. Is that
    (tracking who created a role and letting the creator do more thing) the
    part that got chopped away in your last patch in order to find a common
    ground?
    
    Is there now any way non-Superusers can assign groups to other users? I
    feel this (next to creating users/groups) is the primary thing those
    CREATEROLE admins are supposed to do/where doing up to now.
    
    
    Again, sorry if this was all discussed previously, I only skimmed this
    thread.
    
    Two more comments regarding the code:
    
    > > > b/src/include/catalog/pg_authid.dat
    > > > index 6c28119fa1..4829a6dbd2 100644
    > > > --- a/src/include/catalog/pg_authid.dat
    > > > +++ b/src/include/catalog/pg_authid.dat
    > > > @@ -22,67 +22,93 @@
    > > > { oid => '10', oid_symbol => 'BOOTSTRAP_SUPERUSERID',
    > > >   rolname => 'POSTGRES', rolsuper => 't', rolinherit => 't',
    > > >   rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't',
    > > > -  rolreplication => 't', rolbypassrls => 't', rolconnlimit =>
    > > > '-1',
    > > > +  rolreplication => 't', rolbypassrls => 't', adminherit => 't',
    > > > admcreaterole => 't',
    > > > +  admcreatedb => 't', admcanlogin => 't', admreplication => 't',
    > > > admbypassrls => 't',
    > > > +  admconnlimit => 't', admpassword => 't', admvaliduntil => 't',
    > > > rolconnlimit => '-1',
    > > >   rolpassword => '_null_', rolvaliduntil => '_null_' },
    > > 
    > > Those sure are a couple of new columns in pg_authid, but oh well...
    > 
    > Yes, that's also a big part of what people might object to.  I think
    > it's a reasonable objection, but I don't know where else to put the
    > information, given the lack of an aclitem[]?
    
    Yeah, it crossed my mind that an array might not be bad. In any case,
    if we can fix CREATEROLE for good, a couple of extra columns in
    pg_authid might be a small price to pay.
    
    diff --git a/src/include/catalog/pg_authid.h
    > > > b/src/include/catalog/pg_authid.h
    > > > index 4b65e39a1f..4acdcaa685 100644
    > > > --- a/src/include/catalog/pg_authid.h
    > > > +++ b/src/include/catalog/pg_authid.h
    > > > @@ -39,6 +39,16 @@ CATALOG(pg_authid,1260,AuthIdRelationId)
    > > > BKI_SHARED_RELATION BKI_ROWTYPE_OID(284
    > > >         bool            rolcanlogin;    /* allowed to log in as
    > > > session user? */
    > > >         bool            rolreplication; /* role used for
    > > > streaming replication */
    > > >         bool            rolbypassrls;   /* bypasses row-level
    > > > security? */
    > > > +
    > > > +       bool            adminherit;             /* allowed to
    > > > administer inherit? */
    > > > +       bool            admcreaterole;  /* allowed to administer
    > > > createrole? */
    > > > +       bool            admcreatedb;    /* allowed to administer
    > > > createdb?? */
    > > > +       bool            admcanlogin;    /* allowed to administer
    > > > login? */
    > > > +       bool            admreplication; /* allowed to administer
    > > > replication? */
    > > > +       bool            admbypassrls;   /* allowed to administer
    > > > bypassesrls? */
    > > > +       bool            admconnlimit;   /* allowed to administer
    > > > connlimit? */
    > > > +       bool            admpassword;    /* allowed to administer
    > > > password? */
    > > > +       bool            admvaliduntil;  /* allowed to administer
    > > > validuntil? */
    > > >         int32           rolconnlimit;   /* max connections
    > > > allowed (-1=no limit) */
    > > 
    > > It's cosmetic, but the space between rolbypassrls and adminherit is
    > > maybe not needed, and I'd put rolconnlimit first (even though it
    > > has a different type).
    > 
    > Oh, totally agree.  I had that blank there during development because
    > the "rol..." and "adm..." all started to blur together.
    
    The way the adm* privs are now somewhere in the middle of the rol*
    privs also looks weird for the end-user and there does not seems to be
    some greater scheme behind it:
    
    postgres=# SELECT * FROM pg_authid WHERE rolname = 'admin' \gx 
    -[ RECORD 1 ]--+------
    oid            | 16385
    rolname        | admin
    rolsuper       | f
    rolinherit     | t
    rolcreaterole  | t
    rolcreatedb    | f
    rolcanlogin    | t
    rolreplication | f
    rolbypassrls   | f
    adminherit     | t
    admcreaterole  | t
    admcreatedb    | t
    admcanlogin    | t
    admreplication | f
    admbypassrls   | f
    admconnlimit   | t
    admpassword    | t
    admvaliduntil  | t
    rolconnlimit   | -1
    rolpassword    | 
    rolvaliduntil  | 
    
    
    Michael
    
    -- 
    Michael Banck
    Teamleiter PostgreSQL-Team
    Projektleiter
    Tel.: +49 2166 9901-171
    E-Mail: michael.banck@credativ.de
    
    credativ GmbH, HRB Mönchengladbach 12080
    USt-ID-Nummer: DE204566209
    Trompeterallee 108, 41189 Mönchengladbach
    Geschäftsführung: Dr. Michael Meskes, Geoff Richardson, Peter Lilley
    
    Unser Umgang mit personenbezogenen Daten unterliegt
    folgenden Bestimmungen: https://www.credativ.de/datenschutz
    
    
    
    
    
  46. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-31T16:53:30Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 25, 2022, at 12:44 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > I agree that CREATEROLE is overpowered and that the goal of this should
    > > be to provide a way for roles to be created and dropped that doesn't
    > > give the user who has that power everything that CREATEROLE currently
    > > does.
    > 
    > I'm attaching a patch that attempts to fix CREATEROLE without any connection to role ownership.
    
    Alright.
    
    > >  The point I was making is that the concept of role ownership
    > > isn't intrinsically linked to that and is, therefore, as you say, gravy.
    > 
    > I agree, they aren't intrinsically linked, though the solution to one might interact in some ways with the solution to the other.
    
    Sure.
    
    > > That isn't to say that I'm entirely against the role ownership idea but
    > > I'd want it to be focused on the goal of providing ways of creating and
    > > dropping users and otherwise performing that kind of administration and
    > > that doesn't require the specific change to make owners be members of
    > > all roles they own and automatically have all privileges of those roles
    > > all the time.
    > 
    > The attached WIP patch attempts to solve most of the CREATEROLE problems but not the problem of which role who can drop which other role.  That will likely require an ownership concept.
    
    Yeah, we do need to have a way to determine who is allowed to drop
    roles and role ownership seems like it's one possible approach to that.
    
    > The main idea here is that having CREATEROLE doesn't give you ADMIN on roles, nor on role attributes.  For role attributes, the syntax has been extended.  An excerpt from the patch's regression test illustrates some of that concept:
    > 
    > -- ok, superuser can create a role that can create login replication users, but
    > -- cannot itself login, nor perform replication
    > CREATE ROLE regress_role_repladmin
    >     CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
    >     NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
    >     NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
    >     NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
    >     NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    > 
    > -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    > CREATE ROLE regress_role_minoradmin
    >     NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
    >     CREATEROLE WITHOUT ADMIN OPTION
    >     NOCREATEDB WITHOUT ADMIN OPTION
    >     NOLOGIN WITHOUT ADMIN OPTION
    >     NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
    >     NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
    >     NOINHERIT WITHOUT ADMIN OPTION
    >     CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
    >     VALID ALWAYS WITHOUT ADMIN OPTION
    >     PASSWORD NULL WITHOUT ADMIN OPTION;
    
    Right, this was one of the approaches that I was thinking could work for
    managing role attributes and it's very similar to roles and the admin
    option for them.  As I suggested at least once, another possible
    approach could be to have login users not be able to create roles but
    for them to be able to SET ROLE to a role which is able to create roles,
    and then, using your prior method, only allow the attributes which that
    role has to be able to be given to other roles.  That essentially makes
    a role be a proxy for the per-attribute admin options.  There's pros and
    cons for each approach and so I'm curious as to which you feel is the
    better approach?  I get the feeling that you're more inclined to go with
    the approach of having an admin option for each role attribute (having
    written this WIP patch) but I'm not sure if that is because you
    contempltaed both and felt this was better for some reason or more
    because I wasn't explaining the other approach very well, or if there
    was some other reason.
    
    > -- fail, having CREATEROLE is not enough to create roles in privileged roles
    > SET SESSION AUTHORIZATION regress_role_minoradmin;
    > CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    > ERROR:  must have admin option on role "pg_read_all_data"
    
    I would say not just privileged roles, but any roles that the user
    doesn't have admin rights on.
    
    > Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied hinges on whether the role is given CREATEROLE.  That hackery is necessary to preserve backwards compatibility.  If we don't care about compatibility, I could change the patch to make "WITHOUT ADMIN OPTION" implied for all attributes when not specified.
    
    Given the relative size of the changes we're talking about regarding
    CREATEROLE, I don't really think we need to stress about backwards
    compatibility too much.
    
    Thanks,
    
    Stephen
    
  47. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-31T17:18:12Z

    
    > On Jan 31, 2022, at 12:43 AM, Michael Banck <michael.banck@credativ.de> wrote:
    
    > Ok, sure. I think this topic is hugely important and as I read the
    > patch anyway, I added some comments, but yeah, we need to figure out
    > the fundamentals first.
    
    Right.
    
    Perhaps some background on this patch series will help.  The patch versions before v8 were creating an owner-owned relationship between the creator and the createe, and a lot of privileges were dependent on that ownership.  Stephen objected that we were creating parallel tracks on which the privilege system was running; things like belonging to a role or having admin on a role were partially conflated with owning a role.  He also objected that the pre-v8 patch sets allowed a creator role with the CREATEROLE privilege to give away any privilege the creator had, rather than needing to have GRANT or ADMIN option on the privilege being given.
    
    The v8-WIP patch is not a complete replacement for the pre-v8 patches.  It's just a balloon I'm floating to try out candidate solutions to some of Stephen's objections.  In the long run, I want the solution to Stephen's objections to not create problems for anybody who liked the way the pre-v8 patches worked (Robert, Andrew, and to some extent me.)
    
    In this WIP patch, for a creator to give *anything* away to a createe, the creator must have GRANT or ADMIN on the thing being given.  That includes attributes like BYPASSRLS, CREATEDB, LOGIN, etc., and also ADMIN on any role the createe is granted into.
    
    I tried to structure things for backwards compatibility, considering which things roles with CREATEROLE could give away historically.  It turns out they can give away most everything, but not SUPERUSER, BYPASSRLS, or REPLICATION.  So I structured the default privileges for CREATEROLE to match.  But I'm uncertain that design is any good, and your comments below suggest that you find it pretty hard to use.
    
    Part of the problem with trying to be backwards compatible is that we must break compatibility anyway, to address the problem that historically having CREATEROLE meant you effectively had ADMIN on all non-superuser roles.  That's got to change.  So in part I'm asking pgsql-hackers if partial backwards compatibility is worth the bother.
    
    If we don't go with backwards compatibility, then CREATEROLE would only allow you to create a new role, but not to give that role LOGIN, nor CREATEDB, etc.  You'd need to also have admin option on those things.  To create a role that can give those things away, you'd need to run something like:
    
    CREATE ROLE michael
    	CREATEROLE WITH ADMIN OPTION    -- can further give away "createrole"
    	CREATEDB WITH ADMIN OPTION    -- can further give away "createdb"
    	LOGIN WITH ADMIN OPTION    -- can further give away "login"
    	NOREPLICATION WITHOUT ADMIN OPTION    -- this would be implied anyway
    	NOBYPASSRLS WITHOUT ADMIN OPTION    -- this would be implied anyway
    	CONNECTION LIMIT WITH ADMIN OPTION    -- can specify connection limits
    	PASSWORD WITH ADMIN OPTION    -- can specify passwords
    	VALID UNTIL WITH ADMIN OPTION    -- can specify expiration
    
    (I'm on the fence about the phrase "WITH ADMIN OPTION" vs. the phrase "WITH GRANT OPTION".)
    
    Even then, when "michael" creates new roles, if he wants to be able to further administer those roles, he needs to remember to give himself ADMIN membership in that role at creation time.  After the role is created, if he doesn't have ADMIN, he can't give it to himself.  So, at create time, he needs to remember to do this:
    
    SET ROLE michael;
    CREATE ROLE mark ADMIN michael;
    
    But that's still a bit strange, because "ADMIN michael" means that michael can grant other roles membership in "mark", not that michael can, for example, change mark's password.  If we don't want CREATEROLE to imply that you can mess around with arbitrary roles (rather than only roles that you created or have been transferred control over) then we need the concept of role ownership.  This patch doesn't go that far, so for now, only superusers can do those things.  Assuming some form of this patch is acceptable, the v9 series will resurrect some of the pre-v7 logic for role ownership and say that the owner can do those things.
    
    
    >>> One thing I noticed (and which will likely make DBAs grumpy) is that it
    >>> seems being able to create users (as opposed to non-login roles/groups)
    >>> depends on when you get the CREATEROLE attribute (on role creation or
    >>> later), viz:
    >>> 
    >>> postgres=# CREATE USER admin CREATEROLE;
    >>> CREATE ROLE
    >>> postgres=# SET ROLE admin;
    >>> SET
    >>> postgres=> CREATE USER testuser; -- this works
    >>> CREATE ROLE
    >>> postgres=> RESET ROLE;
    >>> RESET
    >>> postgres=# CREATE USER admin2;
    >>> CREATE ROLE
    >>> postgres=# ALTER ROLE admin2 CREATEROLE; -- we get CREATEROLE after the fact
    >>> ALTER ROLE
    >>> postgres=# SET ROLE admin2;
    >>> SET
    >>> postgres=> CREATE USER testuser2; -- bam
    >>> ERROR:  must have grant option on LOGIN privilege to create login users
    >>> postgres=# SELECT rolname, admcreaterole, admcanlogin FROM
    >>> pg_authid
    >>> WHERE rolname LIKE 'admin%';
    >>> rolname | admcreaterole | admcanlogin 
    >>> ---------+---------------+-------------
    >>> admin   | t             | t
    >>> admin2  | f             | f
    >>> (2 rows)
    >>> 
    >>> Is that intentional? If it is, I think it would be nice if this
    >>> could be
    >>> changed, unless I'm missing some serious security concerns or so. 
    >> 
    >> It's intentional, but part of what I wanted review comments about. 
    >> The issue is that historically:
    >> 
    >>   CREATE USER michael CREATEROLE
    >> 
    >> meant that you could go on to do things like create users with LOGIN
    >> privilege.  I could take that away, which would be a backwards
    >> compatibility break, or I can do the weird thing this patch does.  Or
    >> I could have your
    >> 
    >>   ALTER ROLE admin2 CREATEROLE;
    >> 
    >> also grant the other privileges like LOGIN unless you explicitly say
    >> otherwise with a bunch of explicit WITHOUT ADMIN OPTION clauses. 
    >> Finding out which of those this is preferred was a big part of why I
    >> put this up for review.  Thanks for calling it out in under 24 hours!
    > 
    > Ok, so what I would have needed to do in the above in order to have
    > "admin2" and "admin" be the same as far as creating login users is (I
    > believe):
    > 
    > ALTER ROLE admin2 CREATEROLE LOGIN WITH ADMIN OPTION;
    
    Yes, those it's more likely admin2 would have been created with these privileges to begin with, if the creator intended admin2 to do such things. 
    
    > I think if possible, it would be nice to just have this part as default
    > if possible. I.e. CREATEROLE and HASLOGIN are historically so much
    > intertwined that I think the above should be implicit (again, if that
    > is possible); I don't care and/or haven't made up my mind about any of
    > the other options so far...
    
    Possibily.  But then, if you really wanted to grant someone CREATEROLE but not anything else, you'd need to remember which other things are implicit, and explicitly disavow them, like:
    
    ALTER ROLE admin2 CREATEROLE (WITHOUT this, WITHOUT that, WITHOUT the other)
    
    and I think that mostly stinks.
    
    > Ok, so now that I had another look, I see we are going down Pandora's
    > box: For any of the other things a role admin would like to do (change
    > password, change conn limit), one would have to go with this weird
    > disconnect between CREATE USER admin CREATEROLE and ALTER USER admin2
    > CREATEROLE [massive list of WITH ADMIN OPTION], and then I'm not sure
    > where we stop.
    
    I agree.  That's a good argument for just breaking backward compatibility.
    
    > By the way, is there now even a way to add admpassword to a role after
    > it got created?
    > 
    > postgres=# SET ROLE admin2;
    > SET
    > postgres=> \password test
    > Enter new password for user "test": 
    > Enter it again: 
    > ERROR:  must have admin on password to change password attribute
    > postgres=> RESET ROLE;
    > RESET
    > postgres=# ALTER ROLE admin2 PASSWORD WITH ADMIN OPTION;
    > ERROR:  syntax error at or near "WITH"
    > UPDATE pg_authid SET admpassword = 't' WHERE rolname = 'admin2';
    > UPDATE 1
    > postgres=# SET ROLE admin2;
    > SET
    > postgres=> \password test
    > Enter new password for user "test": 
    > Enter it again: 
    > postgres=> 
    
    I don't really have this worked out yet.  That's mostly because I'm planning to fix it with role ownership, but perhaps there is a better way?
    
    > However, the next thing is:
    > 
    > postgres=# SET ROLE admin;
    > SET
    > postgres=> CREATE GROUP testgroup;
    > CREATE ROLE
    > postgres=> GRANT testgroup TO test;
    > ERROR:  must have admin option on role "testgroup"
    > 
    > First off, what does "admin option" mean on a role?
    
    From the docs for "CREATE ROLE", https://www.postgresql.org/docs/14/sql-createrole.html
    
      The ADMIN clause is like ROLE, but the named roles are added to the new role WITH ADMIN OPTION, giving them the right to grant membership in this role to others.
    
    > I then tried this:
    > 
    > postgres=# CREATE USER admin3 CREATEROLE WITH ADMIN OPTION;
    > CREATE ROLE
    > postgres=# SET ROLE admin3;
    > SET
    > postgres=> CREATE USER test3;
    > CREATE ROLE
    > postgres=> CREATE GROUP testgroup3;
    > CREATE ROLE
    > postgres=> GRANT testgroup3 TO test3;
    > ERROR:  must have admin option on role "testgroup3"
    > 
    > So I created both user and group, I have the CREATEROLE priv (with or
    > without admin option), but I still can't assign the group. Is that
    > (tracking who created a role and letting the creator do more thing) the
    > part that got chopped away in your last patch in order to find a common
    > ground?
    
    You need ADMIN on the role, not on CREATEROLE.  To add members to a target role, you must have ADMIN on that target role.  To create new roles with CREATEROLE privilege, you must have ADMIN on the CREATEROLE privilege.
    
    > Is there now any way non-Superusers can assign groups to other users?
    
    Yes, by having ADMIN on those groups.
    
    > I
    > feel this (next to creating users/groups) is the primary thing those
    > CREATEROLE admins are supposed to do/where doing up to now.
    
    Right.  In the past, having CREATEROLE implied having ADMIN on every role.  I'm intentionally breaking that.
    
    > The way the adm* privs are now somewhere in the middle of the rol*
    > privs also looks weird for the end-user and there does not seems to be
    > some greater scheme behind it:
    
    Because they are not variable length nor nullable, they must come before such fields (namely, rolpassword and rolvaliduntil).  They don't really need to come before rolconnlimit, but I liked the idea of packing twelve booleans together, since with "bool" typedef'd to unsigned char, that's twelve contiguous bytes, starting after oid (4 bytes) and rolname (64 bytes) and likely fitting nicely without padding bytes on at least some platforms.  If I split them on either side of rolconnlimit (which is 4 bytes), there'd be seven bools before it and five bools after, which wouldn't pack nicely.
     
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  48. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-31T17:29:40Z

    
    > On Jan 31, 2022, at 8:53 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > Yeah, we do need to have a way to determine who is allowed to drop
    > roles and role ownership seems like it's one possible approach to that.
    
    Which other ways are on the table?  Having ADMIN on a role doesn't allow you to do that, but maybe it could?  What else?
    
    >> The main idea here is that having CREATEROLE doesn't give you ADMIN on roles, nor on role attributes.  For role attributes, the syntax has been extended.  An excerpt from the patch's regression test illustrates some of that concept:
    >> 
    >> -- ok, superuser can create a role that can create login replication users, but
    >> -- cannot itself login, nor perform replication
    >> CREATE ROLE regress_role_repladmin
    >>    CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
    >>    NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
    >>    NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
    >>    NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
    >>    NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    >> 
    >> -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    >> CREATE ROLE regress_role_minoradmin
    >>    NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
    >>    CREATEROLE WITHOUT ADMIN OPTION
    >>    NOCREATEDB WITHOUT ADMIN OPTION
    >>    NOLOGIN WITHOUT ADMIN OPTION
    >>    NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
    >>    NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
    >>    NOINHERIT WITHOUT ADMIN OPTION
    >>    CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
    >>    VALID ALWAYS WITHOUT ADMIN OPTION
    >>    PASSWORD NULL WITHOUT ADMIN OPTION;
    > 
    > Right, this was one of the approaches that I was thinking could work for
    > managing role attributes and it's very similar to roles and the admin
    > option for them.  As I suggested at least once, another possible
    > approach could be to have login users not be able to create roles but
    > for them to be able to SET ROLE to a role which is able to create roles,
    > and then, using your prior method, only allow the attributes which that
    > role has to be able to be given to other roles.
    
    I'm not sure how that works.  If I have a group named "administrators" which as multiple attributes like BYPASSRLS and such, and user "stephen" is a member of "administrators", then stephen can not only give away bypassrls to new users but also has it himself.  How is that an improvement?  (I mean this as a question, not as criticism.)
    
    >  That essentially makes
    > a role be a proxy for the per-attribute admin options.  There's pros and
    > cons for each approach and so I'm curious as to which you feel is the
    > better approach?  I get the feeling that you're more inclined to go with
    > the approach of having an admin option for each role attribute (having
    > written this WIP patch) but I'm not sure if that is because you
    > contempltaed both and felt this was better for some reason or more
    > because I wasn't explaining the other approach very well, or if there
    > was some other reason.
    
    I need more explanation of the other option you are contemplating.  My apologies if I'm being thick-headed.
    
    >> -- fail, having CREATEROLE is not enough to create roles in privileged roles
    >> SET SESSION AUTHORIZATION regress_role_minoradmin;
    >> CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    >> ERROR:  must have admin option on role "pg_read_all_data"
    > 
    > I would say not just privileged roles, but any roles that the user
    > doesn't have admin rights on.
    
    Yes, that's how it works.  But this portion of the test is only checking the interaction between CREATEROLE and built-in privileged roles, hence the comment.
    
    >> Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied hinges on whether the role is given CREATEROLE.  That hackery is necessary to preserve backwards compatibility.  If we don't care about compatibility, I could change the patch to make "WITHOUT ADMIN OPTION" implied for all attributes when not specified.
    > 
    > Given the relative size of the changes we're talking about regarding
    > CREATEROLE, I don't really think we need to stress about backwards
    > compatibility too much.
    
    Yeah, I'm leaning pretty strongly that way, too.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  49. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-01-31T18:50:14Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 31, 2022, at 8:53 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > > Yeah, we do need to have a way to determine who is allowed to drop
    > > roles and role ownership seems like it's one possible approach to that.
    > 
    > Which other ways are on the table?  Having ADMIN on a role doesn't allow you to do that, but maybe it could?  What else?
    
    Supporting that through ADMIN is one option, another would be a
    'DROPROLE' attribute, though we'd want a way to curtail that from being
    able to be used for just any role and that does lead down a path similar
    to ownership or just generally the concept that some roles have certain
    rights over certain other roles (whether you create them or not...).
    
    I do think there's a lot of value in being able to segregate certain
    rights- consider that you may want a role that's able to create other
    roles, perhaps grant them into some set of roles, can lock those roles
    (prevent them from logging in, maybe do a password reset, something like
    that), but which *isn't* able to drop those roles (and all their
    objects) as that's dangerous and mistakes can certainly happen, or be
    able to become that role because the creating role simply doesn't have
    any need to be able to do that (or desire to in many cases, as we
    discussed in the landlord-vs-tenant sub-thread).
    
    Naturally, you'd want *some* role to be able to drop that role (and one
    that doesn't have full superuser access) but that might be a role that's
    not able to create new roles or take over accounts.
    
    Separation of concerns and powers and all of that is what we want to be
    going for here, more generically, which is why I was opposed to the
    blanket "owners have all rights of all roles they own" implementation.
    That approach doesn't support the ability to have a relatively
    unprivileged role that's able to create other roles, which seems like a
    pretty important use-case for us to be considering.
    
    The terminology seems to also be driving us in a certain direction and I
    don't know that it's necessarily a good one.  That is- the term 'owner'
    implies certain things and maybe that's where some of the objection to
    my argument that owners shouldn't necessarily have all rights of the
    roles they 'own' comes from (ok- I'll also put out there for general
    consideration that since we're talking about roles, and login roles are
    generally associated with people, that maybe 'owner' isn't a great term
    to use for this anyway ...).  I feel like the 'owner' concept came from
    the way we have table owners and function owners and database owners
    today rather than from a starting point of what do we wish to
    specifically enable.
    
    Perhaps instead of starting from the 'owner' concept, we start from the
    question about the kinds of things we want roles to be able to do and
    perhaps that will help inform the terminology.
    
    - Create new roles
    - Drop an existing role
    - Drop objects which belong to a role
    - Lock existing roles
    - Change/reset the PW of existing roles
    - Give roles to other roles
    - Revoke access to some roles from other roles
    - Give select role attributes to a role
    - Revoke role attributes from a role
    - Traditional role-based access control (group memberships, SET ROLE)
    
    Certain of the above are already covered by the existing role membership
    system and with the admin option, though there's definitely an argument
    to be made as to if that is as capable as we'd like it to be (there's no
    way to, today at least, GRANT *just* the admin option, for example, and
    maybe that's something that it would actually be sensible to support).
    
    Perhaps there is a need to have a user who has all of the above
    capabilities and maybe that would be an 'owner' or 'manager', but as I
    tried to illustrate above, there's definitely use-cases for giving
    a role only some of the above capabilities rather than all of them
    together at once.
    
    > >> The main idea here is that having CREATEROLE doesn't give you ADMIN on roles, nor on role attributes.  For role attributes, the syntax has been extended.  An excerpt from the patch's regression test illustrates some of that concept:
    > >> 
    > >> -- ok, superuser can create a role that can create login replication users, but
    > >> -- cannot itself login, nor perform replication
    > >> CREATE ROLE regress_role_repladmin
    > >>    CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
    > >>    NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
    > >>    NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
    > >>    NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
    > >>    NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    > >> 
    > >> -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    > >> CREATE ROLE regress_role_minoradmin
    > >>    NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
    > >>    CREATEROLE WITHOUT ADMIN OPTION
    > >>    NOCREATEDB WITHOUT ADMIN OPTION
    > >>    NOLOGIN WITHOUT ADMIN OPTION
    > >>    NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
    > >>    NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
    > >>    NOINHERIT WITHOUT ADMIN OPTION
    > >>    CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
    > >>    VALID ALWAYS WITHOUT ADMIN OPTION
    > >>    PASSWORD NULL WITHOUT ADMIN OPTION;
    > > 
    > > Right, this was one of the approaches that I was thinking could work for
    > > managing role attributes and it's very similar to roles and the admin
    > > option for them.  As I suggested at least once, another possible
    > > approach could be to have login users not be able to create roles but
    > > for them to be able to SET ROLE to a role which is able to create roles,
    > > and then, using your prior method, only allow the attributes which that
    > > role has to be able to be given to other roles.
    > 
    > I'm not sure how that works.  If I have a group named "administrators" which as multiple attributes like BYPASSRLS and such, and user "stephen" is a member of "administrators", then stephen can not only give away bypassrls to new users but also has it himself.  How is that an improvement?  (I mean this as a question, not as criticism.)
    
    That's not how role attributes work though- "stephen" only has the
    'bypassrls' role attribute after a 'set role administrators'.  This has
    been one of the issues with role attributes in general as there's no way
    to change that (unlike the 'inherit' option for roles themselves) but in
    this particular case it might be to our advantage.
    
    > >  That essentially makes
    > > a role be a proxy for the per-attribute admin options.  There's pros and
    > > cons for each approach and so I'm curious as to which you feel is the
    > > better approach?  I get the feeling that you're more inclined to go with
    > > the approach of having an admin option for each role attribute (having
    > > written this WIP patch) but I'm not sure if that is because you
    > > contempltaed both and felt this was better for some reason or more
    > > because I wasn't explaining the other approach very well, or if there
    > > was some other reason.
    > 
    > I need more explanation of the other option you are contemplating.  My apologies if I'm being thick-headed.
    
    Hopefully the above helps.  Note that in order to not allow the
    "stephen" role simply alter itself to have the bypassrls role attribute,
    we'd need to consider roles to not have 'ownership' (or whatever) over
    themselves, which leads into the prior complaint I made around roles
    having 'admin' rights on themselves which I generally don't feel is
    correct either.
    
    > >> -- fail, having CREATEROLE is not enough to create roles in privileged roles
    > >> SET SESSION AUTHORIZATION regress_role_minoradmin;
    > >> CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    > >> ERROR:  must have admin option on role "pg_read_all_data"
    > > 
    > > I would say not just privileged roles, but any roles that the user
    > > doesn't have admin rights on.
    > 
    > Yes, that's how it works.  But this portion of the test is only checking the interaction between CREATEROLE and built-in privileged roles, hence the comment.
    
    But.. predefined roles aren't actually different in this regard from any
    other role, so I disagree that such a test of explicitly predefined
    roles makes sense..?
    
    > >> Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied hinges on whether the role is given CREATEROLE.  That hackery is necessary to preserve backwards compatibility.  If we don't care about compatibility, I could change the patch to make "WITHOUT ADMIN OPTION" implied for all attributes when not specified.
    > > 
    > > Given the relative size of the changes we're talking about regarding
    > > CREATEROLE, I don't really think we need to stress about backwards
    > > compatibility too much.
    > 
    > Yeah, I'm leaning pretty strongly that way, too.
    
    Great.
    
    Thanks,
    
    Stephen
    
  50. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-01-31T18:57:34Z

    On Mon, Jan 31, 2022 at 1:50 PM Stephen Frost <sfrost@snowman.net> wrote:
    >
    > Greetings,
    >
    > * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > > On Jan 31, 2022, at 8:53 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > > > Yeah, we do need to have a way to determine who is allowed to drop
    > > > roles and role ownership seems like it's one possible approach to that.
    > >
    > > Which other ways are on the table?  Having ADMIN on a role doesn't allow you to do that, but maybe it could?  What else?
    >
    > Supporting that through ADMIN is one option, another would be a
    > 'DROPROLE' attribute, though we'd want a way to curtail that from being
    > able to be used for just any role and that does lead down a path similar
    > to ownership or just generally the concept that some roles have certain
    > rights over certain other roles (whether you create them or not...).
    >
    > I do think there's a lot of value in being able to segregate certain
    > rights- consider that you may want a role that's able to create other
    > roles, perhaps grant them into some set of roles, can lock those roles
    > (prevent them from logging in, maybe do a password reset, something like
    > that), but which *isn't* able to drop those roles (and all their
    > objects) as that's dangerous and mistakes can certainly happen, or be
    > able to become that role because the creating role simply doesn't have
    > any need to be able to do that (or desire to in many cases, as we
    > discussed in the landlord-vs-tenant sub-thread).
    >
    
    This is precisely the use case I am trying to accomplish with this
    patchset, roughly:
    
    - An automated bot that creates users and adds them to the employees role
    - Bot cannot access any employee (or other roles) table data
    - Bot cannot become any employee
    - Bot can disable the login of any employee
    
    Yes there are attack surfaces around the fringes of login, etc but
    those can be mitigated with certificate authentication. My pg_hba
    would require any role in the employees role to use cert auth.
    
    This would adequately mitigate many threats while greatly enhancing
    user management.
    
    > Naturally, you'd want *some* role to be able to drop that role (and one
    > that doesn't have full superuser access) but that might be a role that's
    > not able to create new roles or take over accounts.
    
    I suspect some kind of web backend to handle manual user pruning. I
    don't expect Bot to automatically drop users because mistakes can
    happen, and disabling the login ability seems like an adequate
    tradeoff.
    
    > Separation of concerns and powers and all of that is what we want to be
    > going for here, more generically, which is why I was opposed to the
    > blanket "owners have all rights of all roles they own" implementation.
    > That approach doesn't support the ability to have a relatively
    > unprivileged role that's able to create other roles, which seems like a
    > pretty important use-case for us to be considering.
    
    Agreed.
    
    > The terminology seems to also be driving us in a certain direction and I
    > don't know that it's necessarily a good one.  That is- the term 'owner'
    > implies certain things and maybe that's where some of the objection to
    > my argument that owners shouldn't necessarily have all rights of the
    > roles they 'own' comes from (ok- I'll also put out there for general
    > consideration that since we're talking about roles, and login roles are
    > generally associated with people, that maybe 'owner' isn't a great term
    > to use for this anyway ...).  I feel like the 'owner' concept came from
    > the way we have table owners and function owners and database owners
    > today rather than from a starting point of what do we wish to
    > specifically enable.
    >
    > Perhaps instead of starting from the 'owner' concept, we start from the
    > question about the kinds of things we want roles to be able to do and
    > perhaps that will help inform the terminology.
    >
    > - Create new roles
    > - Drop an existing role
    > - Drop objects which belong to a role
    > - Lock existing roles
    > - Change/reset the PW of existing roles
    > - Give roles to other roles
    > - Revoke access to some roles from other roles
    > - Give select role attributes to a role
    > - Revoke role attributes from a role
    > - Traditional role-based access control (group memberships, SET ROLE)
    >
    > Certain of the above are already covered by the existing role membership
    > system and with the admin option, though there's definitely an argument
    > to be made as to if that is as capable as we'd like it to be (there's no
    > way to, today at least, GRANT *just* the admin option, for example, and
    > maybe that's something that it would actually be sensible to support).
    >
    > Perhaps there is a need to have a user who has all of the above
    > capabilities and maybe that would be an 'owner' or 'manager', but as I
    > tried to illustrate above, there's definitely use-cases for giving
    > a role only some of the above capabilities rather than all of them
    > together at once.
    >
    > > >> The main idea here is that having CREATEROLE doesn't give you ADMIN on roles, nor on role attributes.  For role attributes, the syntax has been extended.  An excerpt from the patch's regression test illustrates some of that concept:
    > > >>
    > > >> -- ok, superuser can create a role that can create login replication users, but
    > > >> -- cannot itself login, nor perform replication
    > > >> CREATE ROLE regress_role_repladmin
    > > >>    CREATEROLE WITHOUT ADMIN OPTION     -- can create roles, but cannot give it away
    > > >>    NOCREATEDB WITHOUT ADMIN OPTION     -- cannot create db, nor give it away
    > > >>    NOLOGIN WITH ADMIN OPTION           -- cannot log in, but can give it away
    > > >>    NOREPLICATION WITH ADMIN OPTION     -- cannot replicate, but can give it away
    > > >>    NOBYPASSRLS WITHOUT ADMIN OPTION;   -- cannot bypassrls, nor give it away
    > > >>
    > > >> -- ok, superuser can create a role with CREATEROLE but restrict give-aways
    > > >> CREATE ROLE regress_role_minoradmin
    > > >>    NOSUPERUSER                         -- WITHOUT ADMIN OPTION is implied
    > > >>    CREATEROLE WITHOUT ADMIN OPTION
    > > >>    NOCREATEDB WITHOUT ADMIN OPTION
    > > >>    NOLOGIN WITHOUT ADMIN OPTION
    > > >>    NOREPLICATION                       -- WITHOUT ADMIN OPTION is implied
    > > >>    NOBYPASSRLS                         -- WITHOUT ADMIN OPTION is implied
    > > >>    NOINHERIT WITHOUT ADMIN OPTION
    > > >>    CONNECTION LIMIT NONE WITHOUT ADMIN OPTION
    > > >>    VALID ALWAYS WITHOUT ADMIN OPTION
    > > >>    PASSWORD NULL WITHOUT ADMIN OPTION;
    > > >
    > > > Right, this was one of the approaches that I was thinking could work for
    > > > managing role attributes and it's very similar to roles and the admin
    > > > option for them.  As I suggested at least once, another possible
    > > > approach could be to have login users not be able to create roles but
    > > > for them to be able to SET ROLE to a role which is able to create roles,
    > > > and then, using your prior method, only allow the attributes which that
    > > > role has to be able to be given to other roles.
    > >
    > > I'm not sure how that works.  If I have a group named "administrators" which as multiple attributes like BYPASSRLS and such, and user "stephen" is a member of "administrators", then stephen can not only give away bypassrls to new users but also has it himself.  How is that an improvement?  (I mean this as a question, not as criticism.)
    >
    > That's not how role attributes work though- "stephen" only has the
    > 'bypassrls' role attribute after a 'set role administrators'.  This has
    > been one of the issues with role attributes in general as there's no way
    > to change that (unlike the 'inherit' option for roles themselves) but in
    > this particular case it might be to our advantage.
    >
    > > >  That essentially makes
    > > > a role be a proxy for the per-attribute admin options.  There's pros and
    > > > cons for each approach and so I'm curious as to which you feel is the
    > > > better approach?  I get the feeling that you're more inclined to go with
    > > > the approach of having an admin option for each role attribute (having
    > > > written this WIP patch) but I'm not sure if that is because you
    > > > contempltaed both and felt this was better for some reason or more
    > > > because I wasn't explaining the other approach very well, or if there
    > > > was some other reason.
    > >
    > > I need more explanation of the other option you are contemplating.  My apologies if I'm being thick-headed.
    >
    > Hopefully the above helps.  Note that in order to not allow the
    > "stephen" role simply alter itself to have the bypassrls role attribute,
    > we'd need to consider roles to not have 'ownership' (or whatever) over
    > themselves, which leads into the prior complaint I made around roles
    > having 'admin' rights on themselves which I generally don't feel is
    > correct either.
    >
    > > >> -- fail, having CREATEROLE is not enough to create roles in privileged roles
    > > >> SET SESSION AUTHORIZATION regress_role_minoradmin;
    > > >> CREATE ROLE regress_nosuch_read_all_data IN ROLE pg_read_all_data;
    > > >> ERROR:  must have admin option on role "pg_read_all_data"
    > > >
    > > > I would say not just privileged roles, but any roles that the user
    > > > doesn't have admin rights on.
    > >
    > > Yes, that's how it works.  But this portion of the test is only checking the interaction between CREATEROLE and built-in privileged roles, hence the comment.
    >
    > But.. predefined roles aren't actually different in this regard from any
    > other role, so I disagree that such a test of explicitly predefined
    > roles makes sense..?
    >
    > > >> Whether "WITH ADMIN OPTION" or "WITHOUT ADMIN OPTION" is implied hinges on whether the role is given CREATEROLE.  That hackery is necessary to preserve backwards compatibility.  If we don't care about compatibility, I could change the patch to make "WITHOUT ADMIN OPTION" implied for all attributes when not specified.
    > > >
    > > > Given the relative size of the changes we're talking about regarding
    > > > CREATEROLE, I don't really think we need to stress about backwards
    > > > compatibility too much.
    > >
    > > Yeah, I'm leaning pretty strongly that way, too.
    >
    > Great.
    >
    > Thanks,
    >
    > Stephen
    
    
    
    
  51. Re: CREATEROLE and role ownership hierarchies

    Michael Banck <michael.banck@credativ.de> — 2022-01-31T19:55:56Z

    Hi,
    
    Am Montag, dem 31.01.2022 um 09:18 -0800 schrieb Mark Dilger:
    > On Jan 31, 2022, at 12:43 AM, Michael Banck < 
    > michael.banck@credativ.de> wrote:
    
    > Ok, sure. I think this topic is hugely important and as I read the
    > patch anyway, I added some comments, but yeah, we need to figure out
    > the fundamentals first.
    
    Right.
    
    Perhaps some background on this patch series will help.  
    [...]
    
    Thanks a lot!
    
    
    If we don't go with backwards compatibility, then CREATEROLE would only
    allow you to create a new role, but not to give that role LOGIN, nor
    CREATEDB, etc.  You'd need to also have admin option on those things. 
    To create a role that can give those things away, you'd need to run
    something like:
    
    CREATE ROLE michael
            CREATEROLE WITH ADMIN OPTION    -- can further give away
    "createrole"
            CREATEDB WITH ADMIN OPTION    -- can further give away
    "createdb"
            LOGIN WITH ADMIN OPTION    -- can further give away "login"
            NOREPLICATION WITHOUT ADMIN OPTION    -- this would be implied
    anyway
            NOBYPASSRLS WITHOUT ADMIN OPTION    -- this would be implied anyway
    
    
            CONNECTION LIMIT WITH ADMIN OPTION    -- can specify connection
    limits
            PASSWORD WITH ADMIN OPTION    -- can specify passwords
            VALID UNTIL WITH ADMIN OPTION    -- can specify expiration
    
    Those last three don't work for me:
    
    postgres=# CREATE ROLE admin3 VALID UNTIL WITH ADMIN OPTION;
    ERROR:  syntax error at or near "WITH"
    
    postgres=# CREATE ROLE admin3 PASSWORD WITH ADMIN OPTION;
    ERROR:  syntax error at or near "WITH"
    
    postgres=# CREATE ROLE admin3 CONNECTION LIMIT WITH ADMIN OPTION;
    ERROR:  syntax error at or near "WITH"
    
    > (I'm on the fence about the phrase "WITH ADMIN OPTION" vs. the phrase
    > "WITH GRANT OPTION".)
    > 
    > Even then, when "michael" creates new roles, if he wants to be able
    > to further administer those roles, he needs to remember to give
    > himself ADMIN membership in that role at creation time.  After the
    > role is created, if he doesn't have ADMIN, he can't give it to
    > himself.  So, at create time, he needs to remember to do this:
    > 
    > SET ROLE michael;
    > CREATE ROLE mark ADMIN michael;
    
    What would happen if ADMIN was implicit if michael is a non-superuser
    and there's no ADMIN in the CREATE ROLE statement? It would be
    backwards-compatible, one could still let somebody else be ADMIN, but 
    ISTM a CREATEROLE role could no longer admin a role already existing
    previously/it didn't create/got assigned admin for (e.g. the predefined
    roles).
    
    I.e. (responding what you wrote much further below), the CREATEROLE
    role would no longer be ADMIN for all roles, just automatically for the
    ones it created.
    
    > But that's still a bit strange, because "ADMIN michael" means that
    > michael can grant other roles membership in "mark", not that michael
    > can, for example, change mark's password.
    
    Yeah, changing a password is one of the important tasks of a delegated
    role admin, if no superusers are around.
    
    > If we don't want CREATEROLE to imply that you can mess around with
    > arbitrary roles (rather than only roles that you created or have been
    > transferred control over) then we need the concept of role
    > ownership.  This patch doesn't go that far, so for now, only
    > superusers can do those things.  Assuming some form of this patch is
    > acceptable, the v9 series will resurrect some of the pre-v7 logic for
    > role ownership and say that the owner can do those things.
    
    > > Ok, so what I would have needed to do in the above in order to have
    > > "admin2" and "admin" be the same as far as creating login users is (I
    > > believe):
    > > 
    > > ALTER ROLE admin2 CREATEROLE LOGIN WITH ADMIN OPTION;
    > 
    > Yes, those it's more likely admin2 would have been created with these
    > privileges to begin with, if the creator intended admin2 to do such
    > things.
    
    Right, maybe people just have to adjust to the new way. It still feels
    strange that whatever you do at role creation time is more meaningful
    than when altering a role. 
    
    > 
    > > By the way, is there now even a way to add admpassword to a role
    > > after it got created?
    > > 
    > > postgres=# SET ROLE admin2;
    > > SET
    > > postgres=> \password test
    > > Enter new password for user "test": 
    > > Enter it again: 
    > > ERROR:  must have admin on password to change password attribute
    > > postgres=> RESET ROLE;
    > > RESET
    > > postgres=# ALTER ROLE admin2 PASSWORD WITH ADMIN OPTION;
    > > ERROR:  syntax error at or near "WITH"
    > > UPDATE pg_authid SET admpassword = 't' WHERE rolname = 'admin2';
    > > UPDATE 1
    > > postgres=# SET ROLE admin2;
    > > SET
    > > postgres=> \password test
    > > Enter new password for user "test": 
    > > Enter it again: 
    > > postgres=> 
    > 
    > I don't really have this worked out yet.  That's mostly because I'm
    > planning to fix it with role ownership, but perhaps there is a better
    > way?
    
    Well see above, maybe the patch is just broken/unfinished with respect
    to PASSWORD and the others? It works for REPLICATION e.g.:
    
    postgres=# ALTER ROLE admin2 REPLICATION WITH ADMIN OPTION;
    ALTER ROLE
    
    > > However, the next thing is:
    > > 
    > > postgres=# SET ROLE admin;
    > > SET
    > > postgres=> CREATE GROUP testgroup;
    > > CREATE ROLE
    > > postgres=> GRANT testgroup TO test;
    > > ERROR:  must have admin option on role "testgroup"
    > > 
    > > First off, what does "admin option" mean on a role?
    > 
    > From the docs for "CREATE ROLE",     
    > https://www.postgresql.org/docs/14/sql-createrole.html
    > 
    >   The ADMIN clause is like ROLE, but the named roles are added to the
    > new role WITH ADMIN OPTION, giving them the right to grant membership
    > in this role to others.
    
    Hrm, I see; I guess I never paid attention to that part so far. The
    CREATEROLE thing or SUPERUSER was all I ever needed so far.
    
    And with that I guess I should really bow out of this thread and start
    reading from the beginning.
    
    > > I then tried this:
    > > 
    > > postgres=# CREATE USER admin3 CREATEROLE WITH ADMIN OPTION;
    > > CREATE ROLE
    > > postgres=# SET ROLE admin3;
    > > SET
    > > postgres=> CREATE USER test3;
    > > CREATE ROLE
    > > postgres=> CREATE GROUP testgroup3;
    > > CREATE ROLE
    > > postgres=> GRANT testgroup3 TO test3;
    > > ERROR:  must have admin option on role "testgroup3"
    > > 
    > > So I created both user and group, I have the CREATEROLE priv (with or
    > > without admin option), but I still can't assign the group. Is that
    > > (tracking who created a role and letting the creator do more thing) the
    > > part that got chopped away in your last patch in order to find a common
    > > ground?
    > 
    > You need ADMIN on the role, not on CREATEROLE.  To add members to a
    > target role, you must have ADMIN on that target role.  To create new
    > roles with CREATEROLE privilege, you must have ADMIN on the
    > CREATEROLE privilege.
    
    Right ok. Maybe it's just me, but I feel a lot of people will need to
    learn a lot more than they'd like to know about the ADMIN thing after
    this patch goes in.
    
    > 
    > > I
    > > feel this (next to creating users/groups) is the primary thing those
    > > CREATEROLE admins are supposed to do/where doing up to now.
    > 
    > Right.  In the past, having CREATEROLE implied having ADMIN on every
    > role.  I'm intentionally breaking that.
    
    
    Right; I commented on that above.
    
    > > The way the adm* privs are now somewhere in the middle of the rol*
    > > privs also looks weird for the end-user and there does not seems to be
    > > some greater scheme behind it:
    > 
    > Because they are not variable length nor nullable, they must come
    > before such fields (namely, rolpassword and rolvaliduntil).  They
    > don't really need to come before rolconnlimit, but I liked the idea
    > of packing twelve booleans together, since with "bool" typedef'd to
    > unsigned char, that's twelve contiguous bytes, starting after oid (4
    > bytes) and rolname (64 bytes) and likely fitting nicely without
    > padding bytes on at least some platforms.  If I split them on either
    > side of rolconnlimit (which is 4 bytes), there'd be seven bools
    > before it and five bools after, which wouldn't pack nicely.
    
    Hrm ok, but it's a user-visible column ordering, so I'm wondering
    whether that should trump efficiency here.
    
    
    Michael
    
    -- 
    Michael Banck
    Teamleiter PostgreSQL-Team
    Projektleiter
    Tel.: +49 2166 9901-171
    E-Mail: michael.banck@credativ.de
    
    credativ GmbH, HRB Mönchengladbach 12080
    USt-ID-Nummer: DE204566209
    Trompeterallee 108, 41189 Mönchengladbach
    Geschäftsführung: Dr. Michael Meskes, Geoff Richardson, Peter Lilley
    
    Unser Umgang mit personenbezogenen Daten unterliegt
    folgenden Bestimmungen: https://www.credativ.de/datenschutz
    
    
    
    
    
    
  52. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-01-31T21:09:44Z

    
    > On Jan 31, 2022, at 10:50 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > Supporting that through ADMIN is one option, another would be a
    > 'DROPROLE' attribute, though we'd want a way to curtail that from being
    > able to be used for just any role and that does lead down a path similar
    > to ownership or just generally the concept that some roles have certain
    > rights over certain other roles (whether you create them or not...).
    
    I've been operating under the assumption that I have a lot more freedom to create new features than to change how existing features behave, for two reasons: backwards compatibility and sql-spec compliance.
    
    Changing how having ADMIN on a role works seems problematic for both those reasons.  My family got me socks for Christmas, not what I actually wanted, a copy of the SQL-spec.  So I'm somewhat guessing here.  But I believe we'd have problems if we "fixed" the part where a role can revoke ADMIN from others on themselves.  Whatever we have, whether we call it "ownership", it can't be something a role can unilaterally revoke.
    
    As for a 'DROPROLE' attribute, I don't think that gets us anywhere.  You don't seem to think so, either.  So that leaves us with "ownership", perhaps by another word?  I only chose that word because it's what we use elsewhere, but if we want to call it "managementship" and "manager" or whatever, that's fine.  I'm not to the point of debating the terminology just yet.  I'm still trying to get the behavior nailed down.
    
    > I do think there's a lot of value in being able to segregate certain
    > rights- consider that you may want a role that's able to create other
    > roles, perhaps grant them into some set of roles, can lock those roles
    > (prevent them from logging in, maybe do a password reset, something like
    > that), but which *isn't* able to drop those roles (and all their
    > objects) as that's dangerous and mistakes can certainly happen, or be
    > able to become that role because the creating role simply doesn't have
    > any need to be able to do that (or desire to in many cases, as we
    > discussed in the landlord-vs-tenant sub-thread).
    
    I'm totally on the same page.  Your argument upthread about wanting any malfeasance on the part of a service provider showing up in the audit logs was compelling.  Even for those things the "owner"/"manager" has the rights to do, we might want to make them choose to do it explicitly and not merely do it by accident.
    
    > Naturally, you'd want *some* role to be able to drop that role (and one
    > that doesn't have full superuser access) but that might be a role that's
    > not able to create new roles or take over accounts.
    
    I think it's important to go beyond the idea of a role attribute here.  It's not that role "bob" can drop roles.  It's that "bob" can drop *specific* roles, and for that, there has to be some kind of dependency tracked between "bob" and those other roles.  I'm calling that "ownership".  I think that language isn't just arbitrary, but actually helpful (technically, not politically) because REASSIGN OWNED should treat this kind of relationship exactly the same as it treats ownership of schemas, tables, functions, etc.
    
    > Separation of concerns and powers and all of that is what we want to be
    > going for here, more generically, which is why I was opposed to the
    > blanket "owners have all rights of all roles they own" implementation.
    
    I'm hoping to bring back, in v9, the idea of ownership/managership.  The real sticking point here is that we (Robert, Andrew, I, and possibly others) want to be able to drop in a non-superuser-creator-role into existing systems that use superuser for role management.  We'd like it to be as transparent a switch as possible.
    
    With a superuser creating a role, that superuser can come back and muck with the role afterward, and the role can't revoke the superuser's right to do so.  It's not enough that a non-superuser-creator-role (henceforth, "manager") can grant itself ADMIN on the created role.  It also needs to be able to set passwords, transfer object ownerships to/from the role, grant the role into other roles or other roles into it, etc.  All of that has to be sandboxed such that the "manager" can't touch stuff outside the manager's sandbox, but within the sandbox, it shouldn't make any practical difference that the manager isn't actually a superuser.
    
    I think what I had in v7 was almost right.  I'm hoping that we just need to adjust things like the idea that managers always have implicit membership in and ADMIN on roles they manage.  I think that needs to be optional, and the audit logs could show if the manager granted themselves such things, as it might violate policy and be a red flag in the audit log.
    
    > That approach doesn't support the ability to have a relatively
    > unprivileged role that's able to create other roles, which seems like a
    > pretty important use-case for us to be considering.
    
    I think we have that ability. It's just that the creator role isn't "relatively unprivileged" vis-a-vis the created role.  But that could be handled by creating the role and then transferring the ownership to some other role, or specifying in the CREATE ROLE command that the creator doesn't want those privileges, etc.  That requires some tinkering with the design, though, because the permission to perform the ownership transfer to that other role would need to be circumscribed to not give away other privileges, like the right to become that other role, or the specification that the creator disavows certain privileges over the created role might need to be something the creator could get back by force with some subsequent GRANT command, or ...?
    
    > The terminology seems to also be driving us in a certain direction and I
    > don't know that it's necessarily a good one.  That is- the term 'owner'
    > implies certain things and maybe that's where some of the objection to
    > my argument that owners shouldn't necessarily have all rights of the
    > roles they 'own' comes from
    
    I think it does follow pretty closely the concept of ownership of objects, though.  So closely, in fact, that I don't really see any daylight between the two concepts.
    
    > (ok- I'll also put out there for general
    > consideration that since we're talking about roles, and login roles are
    > generally associated with people, that maybe 'owner' isn't a great term
    > to use for this anyway ...).
    
    Technically, we're talking about roles within computers owning other roles within computers, not about people owning people.  We already have a command called REASSIGN OWNED, and if we don't call this ownership, then that command gets really squirrelly.  Does it also reassign "managed"?
    
    On the other hand, I'm not looking to create offense, so if this language seems unacceptable, perhaps you could propose something else?
    
    >  I feel like the 'owner' concept came from
    > the way we have table owners and function owners and database owners
    > today rather than from a starting point of what do we wish to
    > specifically enable.
    
    Let's compare this to the idea of owning a table.  Can the owner of a table revoke SELECT from themselves? Yes, they can.  They can also give it back to themselves:
    
    CREATE ROLE michael;
    SET ROLE michael;
    CREATE TABLE michael_table (i INTEGER);
    REVOKE SELECT ON michael_table FROM PUBLIC, michael;
    SELECT * FROM michael_table;
    ERROR:  permission denied for table michael_table
    GRANT SELECT ON michael_table TO michael;
    SELECT * FROM michael_table;
     i 
    ---
    (0 rows)
    
    So I'm curious if we can have the same idea for ADMIN of a role?  The owner can revoke the role from themselves, and they can also grant it back.  Would that be acceptable?
    
    > Perhaps instead of starting from the 'owner' concept, we start from the
    > question about the kinds of things we want roles to be able to do and
    > perhaps that will help inform the terminology.
    > 
    > - Create new roles
    > - Drop an existing role
    > - Drop objects which belong to a role
    > - Lock existing roles
    > - Change/reset the PW of existing roles
    > - Give roles to other roles
    > - Revoke access to some roles from other roles
    > - Give select role attributes to a role
    > - Revoke role attributes from a role
    > - Traditional role-based access control (group memberships, SET ROLE)
    
    I agree we want the ability to do these things, and not as a single CREATEROLE privilege, but separable.  The pre-v8 patch was separating only one who the role owner was, but v8 is attempting to separate these further, and I think that's the right way to go.
    
    > Certain of the above are already covered by the existing role membership
    > system and with the admin option, though there's definitely an argument
    > to be made as to if that is as capable as we'd like it to be (there's no
    > way to, today at least, GRANT *just* the admin option, for example, and
    > maybe that's something that it would actually be sensible to support).
    
    I think the ADMIN stuff *would* be the way to go, but for it's weird self-administration feature.  That to me seems to kill the idea.  What do you think?
    
    > Perhaps there is a need to have a user who has all of the above
    > capabilities and maybe that would be an 'owner' or 'manager', but as I
    > tried to illustrate above, there's definitely use-cases for giving
    > a role only some of the above capabilities rather than all of them
    > together at once.
    
    I'm using the terms "owner"/"manager" without regard for whether they have all those abilities or just some of them.  However, I think these terms don't apply for just the traditional ADMIN option on the role.  In that case, calling it "ownership" or "managership" is inappropriate.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  53. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-02-01T21:10:13Z

    On 1/31/22 12:18, Mark Dilger wrote:
    >
    >> On Jan 31, 2022, at 12:43 AM, Michael Banck <michael.banck@credativ.de> wrote:
    >> Ok, sure. I think this topic is hugely important and as I read the
    >> patch anyway, I added some comments, but yeah, we need to figure out
    >> the fundamentals first.
    > Right.
    >
    > Perhaps some background on this patch series will help.  The patch versions before v8 were creating an owner-owned relationship between the creator and the createe, and a lot of privileges were dependent on that ownership.  Stephen objected that we were creating parallel tracks on which the privilege system was running; things like belonging to a role or having admin on a role were partially conflated with owning a role.  He also objected that the pre-v8 patch sets allowed a creator role with the CREATEROLE privilege to give away any privilege the creator had, rather than needing to have GRANT or ADMIN option on the privilege being given.
    >
    > The v8-WIP patch is not a complete replacement for the pre-v8 patches.  It's just a balloon I'm floating to try out candidate solutions to some of Stephen's objections.  In the long run, I want the solution to Stephen's objections to not create problems for anybody who liked the way the pre-v8 patches worked (Robert, Andrew, and to some extent me.)
    >
    > In this WIP patch, for a creator to give *anything* away to a createe, the creator must have GRANT or ADMIN on the thing being given.  That includes attributes like BYPASSRLS, CREATEDB, LOGIN, etc., and also ADMIN on any role the createe is granted into.
    >
    > I tried to structure things for backwards compatibility, considering which things roles with CREATEROLE could give away historically.  It turns out they can give away most everything, but not SUPERUSER, BYPASSRLS, or REPLICATION.  So I structured the default privileges for CREATEROLE to match.  But I'm uncertain that design is any good, and your comments below suggest that you find it pretty hard to use.
    >
    > Part of the problem with trying to be backwards compatible is that we must break compatibility anyway, to address the problem that historically having CREATEROLE meant you effectively had ADMIN on all non-superuser roles.  That's got to change.  So in part I'm asking pgsql-hackers if partial backwards compatibility is worth the bother.
    >
    > If we don't go with backwards compatibility, then CREATEROLE would only allow you to create a new role, but not to give that role LOGIN, nor CREATEDB, etc.  You'd need to also have admin option on those things.  To create a role that can give those things away, you'd need to run something like:
    >
    > CREATE ROLE michael
    > 	CREATEROLE WITH ADMIN OPTION    -- can further give away "createrole"
    > 	CREATEDB WITH ADMIN OPTION    -- can further give away "createdb"
    > 	LOGIN WITH ADMIN OPTION    -- can further give away "login"
    > 	NOREPLICATION WITHOUT ADMIN OPTION    -- this would be implied anyway
    > 	NOBYPASSRLS WITHOUT ADMIN OPTION    -- this would be implied anyway
    > 	CONNECTION LIMIT WITH ADMIN OPTION    -- can specify connection limits
    > 	PASSWORD WITH ADMIN OPTION    -- can specify passwords
    > 	VALID UNTIL WITH ADMIN OPTION    -- can specify expiration
    >
    > (I'm on the fence about the phrase "WITH ADMIN OPTION" vs. the phrase "WITH GRANT OPTION".)
    >
    > Even then, when "michael" creates new roles, if he wants to be able to further administer those roles, he needs to remember to give himself ADMIN membership in that role at creation time.  After the role is created, if he doesn't have ADMIN, he can't give it to himself.  So, at create time, he needs to remember to do this:
    >
    > SET ROLE michael;
    > CREATE ROLE mark ADMIN michael;
    >
    > But that's still a bit strange, because "ADMIN michael" means that michael can grant other roles membership in "mark", not that michael can, for example, change mark's password.  If we don't want CREATEROLE to imply that you can mess around with arbitrary roles (rather than only roles that you created or have been transferred control over) then we need the concept of role ownership.  This patch doesn't go that far, so for now, only superusers can do those things.  Assuming some form of this patch is acceptable, the v9 series will resurrect some of the pre-v7 logic for role ownership and say that the owner can do those things.
    >
    
    This seems complicated. Maybe the previous proposal was too simple, but
    simplicity has some virtues. It seemed to me that more complex rules
    could possibly have been implemented for those who really needed them by
    using SECURITY DEFINER functions. The whole 'NOFOO WITH ADMIN OPTION'
    thing seems to me a bit like a POLA violation. Nevertheless I can
    probably live with it as long as it's *really* well documented. Even so
    I suspect it would be too complex for many, and they will just continue
    to use superusers to create and manage roles if possible.
    
    
    cheers
    
    
    andrew
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  54. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-02-01T22:27:35Z

    
    > On Feb 1, 2022, at 1:10 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
    > 
    > The whole 'NOFOO WITH ADMIN OPTION'
    > thing seems to me a bit like a POLA violation. Nevertheless I can
    > probably live with it as long as it's *really* well documented. Even so
    > I suspect it would be too complex for many, and they will just continue
    > to use superusers to create and manage roles if possible.
    
    I agree with the sentiment, but it might help to distinguish between surprising behavior vs. surprising grammar.
    
    In existing postgresql releases, having CREATEROLE means you can give away most attributes, including ones you yourself don't have (createdb, login).  So we already have the concept of NOFOO WITH ADMIN OPTION, we just don't call it that.  In pre-v8 patches on this thread, I got rid of that; you *must* have the attribute to give it away.  But maybe that was too restrictive, and we need a way to specify, attribute by attribute, how this works.  Is this just a problem of surprising grammar?  Is it surprising behavior?  If the latter, I'm inclined to give up this WIP as having been a bad move.  If the former, I'll try to propose some less objectionable grammar.
     
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  55. Re: CREATEROLE and role ownership hierarchies

    Andrew Dunstan <andrew@dunslane.net> — 2022-02-01T23:38:49Z

    On 2/1/22 17:27, Mark Dilger wrote:
    >
    >> On Feb 1, 2022, at 1:10 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
    >>
    >> The whole 'NOFOO WITH ADMIN OPTION'
    >> thing seems to me a bit like a POLA violation. Nevertheless I can
    >> probably live with it as long as it's *really* well documented. Even so
    >> I suspect it would be too complex for many, and they will just continue
    >> to use superusers to create and manage roles if possible.
    > I agree with the sentiment, but it might help to distinguish between surprising behavior vs. surprising grammar.
    >
    > In existing postgresql releases, having CREATEROLE means you can give away most attributes, including ones you yourself don't have (createdb, login).  So we already have the concept of NOFOO WITH ADMIN OPTION, we just don't call it that.  In pre-v8 patches on this thread, I got rid of that; you *must* have the attribute to give it away.  But maybe that was too restrictive, and we need a way to specify, attribute by attribute, how this works.  Is this just a problem of surprising grammar?  Is it surprising behavior?  If the latter, I'm inclined to give up this WIP as having been a bad move.  If the former, I'll try to propose some less objectionable grammar.
    >  
    >
    
    Certainly the grammar would need to be better. But I'm not sure any
    grammar that expresses what is supported here is not going to be
    confusing, because the underlying scheme seems complex. But I'm
    persuadable. I'd like to hear from others on the subject.
    
    
    cheers
    
    
    andrew
    
    --
    Andrew Dunstan
    EDB: https://www.enterprisedb.com
    
    
    
    
    
  56. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-02-02T16:45:29Z

    On Tue, Feb 1, 2022 at 6:38 PM Andrew Dunstan <andrew@dunslane.net> wrote:
    > > In existing postgresql releases, having CREATEROLE means you can give away most attributes, including ones you yourself don't have (createdb, login).  So we already have the concept of NOFOO WITH ADMIN OPTION, we just don't call it that.  In pre-v8 patches on this thread, I got rid of that; you *must* have the attribute to give it away.  But maybe that was too restrictive, and we need a way to specify, attribute by attribute, how this works.  Is this just a problem of surprising grammar?  Is it surprising behavior?  If the latter, I'm inclined to give up this WIP as having been a bad move.  If the former, I'll try to propose some less objectionable grammar.
    > >
    >
    > Certainly the grammar would need to be better. But I'm not sure any
    > grammar that expresses what is supported here is not going to be
    > confusing, because the underlying scheme seems complex. But I'm
    > persuadable. I'd like to hear from others on the subject.
    
    Well, we've been moving more and more in the direction of using
    predefined roles to manage access. The things that are basically
    Boolean flags on the role are mostly legacy stuff. So my tentative
    opinion (and I'm susceptible to being persuaded that I'm wrong here)
    is that putting a lot of work into fleshing out that infrastructure
    does not necessarily make a ton of sense. Are we ever going to add
    even one more flag that works that way?
    
    Also, any account that can create roles is a pretty high-privilege
    account. Maybe it's superuser, or maybe not, but it's certainly
    powerful. In my opinion, that makes fine distinctions here less
    important. Is there really an argument for saying "well, we're going
    to let you bypass RLS, but we're not going to let you give that
    privilege to others"? It seems contrived to think of restricting a
    role that is powerful enough to create whole new accounts in such a
    way. I'm not saying that someone couldn't have a use case for it, but
    I think it'd be a pretty thin use case.
    
    In short, I think it makes tons of sense to say that CREATEROLE lets
    you give to others those role flags which you have, but not the ones
    you lack. However, to me, it feels like overengineering to distinguish
    between things you have and can give away, things you have and can't
    give away, and things you don't even have.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  57. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-02-02T19:52:07Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Jan 31, 2022, at 10:50 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > > Supporting that through ADMIN is one option, another would be a
    > > 'DROPROLE' attribute, though we'd want a way to curtail that from being
    > > able to be used for just any role and that does lead down a path similar
    > > to ownership or just generally the concept that some roles have certain
    > > rights over certain other roles (whether you create them or not...).
    > 
    > I've been operating under the assumption that I have a lot more freedom to create new features than to change how existing features behave, for two reasons: backwards compatibility and sql-spec compliance.
    
    I agree that those are concerns that need to be considered, though I'm
    more concerned about the SQL compliance and less about backwards
    compatibility in this case.  For one thing, I'm afraid that we're not as
    compliant as we really should be and that should really drive us to make
    change here anyway, to get closer to what the spec calls for.
    
    > Changing how having ADMIN on a role works seems problematic for both those reasons.  My family got me socks for Christmas, not what I actually wanted, a copy of the SQL-spec.  So I'm somewhat guessing here.  But I believe we'd have problems if we "fixed" the part where a role can revoke ADMIN from others on themselves.  Whatever we have, whether we call it "ownership", it can't be something a role can unilaterally revoke.
    > 
    > As for a 'DROPROLE' attribute, I don't think that gets us anywhere.  You don't seem to think so, either.  So that leaves us with "ownership", perhaps by another word?  I only chose that word because it's what we use elsewhere, but if we want to call it "managementship" and "manager" or whatever, that's fine.  I'm not to the point of debating the terminology just yet.  I'm still trying to get the behavior nailed down.
    
    Yeah, didn't mean to imply that those were great ideas or that I was
    particularly advocating for them, but just to bring up some other ideas
    to try and get more thought going into this.
    
    > > I do think there's a lot of value in being able to segregate certain
    > > rights- consider that you may want a role that's able to create other
    > > roles, perhaps grant them into some set of roles, can lock those roles
    > > (prevent them from logging in, maybe do a password reset, something like
    > > that), but which *isn't* able to drop those roles (and all their
    > > objects) as that's dangerous and mistakes can certainly happen, or be
    > > able to become that role because the creating role simply doesn't have
    > > any need to be able to do that (or desire to in many cases, as we
    > > discussed in the landlord-vs-tenant sub-thread).
    > 
    > I'm totally on the same page.  Your argument upthread about wanting any malfeasance on the part of a service provider showing up in the audit logs was compelling.  Even for those things the "owner"/"manager" has the rights to do, we might want to make them choose to do it explicitly and not merely do it by accident.
    
    Glad to hear that.
    
    > > Naturally, you'd want *some* role to be able to drop that role (and one
    > > that doesn't have full superuser access) but that might be a role that's
    > > not able to create new roles or take over accounts.
    > 
    > I think it's important to go beyond the idea of a role attribute here.  It's not that role "bob" can drop roles.  It's that "bob" can drop *specific* roles, and for that, there has to be some kind of dependency tracked between "bob" and those other roles.  I'm calling that "ownership".  I think that language isn't just arbitrary, but actually helpful (technically, not politically) because REASSIGN OWNED should treat this kind of relationship exactly the same as it treats ownership of schemas, tables, functions, etc.
    
    I agree that role attributes isn't a good approach and that we should be
    moving away from it.
    
    I'm less sure that the existance of REASSIGN OWNED for schemas and
    tables and such should be the driver for what this capability of one
    role being able to drop another role needs to be called.
    
    > > Separation of concerns and powers and all of that is what we want to be
    > > going for here, more generically, which is why I was opposed to the
    > > blanket "owners have all rights of all roles they own" implementation.
    > 
    > I'm hoping to bring back, in v9, the idea of ownership/managership.  The real sticking point here is that we (Robert, Andrew, I, and possibly others) want to be able to drop in a non-superuser-creator-role into existing systems that use superuser for role management.  We'd like it to be as transparent a switch as possible.
    
    That description itself really makes me wonder about the sense of what
    was proposed.  Specifically "existing systems that use superuser for
    role management" doesn't make me picture a system where this manager
    role has any need to run SELECT statements against the tables created by
    the role that it created- SELECT'ing data from tables isn't in the
    purview of 'role management' (and before someone complains that pg_dump
    is part of this, I wouldn't call running pg_dump role management but
    rather data export or, used very loosely, 'backup').  To the end, I push
    back with: what exactly is this existing superuser doing that's role
    management?  The specific use-case, not just 'role management'.  What
    Joshua outlined was a reasonably defined use-case and that's what I'm
    trying to get at here.
    
    > With a superuser creating a role, that superuser can come back and muck with the role afterward, and the role can't revoke the superuser's right to do so.  It's not enough that a non-superuser-creator-role (henceforth, "manager") can grant itself ADMIN on the created role.  It also needs to be able to set passwords, transfer object ownerships to/from the role, grant the role into other roles or other roles into it, etc.  All of that has to be sandboxed such that the "manager" can't touch stuff outside the manager's sandbox, but within the sandbox, it shouldn't make any practical difference that the manager isn't actually a superuser.
    
    I appreciate that there needs to be a role who has certain rights over
    other roles and that those rights can't be revoked by the role.  The
    right to grant ADMIN on a create role is, itself, a right and what I was
    suggesting is that it could be one that the created role isn't able to
    revoke.  I disagree that the only possible role that could create some
    other role must necessarily be able to be essentially superuser when it
    comes to that created role.  I pointed out exactly the use-case where
    that isn't the case and nothing here has said anything to refute the
    existance of that use-case but seems to instead just focus on this idea
    that we must have a 'mini superuser'.
    
    > I think what I had in v7 was almost right.  I'm hoping that we just need to adjust things like the idea that managers always have implicit membership in and ADMIN on roles they manage.  I think that needs to be optional, and the audit logs could show if the manager granted themselves such things, as it might violate policy and be a red flag in the audit log.
    
    I'd like to also move in a direction where implicit membership in and
    ADMIN rights on the role is optional and potentially not even something
    that the creating role is able to grant themselves- though *some* role
    would need that ability and, ideally, it would be one that can be
    granted out individually without being a full superuser.
    
    > > That approach doesn't support the ability to have a relatively
    > > unprivileged role that's able to create other roles, which seems like a
    > > pretty important use-case for us to be considering.
    > 
    > I think we have that ability. It's just that the creator role isn't "relatively unprivileged" vis-a-vis the created role.  But that could be handled by creating the role and then transferring the ownership to some other role, or specifying in the CREATE ROLE command that the creator doesn't want those privileges, etc.  That requires some tinkering with the design, though, because the permission to perform the ownership transfer to that other role would need to be circumscribed to not give away other privileges, like the right to become that other role, or the specification that the creator disavows certain privileges over the created role might need to be something the creator could get back by force with some subsequent GRANT command, or ...?
    
    If it's not relatively unprivileged regarding the created role then it's
    not the ability which I outlined and therefore doesn't solve the
    described use-case.  I don't really feel that making it possible for the
    creating role to give up those rights actually solves for the attack
    vector that is someone gaining access to the creating role's access,
    which is what we're talking about trying to address by having a separate
    role whose only ability is to create roles which it then isn't able to
    become or otherwise impact.
    
    > > The terminology seems to also be driving us in a certain direction and I
    > > don't know that it's necessarily a good one.  That is- the term 'owner'
    > > implies certain things and maybe that's where some of the objection to
    > > my argument that owners shouldn't necessarily have all rights of the
    > > roles they 'own' comes from
    > 
    > I think it does follow pretty closely the concept of ownership of objects, though.  So closely, in fact, that I don't really see any daylight between the two concepts.
    
    Except that at least in the case we're contemplating, it's not desired
    for the creator of the role to have absolute authority over the created
    role.  That's a pretty big difference between roles and objects.  That
    we aren't seeing the distinction here is part of what I'm getting at
    with the above paragraph.
    
    > > (ok- I'll also put out there for general
    > > consideration that since we're talking about roles, and login roles are
    > > generally associated with people, that maybe 'owner' isn't a great term
    > > to use for this anyway ...).
    > 
    > Technically, we're talking about roles within computers owning other roles within computers, not about people owning people.  We already have a command called REASSIGN OWNED, and if we don't call this ownership, then that command gets really squirrelly.  Does it also reassign "managed"?
    
    Technically we were talking about PostgreSQL clusters that are just data
    files and processes within computers when it came to primaries and
    replicas, but other terms were used previously and we generally agreed
    that we should probably move away from those terms.  Today REASSIGN
    OWNED only talks about tables and views and other things which are
    quite distinct from individuals.
    
    > On the other hand, I'm not looking to create offense, so if this language seems unacceptable, perhaps you could propose something else?
    
    Manager might be one, but as I try to get at below, what I'm thinking
    about is a set of privileges that roles have and there isn't a concept
    of "owner" or "manager" but rather "role X has S, T, V privileges on
    roles A, B, C".  Conceptually perhaps we can consider a role that has
    ALL privileges over another role to be that role's 'manager' or 'owner'
    but we don't really even need to go into that once we've broken down the
    privileges.
    
    > >  I feel like the 'owner' concept came from
    > > the way we have table owners and function owners and database owners
    > > today rather than from a starting point of what do we wish to
    > > specifically enable.
    > 
    > Let's compare this to the idea of owning a table.  Can the owner of a table revoke SELECT from themselves? Yes, they can.  They can also give it back to themselves:
    > 
    > CREATE ROLE michael;
    > SET ROLE michael;
    > CREATE TABLE michael_table (i INTEGER);
    > REVOKE SELECT ON michael_table FROM PUBLIC, michael;
    > SELECT * FROM michael_table;
    > ERROR:  permission denied for table michael_table
    > GRANT SELECT ON michael_table TO michael;
    > SELECT * FROM michael_table;
    >  i 
    > ---
    > (0 rows)
    > 
    > So I'm curious if we can have the same idea for ADMIN of a role?  The owner can revoke the role from themselves, and they can also grant it back.  Would that be acceptable?
    
    That might be acceptable for the ADMIN privilege of a role itself though
    I'm not sure if that's really all that distinct from ADMIN.
    
    > > Perhaps instead of starting from the 'owner' concept, we start from the
    > > question about the kinds of things we want roles to be able to do and
    > > perhaps that will help inform the terminology.
    > > 
    > > - Create new roles
    > > - Drop an existing role
    > > - Drop objects which belong to a role
    > > - Lock existing roles
    > > - Change/reset the PW of existing roles
    > > - Give roles to other roles
    > > - Revoke access to some roles from other roles
    > > - Give select role attributes to a role
    > > - Revoke role attributes from a role
    > > - Traditional role-based access control (group memberships, SET ROLE)
    > 
    > I agree we want the ability to do these things, and not as a single CREATEROLE privilege, but separable.  The pre-v8 patch was separating only one who the role owner was, but v8 is attempting to separate these further, and I think that's the right way to go.
    
    Right, these should be separable, and I don't mean just the role
    attributes but rather the above as distinct privileges.  Whereby a role
    could have the right to create other roles but *not* have the right to
    drop roles (either the one they created, or perhaps any others, or maybe
    even to have some distinct set of roles that they're able to drop that's
    different from the roles they created), as an example.
    
    > > Certain of the above are already covered by the existing role membership
    > > system and with the admin option, though there's definitely an argument
    > > to be made as to if that is as capable as we'd like it to be (there's no
    > > way to, today at least, GRANT *just* the admin option, for example, and
    > > maybe that's something that it would actually be sensible to support).
    > 
    > I think the ADMIN stuff *would* be the way to go, but for it's weird self-administration feature.  That to me seems to kill the idea.  What do you think?
    
    I don't think the self-administration stuff that we have for role ADMIN
    rights is actually sensible and, while I know it's a backwards
    compatibility break, it's something we should fix.
    
    > > Perhaps there is a need to have a user who has all of the above
    > > capabilities and maybe that would be an 'owner' or 'manager', but as I
    > > tried to illustrate above, there's definitely use-cases for giving
    > > a role only some of the above capabilities rather than all of them
    > > together at once.
    > 
    > I'm using the terms "owner"/"manager" without regard for whether they have all those abilities or just some of them.  However, I think these terms don't apply for just the traditional ADMIN option on the role.  In that case, calling it "ownership" or "managership" is inappropriate.
    
    I don't think it's sensible to have one term that means "all" and then
    use that same term to also mean "only some".  That strikes me as
    confusing and I don't know that we need to even have an explicit name
    for the role that has 'all' of the rights or that we need to provide a
    name for one that only has 'some' of them- they're just roles that have
    certain privileges.  The question that we need to solve is how to give
    users the ability to choose what roles have which of the privileges that
    we've outlined above and agreed should be separable.
    
    THanks,
    
    Stephen
    
  58. Re: CREATEROLE and role ownership hierarchies

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-02-02T20:23:26Z

    
    > On Feb 2, 2022, at 11:52 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > The question that we need to solve is how to give
    > users the ability to choose what roles have which of the privileges that
    > we've outlined above and agreed should be separable.
    
    Ok, there are really two different things going on here, and the conversation keeps conflating them.  Maybe I'm wrong, but I think the conflation of these things is the primary problem preventing us from finishing up the design.
    
    Thing 1:   The superuser needs to be able to create roles who can create other roles.  Let's call them "creators".  Not every organization will want the same level of privilege to be given to a creator, or even that all creators have equal levels of privilege.  So when the superuser creates a creator, the superuser needs to be able to configure what exactly what that creator can do.  This includes which attributes the creator can give to new roles.  It *might* include whether the creator maintains a dependency link with the created role, called "ownership" or somesuch.  It *might* include whether the creator can create roles into which the creator is granted membership/administership.  But there really isn't any reason that these things should be all-or-nothing.  Maybe one creator maintains a dependency link with created roles, and that dependency link entails some privileges.  Maybe other creators do not maintain such a link.  It seems like superuser can define a creator in many different ways, as long as we nail down what those ways are, and what they mean.
    
    Thing 2:  The creator needs to be able to specify which attributes and role memberships are set up with for roles the creator creates.  To the extent that the creator has been granted the privilege to create yet more creators, this recurses to Thing 1.  But not all creators will have that ability.
    
    
    I think the conversation gets off topic and disagreement abounds when Thing 1 is assumed to be hardcoded, leaving just the details of Thing 2 to be discussed.
    
    It's perfectly reasonable (in my mind) that Robert, acting as superuser, may want to create a creator who acts like a superuser over the sandbox, while at the same time Stephen, acting as superuser, may want to create a creator who acts as a low privileged bot that only adds and removes roles, but cannot read their tables, SET ROLE to them, etc.
    
    I don't see any reason that Robert and Stephen can't both get what they want.  We just have to make Thing 1 flexible enough.
    
    Do you agree at least with this much?  If so, I think we can hammer out what to do about Thing 1 and get something committed in time for postgres 15.  If not, then I'm probably going to stop working on this until next year, because at this point, we don't have enough time to finish.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  59. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-02-02T20:49:48Z

    On Wed, Feb 2, 2022 at 3:23 PM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    > It's perfectly reasonable (in my mind) that Robert, acting as superuser, may want to create a creator who acts like a superuser over the sandbox, while at the same time Stephen, acting as superuser, may want to create a creator who acts as a low privileged bot that only adds and removes roles, but cannot read their tables, SET ROLE to them, etc.
    >
    > I don't see any reason that Robert and Stephen can't both get what they want.  We just have to make Thing 1 flexible enough.
    
    Hmm, that would be fine with me. I don't mind a bit if other people
    get what they want, as long as I can get what I want, too! In fact,
    I'd prefer it if other people also get what they want...
    
    That having been said, I have some reservations if it involves tightly
    coupling new features that we're trying to add to existing things that
    may or may not be that well designed, like the role-level INHERIT
    flag, or WITH ADMIN OPTION, or the not-properly maintained
    pg_auth_members.grantor column, or even the SQL standard. I'm not
    saying we should ignore any of those things and I don't think that we
    should ... but at the same time, we can't whether the feature does
    what people want it to do, either. If we do, this whole thing is
    really a complete waste of time. If a patch achieves infinitely large
    amounts of backward compatibility, standards compliance, and
    conformity with existing design but doesn't do the right stuff, forget
    it!
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  60. Re: CREATEROLE and role ownership hierarchies

    Maciek Sakrejda <m.sakrejda@gmail.com> — 2022-02-04T05:38:39Z

    I'm chiming in a little late here, but as someone who worked on a
    system to basically work around the lack of unprivileged CREATE ROLE
    for a cloud provider (I worked on the Heroku Data team for several
    years), I thought it might be useful to offer my perspective. This is,
    of course, not the only use case, but maybe it's useful to have
    something concrete. As a caveat, I don't know how current this still
    is (I no longer work there, though the docs [1] seem to still describe
    the same system), or if there are better ways to achieve the goals of
    a service provider.
    
    Broadly, the general use case is something like what Robert has
    sketched out in his e-mails. Heroku took care of setting up the
    database, archiving, replication, and any other system-level config.
    It would then keep the superuser credentials private, create a
    database, and a user that owned that database and had all the
    permissions we could grant it without compromising the integrity of
    the system. (We did not want customers to break their databases, both
    to ensure a better user experience and to avoid getting paged.)
    Initially, this meant customers got just the one database user because
    of CREATE ROLE's limitations.
    
    To work around that, at some point, we added an API that would CREATE
    ROLE for you, accessible through a dashboard and the Heroku CLI. This
    ran CREATE ROLE (or DROP ROLE) for you, but otherwise it largely let
    you configure the resulting roles as you pleased (using the original
    role we create for you). We wanted to avoid reinventing the wheel as
    much as possible, and the customer database (including the role
    configuration) was mostly a black box for us (we did manage some
    predefined permissions configurations through our dashboard, but the
    Postgres catalogs were the source of truth for that).
    
    Thinking about how this would fit into a potential non-superuser
    CREATE ROLE world, the sandbox superuser model discussed above covers
    this pretty well, though I share some of Robert's concerns around how
    this fits into existing systems.
    
    Hope this is useful feedback. Thanks for working on this!
    
    [1]: https://devcenter.heroku.com/articles/heroku-postgresql-credentials
    
    
    
    
  61. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-02-17T17:40:09Z

    On Mon, Jan 31, 2022 at 1:57 PM Joshua Brindle
    <joshua.brindle@crunchydata.com> wrote:
    > This is precisely the use case I am trying to accomplish with this
    > patchset, roughly:
    >
    > - An automated bot that creates users and adds them to the employees role
    > - Bot cannot access any employee (or other roles) table data
    > - Bot cannot become any employee
    > - Bot can disable the login of any employee
    >
    > Yes there are attack surfaces around the fringes of login, etc but
    > those can be mitigated with certificate authentication. My pg_hba
    > would require any role in the employees role to use cert auth.
    >
    > This would adequately mitigate many threats while greatly enhancing
    > user management.
    
    So, where do we go from here?
    
    I've been thinking about this comment a bit. On the one hand, I have
    some reservations about the phrase "the use case I am trying to
    accomplish with this patchset," because in the end, this is not your
    patch set. It's not reasonable to complain that a patch someone else
    wrote doesn't solve your problem; of course everyone writes patches to
    solve their own problems, or those of their employer, not other
    people's problems. And that's as it should be, else we will have few
    contributors. On the other hand, to the extent that this patch set
    makes things worse for a reasonable use case which you have in mind,
    that's an entirely legitimate complaint.
    
    After a bit of testing, it seems to me that as things stand today,
    things are nearly perfect for the use case that you have in mind. I
    would be interested to know whether you agree. If I set up an account
    and give it CREATEROLE, it can create users, and it can put them into
    the employees role, but it can't SET ROLE to any of those accounts. It
    can also ALTER ROLE ... NOLOGIN on any of those accounts. The only gap
    I see is that there are certain role-based flags which the CREATEROLE
    account cannot set: SUPERUSER, BYPASSRLS, REPLICATION. You might
    prefer a system where your bot account had the option to grant those
    privileges also, and I think that's a reasonable thing to want.
    
    However, I *also* think it's reasonable to want an account that can
    create roles but can't give to those roles membership in roles that it
    does not itself possess. Likewise, I think it's reasonable to want an
    account that can only drop roles which that account itself created.
    These kinds of requirements stem from a different use case than what
    you are talking about here, but they seem like fine things to want,
    and as far as I know we have pretty broad agreement that they are
    reasonable. It seems extremely difficult to make a convincing argument
    that this is not a thing which anyone should want to block:
    
    rhaas=> create role bob role pg_execute_server_program;
    CREATE ROLE
    
    Honestly, that seems like a big yikes from here. How is it OK to block
    "create role bob superuser" yet allow that command? I'm inclined to
    think that's just broken. Even if the role were pg_write_all_data
    rather than pg_execute_server_program, it's still a heck of a lot of
    power to be handing out, and I don't see how anyone could make a
    serious argument that we shouldn't have an option to restrict that.
    
    Let me separate the two features that I just mentioned and talk about
    them individually:
    
    1. Don't allow a CREATEROLE user to give out membership in groups
    which that user does not possess. Leaving aside the details of any
    previously-proposed patches and just speaking theoretically, how can
    this be implemented? I can think of a few ideas. We could (1A) just
    change CREATEROLE to work that way, but IIUC that would break the use
    case you outline here, so I guess that's off the table unless I am
    misunderstanding the situation. We could also (1B) add a second role
    attribute with a different name, like, err, CREATEROLEWEAKLY, that
    behaves in that way, leaving the existing one untouched. But we could
    also take it a lot further, because someone might want to let an
    account hand out a set of privileges which corresponds neither to the
    privileges of that account nor to the full set of available
    privileges. That leads to another idea: (1C) implement an in-database
    system that lets you specify which privileges an account has, and,
    separately, which ones it can assign to others. I am skeptical of that
    idea because it seems really, really complicated, not only from an
    implementation standpoint but even just from a user-experience
    standpoint. Suppose user 'userbot' has rights to grant a suitable set
    of groups to the new users that it creates -- but then someone creates
    a new group. Should that also be added to the things 'userbot' can
    grant or not? What if we have 'userbot1' through 'userbot6' and each
    of them can grant a different set of roles? I wouldn't mind (1D)
    providing a hook that allows the system administrator to install a
    loadable module that can enforce any rules it likes, but it seems way
    too complicated to me to expose all of this configuration as SQL,
    especially because for what I want to do, either (1A) or (1B) is
    adequate, and (1B) is a LOT simpler than (1C). It also caters to what
    I believe to be a common thing to want, without prejudice to the
    possibility that other people want other things.
    
    Joshua, what is your opinion on this point?
    
    2. Only allow a CREATEROLE user to drop users which that account
    created, and not just any role that isn't a superuser. Again leaving
    aside previous proposals, this cannot be implemented without providing
    some means by which we know which CREATEROLE user created which other
    user. I believe there are a variety of words we could use to describe
    that linkage, and I don't deeply care which ones we pick, although I
    have my own preferences. We could speak of the CREATEROLE user being
    the owner, manager, or administrator of the created role. We could
    speak of a new kind of object, a TENANT, of which the CREATEROLE user
    is the administrator and to which the created user is linked. I
    proposed this previously and it's still my favorite idea. There are no
    doubt other options as well. But it's axiomatic that we cannot
    restrict the rights of a CREATEROLE user to drop other roles to a
    subset of roles without having some way to define which subset is at
    issue.
    
    Now, my motivation for wanting this feature is pretty simple: I want
    to have something that feels like a superuser but isn't a full
    superuser, and can't interfere with accounts set up by the service
    provider, but can do whatever they want to the other ones. But I think
    this is potentially useful in the userbot case that you (Joshua)
    mention as well, because it seems like it could be pretty desirable to
    have a certain list of users which the userbot can't remove, just for
    safety, either to limit the damage if somebody gets into that account,
    or just to keep the bot from going nuts and doing something it
    shouldn't in the event of a programming error. Now, if you DON'T care
    about the userbot being able to access this functionality, that's fine
    with me, because then there's nothing left to do but argue about what
    to call the linkage between the CREATEROLE user and the created user.
    Your userbot need not participate in whatever system we decide on, and
    things are no worse for that use case than they are today.
    
    But if you DO want the userbot to be able to access that
    functionality, then things are more complicated, because now the
    linkage has to be special-purpose. In that scenario, we can't say that
    the right of a CREATEROLE user to drop a certain other role implies
    having the privileges of that other role, because in your use case,
    you don't want that, whereas in mine, I do. What makes this
    particularly ugly is that we can't, as things currently stand, use a
    role as the grouping mechanism, because of the fact that a role can
    revoke membership in itself from some other role. It will not do for
    roles to remove themselves from the set of roles that the CREATEROLE
    user can drop. If we changed that behavior, then perhaps we could just
    define a way to say that role X can drop roles if they are members of
    group G. In my tenant scenario, G would be granted to X, and in your
    userbot scenario, it wouldn't. Everybody wins, except for any people
    who like the ability of roles to revoke themselves from any group
    whatsoever.
    
    So that leads to these questions: (2A) Do you care about restricting
    which roles the userbot can drop? (2B) If yes, do you endorse
    restricting the ability of roles to revoke themselves from other
    roles?
    
    I think that we don't have any great problems here, at least as far as
    this very specific issue is concerned, if either the answer to (2A) is
    no or the answer to (2B) is yes. However, if the answer to (2A) is yes
    and the answer to (2B) is no, there are difficulties. Evidently in
    that case we need some new kind of thing that behaves mostly likes a
    group of roles but isn't actually a group of roles -- and that thing
    needs to prohibit self-revocation. Given what I've written above, you
    may be able to guess my preferred solution: let's call it a TENANT.
    Then, my pseudo-super-user can have permission to (i) create roles in
    that tenant, (ii) drop roles in that tenant, and (iii) assume the
    privileges of roles in that tenant -- and your userbot can have
    privileges to do (i) and (ii) but not (iii). All we need do is add a
    roltenant column to pg_authid and find three bits someplace
    corresponding to (i)-(iii), and we are home.
    
    Thoughts?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  62. Re: CREATEROLE and role ownership hierarchies

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-02-22T15:54:04Z

    On Thu, Feb 17, 2022 at 12:40 PM Robert Haas <robertmhaas@gmail.com> wrote:
    >
    > On Mon, Jan 31, 2022 at 1:57 PM Joshua Brindle
    > <joshua.brindle@crunchydata.com> wrote:
    > > This is precisely the use case I am trying to accomplish with this
    > > patchset, roughly:
    > >
    > > - An automated bot that creates users and adds them to the employees role
    > > - Bot cannot access any employee (or other roles) table data
    > > - Bot cannot become any employee
    > > - Bot can disable the login of any employee
    > >
    > > Yes there are attack surfaces around the fringes of login, etc but
    > > those can be mitigated with certificate authentication. My pg_hba
    > > would require any role in the employees role to use cert auth.
    > >
    > > This would adequately mitigate many threats while greatly enhancing
    > > user management.
    >
    > So, where do we go from here?
    >
    > I've been thinking about this comment a bit. On the one hand, I have
    > some reservations about the phrase "the use case I am trying to
    > accomplish with this patchset," because in the end, this is not your
    > patch set. It's not reasonable to complain that a patch someone else
    > wrote doesn't solve your problem; of course everyone writes patches to
    > solve their own problems, or those of their employer, not other
    > people's problems. And that's as it should be, else we will have few
    > contributors. On the other hand, to the extent that this patch set
    > makes things worse for a reasonable use case which you have in mind,
    > that's an entirely legitimate complaint.
    
    Yes, absolutely. It is my understanding that generally a community
    consensus is attempted, I was throwing my (and Crunchy's) use case out
    there as a possible goal, and I have spent time reviewing and testing
    the patch, so I think that is fair. Obviously I am not in the position
    to stipulate hard requirements.
    
    > After a bit of testing, it seems to me that as things stand today,
    > things are nearly perfect for the use case that you have in mind. I
    > would be interested to know whether you agree. If I set up an account
    > and give it CREATEROLE, it can create users, and it can put them into
    > the employees role, but it can't SET ROLE to any of those accounts. It
    > can also ALTER ROLE ... NOLOGIN on any of those accounts. The only gap
    > I see is that there are certain role-based flags which the CREATEROLE
    > account cannot set: SUPERUSER, BYPASSRLS, REPLICATION. You might
    > prefer a system where your bot account had the option to grant those
    > privileges also, and I think that's a reasonable thing to want.
    
    I believe the only issue in the existing patchset was that membership
    was required in employees was required for the Bot, but I can apply
    the current patchset and test it out more in a bit.
    
    > However, I *also* think it's reasonable to want an account that can
    > create roles but can't give to those roles membership in roles that it
    > does not itself possess. Likewise, I think it's reasonable to want an
    > account that can only drop roles which that account itself created.
    > These kinds of requirements stem from a different use case than what
    > you are talking about here, but they seem like fine things to want,
    > and as far as I know we have pretty broad agreement that they are
    > reasonable. It seems extremely difficult to make a convincing argument
    > that this is not a thing which anyone should want to block:
    >
    > rhaas=> create role bob role pg_execute_server_program;
    > CREATE ROLE
    >
    > Honestly, that seems like a big yikes from here. How is it OK to block
    > "create role bob superuser" yet allow that command? I'm inclined to
    > think that's just broken. Even if the role were pg_write_all_data
    > rather than pg_execute_server_program, it's still a heck of a lot of
    > power to be handing out, and I don't see how anyone could make a
    > serious argument that we shouldn't have an option to restrict that.
    
    Yes, agreed 100%. To be clear, I do not want Bot in the above use case
    to be able to add any role other than employees to new roles it
    creates. So we are in complete agreement there, the only difference is
    that I do not want Bot to be able to become those roles (or use any
    access granted via those roles), it's only job is to manage roles, not
    look at data.
    
    > Let me separate the two features that I just mentioned and talk about
    > them individually:
    >
    > 1. Don't allow a CREATEROLE user to give out membership in groups
    > which that user does not possess. Leaving aside the details of any
    > previously-proposed patches and just speaking theoretically, how can
    > this be implemented? I can think of a few ideas. We could (1A) just
    > change CREATEROLE to work that way, but IIUC that would break the use
    > case you outline here, so I guess that's off the table unless I am
    > misunderstanding the situation. We could also (1B) add a second role
    > attribute with a different name, like, err, CREATEROLEWEAKLY, that
    > behaves in that way, leaving the existing one untouched. But we could
    > also take it a lot further, because someone might want to let an
    > account hand out a set of privileges which corresponds neither to the
    > privileges of that account nor to the full set of available
    > privileges. That leads to another idea: (1C) implement an in-database
    > system that lets you specify which privileges an account has, and,
    > separately, which ones it can assign to others. I am skeptical of that
    > idea because it seems really, really complicated, not only from an
    > implementation standpoint but even just from a user-experience
    > standpoint. Suppose user 'userbot' has rights to grant a suitable set
    > of groups to the new users that it creates -- but then someone creates
    > a new group. Should that also be added to the things 'userbot' can
    > grant or not? What if we have 'userbot1' through 'userbot6' and each
    > of them can grant a different set of roles? I wouldn't mind (1D)
    > providing a hook that allows the system administrator to install a
    > loadable module that can enforce any rules it likes, but it seems way
    > too complicated to me to expose all of this configuration as SQL,
    > especially because for what I want to do, either (1A) or (1B) is
    > adequate, and (1B) is a LOT simpler than (1C). It also caters to what
    > I believe to be a common thing to want, without prejudice to the
    > possibility that other people want other things.
    
    if 1A worked for admins, or members I think it may work (i.e., Bot is
    admin of employees but not a member of employees and therefore can
    manage employees but not become them or read their tables)
    
    For example, today this works (in master):
    
    postgres=# CREATE USER creator password 'a';
    CREATE ROLE
    postgres=# CREATE ROLE employees ADMIN creator NOLOGIN;
    CREATE ROLE
    
    as creator:
    postgres=> CREATE USER joshua IN ROLE employees PASSWORD 'a';
    ERROR:  permission denied to create role
    
    as superuser:
    postgres=# CREATE USER joshua LOGIN PASSWORD 'a';
    CREATE ROLE
    
    as creator:
    postgres=> GRANT employees TO joshua;
    GRANT ROLE
    postgres=> SET ROLE joshua;
    ERROR:  permission denied to set role "joshua"
    postgres=> SET ROLE employees;
    SET
    
    So ADMIN of a role can add membership, but not create, and
    unfortunately can SET ROLE to employees.
    
    Can ADMIN mean "can create and drop roles with membership of this role
    but not implicitly be a member of the role"?
    
    I think Stephen was advocating for this but wanted to look at the SQL
    spec to see if it conflicts.
    
    The current (v8) patch conflates membership and admin:
    
    postgres=# CREATE USER user_creator CREATEROLE WITHOUT ADMIN OPTION
    PASSWORD 'a';
    CREATE ROLE
    postgres=# CREATE ROLE employees ADMIN user_creator NOLOGIN;
    CREATE ROLE
    
    (Note I never GRANTED employees to user_creator):
    
    postgres=# \du
                                          List of roles
      Role name   |                         Attributes
        |  Member of
    --------------+------------------------------------------------------------+-------------
     employees    | Cannot login                                               | {}
     postgres     | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
     user_creator | Create role
        | {employees}
    
    postgres=# REVOKE employees FROM user_creator;
    REVOKE ROLE
    
    as user_creator:
    postgres=> CREATE USER joshua2 IN ROLE employees;
    ERROR:  must have admin option on role "employees"
    
    This seems non-intuitive to me, employees was never granted, but after
    being revoked the admin option is gone.
    
    > Joshua, what is your opinion on this point?
    >
    > 2. Only allow a CREATEROLE user to drop users which that account
    > created, and not just any role that isn't a superuser. Again leaving
    > aside previous proposals, this cannot be implemented without providing
    > some means by which we know which CREATEROLE user created which other
    > user. I believe there are a variety of words we could use to describe
    > that linkage, and I don't deeply care which ones we pick, although I
    > have my own preferences. We could speak of the CREATEROLE user being
    > the owner, manager, or administrator of the created role. We could
    > speak of a new kind of object, a TENANT, of which the CREATEROLE user
    > is the administrator and to which the created user is linked. I
    > proposed this previously and it's still my favorite idea. There are no
    > doubt other options as well. But it's axiomatic that we cannot
    > restrict the rights of a CREATEROLE user to drop other roles to a
    > subset of roles without having some way to define which subset is at
    > issue.
    >
    > Now, my motivation for wanting this feature is pretty simple: I want
    > to have something that feels like a superuser but isn't a full
    > superuser, and can't interfere with accounts set up by the service
    > provider, but can do whatever they want to the other ones. But I think
    > this is potentially useful in the userbot case that you (Joshua)
    > mention as well, because it seems like it could be pretty desirable to
    > have a certain list of users which the userbot can't remove, just for
    > safety, either to limit the damage if somebody gets into that account,
    > or just to keep the bot from going nuts and doing something it
    > shouldn't in the event of a programming error. Now, if you DON'T care
    > about the userbot being able to access this functionality, that's fine
    > with me, because then there's nothing left to do but argue about what
    > to call the linkage between the CREATEROLE user and the created user.
    > Your userbot need not participate in whatever system we decide on, and
    > things are no worse for that use case than they are today.
    
    Not being able to drop roles that weren't created or managed by the
    Bot is good. Being able to specify exactly what roles the Bot can drop
    is ideal, we may want no automated drops whatsoever (just automated
    disabling, to constrain possible damage).
    
    > But if you DO want the userbot to be able to access that
    > functionality, then things are more complicated, because now the
    > linkage has to be special-purpose. In that scenario, we can't say that
    > the right of a CREATEROLE user to drop a certain other role implies
    > having the privileges of that other role, because in your use case,
    > you don't want that, whereas in mine, I do. What makes this
    > particularly ugly is that we can't, as things currently stand, use a
    > role as the grouping mechanism, because of the fact that a role can
    > revoke membership in itself from some other role. It will not do for
    > roles to remove themselves from the set of roles that the CREATEROLE
    > user can drop. If we changed that behavior, then perhaps we could just
    > define a way to say that role X can drop roles if they are members of
    > group G. In my tenant scenario, G would be granted to X, and in your
    > userbot scenario, it wouldn't. Everybody wins, except for any people
    > who like the ability of roles to revoke themselves from any group
    > whatsoever.
    >
    > So that leads to these questions: (2A) Do you care about restricting
    > which roles the userbot can drop? (2B) If yes, do you endorse
    > restricting the ability of roles to revoke themselves from other
    > roles?
    
    2A, yes
    2B, yes, and IIUC this already exists:
    postgres=> select current_user;
     current_user
    --------------
     joshua
    (1 row)
    
    postgres=> REVOKE employees FROM joshua;
    ERROR:  must have admin option on role "employees"
    
    > I think that we don't have any great problems here, at least as far as
    > this very specific issue is concerned, if either the answer to (2A) is
    > no or the answer to (2B) is yes. However, if the answer to (2A) is yes
    > and the answer to (2B) is no, there are difficulties. Evidently in
    > that case we need some new kind of thing that behaves mostly likes a
    > group of roles but isn't actually a group of roles -- and that thing
    > needs to prohibit self-revocation. Given what I've written above, you
    > may be able to guess my preferred solution: let's call it a TENANT.
    > Then, my pseudo-super-user can have permission to (i) create roles in
    > that tenant, (ii) drop roles in that tenant, and (iii) assume the
    > privileges of roles in that tenant -- and your userbot can have
    > privileges to do (i) and (ii) but not (iii). All we need do is add a
    > roltenant column to pg_authid and find three bits someplace
    > corresponding to (i)-(iii), and we are home.
    
    I believe this works.
    
    > Thoughts?
    >
    > --
    > Robert Haas
    > EDB: http://www.enterprisedb.com
    
    
    
    
  63. Re: CREATEROLE and role ownership hierarchies

    Stephen Frost <sfrost@snowman.net> — 2022-02-28T19:09:23Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > 1. Don't allow a CREATEROLE user to give out membership in groups
    > which that user does not possess. Leaving aside the details of any
    > previously-proposed patches and just speaking theoretically, how can
    > this be implemented? I can think of a few ideas. We could (1A) just
    > change CREATEROLE to work that way, but IIUC that would break the use
    > case you outline here, so I guess that's off the table unless I am
    > misunderstanding the situation. We could also (1B) add a second role
    > attribute with a different name, like, err, CREATEROLEWEAKLY, that
    > behaves in that way, leaving the existing one untouched. But we could
    > also take it a lot further, because someone might want to let an
    > account hand out a set of privileges which corresponds neither to the
    > privileges of that account nor to the full set of available
    > privileges. That leads to another idea: (1C) implement an in-database
    > system that lets you specify which privileges an account has, and,
    > separately, which ones it can assign to others. I am skeptical of that
    > idea because it seems really, really complicated, not only from an
    > implementation standpoint but even just from a user-experience
    > standpoint. Suppose user 'userbot' has rights to grant a suitable set
    > of groups to the new users that it creates -- but then someone creates
    > a new group. Should that also be added to the things 'userbot' can
    > grant or not? What if we have 'userbot1' through 'userbot6' and each
    > of them can grant a different set of roles? I wouldn't mind (1D)
    > providing a hook that allows the system administrator to install a
    > loadable module that can enforce any rules it likes, but it seems way
    > too complicated to me to expose all of this configuration as SQL,
    > especially because for what I want to do, either (1A) or (1B) is
    > adequate, and (1B) is a LOT simpler than (1C). It also caters to what
    > I believe to be a common thing to want, without prejudice to the
    > possibility that other people want other things.
    
    I'm generally in support of changing CREATEROLE to only allow roles that
    the role with CREATEROLE is an admin of to be allowed as part of the
    command (throwing an error in other cases).  That doesn't solve other
    use-cases which would certainly be nice to solve but it would at least
    reduce the shock people have when they discover how CREATEROLE actually
    works (that is, the way we document it to work, but that's ultimately
    not what people expect).
    
    If that's all this was about then that would be one thing, but folks are
    interested in doing more here and that's good because there's a lot here
    that could be (and I'd say should be..) done.
    
    I'm not a fan of 1B.  In general, I'm in support of 1C but I don't feel
    that absolutely everything must be done for 1C right from the start-
    rather, I would argue that we'd be better off building a way for 1C to
    be improved upon in the future, akin to our existing privilege system
    where we've added things like the ability to GRANT TRUNCATE rights which
    didn't originally exist.  I don't think 1D is a reasonable way to
    accomplish that though, particularly as this involves storing
    information about roles which needs to be cleaned up if those roles are
    removed or modified.  I also don't really agree with the statement that
    this ends up being too complicated for SQL.
    
    > 2. Only allow a CREATEROLE user to drop users which that account
    > created, and not just any role that isn't a superuser. Again leaving
    > aside previous proposals, this cannot be implemented without providing
    > some means by which we know which CREATEROLE user created which other
    > user. I believe there are a variety of words we could use to describe
    > that linkage, and I don't deeply care which ones we pick, although I
    > have my own preferences. We could speak of the CREATEROLE user being
    > the owner, manager, or administrator of the created role. We could
    > speak of a new kind of object, a TENANT, of which the CREATEROLE user
    > is the administrator and to which the created user is linked. I
    > proposed this previously and it's still my favorite idea. There are no
    > doubt other options as well. But it's axiomatic that we cannot
    > restrict the rights of a CREATEROLE user to drop other roles to a
    > subset of roles without having some way to define which subset is at
    > issue.
    
    I don't think it's a great plan to limit who is allowed to DROP roles to
    be just those that a given role created.  I also don't like the idea of
    introducing a single field for owner/manager/tenant/whatever to the role
    system- instead we should add other ways that roles can be associated to
    each other by extending the existing system that we have for that, which
    is role membership.  Role membership today is pretty limited but I don't
    see any reason why we couldn't improve on that in a way that's flexible
    and allows us to define new associations in the future.  The biggest
    difference between a 'tenant' or such as proposed vs. a role association
    is in where the information is tracked and what exactly it means.
    Saying "I want a owner" or such is easy because it's basically punting
    on the complciated bit of asking the question: what does that *mean*
    when it comes to what rights that includes vs. doesn't?  What if I only
    want some of those rights to be given away but not all of them?  We have
    that system for tables/schemas/etc, and it hasn't been great as we've
    seen through the various requests to add things like GRANT TRUNCATE.
    
    > But if you DO want the userbot to be able to access that
    > functionality, then things are more complicated, because now the
    > linkage has to be special-purpose. In that scenario, we can't say that
    > the right of a CREATEROLE user to drop a certain other role implies
    > having the privileges of that other role, because in your use case,
    > you don't want that, whereas in mine, I do. What makes this
    > particularly ugly is that we can't, as things currently stand, use a
    > role as the grouping mechanism, because of the fact that a role can
    > revoke membership in itself from some other role. It will not do for
    > roles to remove themselves from the set of roles that the CREATEROLE
    > user can drop. If we changed that behavior, then perhaps we could just
    > define a way to say that role X can drop roles if they are members of
    > group G. In my tenant scenario, G would be granted to X, and in your
    > userbot scenario, it wouldn't. Everybody wins, except for any people
    > who like the ability of roles to revoke themselves from any group
    > whatsoever.
    
    The ability of a role to revoke itself from some other role is just
    something we need to accept as being a change that needs to be made, and
    I do believe that such a change is supported by the standard, in that a
    REVOKE will only work if you have the right to make it as the user who
    performed the GRANT in the first place.
    
    > So that leads to these questions: (2A) Do you care about restricting
    > which roles the userbot can drop? (2B) If yes, do you endorse
    > restricting the ability of roles to revoke themselves from other
    > roles?
    
    As with Joshua, and as hopefully came across from the above discussion,
    I'm also a 'yes and yes' on these two.
    
    > I think that we don't have any great problems here, at least as far as
    > this very specific issue is concerned, if either the answer to (2A) is
    > no or the answer to (2B) is yes. However, if the answer to (2A) is yes
    > and the answer to (2B) is no, there are difficulties. Evidently in
    > that case we need some new kind of thing that behaves mostly likes a
    > group of roles but isn't actually a group of roles -- and that thing
    > needs to prohibit self-revocation. Given what I've written above, you
    > may be able to guess my preferred solution: let's call it a TENANT.
    > Then, my pseudo-super-user can have permission to (i) create roles in
    > that tenant, (ii) drop roles in that tenant, and (iii) assume the
    > privileges of roles in that tenant -- and your userbot can have
    > privileges to do (i) and (ii) but not (iii). All we need do is add a
    > roltenant column to pg_authid and find three bits someplace
    > corresponding to (i)-(iii), and we are home.
    
    Where are those bits going to go though..?  I don't think they should go
    into pg_authid, nor do I feel that this 'tenant' or such should go there
    either because pg_authid is about describing individual roles, not about
    role associations.  Instead, I'd suggest that those bits go into
    pg_auth_members in the form of additional columns to describe the role
    associations.  That is, instead of the existance of a row in
    pg_auth_members meaning that one role has membership in another role, we
    give users the choice of if that's the case or not with a separate
    column.  That would then neatly give us a way for a role to have admin
    rights over another role but not membership in that role.  We could then
    further extend this by adding other columns to pg_auth_members for other
    rights as users decide they need them- such as the ability for a role to
    DROP some set of roles.
    
    > 2A, yes
    > 2B, yes, and IIUC this already exists:
    > postgres=> select current_user;
    >  current_user
    > --------------
    >  joshua
    > (1 row)
    > 
    > postgres=> REVOKE employees FROM joshua;
    > ERROR:  must have admin option on role "employees"
    
    That's not the right direction though, or, at least, might not be in the
    case being discussed (though, I suppose, we could discuss that..).  In
    what you're showing, employees doesn't have the rights of joshua, but
    joshua has the rights of employees.  If, instead, joshua was GRANT'd to
    admin and joshua decided that they didn't care for that, they can:
    
    => select current_user;
     current_user 
    --------------
     joshua
    (1 row)
    
    => \du
                                        List of roles
     Role name |                         Attributes                         |  Member of  
    -----------+------------------------------------------------------------+-------------
     admin     | Cannot login                                               | {joshua}
     employees | Cannot login                                               | {}
     joshua    |                                                            | {employees}
     sfrost    | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
    
    => revoke joshua from admin;
    REVOKE ROLE
    
    =*> \du
                                        List of roles
     Role name |                         Attributes                         |  Member of  
    -----------+------------------------------------------------------------+-------------
     admin     | Cannot login                                               | {}
     employees | Cannot login                                               | {}
     joshua    |                                                            | {employees}
     sfrost    | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
    
    Even though, in this case, it was 'sfrost' (a superuser) who GRANT'd
    joshua to admin.
    
    Thanks,
    
    Stephen
    
  64. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-03-01T18:13:55Z

    [ Been away, catching up on email. ]
    
    On Tue, Feb 22, 2022 at 10:54 AM Joshua Brindle
    <joshua.brindle@crunchydata.com> wrote:
    > Yes, absolutely. It is my understanding that generally a community
    > consensus is attempted, I was throwing my (and Crunchy's) use case out
    > there as a possible goal, and I have spent time reviewing and testing
    > the patch, so I think that is fair. Obviously I am not in the position
    > to stipulate hard requirements.
    
    I agree with all of that -- and thanks for writing back.
    
    > if 1A worked for admins, or members I think it may work (i.e., Bot is
    > admin of employees but not a member of employees and therefore can
    > manage employees but not become them or read their tables)
    >
    > For example, today this works (in master):
    >
    > postgres=# CREATE USER creator password 'a';
    > CREATE ROLE
    > postgres=# CREATE ROLE employees ADMIN creator NOLOGIN;
    > CREATE ROLE
    >
    > as creator:
    > postgres=> CREATE USER joshua IN ROLE employees PASSWORD 'a';
    > ERROR:  permission denied to create role
    >
    > as superuser:
    > postgres=# CREATE USER joshua LOGIN PASSWORD 'a';
    > CREATE ROLE
    >
    > as creator:
    > postgres=> GRANT employees TO joshua;
    > GRANT ROLE
    > postgres=> SET ROLE joshua;
    > ERROR:  permission denied to set role "joshua"
    > postgres=> SET ROLE employees;
    > SET
    >
    > So ADMIN of a role can add membership, but not create, and
    > unfortunately can SET ROLE to employees.
    >
    > Can ADMIN mean "can create and drop roles with membership of this role
    > but not implicitly be a member of the role"?
    
    I foresee big problems trying to go in this direction. According to
    the documentation, "the ADMIN clause is like ROLE, but the named roles
    are added to the new role WITH ADMIN OPTION, giving them the right to
    grant membership in this role to others." And for me, the name "WITH
    ADMIN OPTION" is a huge red flag. You grant membership in a role, and
    you may grant that membership with the admin option, or without the
    admin option, but either way you are granting membership. And to me
    that is just built into the phraseology. You may be able to buy the
    car that you want with or without the all-wheel drive option, and you
    may even be able to upgrade a car purchased without that option to
    have it later, but you can't buy all-wheel drive in the abstract
    without an association to some particular car. That's what it means
    for it to be an option.
    
    Now, I think there is a good argument to be made that in this case the
    fact that the administration privileges are an option associated with
    membership is artificial. I expect we can all agree that it is
    conceptually easy to understand the idea of being able to administer a
    role and the idea of having that role's privileges as two separate
    concepts, neither dependent upon the other, and certainly the SQL
    syntax could be written in a way that makes that very natural. But as
    it is, what is the equivalent of GRANT employees TO bot WITH ADMIN
    OPTION when you want to convey only administration rights and not
    membership? GRANT employees TO bot WITH ADMIN OPTION BUT WITHOUT THE
    UNDERLYING MEMBERSHIP TO WHICH ADMIN IS AN OPTION? Maybe that sounds
    sarcastic, but to me it seems like a genuinely serious problem. People
    construct a mental model of how stuff works based to a significant
    degree on the structure of the syntax, and I really don't see an
    obvious way of extending the grammar in a way that is actually going
    to make sense to people.
    
    > The current (v8) patch conflates membership and admin:
    >
    > postgres=# CREATE USER user_creator CREATEROLE WITHOUT ADMIN OPTION
    > PASSWORD 'a';
    > CREATE ROLE
    > postgres=# CREATE ROLE employees ADMIN user_creator NOLOGIN;
    > CREATE ROLE
    >
    > (Note I never GRANTED employees to user_creator):
    
    I think you did, because even right now without the patch "ADMIN
    whatever" is documented to mean membership with admin option.
    
    > > So that leads to these questions: (2A) Do you care about restricting
    > > which roles the userbot can drop? (2B) If yes, do you endorse
    > > restricting the ability of roles to revoke themselves from other
    > > roles?
    >
    > 2A, yes
    > 2B, yes, and IIUC this already exists:
    > postgres=> select current_user;
    >  current_user
    > --------------
    >  joshua
    > (1 row)
    >
    > postgres=> REVOKE employees FROM joshua;
    > ERROR:  must have admin option on role "employees"
    
    No, because as Stephen correctly points out, you've got that REVOKE
    command backwards.
    
    > > I think that we don't have any great problems here, at least as far as
    > > this very specific issue is concerned, if either the answer to (2A) is
    > > no or the answer to (2B) is yes. However, if the answer to (2A) is yes
    > > and the answer to (2B) is no, there are difficulties. Evidently in
    > > that case we need some new kind of thing that behaves mostly likes a
    > > group of roles but isn't actually a group of roles -- and that thing
    > > needs to prohibit self-revocation. Given what I've written above, you
    > > may be able to guess my preferred solution: let's call it a TENANT.
    > > Then, my pseudo-super-user can have permission to (i) create roles in
    > > that tenant, (ii) drop roles in that tenant, and (iii) assume the
    > > privileges of roles in that tenant -- and your userbot can have
    > > privileges to do (i) and (ii) but not (iii). All we need do is add a
    > > roltenant column to pg_authid and find three bits someplace
    > > corresponding to (i)-(iii), and we are home.
    >
    > I believe this works.
    
    Cool.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  65. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-03-01T20:03:48Z

    On Mon, Feb 28, 2022 at 2:09 PM Stephen Frost <sfrost@snowman.net> wrote:
    > I'm generally in support of changing CREATEROLE to only allow roles that
    > the role with CREATEROLE is an admin of to be allowed as part of the
    > command (throwing an error in other cases).  That doesn't solve other
    > use-cases which would certainly be nice to solve but it would at least
    > reduce the shock people have when they discover how CREATEROLE actually
    > works (that is, the way we document it to work, but that's ultimately
    > not what people expect).
    
    So I'm 100% good with that because it does exactly what I want, but my
    understanding of the situation is that it breaks the userbot case that
    Joshua is talking about. Right now, with stock PostgreSQL in any
    released version, his userbot can have CREATEROLE and give out roles
    that it doesn't itself possess. If we restrict CREATEROLE in the way
    described here, that's no longer possible.
    
    Now it's possible that you and/or he would take the position that
    we're still coming out ahead despite that functional regression,
    because as I now understand after reading Joshua's latest email, he
    doesn't want the userbot to be able to grant ANY role, just the
    'employees' role - and today he can't get that. So in a modified
    universe where we restrict the privileges of CREATEROLE, then on the
    one hand he GAINS the ability to have a userbot that can grant some
    roles but not others, but on the other hand, he's forced to give the
    userbot the roles he wants it to be able to hand out. Is that better
    overall or worse?
    
    To really give him EXACTLY what he wants, we need a way of specifying
    administration without membership. See my last reply to the thread for
    my concerns about that.
    
    > I don't think it's a great plan to limit who is allowed to DROP roles to
    > be just those that a given role created.  I also don't like the idea of
    > introducing a single field for owner/manager/tenant/whatever to the role
    > system- instead we should add other ways that roles can be associated to
    > each other by extending the existing system that we have for that, which
    > is role membership.  Role membership today is pretty limited but I don't
    > see any reason why we couldn't improve on that in a way that's flexible
    > and allows us to define new associations in the future.  The biggest
    > difference between a 'tenant' or such as proposed vs. a role association
    > is in where the information is tracked and what exactly it means.
    > Saying "I want a owner" or such is easy because it's basically punting
    > on the complciated bit of asking the question: what does that *mean*
    > when it comes to what rights that includes vs. doesn't?  What if I only
    > want some of those rights to be given away but not all of them?  We have
    > that system for tables/schemas/etc, and it hasn't been great as we've
    > seen through the various requests to add things like GRANT TRUNCATE.
    
    Well, there's no accounting for taste, but I guess I see this pretty
    much opposite to the way you do. I think GRANT TRUNCATE is nice and
    simple and clear. It does one thing and it's easy to understand what
    that thing is, and it has very few surprising or undocumented side
    effects. On the other hand, role membership is a mess, and it's not at
    all clear how to sort that mess out. I guess I agree with you that it
    would be nice if it could be done, but the list of problems is pretty
    substantial. Like, membership implies the right to SET ROLE, and also
    the right to implicitly exercise the privileges of the role, and
    you've complained about that fuzziness. And ADMIN OPTION implies
    membership, and you don't like that either. And elsewhere it's been
    raised that nobody would expect to have a table end up owned by
    'pg_execute_server_programs', or a user logged in directly as
    'employees' rather than as some particular employee, but all that
    stuff can happen, and some of it can't even be effectively prevented
    with good configuration. 'toe' can be a member of 'foot' while, which
    makes sense to everybody, and at the same time, 'foot' can be a member
    of 'toe', which doesn't make any sense at all. And because both
    directions are possible even experienced PostgreSQL users and hackers
    get confused, as demonstrated by Joshua's having just got the
    revoke-from-role case backwards.
    
    Of those four problems, the last two are clearly the result of
    conflating users with groups - and really also with capabilities - and
    having a unified role concept that encompasses all of those things. I
    think we would be better off if we had not done that, both in the
    sense that I think the system would be less confusing to understand,
    and also in the sense that we would likely have fewer security bugs.
    And similarly I agree with you that it would be better if the right to
    administer a role were clearly separated from membership in a role,
    and if the right to use the privileges of a role were separated from
    the ability to SET ROLE to it. However, unlike you, I see the whole
    'role membership' concept as the problem, not the solution. We
    conflate a bunch of different kinds of things together and call them
    all 'roles' and a bunch of other things together and call them
    'membership' and then we end up with an awkward mess. That's how I see
    it, anyway.
    
    > The ability of a role to revoke itself from some other role is just
    > something we need to accept as being a change that needs to be made, and
    > I do believe that such a change is supported by the standard, in that a
    > REVOKE will only work if you have the right to make it as the user who
    > performed the GRANT in the first place.
    
    Great. I propose that we sever that issue and discuss it on a new
    thread to avoid confusion. I believe there is some debate to be had
    about exactly what we want the behavior to be in this area, but if we
    can reach consensus on that point, this shouldn't be too hard to knock
    out. I will take it as an action item to get that thread going, if
    that works for you.
    
    > > So that leads to these questions: (2A) Do you care about restricting
    > > which roles the userbot can drop? (2B) If yes, do you endorse
    > > restricting the ability of roles to revoke themselves from other
    > > roles?
    >
    > As with Joshua, and as hopefully came across from the above discussion,
    > I'm also a 'yes and yes' on these two.
    
    Great.
    
    > > I think that we don't have any great problems here, at least as far as
    > > this very specific issue is concerned, if either the answer to (2A) is
    > > no or the answer to (2B) is yes. However, if the answer to (2A) is yes
    > > and the answer to (2B) is no, there are difficulties. Evidently in
    > > that case we need some new kind of thing that behaves mostly likes a
    > > group of roles but isn't actually a group of roles -- and that thing
    > > needs to prohibit self-revocation. Given what I've written above, you
    > > may be able to guess my preferred solution: let's call it a TENANT.
    > > Then, my pseudo-super-user can have permission to (i) create roles in
    > > that tenant, (ii) drop roles in that tenant, and (iii) assume the
    > > privileges of roles in that tenant -- and your userbot can have
    > > privileges to do (i) and (ii) but not (iii). All we need do is add a
    > > roltenant column to pg_authid and find three bits someplace
    > > corresponding to (i)-(iii), and we are home.
    >
    > Where are those bits going to go though..?  I don't think they should go
    > into pg_authid, nor do I feel that this 'tenant' or such should go there
    > either because pg_authid is about describing individual roles, not about
    > role associations.  Instead, I'd suggest that those bits go into
    > pg_auth_members in the form of additional columns to describe the role
    > associations.  That is, instead of the existance of a row in
    > pg_auth_members meaning that one role has membership in another role, we
    > give users the choice of if that's the case or not with a separate
    > column.  That would then neatly give us a way for a role to have admin
    > rights over another role but not membership in that role.  We could then
    > further extend this by adding other columns to pg_auth_members for other
    > rights as users decide they need them- such as the ability for a role to
    > DROP some set of roles.
    
    What I had in mind is to add a pg_tenant catalog (tenid, tenname) and
    add some columns to the pg_authid catalog (roltenant, roltenantrights,
    or something like that). See above for why I am not excited about
    piggybacking more things onto role membership.
    
    > => revoke joshua from admin;
    > REVOKE ROLE
    >
    > =*> \du
    >                                     List of roles
    >  Role name |                         Attributes                         |  Member of
    > -----------+------------------------------------------------------------+-------------
    >  admin     | Cannot login                                               | {}
    >  employees | Cannot login                                               | {}
    >  joshua    |                                                            | {employees}
    >  sfrost    | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
    >
    > Even though, in this case, it was 'sfrost' (a superuser) who GRANT'd
    > joshua to admin.
    
    Quite so.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  66. role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-04T20:50:01Z

    On Mon, Feb 28, 2022 at 2:09 PM Stephen Frost <sfrost@snowman.net> wrote:
    > The ability of a role to revoke itself from some other role is just
    > something we need to accept as being a change that needs to be made, and
    > I do believe that such a change is supported by the standard, in that a
    > REVOKE will only work if you have the right to make it as the user who
    > performed the GRANT in the first place.
    
    Moving this part of the discussion to a new thread to reduce confusion
    and hopefully get broader input on this topic. It seems like Stephen
    and I agree in principle that some change here is a good idea. If
    anyone else thinks it's a bad idea, then this would be a great time to
    mention that, ideally with reasons. If you agree that it's a good
    idea, then it would be great to have your views on the follow-up
    questions which I shall pose below. To the extent that it is
    reasonably possible to do so, I would like to try to keep focused on
    specific design questions rather than getting tangled up in general
    discussion of long-term direction. First, a quick overview of the
    issue for those who have not followed the earlier threads in their
    grueling entirety:
    
    rhaas=# create user boss;
    CREATE ROLE
    rhaas=# create user peon;
    CREATE ROLE
    rhaas=# grant peon to boss;
    GRANT ROLE
    rhaas=# \c - peon
    You are now connected to database "rhaas" as user "peon".
    rhaas=> revoke peon from boss; -- i don't like being bossed around!
    REVOKE ROLE
    
    I argue (and Stephen seems to agree) that the peon shouldn't be able
    to undo the superuser's GRANT. Furthermore, we also seem to agree that
    you don't necessarily have to be the exact user who performed the
    grant. For example, it would be shocking if one superuser couldn't
    remove a grant made by another superuser, or for that matter if a
    superuser couldn't remove a grant made by a non-superuser. But there
    are a few open questions in my mind:
    
    1. What should be the exact rule for whether A can remove a grant made
    by B? Is it has_privs_of_role()? is_member_of_role()? Something else?
    
    2. What happens if the same GRANT is enacted by multiple users? For
    example, suppose peon does "GRANT peon to boss" and then the superuser
    does the same thing afterwards, or vice versa? One design would be to
    try to track those as two separate grants, but I'm not sure if we want
    to add that much complexity, since that's not how we do it now and it
    would, for example, implicate the choice of PK on the pg_auth_members
    table. An idea that occurs to me is to say that the first GRANT works
    and becomes the grantor of record, and any duplicate GRANT that
    happens later issues a NOTICE without changing anything. If the user
    performing the later GRANT has sufficient privileges and wishes to do
    so, s/he can REVOKE first and then re-GRANT. On the other hand, for
    other types of grants, like table privileges, we do track multiple
    grants by different users, so maybe we should do the same thing here:
    
    rhaas=# create table example (a int, b int);
    CREATE TABLE
    rhaas=# grant select on table example to foo with grant option;
    GRANT
    rhaas=# grant select on table example to bar with grant option;
    GRANT
    rhaas=# \c - foo
    You are now connected to database "rhaas" as user "foo".
    rhaas=> grant select on table example to exemplar;
    GRANT
    rhaas=> \c - bar
    You are now connected to database "rhaas" as user "bar".
    rhaas=> grant select on table example to exemplar;
    GRANT
    rhaas=> select relacl from pg_class where relname = 'example';
                                        relacl
    -------------------------------------------------------------------------------
     {rhaas=arwdDxt/rhaas,foo=r*/rhaas,bar=r*/rhaas,exemplar=r/foo,exemplar=r/bar}
    (1 row)
    
    3. What happens if a user is dropped after being recorded as a
    grantor? We actually have a grantor column in pg_auth_members today,
    but it's not properly maintained. If the grantor is dropped the OID
    remains in the table, and could eventually end up pointing to some
    other user if the OID counter wraps around and a new role is created
    with the same OID. That's completely unacceptable for something we
    want to use for any serious purpose. I suggest that what ought to
    happen is the role should acquire a dependency on the grant, such that
    DROP fails and the GRANT is listed as something to be dropped, and
    DROP OWNED BY drops the GRANT. I think this would require adding an
    OID column to pg_auth_members so that a dependency can point to it,
    which sounds like a significant infrastructure change that would need
    to be carefully validated for adverse side effects, but not a huge
    crazy problem that we can't get past.
    
    4. Should we apply this rule to other types of grants, rather than
    just to role membership? Why or why not? Consider this:
    
    rhaas=# create user accountant;
    CREATE ROLE
    rhaas=# create user auditor;
    CREATE ROLE
    rhaas=# create table money (a int, b text);
    CREATE TABLE
    rhaas=# alter table money owner to accountant;
    ALTER TABLE
    rhaas=# grant select on table money to auditor;
    GRANT
    rhaas=# \c - accountant
    You are now connected to database "rhaas" as user "accountant".
    rhaas=> revoke select on table money from auditor;
    REVOKE
    
    I would argue that's exactly the same problem. The superuser has
    decreed that the auditor gets to select from the money table owned by
    the accountant. The fact that the accountant may not be not in favor
    of the auditor seeing what the accountant is doing with the money is
    precisely the reason why we have auditors. That said, if we apply this
    to all object types, it's a much bigger change. Unlike role
    membership, we do record dependencies on table privileges, which makes
    any change here a bit simpler, and you can't drop a role without
    removing the associated grants first. However, when the superuser
    performs the GRANT as in the above example, the grantor is recorded as
    the table owner, not the superuser! So if we really want role
    membersip and other kinds of grants to behave in the same way, we have
    our work cut out for us here.
    
    Please note that it is not really my intention to try to shove
    anything into v15 here. If it so happens that we quickly agree on
    something that already exists in the patches Mark's already written,
    and we also agree that those patches are in good enough shape that we
    can commit something in the next few weeks, fantastic, but I'm not
    necessarily expecting that. What I do want to do is agree on a plan so
    that, if somebody does the work to implement said plan, we do not then
    end up relitigating the whole thing and coming to a different
    conclusion the second time. This being a community whose membership
    varies from time to time and the opinions of whose members vary from
    time to time, such misadventure can never be entirely ruled out.
    However, I would like to minimize the chances of such an outcome as
    much as we can.
    
    Thanks,
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  67. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-04T21:34:23Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > 1. What should be the exact rule for whether A can remove a grant made
    > by B? Is it has_privs_of_role()? is_member_of_role()? Something else?
    
    No strong opinion here, but I'd lean slightly to the more restrictive
    option.
    
    > 2. What happens if the same GRANT is enacted by multiple users? For
    > example, suppose peon does "GRANT peon to boss" and then the superuser
    > does the same thing afterwards, or vice versa? One design would be to
    > try to track those as two separate grants, but I'm not sure if we want
    > to add that much complexity, since that's not how we do it now and it
    > would, for example, implicate the choice of PK on the pg_auth_members
    > table.
    
    As you note later, we *do* track such grants separately in ordinary
    ACLs, and I believe this is clearly required by the SQL spec.
    It says (for privileges on objects):
    
        Each privilege is represented by a privilege descriptor.
        A privilege descriptor contains:
        — The identification of the object on which the privilege is granted.
        — The <authorization identifier> of the grantor of the privilege.
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        — The <authorization identifier> of the grantee of the privilege.
        — Identification of the action that the privilege allows.
        — An indication of whether or not the privilege is grantable.
        — An indication of whether or not the privilege has the WITH HIERARCHY OPTION specified.
    
    Further down (4.42.3 in SQL:2021), the granting of roles is described,
    and that says:
    
        Each role authorization is described by a role authorization descriptor.
        A role authorization descriptor includes:
        — The role name of the role.
        — The authorization identifier of the grantor.
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        — The authorization identifier of the grantee.
        — An indication of whether or not the role authorization is grantable.
    
    If we are not tracking the grantors of role authorizations,
    then we are doing it wrong and we ought to fix that.
    
    > 3. What happens if a user is dropped after being recorded as a
    > grantor?
    
    Should work the same as it does now for ordinary ACLs, ie, you
    gotta drop the grant first.
    
    > 4. Should we apply this rule to other types of grants, rather than
    > just to role membership?
    
    I am not sure about the reasoning behind the existing rule that
    superuser-granted privileges are recorded as being granted by the
    object owner.  It does feel more like a wart than something we want.
    It might have been a hack to deal with the lack of GRANTED BY
    options in GRANT/REVOKE back in the day.
    
    Changing it could have some bad compatibility consequences though.
    In particular, I believe it would break existing pg_dump files,
    in that after restore all privileges would be attributed to the
    restoring superuser, and there'd be no very easy way to clean that
    up.
    
    > Please note that it is not really my intention to try to shove
    > anything into v15 here.
    
    Agreed, this is not something to move on quickly.  We might want
    to think about adjusting pg_dump to use explicit GRANTED BY
    options in GRANT/REVOKE a release or two before making incompatible
    changes.
    
    			regards, tom lane
    
    
    
    
  68. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-04T22:20:10Z

    On Fri, Mar 4, 2022 at 1:50 PM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Mon, Feb 28, 2022 at 2:09 PM Stephen Frost <sfrost@snowman.net> wrote:
    > > The ability of a role to revoke itself from some other role is just
    > > something we need to accept as being a change that needs to be made, and
    > > I do believe that such a change is supported by the standard, in that a
    > > REVOKE will only work if you have the right to make it as the user who
    > > performed the GRANT in the first place.
    >
    > First, a quick overview of the
    > issue for those who have not followed the earlier threads in their
    > grueling entirety:
    >
    > rhaas=# create user boss;
    > CREATE ROLE
    > rhaas=# create user peon;
    > CREATE ROLE
    > rhaas=# grant peon to boss;
    > GRANT ROLE
    > rhaas=# \c - peon
    > You are now connected to database "rhaas" as user "peon".
    > rhaas=> revoke peon from boss; -- i don't like being bossed around!
    > REVOKE ROLE
    >
    >
    The wording for this example is hurting my brain.
    GRANT admin TO joe;
    \c admin
    REVOKE admin FROM joe;
    
    > I argue (and Stephen seems to agree) that the peon shouldn't be able
    > to undo the superuser's GRANT.
    
    
    I think I disagree.  Or, at least, the superuser has full control of
    dictating how role membership is modified and that seems sufficient.
    
    The example above works because of:
    
    "A role is not considered to hold WITH ADMIN OPTION on itself, but it may
    grant or revoke membership in itself from a database session where the
    session user matches the role."
    
    If a superuser doesn't want "admin" to modify its own membership then they
    can prevent anyone but a superuser from being able to have a session_user
    of "admin".  If that happens then the only way a non-superuser can modify
    group membership is by being added to said group WITH ADMIN OPTION.
    
    Now, if two people and a superuser are all doing membership management on
    the same group, and we want to add permission checks and multiple grants as
    tools, instead of having them just communicate with each other, then by all
    means let us do so.  In that case, in answer to questions 2 and 3, we
    should indeed track which session_user made the grant and only allow the
    same session_user or the superuser to revoke it (we'd want to stop
    "ignoring" the GRANTED BY clause of REVOKE ROLE FROM so the superuser at
    least could remove grants made via WITH ADMIN OPTION).
    
    4. Should we apply this rule to other types of grants, rather than
    > just to role membership? Why or why not? Consider this:
    >
    
    
    > The fact that the accountant may not be not in favor
    > of the auditor seeing what the accountant is doing with the money is
    > precisely the reason why we have auditors.
    
    [...]
    
    > However, when the superuser
    > performs the GRANT as in the above example, the grantor is recorded as
    > the table owner, not the superuser! So if we really want role
    > membersip and other kinds of grants to behave in the same way, we have
    > our work cut out for us here.
    >
    
    Yes, this particular choice seems unfortunate, but also not something that
    I think it is necessarily mandatory for us to improve.  If the accountant
    is the owner then yes they get to decide permissions.  In the presence of
    an auditor role either you trust the accountant role to keep the
    permissions in place or you define a superior authority to both the auditor
    and accountant to be the owner.  Or let the superuser manage everything by
    witholding login and WITH ADMIN OPTION privileges from the ownership role.
    
    
    If we do extend role membership tracking I suppose the design question is
    whether the new role grantor dependency tracking will have a superuser be
    the recorded grantor instead of some owner.  Given that roles don't
    presently have an owner concept, consistency with existing permissions in
    this manner would be trickier.  Because of this, I would probably leave
    role grantor tracking at the session_user level while database objects
    continue to emanate from the object owner.  The global vs database
    differences seem like a sufficient theoretical justification for the
    difference in implementation.
    
    David J.
    
  69. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-06T15:19:39Z

    On Fri, Mar 4, 2022 at 5:20 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > I think I disagree.  Or, at least, the superuser has full control of dictating how role membership is modified and that seems sufficient.
    
    The point is that the superuser DOES NOT have full control. The
    superuser cannot prevent relatively low-privileged users from undoing
    things that the superuser did intentionally and doesn't want reversed.
    
    The choice of names in my example wasn't accidental. If the granted
    role is a login role, then the superuser's intention was to vest the
    privileges of that role in some other role, and it is surely not right
    for that role to be able to decide that it doesn't want it's
    privileges to be so granted. That's why I chose the name "peon". In
    your example, where you chose the name "admin", the situation is less
    clear. If we imagine the granted role as a container for a bundle of
    privileges, giving it the ability to administer itself feels more
    reasonable. However, I am very much unconvinced that it's correct even
    there. Suppose the superuser grants "admin" to both "joe" and "sally".
    Now "joe" can SET ROLE to "admin" and revoke it from "sally", and the
    superuser has no tool to prevent this.
    
    Now you can imagine a situation where the superuser is totally OK with
    either "joe" or "sally" having the ability to lock the other one out,
    but I don't think it's right to say that this will be true in all
    cases.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  70. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-06T15:27:40Z

    On Fri, Mar 4, 2022 at 4:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > If we are not tracking the grantors of role authorizations,
    > then we are doing it wrong and we ought to fix that.
    
    Hmm, so maybe that's the place to start. We are tracking it in the
    sense that we record an OID in the catalog, but nothing that happens
    after that makes a lot of sense.
    
    > > 3. What happens if a user is dropped after being recorded as a
    > > grantor?
    >
    > Should work the same as it does now for ordinary ACLs, ie, you
    > gotta drop the grant first.
    
    OK, that makes sense to me.
    
    > Changing it could have some bad compatibility consequences though.
    > In particular, I believe it would break existing pg_dump files,
    > in that after restore all privileges would be attributed to the
    > restoring superuser, and there'd be no very easy way to clean that
    > up.
    
    I kind of wonder whether we ought to attribute all privileges granted
    by any superuser to the bootstrap superuser. That doesn't seem to have
    any meaningful downside, and it could avoid a lot of annoying
    dependencies that serve no real purpose.
    
    > Agreed, this is not something to move on quickly.  We might want
    > to think about adjusting pg_dump to use explicit GRANTED BY
    > options in GRANT/REVOKE a release or two before making incompatible
    > changes.
    
    Uggh. I really want to make some meaningful progress here before the
    heat death of the universe, and I'm not sure that this manner of
    proceeding is really going in that direction. That said, I do entirely
    see your point. Are you thinking we'd actually add a GRANTED BY clause
    to GRANT/REVOKE, vs. just wrapping it in SET ROLE incantations of some
    sort?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  71. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-06T16:34:29Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Fri, Mar 4, 2022 at 4:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> Agreed, this is not something to move on quickly.  We might want
    >> to think about adjusting pg_dump to use explicit GRANTED BY
    >> options in GRANT/REVOKE a release or two before making incompatible
    >> changes.
    
    > Uggh. I really want to make some meaningful progress here before the
    > heat death of the universe, and I'm not sure that this manner of
    > proceeding is really going in that direction. That said, I do entirely
    > see your point. Are you thinking we'd actually add a GRANTED BY clause
    > to GRANT/REVOKE, vs. just wrapping it in SET ROLE incantations of some
    > sort?
    
    I was thinking the former ... however, after a bit of experimentation
    I see that we accept "grant foo to bar granted by baz" a VERY long
    way back, but the "granted by" option for object privileges is
    (a) pretty new and (b) apparently restrictively implemented:
    
    regression=# grant delete on alices_table to bob granted by alice;
    ERROR:  grantor must be current user
    
    That's ... surprising.  I guess whoever put that in was only
    interested in pro-forma SQL syntax compliance and not in making
    a usable feature.
    
    So if we decide to extend this change into object privileges
    it would be advisable to use SET ROLE, else we'd be giving up
    an awful lot of backwards compatibility in dump scripts.
    But if we're only talking about role grants then I think
    GRANTED BY would work fine.
    
    			regards, tom lane
    
    
    
    
  72. Re: role self-revocation

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-03-06T16:40:11Z

    On Sun, Mar 6, 2022 at 10:19 AM Robert Haas <robertmhaas@gmail.com> wrote:
    >
    > On Fri, Mar 4, 2022 at 5:20 PM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > > I think I disagree.  Or, at least, the superuser has full control of dictating how role membership is modified and that seems sufficient.
    >
    > The point is that the superuser DOES NOT have full control. The
    > superuser cannot prevent relatively low-privileged users from undoing
    > things that the superuser did intentionally and doesn't want reversed.
    >
    > The choice of names in my example wasn't accidental. If the granted
    > role is a login role, then the superuser's intention was to vest the
    > privileges of that role in some other role, and it is surely not right
    > for that role to be able to decide that it doesn't want it's
    > privileges to be so granted. That's why I chose the name "peon". In
    > your example, where you chose the name "admin", the situation is less
    > clear. If we imagine the granted role as a container for a bundle of
    > privileges, giving it the ability to administer itself feels more
    > reasonable. However, I am very much unconvinced that it's correct even
    > there. Suppose the superuser grants "admin" to both "joe" and "sally".
    > Now "joe" can SET ROLE to "admin" and revoke it from "sally", and the
    > superuser has no tool to prevent this.
    >
    > Now you can imagine a situation where the superuser is totally OK with
    > either "joe" or "sally" having the ability to lock the other one out,
    > but I don't think it's right to say that this will be true in all
    > cases.
    >
    
    Another example here is usage of groups in pg_hba.conf, if the admin
    has a group of users with stronger authentication requirements: e.g.,
    
    hostssl all             +certonlyusers all cert map=certmap clientcert=1
    
    and one can remove their membership, they can change their
    authentication requirements.
    
    
    
    
  73. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-06T16:53:49Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > ... Suppose the superuser grants "admin" to both "joe" and "sally".
    > Now "joe" can SET ROLE to "admin" and revoke it from "sally", and the
    > superuser has no tool to prevent this.
    
    Really?
    
    regression=# grant admin to joe;
    GRANT ROLE
    regression=# grant admin to sally;
    GRANT ROLE
    regression=# \c - joe
    You are now connected to database "regression" as user "joe".
    regression=> revoke admin from sally;
    ERROR:  must have admin option on role "admin"
    regression=> set role admin;
    SET
    regression=> revoke admin from sally;
    ERROR:  must have admin option on role "admin"
    
    I think there is an issue here around exactly what the admin option
    means, but if it doesn't grant you the ability to remove grants
    made by other people, it's pretty hard to see what it's for.
    
    			regards, tom lane
    
    
    
    
  74. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-06T19:08:42Z

    On Sun, Mar 6, 2022 at 9:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Robert Haas <robertmhaas@gmail.com> writes:
    > > ... Suppose the superuser grants "admin" to both "joe" and "sally".
    > > Now "joe" can SET ROLE to "admin" and revoke it from "sally", and the
    > > superuser has no tool to prevent this.
    >
    > Really?
    >
    > regression=# grant admin to joe;
    > GRANT ROLE
    > regression=# grant admin to sally;
    > GRANT ROLE
    > regression=# \c - joe
    > You are now connected to database "regression" as user "joe".
    > regression=> revoke admin from sally;
    > ERROR:  must have admin option on role "admin"
    > regression=> set role admin;
    > SET
    > regression=> revoke admin from sally;
    > ERROR:  must have admin option on role "admin"
    >
    > I think there is an issue here around exactly what the admin option
    > means, but if it doesn't grant you the ability to remove grants
    > made by other people, it's pretty hard to see what it's for.
    >
    >
    Precisely.
    
    The current system, with the session_user exception, basically guides a
    superuser to define two kinds of roles.
    
    Groups: No login, permission grants
    Users: Login, inherits permissions from groups, can manage group membership
    if given WITH ADMIN OPTION.
    
    The original example using only users is not all that compelling to me.
    IMO, DBAs should not be setting up their system that way.
    
    Two questions remain:
    
    1. Are we willing to get rid of the session_user exception?
    
    2. Do we want to track who the grantor is for role membership grants and
    institute a requirement that non-superusers can only revoke the grants that
    they personally made?
    
    I'm personally in favor of getting rid of the session_user exception, which
    nicely prevents the problem at the beginning of this thread and further
    encourages the DBA to define groups and roles with a greater
    separation-of-concerns design.  WITH ADMIN OPTION is sufficient.
    
    I think tracking grantor information for role membership would allow for
    greater auditing capabilities and a better degree of control in the
    permissions system.
    
    In short, I am in favor of both options.  The grantor tracking seems to be
    headed for acceptance.
    
    So, do we really want to treat every single login role as a potential group
    by keeping the session_user exception?
    
    David J.
    
  75. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T04:01:20Z

    On Sun, Mar 6, 2022 at 8:19 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > The choice of names in my example wasn't accidental. If the granted
    > role is a login role, then the superuser's intention was to vest the
    > privileges of that role in some other role, and it is surely not right
    > for that role to be able to decide that it doesn't want it's
    > privileges to be so granted. That's why I chose the name "peon".
    
    
     >> rhaas [as peon] => revoke peon from boss; -- i don't like being bossed
    around!
    
    Well, the peon is not getting bossed around, the boss is getting peoned
    around and the peon has decided that they like boss too much and don't need
    to do that anymore.
    
    When you grant a group "to" a role you place the role under the group - and
    inheritance flows downward.
    
    In the original thread Stephen wrote:
    
    "This is because we allow 'self administration' of roles, meaning that
    they can decide what other roles they are a member of.:
    
    The example, which you moved here, then attempts to demonstrate this "fact"
    but gets it wrong.  Boss became a member of peon so if you want to
    demonstrate self-administration of a role's membership in a different group
    you have to login as boss, not peon.  Doing that, and then revoking peon
    from boss, yields "ERROR: must have admin option on role "peon"".
    
    So no, without "WITH ADMIN OPTION" a role cannot decide what other roles
    they are a member of.
    
    I don't necessarily have an issue changing self-administration but if the
    motivating concern is that all these new pg_* roles we are creating are
    something a normal user can opt-out of/revoke that simply isn't the case
    today, unless they are added to the pg_* role WITH ADMIN OPTION.
    
    That all said, permissions SHOULD BE strictly additive.  If boss doesn't
    want to be a member of pg_read_all_files allowing them to revoke themself
    from that role seems like it should be acceptable.  If there is fear in
    allowing someone to revoke (not add) themselves as a member of a different
    role that suggests we have a design issue in another feature of the
    system.  Today, they neither grant nor revoke, and the self-revocation
    doesn't seem that important to add.
    
    David J.
    
  76. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T15:12:54Z

    On Sun, Mar 6, 2022 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > I was thinking the former ... however, after a bit of experimentation
    > I see that we accept "grant foo to bar granted by baz" a VERY long
    > way back, but the "granted by" option for object privileges is
    > (a) pretty new and (b) apparently restrictively implemented:
    >
    > regression=# grant delete on alices_table to bob granted by alice;
    > ERROR:  grantor must be current user
    >
    > That's ... surprising.  I guess whoever put that in was only
    > interested in pro-forma SQL syntax compliance and not in making
    > a usable feature.
    
    It appears so: https://www.postgresql.org/message-id/2073b6a9-7f79-5a00-5f26-cd19589a52c7%402ndquadrant.com
    
    It doesn't seem like that would be hard to fix. Maybe we should just do that.
    
    > So if we decide to extend this change into object privileges
    > it would be advisable to use SET ROLE, else we'd be giving up
    > an awful lot of backwards compatibility in dump scripts.
    > But if we're only talking about role grants then I think
    > GRANTED BY would work fine.
    
    OK.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  77. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T15:37:16Z

    On Sun, Mar 6, 2022 at 11:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Really?
    >
    > regression=# grant admin to joe;
    > GRANT ROLE
    > regression=# grant admin to sally;
    > GRANT ROLE
    > regression=# \c - joe
    > You are now connected to database "regression" as user "joe".
    > regression=> revoke admin from sally;
    > ERROR:  must have admin option on role "admin"
    > regression=> set role admin;
    > SET
    > regression=> revoke admin from sally;
    > ERROR:  must have admin option on role "admin"
    
    Oops. I stand corrected.
    
    > I think there is an issue here around exactly what the admin option
    > means, but if it doesn't grant you the ability to remove grants
    > made by other people, it's pretty hard to see what it's for.
    
    Hmm. I think the real issue is what David Johnson calls the session
    user exception. I hadn't quite understood how that played into this.
    According to the documentation: "If WITH ADMIN OPTION is specified,
    the member can in turn grant membership in the role to others, and
    revoke membership in the role as well. Without the admin option,
    ordinary users cannot do that. A role is not considered to hold WITH
    ADMIN OPTION on itself, but it may grant or revoke membership in
    itself from a database session where the session user matches the
    role."
    
    Is there some use case for the behavior described in that last
    sentence? If that exception is the only case in which an unprivileged
    user can revoke a grant made by someone else, then getting rid of it
    seems pretty appealing from where I sit. I can't speak to the
    standards compliance end of things, but it doesn't intrinsically seem
    bothersome that having "WITH ADMIN OPTION" on a role lets you control
    who has membership in said role. And certainly it's not bothersome
    that the superuser can change whatever they want. The problem here is
    just that a user with NO special privileges on any role, including
    their own, can make changes that more privileged users might not like.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  78. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T15:58:39Z

    On Sun, Mar 6, 2022 at 2:09 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > So, do we really want to treat every single login role as a potential group by keeping the session_user exception?
    
    I think that we DO want to continue to treat login roles as
    potentially grantable privileges. That feels fundamentally useful to
    me. The superuser is essentially granted the privileges of all users
    on the system, and has all the rights they have, including the right
    to drop tables owned by those users as if they were the owner of those
    tables. If it's useful for the superuser to implicitly have the rights
    of all users on the system, why should it not be useful for some
    non-superuser to implicitly have the rights of some other users on the
    system? I think it pretty clearly is. If one of my colleagues leaves
    the company, the DBA can say "grant jdoe to rhaas" and let me mess
    around with this stuff. Or, the DBA can grant me the privileges of all
    my direct reports even when they're not leaving so that I can sort out
    anything I need to do without superuser involvement. That all seems
    cool and OK to me.
    
    Now I think it is fair to say that we could have chosen a different
    design, and MAYBE that would have been better. Nobody forced us to
    conflate users and groups into a unified thing called roles, and I
    think there's pretty good evidence that it's confusing and
    counterintuitive in some ways. There's also no intrinsic reason why
    the superuser has to be able to directly exercise the privileges of
    every role rather than, say, having a way to become any given role.
    But at this point, those design decisions are pretty well baked into
    the system design, and I don't really think it's likely that we want
    to change them. To put that another way, just because you don't like
    the idea of granting one login role to another login role, that
    doesn't mean that the feature doesn't exist, and as long as that
    feature does exist, trying to make it work better or differently is
    fair game.
    
    But I think that's separate from your other question about whether we
    should remove the session user exception. That looks tempting to me at
    first glance, because we have exchanged several hundred, and it really
    feels more like several million, emails on this list about how much of
    a problem it is that an unprivileged user can just log in and run a
    REVOKE. It breaks the idea that the people WITH ADMIN OPTION on a role
    are the ones who control membership in that role. Joshua Brindle's
    note upthread about the interaction of this with pg_hba.conf is
    another example of that, and I think there are more. Any idea that a
    role is a general-purpose way of designating a group of users for some
    security critical purpose is threatened if people can make changes to
    the membership of that group without being specifically authorized to
    do so.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  79. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T16:02:16Z

    On Mon, Mar 7, 2022 at 8:37 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > A role is not considered to hold WITH
    > ADMIN OPTION on itself, but it may grant or revoke membership in
    > itself from a database session where the session user matches the
    > role."
    >
    > Is there some use case for the behavior described in that last
    > sentence?
    
    
    I can imagine, in particular combined with CREATEROLE, that this allows for
    any user to delegate their personal permissions to a separate newly created
    user.  Like an assistant.  I'm not all that sure whether CREATEROLE is
    presently safe enough to give to a normal user in order to make this use
    case work but it seems reasonable.
    
    I would be concerned about changing the behavior at this point.  But I
    would be in favor of at least removing the hard-coded exception and linking
    it to a role attribute.  That attribute can default to "SELFADMIN" to match
    the existing behavior but then "NOSELFADMIN" would exist to disable that
    behavior on the per-role basis.  Still tied to session_user as opposed to
    current_user.
    
    David J.
    
    P.S.
    
    create role selfadmin admin selfadmin; -- ERROR: role "selfadmin" is a
    member of role "selfadmin"
    
    create role selfadmin;
    grant selfadmin to selfadmin with admin option; -- ERROR: role "selfadmin"
    is a member of role "selfadmin"
    
    The error message seems odd.  I tried this because instead of a "SELFADMIN"
    attribute adding a role to itself WITH ADMIN OPTION could be defined to
    have the same effect.  You cannot change WITH ADMIN OPTION independently of
    the adding of the role to the group.
    
  80. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-07T16:04:10Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > Hmm. I think the real issue is what David Johnson calls the session
    > user exception. I hadn't quite understood how that played into this.
    > According to the documentation: "If WITH ADMIN OPTION is specified,
    > the member can in turn grant membership in the role to others, and
    > revoke membership in the role as well. Without the admin option,
    > ordinary users cannot do that. A role is not considered to hold WITH
    > ADMIN OPTION on itself, but it may grant or revoke membership in
    > itself from a database session where the session user matches the
    > role."
    
    > Is there some use case for the behavior described in that last
    > sentence?
    
    Good question.  You might try figuring out when that text was added
    and then see if there's relevant discussion in the archives.
    
    Just looking at it now, without having done any historical research,
    I wonder why it is that we don't attach significance to WITH ADMIN
    OPTION being granted to the role itself.  It seems like the second
    part of that sentence is effectively saying that a role DOES have
    admin option on itself, contradicting the first part.
    
    			regards, tom lane
    
    
    
    
  81. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T16:58:22Z

    On Mon, Mar 7, 2022 at 9:04 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Just looking at it now, without having done any historical research,
    >
    I wonder why it is that we don't attach significance to WITH ADMIN
    > OPTION being granted to the role itself.  It seems like the second
    > part of that sentence is effectively saying that a role DOES have
    > admin option on itself, contradicting the first part.
    >
    >
    WITH ADMIN OPTION is inheritable which is really bad if the group has WITH
    ADMIN OPTION on itself.  The session_user exception temporarily grants WITH
    ADMIN OPTION to the group but it is done in such a way so that it is not
    inheritable.
    
    There is no possible way to even assign WITH ADMIN OPTION on a role to
    itself since pg_auth_members doesn't record a self-relationship and
    admin_option only exists there.
    
    David J.
    
    P.S. Feature request; modify \du+ to show which "Member of" roles a given
    role has the WITH ADMIN OPTION privilege on.
    
  82. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T17:51:22Z

    On Mon, Mar 7, 2022 at 11:04 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > Is there some use case for the behavior described in that last
    > > sentence?
    >
    > Good question.  You might try figuring out when that text was added
    > and then see if there's relevant discussion in the archives.
    
    Apparently the permission used to be broader, and commit
    fea164a72a7bfd50d77ba5fb418d357f8f2bb7d0 (February 2014, Noah,
    CVE-2014-0060) restricted it by requiring that (a) the user had to be
    the logged-in user, rather than an identity assumed via SET ROLE (so
    maybe my bogus example from before would have worked in 2013) and (b)
    that we're not in a security-restricted operation at the time.
    
    Interestingly, it appears to me that the behavior wasn't documented
    prior to that commit. The previous text read simply:
    
        If <literal>WITH ADMIN OPTION</literal> is specified, the member can
        in turn grant membership in the role to others, and revoke membership
        in the role as well.  Without the admin option, ordinary users cannot do
        that.
    
    That doesn't give any hint that self-administration is a special case.
    
    I reviewed the (private) discussion of this vulnerability on the
    pgsql-security mailing list where various approaches were considered.
    I think it's safe to share a few broad details about that conversation
    publicly now, since it was many years ago and the fix has long since
    been published. There was discussion of making this
    self-administration behavior something that could be turned off, but
    such a change was deemed too large for the back-branches. There was no
    discussion that I could find about removing the behavior altogether.
    It was noted that having a special case for this was different than
    granting WITH ADMIN OPTION because WITH ADMIN OPTION is inherited and
    being logged in as a certain user is not.
    
    It appears to me that the actual behavior of having is_admin_of_role()
    return true when member == role dates to
    f9fd1764615ed5d85fab703b0ffb0c323fe7dfd5 (Tom Lane, 2005). If I'm not
    reading this code wrong, prior to that commit, it seems to me that we
    only searched the roles that were members of that role, directly or
    indirectly, and you had to have admin_option on the last hop of the
    membership chain in order to get a "true" result. But that commit,
    among other changes, made member == role a special case, but the
    comment just says /* Fast path for simple case */ which makes it
    appear that it wasn't thought to be a behavior change at all, but it
    looks to me like it was. Am I confused?
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  83. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T18:18:40Z

    On Sun, Mar 6, 2022 at 11:01 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > The example, which you moved here, then attempts to demonstrate this "fact" but gets it wrong.  Boss became a member of peon so if you want to demonstrate self-administration of a role's membership in a different group you have to login as boss, not peon.  Doing that, and then revoking peon from boss, yields "ERROR: must have admin option on role "peon"".
    
    This doesn't seem to me to be making a constructive argument. I showed
    an example with certain names demonstrating a certain behavior that I
    find problematic. You don't have to think it's problematic, and you
    can show other examples that demonstrate things you want to show. But
    please don't tell me that when I literally cut and paste the output
    from my terminal into an email window, what I'm showing is somehow
    counterfactual. The behavior as it exists today is surely a fact, and
    an easily demonstrable one at that. It's not a "fact'" in quotes, and
    it doesn't "get it wrong". It is the actual behavior and the example
    with the names I picked demonstrates precisely what I want to
    demonstrate. When you say that I should have chosen a different
    example or used different identifier names or talked about it in
    different way, *that* is an opinion. I believe that you are wholly
    entitled to that opinion, even if (as in this case) I disagree, but I
    believe that it is not right at all to make it sound as if I don't
    have the right to pick the examples I care about, or as if terminal
    output is not a factual representation of how things work today.
    
    > So no, without "WITH ADMIN OPTION" a role cannot decide what other roles they are a member of.
    
    It clearly can in some limited cases, because I showed an example
    demonstrating *exactly that thing*.
    
    > I don't necessarily have an issue changing self-administration but if the motivating concern is that all these new pg_* roles we are creating are something a normal user can opt-out of/revoke that simply isn't the case today, unless they are added to the pg_* role WITH ADMIN OPTION.
    
    I agree with this, but that's not my concern, because that's a
    different use case from the one that I complained about. Since the
    session user exception only applies to login roles, the problem that
    I'm talking about only occurs when a login role is granted to some
    other role.
    
    > That all said, permissions SHOULD BE strictly additive.  If boss doesn't want to be a member of pg_read_all_files allowing them to revoke themself from that role seems like it should be acceptable.  If there is fear in allowing someone to revoke (not add) themselves as a member of a different role that suggests we have a design issue in another feature of the system.  Today, they neither grant nor revoke, and the self-revocation doesn't seem that important to add.
    
    I disagree with this on principle, and I also think that's not how it
    works today. On the general principle, I do not see a compelling
    reason why we should have two systems for maintaining groups of users,
    one of which is used for additive things and one of which is used for
    subtractive things. That is a lot of extra machinery for little gain,
    especially given how close we are to having it sorted out so that the
    same mechanism can serve both purposes. It presently appears to me
    that if we either remove the session user exception OR do the
    grantor-tracking thing discussed earlier, we can get to a place where
    the same facility can be used for either purpose. That would, I think,
    be a significant step forward over the status quo. In terms of how
    things work today, see Joshua Brindle's email about the use of groups
    in pg_hba.conf. That is an excellent example of how removing oneself
    from a group could enable one to bypass security restrictions intended
    by the DBA.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  84. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-07T18:28:14Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > It appears to me that the actual behavior of having is_admin_of_role()
    > return true when member == role dates to
    > f9fd1764615ed5d85fab703b0ffb0c323fe7dfd5 (Tom Lane, 2005). If I'm not
    > reading this code wrong, prior to that commit, it seems to me that we
    > only searched the roles that were members of that role, directly or
    > indirectly, and you had to have admin_option on the last hop of the
    > membership chain in order to get a "true" result. But that commit,
    > among other changes, made member == role a special case, but the
    > comment just says /* Fast path for simple case */ which makes it
    > appear that it wasn't thought to be a behavior change at all, but it
    > looks to me like it was. Am I confused?
    
    Ugh, I think you are right.  It's been a long time of course, but it sure
    looks like that was copied-and-pasted without recognizing that it was
    wrong in this function because of the need to check the admin_option flag.
    And then in the later security discussion we didn't realize that the
    problematic behavior was a flat-out thinko, so we narrowed it as much as
    we could instead of just taking it out.
    
    Does anything interesting break if you do just take it out?
    
    			regards, tom lane
    
    
    
    
  85. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T18:33:04Z

    On Mon, Mar 7, 2022 at 1:28 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Ugh, I think you are right.  It's been a long time of course, but it sure
    > looks like that was copied-and-pasted without recognizing that it was
    > wrong in this function because of the need to check the admin_option flag.
    > And then in the later security discussion we didn't realize that the
    > problematic behavior was a flat-out thinko, so we narrowed it as much as
    > we could instead of just taking it out.
    >
    > Does anything interesting break if you do just take it out?
    
    That is an excellent question, but I haven't had time yet to
    investigate the matter.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  86. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-07T18:45:12Z

    Greetings,
    
    * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > Robert Haas <robertmhaas@gmail.com> writes:
    > > 1. What should be the exact rule for whether A can remove a grant made
    > > by B? Is it has_privs_of_role()? is_member_of_role()? Something else?
    > 
    > No strong opinion here, but I'd lean slightly to the more restrictive
    > option.
    > 
    > > 2. What happens if the same GRANT is enacted by multiple users? For
    > > example, suppose peon does "GRANT peon to boss" and then the superuser
    > > does the same thing afterwards, or vice versa? One design would be to
    > > try to track those as two separate grants, but I'm not sure if we want
    > > to add that much complexity, since that's not how we do it now and it
    > > would, for example, implicate the choice of PK on the pg_auth_members
    > > table.
    > 
    > As you note later, we *do* track such grants separately in ordinary
    > ACLs, and I believe this is clearly required by the SQL spec.
    
    Agreed.
    
    > It says (for privileges on objects):
    > 
    >     Each privilege is represented by a privilege descriptor.
    >     A privilege descriptor contains:
    >     — The identification of the object on which the privilege is granted.
    >     — The <authorization identifier> of the grantor of the privilege.
    >       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >     — The <authorization identifier> of the grantee of the privilege.
    >     — Identification of the action that the privilege allows.
    >     — An indication of whether or not the privilege is grantable.
    >     — An indication of whether or not the privilege has the WITH HIERARCHY OPTION specified.
    > 
    > Further down (4.42.3 in SQL:2021), the granting of roles is described,
    > and that says:
    > 
    >     Each role authorization is described by a role authorization descriptor.
    >     A role authorization descriptor includes:
    >     — The role name of the role.
    >     — The authorization identifier of the grantor.
    >       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >     — The authorization identifier of the grantee.
    >     — An indication of whether or not the role authorization is grantable.
    > 
    > If we are not tracking the grantors of role authorizations,
    > then we are doing it wrong and we ought to fix that.
    
    Yup, and as noted elsewhere, we are tracking it but not properly dealing
    with dependencies nor are we considering the grantor when REVOKE is run.
    
    Looking at the spec for REVOKE is quite useful when trying to understand
    how this is all supposed to work (and, admittedly, isn't something I did
    enough of when I did the original work on roles... sorry about that, was
    early on).  In particular, a REVOKE only works when it finds something
    to revoke/remove, and part of that search includes basically "was it the
    current role who was the grantor?"
    
    The specific language here being: A role authorization descriptor is
    said to be identified if it defines the grant of any of the specified
    roles revoked to grantee with grantor A.
    
    Basically, a role authorization descriptor isn't identified unless it's
    one that this user/role had previously granted.
    
    > > 3. What happens if a user is dropped after being recorded as a
    > > grantor?
    > 
    > Should work the same as it does now for ordinary ACLs, ie, you
    > gotta drop the grant first.
    
    Agreed.
    
    > > 4. Should we apply this rule to other types of grants, rather than
    > > just to role membership?
    > 
    > I am not sure about the reasoning behind the existing rule that
    > superuser-granted privileges are recorded as being granted by the
    > object owner.  It does feel more like a wart than something we want.
    > It might have been a hack to deal with the lack of GRANTED BY
    > options in GRANT/REVOKE back in the day.
    
    Yeah, that doesn't seem right and isn't great.
    
    > Changing it could have some bad compatibility consequences though.
    > In particular, I believe it would break existing pg_dump files,
    > in that after restore all privileges would be attributed to the
    > restoring superuser, and there'd be no very easy way to clean that
    > up.
    
    Ugh, that's pretty grotty, certainly.
    
    > > Please note that it is not really my intention to try to shove
    > > anything into v15 here.
    > 
    > Agreed, this is not something to move on quickly.  We might want
    > to think about adjusting pg_dump to use explicit GRANTED BY
    > options in GRANT/REVOKE a release or two before making incompatible
    > changes.
    
    I'm with Robert on this though- folks should know already that they need
    to use the pg_dump of the version of PG that they want to move to and
    not try to re-use older pg_dump output with newer versions, for a number
    of reasons and this is just another.
    
    Thanks,
    
    Stephen
    
  87. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T18:47:00Z

    On Mon, Mar 7, 2022 at 11:18 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Sun, Mar 6, 2022 at 11:01 PM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > > The example, which you moved here, then attempts to demonstrate this
    > "fact" but gets it wrong.  Boss became a member of peon so if you want to
    > demonstrate self-administration of a role's membership in a different group
    > you have to login as boss, not peon.  Doing that, and then revoking peon
    > from boss, yields "ERROR: must have admin option on role "peon"".
    >
    > This doesn't seem to me to be making a constructive argument. I showed
    > an example with certain names demonstrating a certain behavior that I
    > find problematic.
    
    
    Whether you choose the wording of the original thread:
    
    "This is because we allow 'self administration' of roles, meaning that
    they can decide what other roles they are a member of."
    
    https://www.postgresql.org/message-id/flat/20211005025746.GN20998%40tamriel.snowman.net
    
    Or you quote at the top of this one:
    
    > The ability of a role to revoke itself from some other role is just
    > something we need to accept as being a change that needs to be made,
    
    This example:
    
    rhaas=# create user boss;
    CREATE ROLE
    rhaas=# create user peon;
    CREATE ROLE
    rhaas=# grant peon to boss;
    GRANT ROLE
    rhaas=# \c - peon
    You are now connected to database "rhaas" as user "peon".
    rhaas=> revoke peon from boss; -- i don't like being bossed around!
    REVOKE ROLE
    
    Fails to demonstrate the boss "can revoke itself from peon" / "boss can
    decide what other roles they are a member of."
    
    You are logged in as peon when you do the revoke, not boss, so the extent
    of what "boss" can or cannot do has not been shown.
    
    boss is a member of peon, not the other way around.  That the wording
    "grant peon to boss" makes you think otherwise is unfortunate.
    
    David J.
    
  88. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-07T18:49:43Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Sun, Mar 6, 2022 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > I was thinking the former ... however, after a bit of experimentation
    > > I see that we accept "grant foo to bar granted by baz" a VERY long
    > > way back, but the "granted by" option for object privileges is
    > > (a) pretty new and (b) apparently restrictively implemented:
    > >
    > > regression=# grant delete on alices_table to bob granted by alice;
    > > ERROR:  grantor must be current user
    > >
    > > That's ... surprising.  I guess whoever put that in was only
    > > interested in pro-forma SQL syntax compliance and not in making
    > > a usable feature.
    > 
    > It appears so: https://www.postgresql.org/message-id/2073b6a9-7f79-5a00-5f26-cd19589a52c7%402ndquadrant.com
    > 
    > It doesn't seem like that would be hard to fix. Maybe we should just do that.
    
    Yeah, that seems like something that should be fixed.  Superusers should
    be allowed to set GRANTED BY to whatever they feel like, and I'd argue
    that a role who wants a GRANT to actually be GRANTED BY some other role
    they're a member of should also be allowed to (as they could anyway by
    doing a SET ROLE), provided that role also has the privileges to do the
    GRANT itself, of course.
    
    > > So if we decide to extend this change into object privileges
    > > it would be advisable to use SET ROLE, else we'd be giving up
    > > an awful lot of backwards compatibility in dump scripts.
    > > But if we're only talking about role grants then I think
    > > GRANTED BY would work fine.
    > 
    > OK.
    
    I'm not quite following this bit.  Where would SET ROLE come into play
    when we're talking about old dump scripts and how the commands in those
    scripts might be interpreted by newer versions of PG..?
    
    Thanks,
    
    Stephen
    
  89. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-07T18:52:41Z

    Stephen Frost <sfrost@snowman.net> writes:
    > * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    >> Agreed, this is not something to move on quickly.  We might want
    >> to think about adjusting pg_dump to use explicit GRANTED BY
    >> options in GRANT/REVOKE a release or two before making incompatible
    >> changes.
    
    > I'm with Robert on this though- folks should know already that they need
    > to use the pg_dump of the version of PG that they want to move to and
    > not try to re-use older pg_dump output with newer versions, for a number
    > of reasons and this is just another.
    
    Yeah, in an ideal world you'd do that, but our users don't always have
    the luxury of living in an ideal world.  Sometimes all you've got is
    an old pg_dump file.  Perhaps this behavior wouldn't mess things up
    enough to make the restored database unusable, but we need to think
    about (and test) that case while we're considering changes.
    
    			regards, tom lane
    
    
    
    
  90. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-07T18:58:22Z

    Stephen Frost <sfrost@snowman.net> writes:
    > I'm not quite following this bit.  Where would SET ROLE come into play
    > when we're talking about old dump scripts and how the commands in those
    > scripts might be interpreted by newer versions of PG..?
    
    No, the concern there is the other way around: what if you take a
    script made by newer pg_dump and try to load it into an older server
    that doesn't have the GRANTED BY option?
    
    We're accustomed to saying that that doesn't work if you use a
    database feature that didn't exist in the old server, but
    privilege grants are hardly that.  I don't want us to change the
    pg_dump output in such a way that the grants can't be restored at all
    to an older server, just because of a syntax choice that we could
    make backwards-compatibly instead of not-backwards-compatibly.
    
    			regards, tom lane
    
    
    
    
  91. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-07T19:17:05Z

    Greetings,
    
    * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > Stephen Frost <sfrost@snowman.net> writes:
    > > * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > >> Agreed, this is not something to move on quickly.  We might want
    > >> to think about adjusting pg_dump to use explicit GRANTED BY
    > >> options in GRANT/REVOKE a release or two before making incompatible
    > >> changes.
    > 
    > > I'm with Robert on this though- folks should know already that they need
    > > to use the pg_dump of the version of PG that they want to move to and
    > > not try to re-use older pg_dump output with newer versions, for a number
    > > of reasons and this is just another.
    > 
    > Yeah, in an ideal world you'd do that, but our users don't always have
    > the luxury of living in an ideal world.  Sometimes all you've got is
    > an old pg_dump file.  Perhaps this behavior wouldn't mess things up
    > enough to make the restored database unusable, but we need to think
    > about (and test) that case while we're considering changes.
    
    I agree it's something to consider and deal with if we're able to do so
    sanely, but I disagree that we should be beholden to old dump files when
    considering how to move the project forward.  Further, they can surely
    build and install the version of PG that goes with that dump file in a
    great many cases and then dump the data out using a newer version of
    pg_dump.  For 5 years they could do that with a completely supported
    version of PG, but we've recently agreed to make an effort to do more
    here by supporting the building of even older versions on modern
    systems.
    
    Thanks,
    
    Stephen
    
  92. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T19:18:35Z

    On Mon, Mar 7, 2022 at 1:58 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Stephen Frost <sfrost@snowman.net> writes:
    > > I'm not quite following this bit.  Where would SET ROLE come into play
    > > when we're talking about old dump scripts and how the commands in those
    > > scripts might be interpreted by newer versions of PG..?
    >
    > No, the concern there is the other way around: what if you take a
    > script made by newer pg_dump and try to load it into an older server
    > that doesn't have the GRANTED BY option?
    >
    > We're accustomed to saying that that doesn't work if you use a
    > database feature that didn't exist in the old server, but
    > privilege grants are hardly that.  I don't want us to change the
    > pg_dump output in such a way that the grants can't be restored at all
    > to an older server, just because of a syntax choice that we could
    > make backwards-compatibly instead of not-backwards-compatibly.
    
    Are you absolutely positive that it's that simple? I mean, what if the
    SET ROLE command has other side effects, or if the GRANT command
    behaves differently in some way as a result of the SET ROLE having
    been done? I feel like a solution that involves explicitly specifying
    the behavior that we want (i.e. GRANTED BY) is likely to be more
    reliable and more secure than a solution which involves absorbing a
    key value from a session property (i.e. the role established by SET
    ROLE). Even if we decide that SET ROLE is the way to go for
    compatibility reasons, I would personally say that it's an inferior
    hack only worth accepting for that reason than a truly desirable
    design.
    
    See CVE-2018-1058 for an example of what I'm talking about. The
    prevailing search_path turned out to affect not only the creation
    schema, as intended, but also the resolution of references to other
    objects mentioned in the CREATE COMMAND, as not intended. I don't see
    a similar hazard here, but I'm worried that there might be one.
    Declarative syntax is a very powerful tool for avoiding those kinds of
    mishaps, and I think we should make as much use of it as we can.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  93. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-07T19:22:06Z

    Greetings,
    
    * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > Stephen Frost <sfrost@snowman.net> writes:
    > > I'm not quite following this bit.  Where would SET ROLE come into play
    > > when we're talking about old dump scripts and how the commands in those
    > > scripts might be interpreted by newer versions of PG..?
    > 
    > No, the concern there is the other way around: what if you take a
    > script made by newer pg_dump and try to load it into an older server
    > that doesn't have the GRANTED BY option?
    
    Wow.  No, I really don't think I can agree that we need to care about
    this.
    
    > We're accustomed to saying that that doesn't work if you use a
    > database feature that didn't exist in the old server, but
    > privilege grants are hardly that.  I don't want us to change the
    > pg_dump output in such a way that the grants can't be restored at all
    > to an older server, just because of a syntax choice that we could
    > make backwards-compatibly instead of not-backwards-compatibly.
    
    GRANTED BY is clearly such a feature that exists in the newer version
    and doesn't exist in the older and I can't agree that we should
    complicate things for ourselves and bend over backwards to try and make
    it work to take a dump from a newer version of PG and make it work on
    random older versions.
    
    Folks are also able to exclude privileges from dumps if they want to.
    
    Where do we document that we are going to put in effort to make these
    kinds of things work?  What other guarantees are we supposed to be
    providing regarding using output from a newer pg_dump against older
    servers?  What about newer custom format dumps?  Surely you're not
    suggesting that we need to back-patch support for them to released
    versions of pg_restore.
    
    Thanks,
    
    Stephen
    
  94. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T19:29:31Z

    On Mon, Mar 7, 2022 at 11:18 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > In terms of how
    >
    things work today, see Joshua Brindle's email about the use of groups
    > in pg_hba.conf. That is an excellent example of how removing oneself
    > from a group could enable one to bypass security restrictions intended
    > by the DBA.
    >
    >
    You mean the one that was based upon your "ooops"...I discounted that
    immediately because members cannot revoke their own membership in a group
    unless they were given WITH ADMIN OPTION on that group.
    
    The mere fact that the pg_hba.conf concern raised there hasn't been
    reported as a live issue suggests the lack of any meaningful design flaw
    here.
    
    That isn't to say that having a LOGIN role get an automatic temporary WITH
    ADMIN OPTION on itself is a good thing - but there isn't any privilege
    escalation vector here to be squashed.  There is just a "DBAs should treat
    LOGIN roles as leaf nodes" expectation in which case there would be no
    superuser granted memberships to be removed.
    
    David J.
    
  95. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T19:46:56Z

    On Mon, Mar 7, 2022 at 2:29 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > You mean the one that was based upon your "ooops"...I discounted that immediately because members cannot revoke their own membership in a group unless they were given WITH ADMIN OPTION on that group.
    
    Oh, hmm. That example might be backwards from the case I'm talking about.
    
    > The mere fact that the pg_hba.conf concern raised there hasn't been reported as a live issue suggests the lack of any meaningful design flaw here.
    
    Not really. The system is full of old bugs, just as all software
    system are, and the particular role self-administration behavior that
    is at issue here appears to be something that was accidentally
    introduced 16 years years ago in a commit that did something else and
    never scrutinized from a design perspective since then.
    
    Personally, I've been shocked by the degree to which this entire area
    seems to be full of design flaws and half-baked code. I mean, just the
    fact that the pg_auth_members.grantor can be left pointing to a role
    OID that no longer exists is pretty crazy, right? I don't think anyone
    today would consider something with that kind of wart committable.
    
    > That isn't to say that having a LOGIN role get an automatic temporary WITH ADMIN OPTION on itself is a good thing - but there isn't any privilege escalation vector here to be squashed.  There is just a "DBAs should treat LOGIN roles as leaf nodes" expectation in which case there would be no superuser granted memberships to be removed.
    
    Well, we may not have found one yet, but that doesn't prove none
    exists. In any case, if we can agree that it's not necessarily a
    desirable behavior, that's good enough for me.
    
    (I still disagree with the idea that LOGIN roles have to be leaf
    nodes. We could have a system where that's true, but that's not how
    the system we actually have is designed.)
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  96. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-07T19:59:54Z

    
    > On Mar 7, 2022, at 10:28 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > 
    > Does anything interesting break if you do just take it out?
    
     SET SESSION AUTHORIZATION regress_priv_group2;
     GRANT regress_priv_group2 TO regress_priv_user5; -- ok: a role can self-admin
    -NOTICE:  role "regress_priv_user5" is already a member of role "regress_priv_group2"
    +ERROR:  must have admin option on role "regress_priv_group2"
    
    This test failure is just a manifestation of the intended change, but assuming we make no other changes, the error message would clearly need to be updated, because it suggests the role should have admin_option on itself, a situation which is not currently supported.
    
    Perhaps we should support that, though, by adding a reflexive aclitem[] to pg_authid (meaning it tracks which privileges a role has on itself) with tracking of who granted it, so that revocation can be handled properly.  The aclitem could start out null, meaning the role has by default the traditional limited self-admin which the code comments discuss:
    
            /*
             * A role can admin itself when it matches the session user and we're
             * outside any security-restricted operation, SECURITY DEFINER or
             * similar context.  SQL-standard roles cannot self-admin.  However,
             * SQL-standard users are distinct from roles, and they are not
             * grantable like roles: PostgreSQL's role-user duality extends the
             * standard.  Checking for a session user match has the effect of
             * letting a role self-admin only when it's conspicuously behaving
             * like a user.  Note that allowing self-admin under a mere SET ROLE
             * would make WITH ADMIN OPTION largely irrelevant; any member could
             * SET ROLE to issue the otherwise-forbidden command.
             *
             * Withholding self-admin in a security-restricted operation prevents
             * object owners from harnessing the session user identity during
             * administrative maintenance.  Suppose Alice owns a database, has
             * issued "GRANT alice TO bob", and runs a daily ANALYZE.  Bob creates
             * an alice-owned SECURITY DEFINER function that issues "REVOKE alice
             * FROM carol".  If he creates an expression index calling that
             * function, Alice will attempt the REVOKE during each ANALYZE.
             * Checking InSecurityRestrictedOperation() thwarts that attack.
             *
             * Withholding self-admin in SECURITY DEFINER functions makes their
             * behavior independent of the calling user.  There's no security or
             * SQL-standard-conformance need for that restriction, though.
             *
             * A role cannot have actual WITH ADMIN OPTION on itself, because that
             * would imply a membership loop.  Therefore, we're done either way.
             */
    
    For non-null aclitem[], we could support REVOKE ADMIN OPTION FOR joe FROM joe, and for explicit re-grants, we could track who granted it, such that further revocations could properly refuse if the revoker doesn't have sufficient privileges vis-a-vis the role that granted it in the first place.
    
    I have not yet tried to implement this, and might quickly hit problems with the idea, but will take a stab at a proof-of-concept patch unless you suggest a better approach.
    
    Thoughts?
    
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  97. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-07T20:01:37Z

    On Mon, Mar 7, 2022 at 2:59 PM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    > This test failure is just a manifestation of the intended change, but assuming we make no other changes, the error message would clearly need to be updated, because it suggests the role should have admin_option on itself, a situation which is not currently supported.
    
    It's been pointed out upthread that this would have undesirable
    security implications, because the admin option would be inherited,
    and the implicit permission isn't.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  98. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-07T20:03:55Z

    
    > On Mar 7, 2022, at 12:01 PM, Robert Haas <robertmhaas@gmail.com> wrote:
    > 
    > It's been pointed out upthread that this would have undesirable
    > security implications, because the admin option would be inherited,
    > and the implicit permission isn't.
    
    Right, but with a reflexive self-admin-option, we could document that it works in a non-inherited way.  We'd just be saying the current hard-coded behavior is an option which can be revoked rather than something you're stuck with.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  99. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-07T20:09:48Z

    
    > On Mar 7, 2022, at 12:03 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
    > 
    > Right, but with a reflexive self-admin-option, we could document that it works in a non-inherited way.  We'd just be saying the current hard-coded behavior is an option which can be revoked rather than something you're stuck with.
    
    We could also say that the default is to not have admin option on yourself, with that being something grantable, but that is a larger change from the historical behavior and might have more consequences for dump/restore, etc.
    
    My concern about just nuking self-admin is that there may be sites which use self-admin and we'd be leaving them without a simple work-around after upgrade, because they couldn't restore the behavior by executing a grant.  They'd have to more fundamentally restructure their role relationships to not depend on self-admin, something which might be harder for them to do.  Perhaps nobody is using self-admin, or very few people are using it, and I'm being overly concerned.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  100. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-07T20:16:09Z

    Mark Dilger <mark.dilger@enterprisedb.com> writes:
    >> On Mar 7, 2022, at 12:01 PM, Robert Haas <robertmhaas@gmail.com> wrote:
    >> 
    >> It's been pointed out upthread that this would have undesirable
    >> security implications, because the admin option would be inherited,
    >> and the implicit permission isn't.
    
    > Right, but with a reflexive self-admin-option, we could document that it works in a non-inherited way.  We'd just be saying the current hard-coded behavior is an option which can be revoked rather than something you're stuck with.
    
    After reflection, I think that role self-admin is probably a bad idea that
    we should stay away from.  It could perhaps be reasonable given some other
    system design and/or syntax than what SQL gives us, but we're dealing in
    SQL.  It doesn't make sense to GRANT a role to itself, and therefore it
    likewise doesn't make sense to GRANT WITH ADMIN OPTION.
    
    Based on Robert's archaeological dig, it now seems that the fact that
    we have any such behavior at all was just a mistake.  What would be
    lost if we drop it?
    
    Having said that, one thing that I find fishy is that it's not clear
    where the admin privilege for a role originates.  After "CREATE ROLE
    alice", alice has no members, therefore none that have admin privilege,
    therefore the only way that the first member could be added is via
    superuser deus ex machina.  This does not seem clean.  If we recorded
    which user created the role, we could act as though that user has
    admin privilege (whether or not it's a member).  Perhaps I'm
    reinventing something that was already discussed upthread.  I wonder
    what the SQL spec has to say on this point, too.
    
    			regards, tom lane
    
    
    
    
  101. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-07T21:34:20Z

    On Mon, Mar 7, 2022 at 1:16 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Based on Robert's archaeological dig, it now seems that the fact that
    > we have any such behavior at all was just a mistake.  What would be
    > lost if we drop it?
    >
    
    Probably nothing that couldn't be replaced, and with a better model, but I
    do have a concern that there are setups in the wild inadvertently using
    this behavior.  Enough so that I would vote to change it but include a
    migration GUC to restore the current behavior, probably with a deprecation
    warning.  Kinda depends on the post-change dump/restore mechanics.  But
    just tearing it out wouldn't seem extraordinary for us.
    
    
    >
    > Having said that, one thing that I find fishy is that it's not clear
    > where the admin privilege for a role originates.
    
    
    I do not see a problem with there being no inherent admin privilege for a
    role.  A superuser or CREATEROLE user holds admin privilege on all roles in
    the cluster.  They can delegate the privilege to administer a role to yet
    another role in the system.  The necessitates creating two roles - the one
    being administered and the one being delegated to.  I don't see a benefit
    to saving which specific superuser or CREATEROLE user "owns" the role that
    is to be administered.  Not unless non-owner CREATEROLE users are prevented
    from exercising admin privileges on the role.  That all said, I'd accept
    the choice to include such ownership information as a requirement for
    meeting the auditing needs of DBAs.  But I would argue that such auditing
    probably needs to be external to the working system - the fact that
    ownership can be changed reduces the benefit of an in-database value.
    
    > If we recorded
    
    which user created the role, we could act as though that user has
    > admin privilege (whether or not it's a member).
    
    
    I suppose we could record the current owner of a role but that seems
    unnecessary.  I dislike using the "created" concept by virtue of the fact
    that, for routines, "security definer" implies creator but it actually
    means "security owner".
    
    David J.
    
  102. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-08T04:14:31Z

    
    > On Mar 7, 2022, at 12:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > 
    > What would be
    > lost if we drop it?
    
    I looked into this a bit.  Removing that bit of code, the only regression test changes for "check-world" are the expected ones, with nothing else breaking.  Running installcheck+pg_upgrade to the patched version of HEAD from each of versions 11, 12, 13 and 14 doesn't turn up anything untoward.  The change I used (for reference) is attached:
    
    
  103. Re: role self-revocation

    Peter Eisentraut <peter.eisentraut@enterprisedb.com> — 2022-03-09T12:55:01Z

    On 07.03.22 19:18, Robert Haas wrote:
    >> That all said, permissions SHOULD BE strictly additive.  If boss doesn't want to be a member of pg_read_all_files allowing them to revoke themself from that role seems like it should be acceptable.  If there is fear in allowing someone to revoke (not add) themselves as a member of a different role that suggests we have a design issue in another feature of the system.  Today, they neither grant nor revoke, and the self-revocation doesn't seem that important to add.
    > I disagree with this on principle, and I also think that's not how it
    > works today. On the general principle, I do not see a compelling
    > reason why we should have two systems for maintaining groups of users,
    > one of which is used for additive things and one of which is used for
    > subtractive things.
    
    Do we have subtractive permissions today?
    
    
    
    
    
  104. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-09T13:02:25Z

    On Wed, Mar 9, 2022 at 7:55 AM Peter Eisentraut
    <peter.eisentraut@enterprisedb.com> wrote:
    > Do we have subtractive permissions today?
    
    Not in the GRANT/REVOKE sense, I think, but you can put a user in a
    group and then mention that group in pg_hba.conf. And that line might
    be "reject" or whatever.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  105. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-09T20:51:10Z

    On Mon, Mar 7, 2022 at 11:14 PM Mark Dilger
    <mark.dilger@enterprisedb.com> wrote:
    > > On Mar 7, 2022, at 12:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > What would be
    > > lost if we drop it?
    >
    > I looked into this a bit.  Removing that bit of code, the only regression test changes for "check-world" are the expected ones, with nothing else breaking.  Running installcheck+pg_upgrade to the patched version of HEAD from each of versions 11, 12, 13 and 14 doesn't turn up anything untoward.
    
    I looked into this a bit, too. I attach a draft patch for removing the
    self-admin exception.
    
    I found that having is_admin_of_role() return true matters in three
    ways: (1) It lets you grant membership in the role to some other role.
    (2) It lets you revoke membership in the role from some other role.
    (3) It changes the return value of pg_role_aclcheck(), which is used
    in the implementation of various SQL-callable functions all invoked
    via the name pg_has_role(). We've mostly been discussing (2) as an
    issue, but (1) and (3) are pretty interesting too. Regarding (3),
    there is a comment in the code indicating that Noah considered the
    self-admin exception something of a wart as far as pg_has_role() is
    concerned. As to (1), I discovered that today you can do this:
    
    rhaas=# create user foo;
    CREATE ROLE
    rhaas=# create user bar;
    CREATE ROLE
    rhaas=# \q
    [rhaas ~]$ psql -U foo rhaas
    psql (15devel)
    Type "help" for help.
    
    rhaas=> grant foo to bar with admin option;
    GRANT ROLE
    
    I don't know why I didn't realize that before. It's a natural result
    of treating the logged-in user as if they had admin option. But it's
    weird that you can't even be granted WITH ADMIN OPTION on your own
    login role, but at the same time without having it you can grant it to
    someone else!
    
    I believe there are three other points worth some consideration here.
    
    First, in the course of my investigation I re-discovered what Tom
    already did a good job articulating:
    
    tgl> Having said that, one thing that I find fishy is that it's not clear
    tgl> where the admin privilege for a role originates.  After "CREATE ROLE
    tgl> alice", alice has no members, therefore none that have admin privilege,
    tgl> therefore the only way that the first member could be added is via
    tgl> superuser deus ex machina.  This does not seem clean.
    
    I agree with that, but I don't think it's a sufficient reason for
    keeping the self-admin exception, because the same problem exists for
    non-login roles. I don't even think it's the right idea conceptually
    to suppose that the power to administer a role originates from the
    role itself. If that were so, then it would be inherited by all
    members of the role along with all the rest of the role's privileges,
    which is so clearly not right that we've already prohibited a role
    from having WITH ADMIN OPTION on itself. In my opinion, the right to
    administer a role - regardless of whether or not it is a login role -
    most naturally vests in the role that created it, or something in that
    direction at least, if not that exact thing. Today, that means the
    superuser or a CREATEROLE user who could hack superuser if they
    wished. In the future, I hope for other alternatives, as recently
    argued on other threads. But we need not resolve the question of how
    that should work exactly in order to agree (as I hope we do) that
    doubling down on the self-administration exception is not the answer.
    
    Second, it occured to me to wonder what implications a change like
    this might have for dump and restore. If privilege restoration somehow
    relied on this behavior, then we'd have a problem. But I don't think
    it does, because (a) pg_dump can SET ROLE but can't change the session
    user without reconnecting, so it's unclear how we could be relying on
    it; (b) it wouldn't work for non-login roles, and it's unlikely that
    we would treat login and non-login roles different in terms of
    restoring privileges, and (c) when I execute the example shown above
    and then run pg_dump, there's no attempt to change the current user,
    it just dumps "GRANT foo TO bar WITH ADMIN OPTION GRANTED BY foo".
    
    Third, it occurred to me to wonder whether some users might be using
    and relying upon this behavior. It's certainly possible, and it does
    suck that we'd be removing it without providing a workable substitute.
    But it's probably not a LOT of users because most people who have
    commented on this topic on this mailing list seem to find granting
    membership in a login role a super-weird thing to do, because a lot of
    people really seem to want every role to be a user or a group, and a
    login role with members feels like it's blurring that line. I'm
    inclined to think that the small number of people who may be unhappy
    is an acceptable price to pay for removing this wart, but it's a
    judgement call and if someone has information to suggest that I'm
    wrong, it'd be good to hear about that.
    
    Thanks,
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  106. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-09T21:01:40Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Mar 7, 2022, at 12:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > tgl> Having said that, one thing that I find fishy is that it's not clear
    > tgl> where the admin privilege for a role originates.  After "CREATE ROLE
    > tgl> alice", alice has no members, therefore none that have admin privilege,
    > tgl> therefore the only way that the first member could be added is via
    > tgl> superuser deus ex machina.  This does not seem clean.
    
    > I agree with that, but I don't think it's a sufficient reason for
    > keeping the self-admin exception, because the same problem exists for
    > non-login roles. I don't even think it's the right idea conceptually
    > to suppose that the power to administer a role originates from the
    > role itself.
    
    Actually, that's the same thing I was trying to say.  But if it doesn't
    originate from the role itself, where does it originate from?
    
    > In my opinion, the right to
    > administer a role - regardless of whether or not it is a login role -
    > most naturally vests in the role that created it, or something in that
    > direction at least, if not that exact thing.
    
    This seems like a reasonable answer to me too: the creating role has admin
    option implicitly, and can then choose to grant that to other roles.
    Obviously some work needs to be done to make that happen (and we should
    see whether the SQL spec has some different idea).
    
    			regards, tom lane
    
    
    
    
  107. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-09T21:15:51Z

    On Wed, Mar 9, 2022 at 4:01 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > In my opinion, the right to
    > > administer a role - regardless of whether or not it is a login role -
    > > most naturally vests in the role that created it, or something in that
    > > direction at least, if not that exact thing.
    >
    > This seems like a reasonable answer to me too: the creating role has admin
    > option implicitly, and can then choose to grant that to other roles.
    > Obviously some work needs to be done to make that happen (and we should
    > see whether the SQL spec has some different idea).
    
    Well, the problem is that as far as I can see, the admin option is an
    optional feature of membership. You can grant someone membership
    without admin option, or with admin option, but you can't grant them
    the admin option without membership, just like you can't purchase an
    upgrade to first class without the underlying plane ticket. What would
    the syntax look even like for this? GRANT foo TO bar WITH ADMIN OPTION
    BUT WITHOUT MEMBERSHIP? Yikes.
    
    But do we really have to solve this problem before we can clean up
    this session exception? I hope not, because I think that's a much
    bigger can of worms than this is.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  108. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-09T21:20:05Z

    Greetings,
    
    * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > Robert Haas <robertmhaas@gmail.com> writes:
    > > On Mar 7, 2022, at 12:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > tgl> Having said that, one thing that I find fishy is that it's not clear
    > > tgl> where the admin privilege for a role originates.  After "CREATE ROLE
    > > tgl> alice", alice has no members, therefore none that have admin privilege,
    > > tgl> therefore the only way that the first member could be added is via
    > > tgl> superuser deus ex machina.  This does not seem clean.
    > 
    > > I agree with that, but I don't think it's a sufficient reason for
    > > keeping the self-admin exception, because the same problem exists for
    > > non-login roles. I don't even think it's the right idea conceptually
    > > to suppose that the power to administer a role originates from the
    > > role itself.
    > 
    > Actually, that's the same thing I was trying to say.  But if it doesn't
    > originate from the role itself, where does it originate from?
    > 
    > > In my opinion, the right to
    > > administer a role - regardless of whether or not it is a login role -
    > > most naturally vests in the role that created it, or something in that
    > > direction at least, if not that exact thing.
    > 
    > This seems like a reasonable answer to me too: the creating role has admin
    > option implicitly, and can then choose to grant that to other roles.
    
    I agree that this has some appeal, but it's not desirable in all cases
    and so I wouldn't want it to be fully baked into the system ala the role
    'owner' concept.
    
    > Obviously some work needs to be done to make that happen (and we should
    > see whether the SQL spec has some different idea).
    
    Agreed on this, though I don't recall it having much to say on it.
    
    Thanks,
    
    Stephen
    
  109. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-09T21:23:48Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Wed, Mar 9, 2022 at 4:01 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > In my opinion, the right to
    > > > administer a role - regardless of whether or not it is a login role -
    > > > most naturally vests in the role that created it, or something in that
    > > > direction at least, if not that exact thing.
    > >
    > > This seems like a reasonable answer to me too: the creating role has admin
    > > option implicitly, and can then choose to grant that to other roles.
    > > Obviously some work needs to be done to make that happen (and we should
    > > see whether the SQL spec has some different idea).
    > 
    > Well, the problem is that as far as I can see, the admin option is an
    > optional feature of membership. You can grant someone membership
    > without admin option, or with admin option, but you can't grant them
    > the admin option without membership, just like you can't purchase an
    > upgrade to first class without the underlying plane ticket. What would
    > the syntax look even like for this? GRANT foo TO bar WITH ADMIN OPTION
    > BUT WITHOUT MEMBERSHIP? Yikes.
    
    I've been meaning to reply to your other email regarding this, but I
    don't really agree that the syntax ends up being so terrible or
    difficult to deal with, considering we have these same general things
    for ALTER ROLE already and there hasn't been all that much complaining.
    That is, we have LOGIN and NOLOGIN, CREATEROLE and NOCREATEROLE, and we
    could have MEMBERSHIP and NOMEMBERSHIP pretty easily here if we wanted
    to.
    
    > But do we really have to solve this problem before we can clean up
    > this session exception? I hope not, because I think that's a much
    > bigger can of worms than this is.
    
    I do believe we can deal with the above independently and at a later
    time and go ahead and clean up the session excepton bit without dealing
    with the above at the same time.
    
    Thanks,
    
    Stephen
    
  110. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-09T21:24:04Z

    I wrote:
    > This seems like a reasonable answer to me too: the creating role has admin
    > option implicitly, and can then choose to grant that to other roles.
    > Obviously some work needs to be done to make that happen (and we should
    > see whether the SQL spec has some different idea).
    
    Ah, here we go: it's buried under CREATE ROLE.  SQL:2021 12.4 <role
    definition> saith that when role A executes CREATE ROLE <role name>,
    then
    
    1) A grantable role authorization descriptor is created whose role name
    is <role name>, whose grantor is "_SYSTEM", and whose grantee is A.
    
    Since nobody is _SYSTEM, this grant can't be deleted except by dropping
    the new role (or, maybe, dropping A?).  So that has nearly the same
    end result as "the creating role has admin option implicitly".  The main
    difference I can see is that it also means the creating role is a *member*
    implicitly, which is something I'd argue we don't want to enforce.  This
    is analogous to the way we let an object owner revoke her own ordinary
    permissions, which the SQL model doesn't allow since those permissions
    were granted to her by _SYSTEM.
    
    			regards, tom lane
    
    
    
    
  111. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-09T21:31:00Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > Well, the problem is that as far as I can see, the admin option is an
    > optional feature of membership. You can grant someone membership
    > without admin option, or with admin option, but you can't grant them
    > the admin option without membership, just like you can't purchase an
    > upgrade to first class without the underlying plane ticket. What would
    > the syntax look even like for this? GRANT foo TO bar WITH ADMIN OPTION
    > BUT WITHOUT MEMBERSHIP? Yikes.
    
    I don't think we need syntax to describe it.  As I just said in my
    other reply, we have a perfectly good precedent for this already
    in ordinary object permissions.  That is: an object owner always,
    implicitly, has GRANT OPTION for all the object's privileges, even
    if she revoked the corresponding plain privilege from herself.
    
    Yeah, this does mean that we're effectively deciding that the creator
    of a role is its owner.  What's the problem with that?
    
    > But do we really have to solve this problem before we can clean up
    > this session exception?
    
    I think we need a plan for where we're going.  I don't see "clean up
    the session exception" as an end in itself; it's part of re-examining
    how all of this ought to work.  I don't say that we have to have a
    complete patch right away, only that we need a coherent end goal.
    
    			regards, tom lane
    
    
    
    
  112. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-09T22:00:51Z

    On Wed, Mar 9, 2022 at 2:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Robert Haas <robertmhaas@gmail.com> writes:
    > > Well, the problem is that as far as I can see, the admin option is an
    > > optional feature of membership. You can grant someone membership
    > > without admin option, or with admin option, but you can't grant them
    > > the admin option without membership, just like you can't purchase an
    > > upgrade to first class without the underlying plane ticket. What would
    > > the syntax look even like for this? GRANT foo TO bar WITH ADMIN OPTION
    > > BUT WITHOUT MEMBERSHIP? Yikes.
    >
    > I don't think we need syntax to describe it.  As I just said in my
    > other reply, we have a perfectly good precedent for this already
    > in ordinary object permissions.  That is: an object owner always,
    > implicitly, has GRANT OPTION for all the object's privileges, even
    > if she revoked the corresponding plain privilege from herself.
    >
    
    So CREATE ROLE will assign ownership of AND membership in the newly created
    role to the session_user UNLESS the OWNER clause is present in which case
    the named role, so long as the session_user can SET ROLE to the named role,
    becomes the owner & member.  Subsequent to that the owner can issue: REVOKE
    new_role FROM role_name where role_name is again the session_user role or
    one that can be SET ROLE to.
    
    
    > Yeah, this does mean that we're effectively deciding that the creator
    > of a role is its owner.  What's the problem with that?
    >
    
    I'm fine with this.  It does introduce an OWNER concept to roles and so at
    minimum we need to add:
    
    ALTER ROLE foo OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER |
    SESSION_USER }
    
    And similar for CREATE ROLE
    And keep the USER alias commands in sync.
    GROUP commands are only present for backward compatibility and so don't get
    updated with new features by design.
    
    Obviously a superuser can change ownership.
    
    Playing with table ownership I find this behavior:
    -- superuser
    CREATE ROLE tblowner;
    CREATE TABLE tblowner_test (id serial primary key);
    ALTER TABLE tblowner_test OWNER TO tblowner;
    
    CREATE ROLE boss;
    GRANT boss TO tblowner;
    
    SET SESSION AUTHORIZATION tblowner;
    ALTER TABLE tblowner_test OWNER TO boss; --works
    
    So tblowner can push their ownership attribute to any group they are a
    member of.  Is that the behavior we want for roles as well?
    
    David J.
    
  113. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-09T22:35:18Z

    "David G. Johnston" <david.g.johnston@gmail.com> writes:
    > So CREATE ROLE will assign ownership of AND membership in the newly created
    > role to the session_user
    
    I would NOT have it automatically assign membership in the new role,
    even though the SQL spec says so.  We've not done that historically
    and it doesn't seem desirable.  In particular, it's *really* not
    desirable for a user (role with LOGIN).
    
    > I'm fine with this.  It does introduce an OWNER concept to roles and so at
    > minimum we need to add:
    > ALTER ROLE foo OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER |
    > SESSION_USER }
    
    Agreed.
    
    			regards, tom lane
    
    
    
    
  114. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T14:46:46Z

    On Wed, Mar 9, 2022 at 4:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > I don't think we need syntax to describe it.  As I just said in my
    > other reply, we have a perfectly good precedent for this already
    > in ordinary object permissions.  That is: an object owner always,
    > implicitly, has GRANT OPTION for all the object's privileges, even
    > if she revoked the corresponding plain privilege from herself.
    >
    > Yeah, this does mean that we're effectively deciding that the creator
    > of a role is its owner.  What's the problem with that?
    
    I don't think that's entirely the wrong concept, but it doesn't make a
    lot of sense in a world where the creator has to be a superuser. If
    alice, bob, and charlie are superusers who take turns creating new
    users, and then we let charlie go due to budget cuts, forcing alice
    and bob to change the owner of all the users he created to some other
    superuser as a condition of dropping his account is a waste of
    everyone's time. They can do exactly the same things to every account
    on the system after we change the role owner as before.
    
    But wait, I hear you cry, what about CREATEROLE? Well, CREATEROLE is
    generally agreed to be broken right now, and if you don't agree with
    that, consider that it can grant pg_execute_server_programs to a
    newly-created account and then explain to me how it's functionally
    different from superuser. The whole area needs a rethink. I believe
    everyone involved in the discussion on the other threads agrees that
    some reform of CREATEROLE is necessary, and more generally with the
    idea that it's useful for non-superusers to be able to create roles.
    But the reasons why people want that vary.
    
    I want that because I want mini-superusers, where alice can administer
    the users that alice creates just as if she were a superuser,
    including having their permissions implicitly and dropping them when
    she wants them gone, but where alice cannot break out to the operating
    system as a true superuser could do. I want this because the lack of
    meaningful privilege separation that led to CVE-2019-9193 being filed
    spuriously is a very real problem. It's a thing a lot of people want,
    and I want to give it to them. David Steele, on the other hand, wants
    to build a user-creating bot that can create accounts but otherwise
    conforms to the principle of least privilege: the bot can stand up
    accounts, can grant them membership in a defined set of groups, but
    cannot exercise the privileges of those accounts (or hack superuser
    either). Other people may well want other things.
    
    And that's why I'm not sure it's really the right idea to say that we
    don't need syntax for this admin-without-member concept. If we just
    want to bolt role ownership onto the existing framework without really
    changing anything else, we can do that without extra syntax and, as
    you say here, make it an implicit property of role ownership. But I
    don't see that as has having much value; we just end up with a bunch
    of superuser owners. Whatever. Now Stephen made the argument that we
    ought to actually have admin-without-member as a first class concept,
    something that could be assigned to arbitrary users. Actually, I think
    he wanted it even more fine grained with that. And I think that could
    make the concept a lot more useful, but then it needs some kind of
    understandable syntax.
    
    There's a lot of moving parts here. It's not just about coming up with
    something that sounds generally logical, but about creating a system
    that has some real-world utility.
    
    > > But do we really have to solve this problem before we can clean up
    > > this session exception?
    >
    > I think we need a plan for where we're going.  I don't see "clean up
    > the session exception" as an end in itself; it's part of re-examining
    > how all of this ought to work.  I don't say that we have to have a
    > complete patch right away, only that we need a coherent end goal.
    
    I'd like to have a plan, too, but if this behavior is accidental, I
    still think we can remove it without making big decisions about future
    direction. The perfect is the enemy of the good.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  115. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T15:56:36Z

    On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Wed, Mar 9, 2022 at 4:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > I don't think we need syntax to describe it.  As I just said in my
    > > other reply, we have a perfectly good precedent for this already
    > > in ordinary object permissions.  That is: an object owner always,
    > > implicitly, has GRANT OPTION for all the object's privileges, even
    > > if she revoked the corresponding plain privilege from herself.
    > >
    > > Yeah, this does mean that we're effectively deciding that the creator
    > > of a role is its owner.  What's the problem with that?
    >
    > I don't think that's entirely the wrong concept, but it doesn't make a
    > lot of sense in a world where the creator has to be a superuser. If
    > alice, bob, and charlie are superusers who take turns creating new
    > users, and then we let charlie go due to budget cuts, forcing alice
    > and bob to change the owner of all the users he created to some other
    > superuser as a condition of dropping his account is a waste of
    > everyone's time. They can do exactly the same things to every account
    > on the system after we change the role owner as before.
    >
    
    Then maybe we should just implement the idea that if a superuser would
    become the owner we instead substitute in the bootstrap user.  Or give the
    DBA the choice whether they want to retain knowledge of specific roles -
    and thus are willing to accept the "waste of time".
    
    
    > But wait, I hear you cry, what about CREATEROLE? Well, CREATEROLE is
    > generally agreed to be broken right now, and if you don't agree with
    > that, consider that it can grant pg_execute_server_programs to a
    > newly-created account and then explain to me how it's functionally
    > different from superuser.
    
    
    CREATEROLE has long been defined as basically having "with admin option" on
    every role in the system.  The failure to special-case the roles that grant
    different aspects of superuser-ness to its members doesn't make CREATEROLE
    itself broken, it makes the implementation of pg_execute_server_programs
    broken.  Only superusers should be considered to have with admin option on
    these roles. They can delegate through the usual membership+admin mechanism
    to a CREATEROLE role if they desire.
    
    
    > The whole area needs a rethink. I believe
    > everyone involved in the discussion on the other threads agrees that
    > some reform of CREATEROLE is necessary, and more generally with the
    > idea that it's useful for non-superusers to be able to create roles.
    >
    
    As the documentation says, using SUPERUSER for day-to-day administration is
    contrary to good security practices.  Role management is considered to be a
    day-to-day administration activity.  I agree with this principle.  It was
    designed to neither be a superuser nor grant superuser, so removing the
    ability to grant the pg_* role memberships remains consistent with its
    original intent.
    
    
    > I want that because I want mini-superusers, where alice can administer
    > the users that alice creates just as if she were a superuser,
    > including having their permissions implicitly and dropping them when
    > she wants them gone, but where alice cannot break out to the operating
    > system as a true superuser could do.
    
    
    CREATEROLE (once the pg_* with admin rules are fixed) + Ownership and rules
    restricting interfering with another role's objects (unless superuser)
    seems to handle this.
    
    
    > the bot can stand up
    > accounts, can grant them membership in a defined set of groups, but
    > cannot exercise the privileges of those accounts (or hack superuser
    > either).
    
    
    The bot should be provided a security definer procedure that encapsulates
    all of this rather than us trying to hack the permission system.  This
    isn't a user permission concern, it is an unauthorized privilege escalation
    concern.  Anyone with the bot's credentials can trivially overcome the
    third restriction by creating a role with the desired membership and then
    logging in as that role - and there is nothing the system can do to prevent
    that while also allowing the other two permissions.
    
    
    > And that's why I'm not sure it's really the right idea to say that we
    > don't need syntax for this admin-without-member concept.
    
    
    We already have this syntax in the form of CREATEROLE.  But we do need a
    fix, just on the group side.  We need a way to define a group as having no
    ADMINS.
    
    ALTER ROLE pg_superuser WITH [NO] ADMIN;
    
    Then adding a role membership including the WITH ADMIN OPTION can be
    rejected, as can the non-superuser situation.  Setting WITH NO ADMIN should
    fail if any existing members have admin.  You must be a superuser to
    execute WITH ADMIN (maybe WITH NO ADMIN as well...).  And possibly even a
    new pg_* role that grants this ability (and maybe some others) for use by a
    backup/restore user.
    
    Or just special-case pg_* roles.
    
    The advantage of exposing this to the DBA is that they can then package
    pg_* roles into a custom group and still have the benefit of superuser only
    administration.  In the special-case implementation the presence of a pg_*
    role in a group hierarchy would then preclude a non-superuser from having
    admin on the entire tree (the pg_* roles are all roots, or in the case of
    pg_monitor, directly emanate from a root role).
    
    David J.
    
    
    David J.
    
  116. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-10T16:19:10Z

    Greetings,
    
    * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com> wrote:
    > > On Wed, Mar 9, 2022 at 4:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > I don't think we need syntax to describe it.  As I just said in my
    > > > other reply, we have a perfectly good precedent for this already
    > > > in ordinary object permissions.  That is: an object owner always,
    > > > implicitly, has GRANT OPTION for all the object's privileges, even
    > > > if she revoked the corresponding plain privilege from herself.
    > > >
    > > > Yeah, this does mean that we're effectively deciding that the creator
    > > > of a role is its owner.  What's the problem with that?
    > >
    > > I don't think that's entirely the wrong concept, but it doesn't make a
    > > lot of sense in a world where the creator has to be a superuser. If
    > > alice, bob, and charlie are superusers who take turns creating new
    > > users, and then we let charlie go due to budget cuts, forcing alice
    > > and bob to change the owner of all the users he created to some other
    > > superuser as a condition of dropping his account is a waste of
    > > everyone's time. They can do exactly the same things to every account
    > > on the system after we change the role owner as before.
    > 
    > Then maybe we should just implement the idea that if a superuser would
    > become the owner we instead substitute in the bootstrap user.  Or give the
    > DBA the choice whether they want to retain knowledge of specific roles -
    > and thus are willing to accept the "waste of time".
    
    This doesn't strike me as going in the right direction.  Falling back to
    the bootstrap superuser is generally a hack and not a great one.  I'll
    also point out that the SQL spec hasn't got a concept of role ownership
    either.
    
    > > But wait, I hear you cry, what about CREATEROLE? Well, CREATEROLE is
    > > generally agreed to be broken right now, and if you don't agree with
    > > that, consider that it can grant pg_execute_server_programs to a
    > > newly-created account and then explain to me how it's functionally
    > > different from superuser.
    > 
    > CREATEROLE has long been defined as basically having "with admin option" on
    > every role in the system.  The failure to special-case the roles that grant
    > different aspects of superuser-ness to its members doesn't make CREATEROLE
    > itself broken, it makes the implementation of pg_execute_server_programs
    > broken.  Only superusers should be considered to have with admin option on
    > these roles. They can delegate through the usual membership+admin mechanism
    > to a CREATEROLE role if they desire.
    
    No, CREATEROLE having admin option on every role in the system is broken
    and always has been.  It's not just an issue for predefined roles like
    pg_execute_server_program, it's an issue for any role that could become
    a superuser either directly or indirectly and that extends beyond the
    predefined ones.  As this issue with CREATEROLE existed way before
    predefined roles were added to PG, claiming that it's an issue with
    predefined roles doesn't make a bit of sense.
    
    > > The whole area needs a rethink. I believe
    > > everyone involved in the discussion on the other threads agrees that
    > > some reform of CREATEROLE is necessary, and more generally with the
    > > idea that it's useful for non-superusers to be able to create roles.
    > 
    > As the documentation says, using SUPERUSER for day-to-day administration is
    > contrary to good security practices.  Role management is considered to be a
    > day-to-day administration activity.  I agree with this principle.  It was
    > designed to neither be a superuser nor grant superuser, so removing the
    > ability to grant the pg_* role memberships remains consistent with its
    > original intent.
    
    That would not be sufficient to make CREATEROLE safe.  Far, far from it.
    
    > > I want that because I want mini-superusers, where alice can administer
    > > the users that alice creates just as if she were a superuser,
    > > including having their permissions implicitly and dropping them when
    > > she wants them gone, but where alice cannot break out to the operating
    > > system as a true superuser could do.
    > 
    > CREATEROLE (once the pg_* with admin rules are fixed) + Ownership and rules
    > restricting interfering with another role's objects (unless superuser)
    > seems to handle this.
    
    This is not sufficient- roles can be not-superuser themselves but have
    the ability to become superuser if GRANT'd a superuser role and
    therefore we can't have a system where CREATEROLE allows arbitrary
    GRANT'ing of roles to each other.  I'm a bit confused too as anything
    where we are curtailing what CREATEROLE roles are able to do in a manner
    that means they're only able to modify some subset of roles should
    equally apply to predefined roles too- that is, CREATEROLE shouldn't be
    the determining factor in the question of if a role can GRANT a
    predefined (or any other role) to some other role- that should be
    governed by the admin option on that role, and that should work exactly
    the same for predefined roles as it does for any other.
    
    I disagree that ownership is needed that's not what the spec calls for
    either.  What we need is more flexibility when it comes to the
    relationships which are allowed to be created between roles and what
    privileges come with them.  To that end, I'd argue that we should be
    extending pg_auth_members, first by separating out membership itself
    into an explicitly tracked attribute (instead of being implicit in the
    existance of a row in the table) and then adding on what other
    privileges we see fit to add, such as the ability to DROP a role.  We
    do need to remove the ability for a role who hasn't been explicitly
    given the admin right on another role to modify that role's membership
    too, as was originally proposed here.  This also seems to more closely
    follow the spec's expectation, something that role ownership doesn't.
    
    > > the bot can stand up
    > > accounts, can grant them membership in a defined set of groups, but
    > > cannot exercise the privileges of those accounts (or hack superuser
    > > either).
    > 
    > The bot should be provided a security definer procedure that encapsulates
    > all of this rather than us trying to hack the permission system.  This
    > isn't a user permission concern, it is an unauthorized privilege escalation
    > concern.  Anyone with the bot's credentials can trivially overcome the
    > third restriction by creating a role with the desired membership and then
    > logging in as that role - and there is nothing the system can do to prevent
    > that while also allowing the other two permissions.
    
    Falling back to security definer functions may be one approach but it's
    not a great one and it only works if it's possible to end up with the
    catalogs having what is actually desired- for example, ADMIN option
    without membership isn't something the catalogs today can understand
    because existance in pg_auth_members implies membership and you can't
    have ADMIN without having that row.  The same issue would exist with
    ownership if ownership implied the same- that's not improving things.
    
    > > And that's why I'm not sure it's really the right idea to say that we
    > > don't need syntax for this admin-without-member concept.
    > 
    > We already have this syntax in the form of CREATEROLE.  But we do need a
    > fix, just on the group side.  We need a way to define a group as having no
    > ADMINS.
    
    We don't have this syntax today nor do we have a way to store such a
    concept in the catalogs either, so I'm pretty baffled by this.  Defining
    a group without admins is, in fact, what we actually have support for
    today in the catalogs- it's just a case where there aren't any rows in
    pg_auth_members which have 'admin_option' as true.  The opposite is what
    we're talking about here- rows which have 'admin_option' as true but
    don't have membership, and that can't be the case today because
    existance in the table itself implies membership.
    
    > ALTER ROLE pg_superuser WITH [NO] ADMIN;
    > 
    > Then adding a role membership including the WITH ADMIN OPTION can be
    > rejected, as can the non-superuser situation.  Setting WITH NO ADMIN should
    > fail if any existing members have admin.  You must be a superuser to
    > execute WITH ADMIN (maybe WITH NO ADMIN as well...).  And possibly even a
    > new pg_* role that grants this ability (and maybe some others) for use by a
    > backup/restore user.
    
    I'm not following this in general or how it helps.  Surely we don't want
    to limit WITH ADMIN to superusers.  As for if we should migrate
    CREATEROLE to a new predefined role, maybe, but that seems like a
    different question.
    
    > Or just special-case pg_* roles.
    
    As I hopefully made clear above, this isn't actually a solution, nor do
    pg_* roles need to be treated somehow differently in this aspect.
    
    > The advantage of exposing this to the DBA is that they can then package
    > pg_* roles into a custom group and still have the benefit of superuser only
    > administration.  In the special-case implementation the presence of a pg_*
    > role in a group hierarchy would then preclude a non-superuser from having
    > admin on the entire tree (the pg_* roles are all roots, or in the case of
    > pg_monitor, directly emanate from a root role).
    
    We are very much trying to move away from 'superuser only
    administration'.
    
    Thanks,
    
    Stephen
    
  117. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-10T16:26:42Z

    
    > On Mar 10, 2022, at 7:56 AM, David G. Johnston <david.g.johnston@gmail.com> wrote:
    > 
    > 
    > I want that because I want mini-superusers, where alice can administer
    > the users that alice creates just as if she were a superuser,
    > including having their permissions implicitly and dropping them when
    > she wants them gone, but where alice cannot break out to the operating
    > system as a true superuser could do.
    > 
    > CREATEROLE (once the pg_* with admin rules are fixed) + Ownership and rules restricting interfering with another role's objects (unless superuser) seems to handle this.
    
    What if one of alice's subordinates also owns roles?  Can alice interfere with *that* role's objects?  I don't see that a simple rule restricting roles from interfering with another role's objects is quite enough.  That raises the question of whether role ownership is transitive, and whether we need a concept similar to inherit/noinherit for ownership.
    
    There is also the problem that CREATEROLE currently allows a set of privileges to be granted to created roles, and that set of privileges is hard-coded.  You've suggested changing the hard-coded rules to remove pg_* roles from the list of grantable privileges, but that's still an inflexible set of hardcoded privileges.  Wouldn't it make more sense for the grantor to need GRANT OPTION on any privilege they give to roles they create?
    
    > the bot can stand up
    > accounts, can grant them membership in a defined set of groups, but
    > cannot exercise the privileges of those accounts (or hack superuser
    > either).
    > 
    > The bot should be provided a security definer procedure that encapsulates all of this rather than us trying to hack the permission system.  This isn't a user permission concern, it is an unauthorized privilege escalation concern.  Anyone with the bot's credentials can trivially overcome the third restriction by creating a role with the desired membership and then logging in as that role - and there is nothing the system can do to prevent that while also allowing the other two permissions.
    
    Doesn't this assume password authentication?  If the server uses ldap authentication, for example, wouldn't the bot need valid ldap credentials for at least one user for this attack to work?  And if CREATEROLE has been made more configurable, wouldn't the bot only be able to grant that ldap user the limited set of privileges that the bot's database user has been granted ADMIN OPTION for?
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  118. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T17:11:36Z

    On Thu, Mar 10, 2022 at 11:19 AM Stephen Frost <sfrost@snowman.net> wrote:
    > I disagree that ownership is needed that's not what the spec calls for
    > either.  What we need is more flexibility when it comes to the
    > relationships which are allowed to be created between roles and what
    > privileges come with them.  To that end, I'd argue that we should be
    > extending pg_auth_members, first by separating out membership itself
    > into an explicitly tracked attribute (instead of being implicit in the
    > existance of a row in the table) and then adding on what other
    > privileges we see fit to add, such as the ability to DROP a role.  We
    > do need to remove the ability for a role who hasn't been explicitly
    > given the admin right on another role to modify that role's membership
    > too, as was originally proposed here.  This also seems to more closely
    > follow the spec's expectation, something that role ownership doesn't.
    
    I do not have a problem with more fine-grained kinds of authorization
    even though I think there are syntactic issues to work out, but I
    strongly disagree with the idea that we can't or shouldn't also have
    role ownership. Marc invented it. Now Tom has invented it
    independently. All sorts of other objects have it already. Trying to
    make it out like this is some kind of kooky idea is not believable.
    Yeah, it's not the most sophisticated or elegant model and that's why
    it's good for us to also have other things, but for simple cases it is
    easy to understand and works great.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  119. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T17:26:08Z

    On Thu, Mar 10, 2022 at 9:19 AM Stephen Frost <sfrost@snowman.net> wrote:
    
    > Greetings,
    >
    > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com>
    > wrote:
    > > > On Wed, Mar 9, 2022 at 4:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > > I don't think we need syntax to describe it.  As I just said in my
    > > > > other reply, we have a perfectly good precedent for this already
    > > > > in ordinary object permissions.  That is: an object owner always,
    > > > > implicitly, has GRANT OPTION for all the object's privileges, even
    > > > > if she revoked the corresponding plain privilege from herself.
    > > > >
    > > > > Yeah, this does mean that we're effectively deciding that the creator
    > > > > of a role is its owner.  What's the problem with that?
    > > >
    > > > I don't think that's entirely the wrong concept, but it doesn't make a
    > > > lot of sense in a world where the creator has to be a superuser. If
    > > > alice, bob, and charlie are superusers who take turns creating new
    > > > users, and then we let charlie go due to budget cuts, forcing alice
    > > > and bob to change the owner of all the users he created to some other
    > > > superuser as a condition of dropping his account is a waste of
    > > > everyone's time. They can do exactly the same things to every account
    > > > on the system after we change the role owner as before.
    > >
    > > Then maybe we should just implement the idea that if a superuser would
    > > become the owner we instead substitute in the bootstrap user.  Or give
    > the
    > > DBA the choice whether they want to retain knowledge of specific roles -
    > > and thus are willing to accept the "waste of time".
    >
    > This doesn't strike me as going in the right direction.  Falling back to
    > the bootstrap superuser is generally a hack and not a great one.  I'll
    > also point out that the SQL spec hasn't got a concept of role ownership
    > either.
    >
    > > > But wait, I hear you cry, what about CREATEROLE? Well, CREATEROLE is
    > > > generally agreed to be broken right now, and if you don't agree with
    > > > that, consider that it can grant pg_execute_server_programs to a
    > > > newly-created account and then explain to me how it's functionally
    > > > different from superuser.
    > >
    > > CREATEROLE has long been defined as basically having "with admin option"
    > on
    > > every role in the system.  The failure to special-case the roles that
    > grant
    > > different aspects of superuser-ness to its members doesn't make
    > CREATEROLE
    > > itself broken, it makes the implementation of pg_execute_server_programs
    > > broken.  Only superusers should be considered to have with admin option
    > on
    > > these roles. They can delegate through the usual membership+admin
    > mechanism
    > > to a CREATEROLE role if they desire.
    >
    > No, CREATEROLE having admin option on every role in the system is broken
    > and always has been.  It's not just an issue for predefined roles like
    > pg_execute_server_program,
    
    
    
    > it's an issue for any role that could become
    > a superuser either directly or indirectly and that extends beyond the
    > predefined ones.
    
    
    The only indirect way for a role to become superuser is to have been
    granted membership in a superuser group, then SET ROLE.  Non-superusers
    cannot do this.  If a superuser does this I consider the outcome to be no
    different than if they go and do:
    
    SET allow_system_table_mods TO true;
    DROP pg_catalog.pg_class;
    
    In short, having a CREATEROLE user issuing:
    GRANT pg_read_all_stats TO davidj;
    should result in the same outcome as them issuing:
    GRANT postgres TO davidj;
    -- ERROR:  must be superuser to alter superusers
    
    Superusers can break their system and we don't go to great effort to stop
    them.  I see no difference here, so arguments of this nature aren't all
    that compelling to me.
    
    CREATEROLE shouldn't be
    > the determining factor in the question of if a role can GRANT a
    > predefined (or any other role) to some other role- that should be
    > governed by the admin option on that role, and that should work exactly
    > the same for predefined roles as it does for any other.
    >
    
    Never granting the CREATEROLE attribute to anyone will give you this
    outcome today.
    
    
    
    > ADMIN option
    > without membership isn't something the catalogs today can understand
    >
    
    Today, they don't need to in order for the system to function within its
    existing design specs.
    
    
    > > ALTER ROLE pg_superuser WITH [NO] ADMIN;
    > >
    > > Then adding a role membership including the WITH ADMIN OPTION can be
    > > rejected, as can the non-superuser situation.  Setting WITH NO ADMIN
    > should
    > > fail if any existing members have admin.  You must be a superuser to
    > > execute WITH ADMIN (maybe WITH NO ADMIN as well...).  And possibly even a
    > > new pg_* role that grants this ability (and maybe some others) for use
    > by a
    > > backup/restore user.
    >
    > I'm not following this in general or how it helps.  Surely we don't want
    > to limit WITH ADMIN to superusers.
    
    
    Today a non-superuser cannot "grant postgres to someuser;"
    
    The point of this attribute is to allow the superuser to apply that rule to
    other roles that aren't superuser.  In particular, the predefined pg_*
    roles.  But it could extend to any other role the superuser would like to
    limit.  It means, for that for named role, ADMIN privileges cannot be
    delegated to other roles - thus all administration of that role's
    membership roster must happen by a superuser.
    
    In particular, this means CREATEROLE roles cannot assign membership in the
    marked roles; just like they cannot assign membership in superuser roles
    today.
    
    For me, because the SUPERUSER cannot have its role become a group without a
    superuser making that choice, and by default the default pg_* roles will
    all have this property as well, and any newly superuser created roles that
    may be members of either superuser or pg_* can have the property defined as
    well, gives full control to the superuser as to how superuser abilities are
    doled out and so the design itself allows for what many of you are
    considering to be "safe usage".  That "unsafe configurations" are possible
    is due to the policy that superusers are unrestricted in what they can do,
    including making unsafe and destructive choices.
    
    In short, removing the self-administration rule solves the "login roles
    should not be automatically considered groups administered by themselves"
    problem - or at least a feature we really don't need.
    And defining a "superuser administration only" attribute to a role solves
    the indirect superuser privileges and assignment thereof by non-superusers
    problem.
    
    I can see value in adding a feature whereby we allow the DBA to define a
    group as a schema-like container and then assign roles to that group with a
    fine-grained permissions model.  My take is this proposal is a new feature
    while the two problems noted above can be solved more readily and with less
    risk with the two suggested changes.
    
    David J.
    
  120. Re: role self-revocation

    Joshua Brindle <joshua.brindle@crunchydata.com> — 2022-03-10T17:26:42Z

    On Thu, Mar 10, 2022 at 12:11 PM Robert Haas <robertmhaas@gmail.com> wrote:
    >
    > On Thu, Mar 10, 2022 at 11:19 AM Stephen Frost <sfrost@snowman.net> wrote:
    > > I disagree that ownership is needed that's not what the spec calls for
    > > either.  What we need is more flexibility when it comes to the
    > > relationships which are allowed to be created between roles and what
    > > privileges come with them.  To that end, I'd argue that we should be
    > > extending pg_auth_members, first by separating out membership itself
    > > into an explicitly tracked attribute (instead of being implicit in the
    > > existance of a row in the table) and then adding on what other
    > > privileges we see fit to add, such as the ability to DROP a role.  We
    > > do need to remove the ability for a role who hasn't been explicitly
    > > given the admin right on another role to modify that role's membership
    > > too, as was originally proposed here.  This also seems to more closely
    > > follow the spec's expectation, something that role ownership doesn't.
    >
    > I do not have a problem with more fine-grained kinds of authorization
    > even though I think there are syntactic issues to work out, but I
    > strongly disagree with the idea that we can't or shouldn't also have
    > role ownership. Marc invented it. Now Tom has invented it
    > independently. All sorts of other objects have it already. Trying to
    > make it out like this is some kind of kooky idea is not believable.
    > Yeah, it's not the most sophisticated or elegant model and that's why
    > it's good for us to also have other things, but for simple cases it is
    > easy to understand and works great.
    
    Ownership implies DAC, the ability to grant others rights to an
    object. It's not "kooky" to see roles as owned objects, but it isn't
    required either. For example most objects on a UNIX system are owned
    and subject to DAC but users aren't.
    
    Stephen's, and now my, issue with ownership is that, since it implies
    DAC, most checks will be bypassed for the owner. We would both prefer
    for everyone to be subject to the grants, including whoever created
    the role.
    
    Rather, we'd like to see a "creators of roles get this set of grants
    against the role by default" and "as a superuser I can revoke grants
    from creators against roles they created"
    
    
    
    
  121. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-10T18:05:54Z

    Greetings,
    
    * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 9:19 AM Stephen Frost <sfrost@snowman.net> wrote:
    > > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > > On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com>
    > > wrote:
    > > > > On Wed, Mar 9, 2022 at 4:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > > > I don't think we need syntax to describe it.  As I just said in my
    > > > > > other reply, we have a perfectly good precedent for this already
    > > > > > in ordinary object permissions.  That is: an object owner always,
    > > > > > implicitly, has GRANT OPTION for all the object's privileges, even
    > > > > > if she revoked the corresponding plain privilege from herself.
    > > > > >
    > > > > > Yeah, this does mean that we're effectively deciding that the creator
    > > > > > of a role is its owner.  What's the problem with that?
    > > > >
    > > > > I don't think that's entirely the wrong concept, but it doesn't make a
    > > > > lot of sense in a world where the creator has to be a superuser. If
    > > > > alice, bob, and charlie are superusers who take turns creating new
    > > > > users, and then we let charlie go due to budget cuts, forcing alice
    > > > > and bob to change the owner of all the users he created to some other
    > > > > superuser as a condition of dropping his account is a waste of
    > > > > everyone's time. They can do exactly the same things to every account
    > > > > on the system after we change the role owner as before.
    > > >
    > > > Then maybe we should just implement the idea that if a superuser would
    > > > become the owner we instead substitute in the bootstrap user.  Or give
    > > the
    > > > DBA the choice whether they want to retain knowledge of specific roles -
    > > > and thus are willing to accept the "waste of time".
    > >
    > > This doesn't strike me as going in the right direction.  Falling back to
    > > the bootstrap superuser is generally a hack and not a great one.  I'll
    > > also point out that the SQL spec hasn't got a concept of role ownership
    > > either.
    > >
    > > > > But wait, I hear you cry, what about CREATEROLE? Well, CREATEROLE is
    > > > > generally agreed to be broken right now, and if you don't agree with
    > > > > that, consider that it can grant pg_execute_server_programs to a
    > > > > newly-created account and then explain to me how it's functionally
    > > > > different from superuser.
    > > >
    > > > CREATEROLE has long been defined as basically having "with admin option"
    > > on
    > > > every role in the system.  The failure to special-case the roles that
    > > grant
    > > > different aspects of superuser-ness to its members doesn't make
    > > CREATEROLE
    > > > itself broken, it makes the implementation of pg_execute_server_programs
    > > > broken.  Only superusers should be considered to have with admin option
    > > on
    > > > these roles. They can delegate through the usual membership+admin
    > > mechanism
    > > > to a CREATEROLE role if they desire.
    > >
    > > No, CREATEROLE having admin option on every role in the system is broken
    > > and always has been.  It's not just an issue for predefined roles like
    > > pg_execute_server_program,
    > 
    > 
    > 
    > > it's an issue for any role that could become
    > > a superuser either directly or indirectly and that extends beyond the
    > > predefined ones.
    > 
    > 
    > The only indirect way for a role to become superuser is to have been
    > granted membership in a superuser group, then SET ROLE.  Non-superusers
    > cannot do this.  If a superuser does this I consider the outcome to be no
    > different than if they go and do:
    
    A non-superuser absolutely can be GRANT'd membership in a superuser role
    and then SET ROLE to that user thus becoming a superuser.  Giving users
    a regular role to log in as and then membership in a role that can
    become a superuser is akin to having a sudoers group in Unix and is good
    practice, not something that everyone should have to be super-dooper
    careful to not do, lest a CREATEROLE user be able to leverage that.
    
    > SET allow_system_table_mods TO true;
    > DROP pg_catalog.pg_class;
    
    I don't equate these in the least.
    
    > In short, having a CREATEROLE user issuing:
    > GRANT pg_read_all_stats TO davidj;
    > should result in the same outcome as them issuing:
    > GRANT postgres TO davidj;
    > -- ERROR:  must be superuser to alter superusers
    
    No, what should matter is if the role doing the GRANT has admin rights
    on pg_read_all_stats, or on the postgres role.  That also happens to be
    what the spec says.
    
    > Superusers can break their system and we don't go to great effort to stop
    > them.  I see no difference here, so arguments of this nature aren't all
    > that compelling to me.
    
    That you don't feel they're compelling don't make them somehow not real,
    nor even particularly uncommon, nor do I view ignoring that possibility
    as somehow creating a strong authentication system.
    
    > CREATEROLE shouldn't be
    > > the determining factor in the question of if a role can GRANT a
    > > predefined (or any other role) to some other role- that should be
    > > governed by the admin option on that role, and that should work exactly
    > > the same for predefined roles as it does for any other.
    > >
    > 
    > Never granting the CREATEROLE attribute to anyone will give you this
    > outcome today.
    
    ... which is why CREATEROLE is broken.
    
    > > ADMIN option
    > > without membership isn't something the catalogs today can understand
    > 
    > Today, they don't need to in order for the system to function within its
    > existing design specs.
    
    Eh?  Your argument here is "don't use CREATEROLE"?  While I agree with
    that being a generally good idea today, it hardly makes sense to suggest
    it in a thread where we're talking about how to make CREATEROLE, or
    something like it, be useful.
    
    > > > ALTER ROLE pg_superuser WITH [NO] ADMIN;
    > > >
    > > > Then adding a role membership including the WITH ADMIN OPTION can be
    > > > rejected, as can the non-superuser situation.  Setting WITH NO ADMIN
    > > should
    > > > fail if any existing members have admin.  You must be a superuser to
    > > > execute WITH ADMIN (maybe WITH NO ADMIN as well...).  And possibly even a
    > > > new pg_* role that grants this ability (and maybe some others) for use
    > > by a
    > > > backup/restore user.
    > >
    > > I'm not following this in general or how it helps.  Surely we don't want
    > > to limit WITH ADMIN to superusers.
    > 
    > Today a non-superuser cannot "grant postgres to someuser;"
    
    No, but a role can be created like 'admin', which a superuser GRANT's
    'postgres' to and then that role can be GRANT'd to anyone by anyone who
    has CREATEROLE rights.  That's not sane.
    
    > The point of this attribute is to allow the superuser to apply that rule to
    > other roles that aren't superuser.  In particular, the predefined pg_*
    > roles.  But it could extend to any other role the superuser would like to
    > limit.  It means, for that for named role, ADMIN privileges cannot be
    > delegated to other roles - thus all administration of that role's
    > membership roster must happen by a superuser.
    
    The whole "X can't modify a superuser role without being a superuser"
    concept is just broken and was a poor choice when it was originally
    done specifically because it only looks at individual roles and their
    specific rolsuper bit, completely ignoring the fact that role membership
    exists as a thing that we should handle sanely, including a
    non-superuser role being grant'd a superuser role.  Predefined roles
    haven't got anything to do with any of this, they only make it more
    obvious to people who didn't understand how the system worked before
    they came along.
    
    I disagree entirely with the idea that we must have some roles who can
    only ever be administered by a superuser.  If anything, we should be
    moving away (as we have, in fact, been doing), from anything being the
    exclusive purview of the superuser.
    
    > In particular, this means CREATEROLE roles cannot assign membership in the
    > marked roles; just like they cannot assign membership in superuser roles
    > today.
    
    I disagree with the idea that we need to mark some roles as only being
    able to be modified by the superuser- why invent this?  We have the
    ADMIN option already and that can be applied to allow any role X to have
    the ability to modify the members of role Y.  That's a whole lot better
    than some explicit flag that says "only superusers can modify this
    role".  If an admin wants that, they can set things up that way already
    today, as long as they don't use the current CREATEROLE attribute.
    Ideally, we'd modify CREATEROLE, or remove it and replace it with
    something better, which still maintains that same flexibility.  What you
    seem to be arguing for here is to rip out the ADMIN functionality, which
    is defined by spec and not even exclusively by PG, and replace it with a
    single per-role flag that says if that role can only be modified by
    superusers.  That seems entirely backwards to me.
    
    > For me, because the SUPERUSER cannot have its role become a group without a
    > superuser making that choice, and by default the default pg_* roles will
    > all have this property as well, and any newly superuser created roles that
    > may be members of either superuser or pg_* can have the property defined as
    > well, gives full control to the superuser as to how superuser abilities are
    > doled out and so the design itself allows for what many of you are
    > considering to be "safe usage".  That "unsafe configurations" are possible
    > is due to the policy that superusers are unrestricted in what they can do,
    > including making unsafe and destructive choices.
    
    I disagree that it's an 'unsafe configuration' for there to ever exist a
    non-superuser role that has been granted a superuser role.  The only
    thing that makes this unsafe is the existance of CREATEROLE.
    
    Why are we making this all about superusers though?  In what you're
    proposing, you're suggesting that it's perfectly fine for any role which
    has CREATEROLE to be able to take over any other role in the entire
    system, excluding predefined roles and superusers.  How is that sane, or
    truely much less than what the superuser has in terms of ability?  The
    short answer is that it's not- which is why we have documented
    CREATEROLE as being 'superuser light'.  The goal here is to get rid of
    that.
    
    > In short, removing the self-administration rule solves the "login roles
    > should not be automatically considered groups administered by themselves"
    > problem - or at least a feature we really don't need.
    > And defining a "superuser administration only" attribute to a role solves
    > the indirect superuser privileges and assignment thereof by non-superusers
    > problem.
    
    But it doesn't *actually* make CREATEROLE something that you can give
    out to folks on a general basis because anyone with CREATEROLE would
    still be able to take over every single non-superuser and non-predefined
    role in the system.  We do *not* want that.
    
    > I can see value in adding a feature whereby we allow the DBA to define a
    > group as a schema-like container and then assign roles to that group with a
    > fine-grained permissions model.  My take is this proposal is a new feature
    > while the two problems noted above can be solved more readily and with less
    > risk with the two suggested changes.
    
    Yes, we're talking about a new feature- one intended to replace the
    broken way that CREATEROLE works, which your proposal doesn't.
    
    Thanks,
    
    Stephen
    
  122. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T18:11:43Z

    On Thu, Mar 10, 2022 at 12:26 PM Joshua Brindle
    <joshua.brindle@crunchydata.com> wrote:
    > Ownership implies DAC, the ability to grant others rights to an
    > object. It's not "kooky" to see roles as owned objects, but it isn't
    > required either. For example most objects on a UNIX system are owned
    > and subject to DAC but users aren't.
    
    I have no issue with anything you write in this paragraph.
    
    > Stephen's, and now my, issue with ownership is that, since it implies
    > DAC, most checks will be bypassed for the owner. We would both prefer
    > for everyone to be subject to the grants, including whoever created
    > the role.
    
    That sounds like MAC, which is usually something that sits on top of
    DAC and is enforced in addition to DAC, not a reason for DAC to not
    exist.
    
    > Rather, we'd like to see a "creators of roles get this set of grants
    > against the role by default" and "as a superuser I can revoke grants
    > from creators against roles they created"
    
    If you create a table, you own it. You get a set of default
    permissions on the table which can be revoked either by you or by
    someone else, and you also have certain intrinsic rights over the
    object as owner which cannot be revoked - including the ability to
    re-grant yourself any previously-revoked permissions. I am not against
    the idea of trying to clean things up so that everything you can do
    with a table is a revocable privilege and you can be the owner without
    having any rights at all, including the right to give yourself other
    rights back, but I cannot believe that the idea of removing table
    ownership as a concept would ever gain consensus on this list.
    Therefore, I also do not think it is reasonable to say that we
    shouldn't introduce a similar concept for object types that don't have
    it yet, such as roles.
    
    But that's not to say that we couldn't decide to do something else
    instead, and that other thing might well be better. Do you want to
    sketch out a full proposal, even just what the syntax would look like,
    and share that here? And if you could explain how I could use it to
    create the mini-superusers that I'm trying to get out of this thing,
    even better.
    
    Thanks,
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  123. Re: role self-revocation

    Peter Eisentraut <peter.eisentraut@enterprisedb.com> — 2022-03-10T19:05:02Z

    On 09.03.22 14:02, Robert Haas wrote:
    > On Wed, Mar 9, 2022 at 7:55 AM Peter Eisentraut
    > <peter.eisentraut@enterprisedb.com> wrote:
    >> Do we have subtractive permissions today?
    > 
    > Not in the GRANT/REVOKE sense, I think, but you can put a user in a
    > group and then mention that group in pg_hba.conf. And that line might
    > be "reject" or whatever.
    
    Well, you can always build an external system that looks at roles and 
    does nonsensical things with it.  But the privilege system itself seems 
    to be additive only.  Personally, I agree with the argument that there 
    should not be any subtractive permissions.  The mental model where 
    permissions are sort of keys to doors or boxes just doesn't work for that.
    
    
    
    
    
  124. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T19:22:05Z

    On Thu, Mar 10, 2022 at 2:05 PM Peter Eisentraut
    <peter.eisentraut@enterprisedb.com> wrote:
    > On 09.03.22 14:02, Robert Haas wrote:
    > > On Wed, Mar 9, 2022 at 7:55 AM Peter Eisentraut
    > > <peter.eisentraut@enterprisedb.com> wrote:
    > >> Do we have subtractive permissions today?
    > >
    > > Not in the GRANT/REVOKE sense, I think, but you can put a user in a
    > > group and then mention that group in pg_hba.conf. And that line might
    > > be "reject" or whatever.
    >
    > Well, you can always build an external system that looks at roles and
    > does nonsensical things with it.  But the privilege system itself seems
    > to be additive only.  Personally, I agree with the argument that there
    > should not be any subtractive permissions.  The mental model where
    > permissions are sort of keys to doors or boxes just doesn't work for that.
    
    I mean, I didn't design pg_hba.conf, but I think it's part of the
    database doing a reasonable thing, not an external system doing a
    nonsensical thing.
    
    I am not sure that I (or anyone) would endorse a system where you can
    say something like GRANT NOT SELECT ON TABLE foo TO bar, essentially
    putting a negative ACL into the system dictating that, regardless of
    any other grants that may exist, foo should not be able to SELECT from
    that table. But I think it's reasonable to use groups as a way of
    referencing a defined collection of users for some purpose. The
    pg_hba.conf thing is an example of that. You put all the users that
    you want to be treated in a certain way for authentication purposes
    into a group, and then you mention the group in the file, and it just
    works. I don't find that an unreasonable design at all. We could've
    created some other kind of grouping mechanism for such purposes that
    is separate from the role system, but we didn't choose to do that. I
    don't know if that was the absolute best possible decision or not, but
    it doesn't seem like an especially bad choice.
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  125. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T19:31:30Z

    On Thu, Mar 10, 2022 at 11:05 AM Stephen Frost <sfrost@snowman.net> wrote:
    
    > Greetings,
    >
    > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > On Thu, Mar 10, 2022 at 9:19 AM Stephen Frost <sfrost@snowman.net>
    > wrote:
    > > > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > > > On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com>
    > > > wrote:
    >
    > > The only indirect way for a role to become superuser is to have been
    > > granted membership in a superuser group, then SET ROLE.  Non-superusers
    > > cannot do this.  If a superuser does this I consider the outcome to be no
    > > different than if they go and do:
    >
    > A non-superuser absolutely can be GRANT'd membership in a superuser role
    > and then SET ROLE to that user thus becoming a superuser.
    
    
    A non-superuser cannot grant a non-superuser membership in a superuser
    role.  A superuser granting a user membership in a superuser role makes
    that user a superuser.  This seems sane.
    
    If a superuser grants a non-superuser membership in a superuser role then
    today a non-superuser can grant a user membership in that intermediate
    role, thus having a non-superuser make another user a superuser.  This is
    arguably a bug that needs to be fixed.
    
    My desired fix is to just require the superuser to mark (or have it marked
    by default ideally) the role inheriting superuser and put the
    responsibility on the superuser.  I agree this is not ideal, but it is
    probably quick and low risk.
    
    I'll let someone else describe the details of the alternative option.  I
    suspect it will end up being a better option in terms of design.  But
    depending on time and risk even knowing that we want the better design
    eventually doesn't preclude getting the easier fix in now.
    
    
    > No, what should matter is if the role doing the GRANT has admin rights
    > on pg_read_all_stats, or on the postgres role.  That also happens to be
    > what the spec says.
    >
    
    Yes, and superusers implicitly have that right, while CREATEROLE users
    implicitly have that right on the pg_* role but not on superuser roles.  I
    just want to plug that hole and include the pg_* roles (or any role for
    that matter) in being able to be denied implied ADMIN rights for
    non-superusers.
    
    
    > Today a non-superuser cannot "grant postgres to someuser;"
    >
    > No, but a role can be created like 'admin', which a superuser GRANT's
    > 'postgres' to and then that role can be GRANT'd to anyone by anyone who
    > has CREATEROLE rights.  That's not sane.
    >
    
    I agree.  And I've suggested a minimal fix, adding an attribute to the role
    that prohibits non-superusers from granting it to others, that removes the
    insane behavior.
    
    I'm on board for a hard-coded fix as well - if a superuser is in the
    membership chain of a role then non-superusers cannot grant membership in
    that role to others.
    
    Neither of those really solves the pg_* roles problem.  We still need to
    indicate that they are somehow special.  Whether it is a nice matrix or
    roles and permissions or a simple attribute that makes them behave like
    they are superuser roles.
    
    
    >
    > I disagree entirely with the idea that we must have some roles who can
    > only ever be administered by a superuser.
    
    
    I don't think this is a must have.  I think that since we do have it today
    that fixes that leverage the status quo in order to be done more easily are
    perfectly valid solutions.
    
    
    
    >   If anything, we should be
    > moving away (as we have, in fact, been doing), from anything being the
    > exclusive purview of the superuser.
    >
    
    I totally agree.
    
    
    >
    > > In particular, this means CREATEROLE roles cannot assign membership in
    > the
    > > marked roles; just like they cannot assign membership in superuser roles
    > > today.
    >
    > I disagree with the idea that we need to mark some roles as only being
    > able to be modified by the superuser- why invent this?
    
    
    Because CREATEUSER is a thing and people want to prevent roles with that
    attribute from assigning membership to the predefined superuser-aspect
    roles.  If I've misunderstood that desire and the scope of delegation given
    by the superuser to CREATEUSER roles is acceptable, then no change here is
    needed.
    
    What you
    > seem to be arguing for here is to rip out the ADMIN functionality, which
    > is defined by spec and not even exclusively by PG, and replace it with a
    > single per-role flag that says if that role can only be modified by
    > superusers.
    
    
    I made the observation that being able to manage the membership of a group
    without having the ability to create new users seems like a half a loaf of
    a feature.  That's it.  I would presume that any redesign of the
    permissions system here would address this adequately.
    
     The
    >
    short answer is that it's not- which is why we have documented
    > CREATEROLE as being 'superuser light'.  The goal here is to get rid of
    > that.
    >
    
    Now you tell me.  Robert should have led with that goal upfront.
    
    >
    > Yes, we're talking about a new feature- one intended to replace the
    > broken way that CREATEROLE works, which your proposal doesn't.
    >
    >
    That is correct, I was trying to figure out minimally invasive fixes to
    what are arguably being called bugs.
    
    David J.
    
  126. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-10T19:45:52Z

    Greetings,
    
    * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 11:05 AM Stephen Frost <sfrost@snowman.net> wrote:
    > > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > > On Thu, Mar 10, 2022 at 9:19 AM Stephen Frost <sfrost@snowman.net> wrote:
    > > > > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > > > > On Thu, Mar 10, 2022 at 7:46 AM Robert Haas <robertmhaas@gmail.com> wrote:
    > >
    > > > The only indirect way for a role to become superuser is to have been
    > > > granted membership in a superuser group, then SET ROLE.  Non-superusers
    > > > cannot do this.  If a superuser does this I consider the outcome to be no
    > > > different than if they go and do:
    > >
    > > A non-superuser absolutely can be GRANT'd membership in a superuser role
    > > and then SET ROLE to that user thus becoming a superuser.
    > 
    > A non-superuser cannot grant a non-superuser membership in a superuser
    > role.  A superuser granting a user membership in a superuser role makes
    > that user a superuser.  This seems sane.
    > 
    > If a superuser grants a non-superuser membership in a superuser role then
    > today a non-superuser can grant a user membership in that intermediate
    > role, thus having a non-superuser make another user a superuser.  This is
    > arguably a bug that needs to be fixed.
    > 
    > My desired fix is to just require the superuser to mark (or have it marked
    > by default ideally) the role inheriting superuser and put the
    > responsibility on the superuser.  I agree this is not ideal, but it is
    > probably quick and low risk.
    > 
    > I'll let someone else describe the details of the alternative option.  I
    > suspect it will end up being a better option in terms of design.  But
    > depending on time and risk even knowing that we want the better design
    > eventually doesn't preclude getting the easier fix in now.
    > 
    > > No, what should matter is if the role doing the GRANT has admin rights
    > > on pg_read_all_stats, or on the postgres role.  That also happens to be
    > > what the spec says.
    > 
    > Yes, and superusers implicitly have that right, while CREATEROLE users
    > implicitly have that right on the pg_* role but not on superuser roles.  I
    > just want to plug that hole and include the pg_* roles (or any role for
    > that matter) in being able to be denied implied ADMIN rights for
    > non-superusers.
    
    CREATEROLE users implicitly have that right on *all non-superuser
    roles*.  Not just the pg_* ones, which is why the pg_* ones aren't any
    different in this regard.
    
    > > Today a non-superuser cannot "grant postgres to someuser;"
    > >
    > > No, but a role can be created like 'admin', which a superuser GRANT's
    > > 'postgres' to and then that role can be GRANT'd to anyone by anyone who
    > > has CREATEROLE rights.  That's not sane.
    > 
    > I agree.  And I've suggested a minimal fix, adding an attribute to the role
    > that prohibits non-superusers from granting it to others, that removes the
    > insane behavior.
    
    I disagree that this is a minimal fix as I don't see it as a fix to the
    actual issue, which is the ability for CREATEROLE users to GRANT role
    membership to all non-superuser roles on the system.  CREATEROLE
    shouldn't be allowing that.
    
    > I'm on board for a hard-coded fix as well - if a superuser is in the
    > membership chain of a role then non-superusers cannot grant membership in
    > that role to others.
    
    Why not just look at the admin_option field of pg_auth_members...?  I
    don't get why that isn't an even more minimal fix than this idea you
    have of adding a column to pg_authid and then propagating around "this
    user could become a superuser" or writing code that has to go check "is
    there some way for this role to become a superuser, either directly or
    through some subset of pg_* roles?"
    
    > Neither of those really solves the pg_* roles problem.  We still need to
    > indicate that they are somehow special.  Whether it is a nice matrix or
    > roles and permissions or a simple attribute that makes them behave like
    > they are superuser roles.
    
    I disagree that they should be considered special when it comes to role
    membership and management.  They're just roles, like any other.
    
    > > I disagree entirely with the idea that we must have some roles who can
    > > only ever be administered by a superuser.
    > 
    > I don't think this is a must have.  I think that since we do have it today
    > that fixes that leverage the status quo in order to be done more easily are
    > perfectly valid solutions.
    
    We have a half-way-implemented attempt at this, not something that's
    actually effective, and therefore I don't agree that we really have it
    today or that we should keep it.  I'd much prefer to throw out nearly
    everything in the system that's doing an explicit check of "does this
    role have a superuser bit set on it?"
    
    > >   If anything, we should be
    > > moving away (as we have, in fact, been doing), from anything being the
    > > exclusive purview of the superuser.
    > >
    > 
    > I totally agree.
    
    Great.
    
    > > > In particular, this means CREATEROLE roles cannot assign membership in
    > > the
    > > > marked roles; just like they cannot assign membership in superuser roles
    > > > today.
    > >
    > > I disagree with the idea that we need to mark some roles as only being
    > > able to be modified by the superuser- why invent this?
    > 
    > Because CREATEUSER is a thing and people want to prevent roles with that
    > attribute from assigning membership to the predefined superuser-aspect
    > roles.  If I've misunderstood that desire and the scope of delegation given
    > by the superuser to CREATEUSER roles is acceptable, then no change here is
    > needed.
    
    We can do that by using the admin_option in pg_auth_members instead
    though and limiting everyone to using that.
    
    > What you
    > > seem to be arguing for here is to rip out the ADMIN functionality, which
    > > is defined by spec and not even exclusively by PG, and replace it with a
    > > single per-role flag that says if that role can only be modified by
    > > superusers.
    > 
    > I made the observation that being able to manage the membership of a group
    > without having the ability to create new users seems like a half a loaf of
    > a feature.  That's it.  I would presume that any redesign of the
    > permissions system here would address this adequately.
    
    If the new design ideas that are being thrown around don't address what
    you're thinking they should, it'd be great to point that out.
    
    >  The
    > >
    > short answer is that it's not- which is why we have documented
    > > CREATEROLE as being 'superuser light'.  The goal here is to get rid of
    > > that.
    > 
    > Now you tell me.  Robert should have led with that goal upfront.
    
    ... blink.
    
    > > Yes, we're talking about a new feature- one intended to replace the
    > > broken way that CREATEROLE works, which your proposal doesn't.
    >
    > That is correct, I was trying to figure out minimally invasive fixes to
    > what are arguably being called bugs.
    
    What's been proposed here doesn't strike me as minimally invasive,
    though I suppose I'm looking at it more from the database system
    perspective and less from the end-user side of things for people who
    actually use CREATEROLE, but in this particular case, that's the side
    I'm on.
    
    Thanks,
    
    Stephen
    
  127. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-10T19:58:49Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > But that's not to say that we couldn't decide to do something else
    > instead, and that other thing might well be better. Do you want to
    > sketch out a full proposal, even just what the syntax would look like,
    > and share that here? And if you could explain how I could use it to
    > create the mini-superusers that I'm trying to get out of this thing,
    > even better.
    
    It'd be useful to have a better definition of exactly what a
    'mini-superuser' is, but at least for the moment when it comes to roles,
    let's look at what the spec says:
    
    CREATE ROLE
      - Who is allowed to run CREATE ROLE is implementation-defined
      - After creation, this is effictively run:
        GRANT new_role TO creator_role WITH ADMIN, GRANTOR "_SYSTEM"
    
    DROP ROLE
      - Any user who has been GRANT'd a role with ADMIN option is able to
        DROP that role.
    
    GRANT ROLE
      - No cycles allowed
      - A role must have ADMIN rights on the role to be able to GRANT it to
        another role.
    
    ALTER ROLE
      - Doesn't exist
    
    This actually looks to me like more-or-less what you're looking for, it
    just isn't what we have today because CREATEROLE brings along with it a
    bunch of other stuff, some of which we want and some that we don't, and
    some things that the SQL spec says ADMIN should be allowed to do (DROP
    ROLE) we don't allow today.
    
    It's also not quite what I want because it requires that membership and
    ADMIN go together where I'd like to be able to have those be
    independently GRANT'able- and then some.
    
    I don't think we're that far from having all of these though.  To start
    with, we remove from CREATEROLE the random things that it does which go
    beyond what folks tend to expect- remove the whole 'grant any role to
    any other' stuff, remove the 'drop role' exception, remove the
    'alter role' stuff.  Do make it so that when you create a role, however,
    the above GRANT is effectively done.  Now, for the items above where we
    removed the checks against have_createrole_privilege() we go back and
    add in checks using is_admin_of_role().  Of course, also remove the role
    self-administration bug.
    
    That's step #1, but it gets us more-or-less what you're looking for, I
    think, and brings us a lot closer to what the spec has.
    
    Step #2 is also in-line with the spec: track GRANTORs and care about
    them, for everything.  We really should have been doing this all along.
    Note that I'm not saying that an owner of a table can't REVOKE some
    right that was GRANT'd on that table, but rather that a user who was
    GRANT'd ADMIN rights on a table and then GRANT'd that right to some
    other user shouldn't have some other user who only has ADMIN rights on
    the table be able to remove that GRANT.  Same goes for roles, meaning
    that you could GRANT rights in a role with ADMIN option and not have to
    be afraid that the role you just gave that to will be able to remove
    *your* ADMIN rights on that role.  In general, I don't think this
    would actually have a very large impact on users because most users
    don't, today, use the ADMIN option much.
    
    Step #3 starts going in the direction of what I'd like to see, which
    would be to break out membership in a role as a separate thing from
    admin rights on that role.  This is also what would help with the 'bot'
    use-case that Joshua (not David Steele, btw) brought up.
    
    Step #4 then breaks the 'admin' option on roles into pieces- a 'drop
    role' right, a 'reset password' right, maybe separate rights for
    different role attributes, etc.  We would likely still keep the
    'admin_option' column in pg_auth_members and just check that first
    and then check the individual rights (similar to table-level vs.
    column-level privileges) so that we stay in line with the spec's
    expectation here and with what users are used to.
    
    In some hyptothetical world, there's even a later step #5 which allows
    us to define user profiles and then grant the ability for a user to
    create a role with a certain profile (but not any arbitrary profile),
    thus making things like the 'bot' even more constrained in terms of
    what it's able to do (maybe it can then create a role that's a member of
    a role without itself being a member of that role or explicitly having
    admin rights in that role, as an example).
    
    Thanks,
    
    Stephen
    
  128. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T20:21:31Z

    On Thu, Mar 10, 2022 at 12:45 PM Stephen Frost <sfrost@snowman.net> wrote:
    
    >
    > * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > > On Thu, Mar 10, 2022 at 11:05 AM Stephen Frost <sfrost@snowman.net>
    > wrote:
    >
    Why not just look at the admin_option field of pg_auth_members...?  I
    > don't get why that isn't an even more minimal fix than this idea you
    > have of adding a column to pg_authid and then propagating around "this
    > user could become a superuser" or writing code that has to go check "is
    > there some way for this role to become a superuser, either directly or
    > through some subset of pg_* roles?"
    >
    
    Indeed, maybe I am wrong on the scope of the patch.  But at least for the
    explicit attribute it should be no more difficult than changing:
    
    if (grouprole_is_superuser and current_role_is_not_superuser) then error:
    to be
    if ((grouoprole_is_superuser OR !groupuser_has_adminattr) AND
    current_role_is_not_superuser) then error;
    
    I have to imagine that given how fundamental inheritance is to our
    permissions system than doing a similar check up the tree wouldn't be
    difficult, but I truly don't know with a strong degree of certainty.
    
    Assuming we don't actually rip out CREATEROLE when this change goes in...do
    you propose to prohibit a CREATEROLE user from altering the membership
    roster of any group which itself is not a member of and also those which it
    is a member of but where admin_option is false?
    
    I don't personally have a problem with the current state where CREATEROLE
    is an admin for, but not a member of, every non-superuser(-related) role in
    the system.  If the consensus is to change that then I suppose this becomes
    the minimally invasive fix that accomplishes that goal as well.  It seems
    incomplete though, since you still need superuser to create a group and add
    the initial WITH ADMIN member to it.  So this seems to work in the "avoid
    using superuser" sense if you've also added something that has what
    CREATEROLE provides today - admin without membership - but that would have
    the benefit of not carrying around all the baggage that CREATEROLE has.
    
    
    > > I made the observation that being able to manage the membership of a
    > group
    > > without having the ability to create new users seems like a half a loaf
    > of
    > > a feature.  That's it.  I would presume that any redesign of the
    > > permissions system here would address this adequately.
    >
    > If the new design ideas that are being thrown around don't address what
    > you're thinking they should, it'd be great to point that out.
    >
    
    I mean, you need a Create Role permission in some form, even if it's
    deprecating the attribute and making it a predefined role.  I picked this
    thread up because it seemed like a limited scope that I could get my head
    around with the time I have, with the main goal to try to understand this
    aspect of the system better.  I haven't gone and looked into the main
    thread yet.
    
    David J.
    
  129. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T20:22:05Z

    On Thu, Mar 10, 2022 at 2:58 PM Stephen Frost <sfrost@snowman.net> wrote:
    > It'd be useful to have a better definition of exactly what a
    > 'mini-superuser' is, but at least for the moment when it comes to roles,
    > let's look at what the spec says:
    
    Gosh, I feel like I've spelled that out approximately 463,121 times
    already. That estimate might be slightly off though; I've been known
    to make mistakes from time to time....
    
    > CREATE ROLE
    >   - Who is allowed to run CREATE ROLE is implementation-defined
    >   - After creation, this is effictively run:
    >     GRANT new_role TO creator_role WITH ADMIN, GRANTOR "_SYSTEM"
    >
    > DROP ROLE
    >   - Any user who has been GRANT'd a role with ADMIN option is able to
    >     DROP that role.
    >
    > GRANT ROLE
    >   - No cycles allowed
    >   - A role must have ADMIN rights on the role to be able to GRANT it to
    >     another role.
    >
    > ALTER ROLE
    >   - Doesn't exist
    >
    > This actually looks to me like more-or-less what you're looking for, it
    > just isn't what we have today because CREATEROLE brings along with it a
    > bunch of other stuff, some of which we want and some that we don't, and
    > some things that the SQL spec says ADMIN should be allowed to do (DROP
    > ROLE) we don't allow today.
    
    The above is mostly fine with me, except for the part about ALTER ROLE
    not existing. I think it's always good to be able to change your mind
    post-CREATE.
    
    Basically, in this sketch, ADMIN OPTION on a role involves the ability
    to DROP it, which means we don't need a separate role owner concept.
    It also involves membership, meaning that you can freely exercise the
    privileges of the role without SET ROLE. While I'm totally down with
    having other possible behaviors as options, that particular behavior
    seems very useful to me, so, sounds great.
    
    > It's also not quite what I want because it requires that membership and
    > ADMIN go together where I'd like to be able to have those be
    > independently GRANT'able- and then some.
    >
    > I don't think we're that far from having all of these though.  To start
    > with, we remove from CREATEROLE the random things that it does which go
    > beyond what folks tend to expect- remove the whole 'grant any role to
    > any other' stuff, remove the 'drop role' exception, remove the
    > 'alter role' stuff.  Do make it so that when you create a role, however,
    > the above GRANT is effectively done.  Now, for the items above where we
    > removed the checks against have_createrole_privilege() we go back and
    > add in checks using is_admin_of_role().  Of course, also remove the role
    > self-administration bug.
    
    What do you mean by the 'drop role' exception?
    
    I don't like removing 'alter role'.
    
    The rest sounds good.
    
    > That's step #1, but it gets us more-or-less what you're looking for, I
    > think, and brings us a lot closer to what the spec has.
    
    Great.
    
    > Step #2 is also in-line with the spec: track GRANTORs and care about
    > them, for everything.  We really should have been doing this all along.
    > Note that I'm not saying that an owner of a table can't REVOKE some
    > right that was GRANT'd on that table, but rather that a user who was
    > GRANT'd ADMIN rights on a table and then GRANT'd that right to some
    > other user shouldn't have some other user who only has ADMIN rights on
    > the table be able to remove that GRANT.  Same goes for roles, meaning
    > that you could GRANT rights in a role with ADMIN option and not have to
    > be afraid that the role you just gave that to will be able to remove
    > *your* ADMIN rights on that role.  In general, I don't think this
    > would actually have a very large impact on users because most users
    > don't, today, use the ADMIN option much.
    
    There are details to work out here, but in general, I like it.
    
    > Step #3 starts going in the direction of what I'd like to see, which
    > would be to break out membership in a role as a separate thing from
    > admin rights on that role.  This is also what would help with the 'bot'
    > use-case that Joshua (not David Steele, btw) brought up.
    
    Woops, apologies for getting the name wrong. I also said Marc earlier
    when I meant Mark, because I work with people named Mark, Marc, and
    Marc, and Mark's spelling got outvoted by some distant corner of my
    brain.
    
    I think this is a fine long-term direction, with the caveat that
    you've not provided enough specifics here for me to really understand
    how it would work. I fear the specifics might be hard to get right,
    both in terms of making it understandable to users and in terms of
    preserving as much backward-compatibility as we can. However, I am not
    opposed to the concept.
    
    > Step #4 then breaks the 'admin' option on roles into pieces- a 'drop
    > role' right, a 'reset password' right, maybe separate rights for
    > different role attributes, etc.  We would likely still keep the
    > 'admin_option' column in pg_auth_members and just check that first
    > and then check the individual rights (similar to table-level vs.
    > column-level privileges) so that we stay in line with the spec's
    > expectation here and with what users are used to.
    
    Same comments as #3, plus I wonder whether it really makes sense to
    separate #3 and #4. But we can decide that when there's a fleshed-out
    design for this.
    
    > In some hyptothetical world, there's even a later step #5 which allows
    > us to define user profiles and then grant the ability for a user to
    > create a role with a certain profile (but not any arbitrary profile),
    > thus making things like the 'bot' even more constrained in terms of
    > what it's able to do (maybe it can then create a role that's a member of
    > a role without itself being a member of that role or explicitly having
    > admin rights in that role, as an example).
    
    Right. I don't object to this either, hypothetically, but I think
    we're a long way from understanding how to get there, and I don't want
    step #1 to get blocked behind all the rest of this. Particularly the
    part where we remove the role self-administration thing.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  130. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-10T20:41:57Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 2:58 PM Stephen Frost <sfrost@snowman.net> wrote:
    > > It'd be useful to have a better definition of exactly what a
    > > 'mini-superuser' is, but at least for the moment when it comes to roles,
    > > let's look at what the spec says:
    > 
    > Gosh, I feel like I've spelled that out approximately 463,121 times
    > already. That estimate might be slightly off though; I've been known
    > to make mistakes from time to time....
    
    If there's a specific message that details it closely on the lists
    somewhere, I'm happy to go review it.  I admit that I didn't go back and
    look for such.
    
    > > CREATE ROLE
    > >   - Who is allowed to run CREATE ROLE is implementation-defined
    > >   - After creation, this is effictively run:
    > >     GRANT new_role TO creator_role WITH ADMIN, GRANTOR "_SYSTEM"
    > >
    > > DROP ROLE
    > >   - Any user who has been GRANT'd a role with ADMIN option is able to
    > >     DROP that role.
    > >
    > > GRANT ROLE
    > >   - No cycles allowed
    > >   - A role must have ADMIN rights on the role to be able to GRANT it to
    > >     another role.
    > >
    > > ALTER ROLE
    > >   - Doesn't exist
    > >
    > > This actually looks to me like more-or-less what you're looking for, it
    > > just isn't what we have today because CREATEROLE brings along with it a
    > > bunch of other stuff, some of which we want and some that we don't, and
    > > some things that the SQL spec says ADMIN should be allowed to do (DROP
    > > ROLE) we don't allow today.
    > 
    > The above is mostly fine with me, except for the part about ALTER ROLE
    > not existing. I think it's always good to be able to change your mind
    > post-CREATE.
    
    Errr, just to be clear, ALTER ROLE doesn't exist *in the spec*.  I
    wasn't suggesting that we get rid of it, just that it doesn't exist in
    the spec and therefore the spec doesn't have anything to say about it.
    
    > Basically, in this sketch, ADMIN OPTION on a role involves the ability
    > to DROP it, which means we don't need a separate role owner concept.
    
    Right.  The above doesn't include any specifics about what to do with
    ALTER ROLE, but my thought would be to have it also be under ADMIN
    OPTION rather than under CREATEROLE, as I tried to outline (though not
    very well, I'll admit) below.
    
    > It also involves membership, meaning that you can freely exercise the
    > privileges of the role without SET ROLE. While I'm totally down with
    > having other possible behaviors as options, that particular behavior
    > seems very useful to me, so, sounds great.
    
    Well, yes and no- by default you're right, presuming everything is set
    as inheirited, but I'd wish for us to keep the option of creating roles
    which are noinherit and having that work just as it does today.
    
    > > It's also not quite what I want because it requires that membership and
    > > ADMIN go together where I'd like to be able to have those be
    > > independently GRANT'able- and then some.
    > >
    > > I don't think we're that far from having all of these though.  To start
    > > with, we remove from CREATEROLE the random things that it does which go
    > > beyond what folks tend to expect- remove the whole 'grant any role to
    > > any other' stuff, remove the 'drop role' exception, remove the
    > > 'alter role' stuff.  Do make it so that when you create a role, however,
    > > the above GRANT is effectively done.  Now, for the items above where we
    > > removed the checks against have_createrole_privilege() we go back and
    > > add in checks using is_admin_of_role().  Of course, also remove the role
    > > self-administration bug.
    > 
    > What do you mean by the 'drop role' exception?
    
    'ability' was probably a better word there.  What I'm talking about is
    changing in DropRole:
    
        if (!have_createrole_privilege())
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("permission denied to drop role")));
    
    to be, more or less:
    
        if (!is_admin_of_role(role))
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                     errmsg("permission denied to drop role")));
    
    > I don't like removing 'alter role'.
    
    Ditto above but for AlterRole.  Taking it away from users with
    CREATEROLE being able to run those commands on $anyrole and instead
    making it so that the role running DROP ROLE or ALTER ROLE needs to have
    ADMIN on the role they're messing with.  I do think we may also need to
    make some adjustments in terms of what a regular user WITH ADMIN on a
    given role is able to do when it comes to ALTER ROLE, in particular, I
    don't think we'll want to remove the existing is-superuser checks
    against a user settings bypassrls or replication or superuser on some
    other role.  Maybe we can provide a way for a non-superuser to be given
    the ability to set those attributes for roles they create, but that
    would be a separate thing.
    
    > The rest sounds good.
    
    Great.
    
    > > That's step #1, but it gets us more-or-less what you're looking for, I
    > > think, and brings us a lot closer to what the spec has.
    > 
    > Great.
    
    Awesome.
    
    > > Step #2 is also in-line with the spec: track GRANTORs and care about
    > > them, for everything.  We really should have been doing this all along.
    > > Note that I'm not saying that an owner of a table can't REVOKE some
    > > right that was GRANT'd on that table, but rather that a user who was
    > > GRANT'd ADMIN rights on a table and then GRANT'd that right to some
    > > other user shouldn't have some other user who only has ADMIN rights on
    > > the table be able to remove that GRANT.  Same goes for roles, meaning
    > > that you could GRANT rights in a role with ADMIN option and not have to
    > > be afraid that the role you just gave that to will be able to remove
    > > *your* ADMIN rights on that role.  In general, I don't think this
    > > would actually have a very large impact on users because most users
    > > don't, today, use the ADMIN option much.
    > 
    > There are details to work out here, but in general, I like it.
    
    Cool.  Note that superusers would still be able to do $anything,
    including removing someone's ADMIN rights on a role even if that
    superuser didn't GRANT it (at least, that's my thinking on this).
    
    > > Step #3 starts going in the direction of what I'd like to see, which
    > > would be to break out membership in a role as a separate thing from
    > > admin rights on that role.  This is also what would help with the 'bot'
    > > use-case that Joshua (not David Steele, btw) brought up.
    > 
    > Woops, apologies for getting the name wrong. I also said Marc earlier
    > when I meant Mark, because I work with people named Mark, Marc, and
    > Marc, and Mark's spelling got outvoted by some distant corner of my
    > brain.
    
    Hah, no worries.
    
    > I think this is a fine long-term direction, with the caveat that
    > you've not provided enough specifics here for me to really understand
    > how it would work. I fear the specifics might be hard to get right,
    > both in terms of making it understandable to users and in terms of
    > preserving as much backward-compatibility as we can. However, I am not
    > opposed to the concept.
    
    We can perhaps debate the specifics around this later.
    
    > > Step #4 then breaks the 'admin' option on roles into pieces- a 'drop
    > > role' right, a 'reset password' right, maybe separate rights for
    > > different role attributes, etc.  We would likely still keep the
    > > 'admin_option' column in pg_auth_members and just check that first
    > > and then check the individual rights (similar to table-level vs.
    > > column-level privileges) so that we stay in line with the spec's
    > > expectation here and with what users are used to.
    > 
    > Same comments as #3, plus I wonder whether it really makes sense to
    > separate #3 and #4. But we can decide that when there's a fleshed-out
    > design for this.
    
    Ditto.  I don't know that they need to be independent either.
    
    > > In some hyptothetical world, there's even a later step #5 which allows
    > > us to define user profiles and then grant the ability for a user to
    > > create a role with a certain profile (but not any arbitrary profile),
    > > thus making things like the 'bot' even more constrained in terms of
    > > what it's able to do (maybe it can then create a role that's a member of
    > > a role without itself being a member of that role or explicitly having
    > > admin rights in that role, as an example).
    > 
    > Right. I don't object to this either, hypothetically, but I think
    > we're a long way from understanding how to get there, and I don't want
    > step #1 to get blocked behind all the rest of this. Particularly the
    > part where we remove the role self-administration thing.
    
    Sure.
    
    Thanks,
    
    Stephen
    
  131. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T21:00:41Z

    On Thu, Mar 10, 2022 at 12:58 PM Stephen Frost <sfrost@snowman.net> wrote:
    
    > I don't think we're that far from having all of these though.  To start
    > with, we remove from CREATEROLE the random things that it does which go
    > beyond what folks tend to expect- remove the whole 'grant any role to
    > any other' stuff, remove the 'drop role' exception, remove the
    > 'alter role' stuff.  Do make it so that when you create a role, however,
    > the above GRANT is effectively done.  Now, for the items above where we
    > removed the checks against have_createrole_privilege() we go back and
    > add in checks using is_admin_of_role().  Of course, also remove the role
    > self-administration bug.
    >
    > That's step #1, but it gets us more-or-less what you're looking for, I
    > think, and brings us a lot closer to what the spec has.
    >
    
    That still leaves attribute specification in place: e.g., REPLICATION,
    CREATEROLE, CREATEDB, etc... (I see BYPASSRLS already is SUPERUSER only)
    
    I dislike changing the documented behavior of CREATEROLE to the degree
    suggested here.  However, there are three choices here, only one of which
    can be chosen:
    
    1. Leave CREATEROLE alone entirely
    2. Make it so CREATEROLE cannot assign membership to the predefined roles
    or superuser (inheritance included), but leave the rest alone.  This would
    be the hard-coded version, not the role attribute one.
    3. Make it so CREATEROLE can only assign membership to roles for which it
    has been made an admin; as well as the other things mentioned
    
    Moving forward I'd prefer options 1 or 2, leaving the ability to
    create/alter/drop a role to be vested via predefined roles.
    
    The rest seems fine at an initial glance.
    
    David J.
    
  132. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T21:51:01Z

    On Thu, Mar 10, 2022 at 3:41 PM Stephen Frost <sfrost@snowman.net> wrote:
    > > Gosh, I feel like I've spelled that out approximately 463,121 times
    > > already. That estimate might be slightly off though; I've been known
    > > to make mistakes from time to time....
    >
    > If there's a specific message that details it closely on the lists
    > somewhere, I'm happy to go review it.  I admit that I didn't go back and
    > look for such.
    
    Probably easier to just say it again: I want to have users that can
    create roles and then have superuser-like powers with respect to those
    roles. They can freely exercise the privileges of those roles, and
    they can do all the things that a superuser can do but only with
    respect to those roles. They cannot break out to the OS. I think it's
    pretty similar to what you are describing, with a couple of possible
    exceptions. For example, would you imagine that being an admin of a
    login role would let you change that user's password? Because that
    would be desirable behavior from where I sit.
    
    > Errr, just to be clear, ALTER ROLE doesn't exist *in the spec*.  I
    > wasn't suggesting that we get rid of it, just that it doesn't exist in
    > the spec and therefore the spec doesn't have anything to say about it.
    
    Oh, OK.
    
    > > Basically, in this sketch, ADMIN OPTION on a role involves the ability
    > > to DROP it, which means we don't need a separate role owner concept.
    >
    > Right.  The above doesn't include any specifics about what to do with
    > ALTER ROLE, but my thought would be to have it also be under ADMIN
    > OPTION rather than under CREATEROLE, as I tried to outline (though not
    > very well, I'll admit) below.
    
    This sentence really confused me at first, but I think you're saying
    that the right to alter a role would be dependent on having ADMIN
    OPTION on the role rather than on having the CREATEROLE attribute.
    That seems like a reasonable idea to me.
    
    > > It also involves membership, meaning that you can freely exercise the
    > > privileges of the role without SET ROLE. While I'm totally down with
    > > having other possible behaviors as options, that particular behavior
    > > seems very useful to me, so, sounds great.
    >
    > Well, yes and no- by default you're right, presuming everything is set
    > as inheirited, but I'd wish for us to keep the option of creating roles
    > which are noinherit and having that work just as it does today.
    
    Hmm, so if I have membership WITH ADMIN OPTION in a role, but my role
    is marked NOINHERIT, that means I can't exercise the privileges of
    that role without SET ROLE. But, can I still do other things to that
    role, such as dropping it? Given the current coding of
    roles_is_member_of(), it seems like I can't. I don't like that, but
    then I don't like much of anything about NOINHERIT. Do you have any
    suggestions for how this could be improved?
    
    To make this more concrete, suppose the superuser does "CREATE USER
    alice CREATEROLE". Alice will have INHERIT, so she'll have control
    over any roles she creates. But if she does "CREATE USER bob
    CREATEROLE NOINHERIT" then neither she nor Bob will be able to control
    the roles bob creates. I'd like to have a way to make it so that
    neither Alice nor any other CREATEROLE users she spins up can create
    roles over which they no longer have control. Because otherwise people
    will do dumb stuff like that and then have to call the superuser to
    sort it out, and the superuser won't like that because s/he is a super
    busy person.
    
    > > What do you mean by the 'drop role' exception?
    >
    > 'ability' was probably a better word there.  What I'm talking about is
    > changing in DropRole:
    >
    > to be, more or less:
    >
    >     if (!is_admin_of_role(role))
    >         ereport(ERROR,
    >                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    >                  errmsg("permission denied to drop role")));
    
    Sounds good.
    
    > > I don't like removing 'alter role'.
    >
    > Ditto above but for AlterRole.  Taking it away from users with
    > CREATEROLE being able to run those commands on $anyrole and instead
    > making it so that the role running DROP ROLE or ALTER ROLE needs to have
    > ADMIN on the role they're messing with.  I do think we may also need to
    > make some adjustments in terms of what a regular user WITH ADMIN on a
    > given role is able to do when it comes to ALTER ROLE, in particular, I
    > don't think we'll want to remove the existing is-superuser checks
    > against a user settings bypassrls or replication or superuser on some
    > other role.  Maybe we can provide a way for a non-superuser to be given
    > the ability to set those attributes for roles they create, but that
    > would be a separate thing.
    
    This too.
    
    > > > Step #2 is also in-line with the spec: track GRANTORs and care about
    > > > them, for everything.  We really should have been doing this all along.
    > >
    > > There are details to work out here, but in general, I like it.
    >
    > Cool.  Note that superusers would still be able to do $anything,
    > including removing someone's ADMIN rights on a role even if that
    > superuser didn't GRANT it (at least, that's my thinking on this).
    
    Agree. I also think that it would be a good idea to attribute grants
    performed by any superuser to the bootstrap superuser, or leave them
    unattributed somehow. Because otherwise dropping superusers becomes a
    pain in the tail for no good reason.
    
    We might also need to think carefully about what happens if for
    example the table owner is changed. If bob owns the table and we
    change the owner to mary, but bob's previous grants are still
    attributed to bob, I'm not sure that's going to be very convenient.
    Possibly if the table owner changes we also change the owner of all
    grants attributed to the old table owner to be attributed to the new
    table owner?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  133. Re: role self-revocation

    Bruce Momjian <bruce@momjian.us> — 2022-03-10T22:00:11Z

    On Thu, Mar 10, 2022 at 02:22:05PM -0500, Robert Haas wrote:
    > I mean, I didn't design pg_hba.conf, but I think it's part of the
    > database doing a reasonable thing, not an external system doing a
    > nonsensical thing.
    
    FYI, I think pg_hba.conf gets away with having negative/reject
    permissions only because it is strictly ordered.
    
    -- 
      Bruce Momjian  <bruce@momjian.us>        https://momjian.us
      EDB                                      https://enterprisedb.com
    
      If only the physical world exists, free will is an illusion.
    
    
    
    
    
  134. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T22:01:43Z

    On Thu, Mar 10, 2022 at 4:00 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > I dislike changing the documented behavior of CREATEROLE to the degree suggested here.  However, there are three choices here, only one of which can be chosen:
    >
    > 1. Leave CREATEROLE alone entirely
    > 2. Make it so CREATEROLE cannot assign membership to the predefined roles or superuser (inheritance included), but leave the rest alone.  This would be the hard-coded version, not the role attribute one.
    > 3. Make it so CREATEROLE can only assign membership to roles for which it has been made an admin; as well as the other things mentioned
    >
    > Moving forward I'd prefer options 1 or 2, leaving the ability to create/alter/drop a role to be vested via predefined roles.
    
    It sounds like you prefer a behavior where CREATEROLE gives power over
    all non-superusers, but that seems pretty limiting to me. Why can't
    someone want to create a user with power over some users but not
    others? For example, the superuser might want to give alice the
    ability to set up new users in the accounting department, but NOT give
    alice the right to tinker with the backup user (who is not a
    superuser, but doesn't have the replication privilege). How would they
    accomplish that in your view?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  135. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-10T22:02:05Z

    On Thu, Mar 10, 2022 at 5:00 PM Bruce Momjian <bruce@momjian.us> wrote:
    > On Thu, Mar 10, 2022 at 02:22:05PM -0500, Robert Haas wrote:
    > > I mean, I didn't design pg_hba.conf, but I think it's part of the
    > > database doing a reasonable thing, not an external system doing a
    > > nonsensical thing.
    >
    > FYI, I think pg_hba.conf gets away with having negative/reject
    > permissions only because it is strictly ordered.
    
    I agree.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  136. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-10T22:12:27Z

    On Thu, Mar 10, 2022 at 3:01 PM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Thu, Mar 10, 2022 at 4:00 PM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > > I dislike changing the documented behavior of CREATEROLE to the degree
    > suggested here.  However, there are three choices here, only one of which
    > can be chosen:
    > >
    > > 1. Leave CREATEROLE alone entirely
    > > 2. Make it so CREATEROLE cannot assign membership to the predefined
    > roles or superuser (inheritance included), but leave the rest alone.  This
    > would be the hard-coded version, not the role attribute one.
    > > 3. Make it so CREATEROLE can only assign membership to roles for which
    > it has been made an admin; as well as the other things mentioned
    > >
    > > Moving forward I'd prefer options 1 or 2, leaving the ability to
    > create/alter/drop a role to be vested via predefined roles.
    >
    > It sounds like you prefer a behavior where CREATEROLE gives power over
    > all non-superusers, but that seems pretty limiting to me.
    >
    
    Doh!  I edited out the part where I made clear I considered options 1 and 2
    as basically being done for a limited period of time while deprecating the
    CREATEROLE attribute altogether in favor of the fine-grained and predefined
    role based permission granting.  I don't want to nerf CREATEROLE as part of
    adding this new feature, instead leave it as close to status quo as
    reasonable so as not to mess up existing setups that make use of it.  We
    can note in the release notes and documentation that we consider CREATEROLE
    to be deprecated and that the new predefined role should be used to give a
    user the ability to create/alter/drop roles, etc...  DBAs should consider
    revoking CREATEROLE from their users and granting them proper memberships
    in the predefined roles and the groups those roles should be administering.
    
    David J.
    
  137. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-10T22:14:36Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > Probably easier to just say it again: I want to have users that can
    > create roles and then have superuser-like powers with respect to those
    > roles. They can freely exercise the privileges of those roles, and
    > they can do all the things that a superuser can do but only with
    > respect to those roles.
    
    This seems reasonable in isolation, but
    
    (1) it implies a persistent relationship between creating and created
    roles.  Whether you want to call that ownership or not, it sure walks
    and quacks like ownership.
    
    (2) it seems exactly contradictory to your later point that
    
    > Agree. I also think that it would be a good idea to attribute grants
    > performed by any superuser to the bootstrap superuser, or leave them
    > unattributed somehow. Because otherwise dropping superusers becomes a
    > pain in the tail for no good reason.
    
    Either there's a persistent relationship or there's not.  I don't
    think it's sensible to treat superusers differently here.
    
    I think that this argument about the difficulty of dropping superusers
    may in fact be the motivation for the existing behavior that object-
    permissions GRANTs done by superusers are attributed to the object
    owner; something you were unhappy about upthread.
    
    In the end these requirements seem mutually contradictory.  Either
    we can have a persistent ownership relationship or not, but I don't
    think we can have it apply in some cases and not others without
    creating worse problems than we solve.  I'm inclined to toss overboard
    the requirement that superusers need to be an easy thing to drop.
    Why is that important, anyway?
    
    > We might also need to think carefully about what happens if for
    > example the table owner is changed. If bob owns the table and we
    > change the owner to mary, but bob's previous grants are still
    > attributed to bob, I'm not sure that's going to be very convenient.
    
    That's already handled, is it not?
    
    regression=# create user alice;
    CREATE ROLE
    regression=# create user bob;
    CREATE ROLE
    regression=# create user charlie;
    CREATE ROLE
    regression=# \c - alice
    You are now connected to database "regression" as user "alice".
    regression=> create table alices_table (f1 int);
    CREATE TABLE
    regression=> grant select on alices_table to bob;
    GRANT
    regression=> \c - postgres
    You are now connected to database "regression" as user "postgres".
    regression=# alter table alices_table owner to charlie;
    ALTER TABLE
    regression=# \dp alices_table
                                       Access privileges
     Schema |     Name     | Type  |    Access privileges    | Column privileges | Policies 
    --------+--------------+-------+-------------------------+-------------------+----------
     public | alices_table | table | charlie=arwdDxt/charlie+|                   | 
            |              |       | bob=r/charlie           |                   | 
    (1 row)
    
    I'm a bit disturbed that parts of this discussion seem to be getting
    conducted with little understanding of the system's existing behaviors.
    We should not be reinventing things we already have perfectly good
    solutions for in the object-privileges domain.
    
    			regards, tom lane
    
    
    
    
  138. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-10T22:17:08Z

    
    > On Mar 10, 2022, at 2:01 PM, Robert Haas <robertmhaas@gmail.com> wrote:
    > 
    > It sounds like you prefer a behavior where CREATEROLE gives power over
    > all non-superusers, but that seems pretty limiting to me. Why can't
    > someone want to create a user with power over some users but not
    > others?
    
    I agree with Robert on this.
    
    Over at [1], I introduced a patch series to (a) change CREATEROLE and (b) introduce role ownership.  Part (a) wasn't that controversial.  The patch series failed to make it for postgres 15 on account of (b).  The patch didn't go quite far enough, but with it applied, this is an example of a min-superuser "lord" operating within database "fiefdom":
    
    fiefdom=# -- mini-superuser who can create roles and write all data
    fiefdom=# CREATE ROLE lord
    fiefdom-#     WITH CREATEROLE
    fiefdom-#     IN ROLE pg_write_all_data;
    CREATE ROLE
    fiefdom=# 
    fiefdom=# -- group which "lord" belongs to
    fiefdom=# CREATE GROUP squire
    fiefdom-#     ROLE lord;
    CREATE ROLE
    fiefdom=# 
    fiefdom=# -- group which "lord" has no connection to
    fiefdom=# CREATE GROUP paladin;
    CREATE ROLE
    fiefdom=# 
    fiefdom=# SET SESSION AUTHORIZATION lord;
    SET
    fiefdom=> 
    fiefdom=> -- fail, merely a member of "squire"
    fiefdom=> CREATE ROLE peon IN ROLE squire;
    ERROR:  must have admin option on role "squire"
    fiefdom=> 
    fiefdom=> -- fail, no privilege to grant CREATEDB 
    fiefdom=> CREATE ROLE peon CREATEDB;
    ERROR:  must have createdb privilege to create createdb users
    fiefdom=> 
    fiefdom=> RESET SESSION AUTHORIZATION;
    RESET
    fiefdom=# 
    fiefdom=# -- grant admin over "squire" to "lord"
    fiefdom=# GRANT squire
    fiefdom-#     TO lord
    fiefdom-#     WITH ADMIN OPTION;
    GRANT ROLE
    fiefdom=# 
    fiefdom=# SET SESSION AUTHORIZATION lord;
    SET
    fiefdom=> 
    fiefdom=> -- ok, have both "CREATEROLE" and admin option for "squire"
    fiefdom=> CREATE ROLE peon IN ROLE squire;
    CREATE ROLE
    fiefdom=> 
    fiefdom=> -- fail, no privilege to grant CREATEDB
    fiefdom=> CREATE ROLE peasant CREATEDB IN ROLE squire;
    ERROR:  must have createdb privilege to create createdb users
    fiefdom=> 
    fiefdom=> RESET SESSION AUTHORIZATION;
    RESET
    fiefdom=# 
    fiefdom=# -- Give lord the missing privilege
    fiefdom=# GRANT CREATEDB TO lord;
    ERROR:  role "createdb" does not exist
    fiefdom=# 
    fiefdom=# RESET SESSION AUTHORIZATION;
    RESET
    fiefdom=# 
    fiefdom=# -- ok, have "CREATEROLE", "CREATEDB", and admin option for "squire"
    fiefdom=# CREATE ROLE peasant CREATEDB IN ROLE squire;
    CREATE ROLE
    
    The problem with this is that "lord" needs CREATEDB to grant CREATEDB, but really it should need something like grant option on "CREATEDB".  But that's hard to do with the existing system, given the way these privilege bits are represented.  If we added a few more built-in pg_* roles, such as pg_create_db, it would just work.  CREATEROLE itself could be reimagined as pg_create_role, and then users could be granted into this role with or without admin option, meaning they could/couldn't further give it away.  I think that would be a necessary component to Joshua's "bot" use-case, since the bot must itself have the privilege to create roles, but shouldn't necessarily be trusted with the privilege to create additional roles who have it.
    
    [1] https://www.postgresql.org/message-id/53C7DF4C-8463-4647-9DFD-779B5E1861C4@amazon.com
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  139. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T13:55:16Z

    On Thu, Mar 10, 2022 at 5:14 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > This seems reasonable in isolation, but
    >
    > (1) it implies a persistent relationship between creating and created
    > roles.  Whether you want to call that ownership or not, it sure walks
    > and quacks like ownership.
    
    I agree. It's been obvious to me from the beginning that we needed
    such a persistent relationship, and also that it needed to be a
    relationship from which the created role couldn't simply walk away.
    Yet, more than six months after the first discussions of this topic,
    we still don't have any kind of agreement on what that thing should be
    called. I like my TENANT idea best, but I'm perfectly willing to call
    it ownership as you seem to prefer or WITH ADMIN OPTION as Stephen
    seems to prefer if one of those ideas gains consensus. But we've
    managed to waste all hope of making any significant progress here for
    an entire release cycle for lack of ability to agree on spelling. I
    think that's unfair to Mark, who put a lot of work into this area and
    got nothing out of it, and I think it sucks for users of PostgreSQL,
    too.
    
    > (2) it seems exactly contradictory to your later point that
    >
    > > Agree. I also think that it would be a good idea to attribute grants
    > > performed by any superuser to the bootstrap superuser, or leave them
    > > unattributed somehow. Because otherwise dropping superusers becomes a
    > > pain in the tail for no good reason.
    >
    > Either there's a persistent relationship or there's not.  I don't
    > think it's sensible to treat superusers differently here.
    >
    > I think that this argument about the difficulty of dropping superusers
    > may in fact be the motivation for the existing behavior that object-
    > permissions GRANTs done by superusers are attributed to the object
    > owner; something you were unhappy about upthread.
    >
    > In the end these requirements seem mutually contradictory.  Either
    > we can have a persistent ownership relationship or not, but I don't
    > think we can have it apply in some cases and not others without
    > creating worse problems than we solve.  I'm inclined to toss overboard
    > the requirement that superusers need to be an easy thing to drop.
    > Why is that important, anyway?
    
    Well, I think you're looking at it the wrong way. Compared to getting
    useful functionality, the relative ease of dropping users is
    completely unimportant. I'm happy to surrender it in exchange for
    something else. I just don't see why we should give it up for nothing.
    If Alice creates non-superusers Bob and Charlie, and Charlie creates
    Doug, we need the persistent relationship to know that Charlie is
    allowed to drop Doug and Bob is not. But if Charlie is a superuser
    anyway, then the persistent relationship is of no use. I don't see the
    point of cluttering up the system with such dependencies. Will I do it
    that way, if that's what it takes to get the patch accepted? Sure. But
    I can't imagine any end-user actually liking it.
    
    > I'm a bit disturbed that parts of this discussion seem to be getting
    > conducted with little understanding of the system's existing behaviors.
    > We should not be reinventing things we already have perfectly good
    > solutions for in the object-privileges domain.
    
    I did wonder whether that might be the existing behavior, but stopping
    to check right at that moment didn't seem that important to me. Maybe
    I should have taken the time, but it's not like we're writing the
    final patch for commit next Tuesday at this point. It's more important
    at this point to get agreement on the principles. That said, I do
    agree that there have been times when we haven't thought hard enough
    about the existing behavior in proposing new behavior. On the third
    hand, though, part of the problem here is that neither Stephen nor I
    are entirely happy with the existing behavior, if for somewhat
    different reasons. It really isn't "perfectly good." On the one hand,
    from a purely technical standpoint, a lot of the behavior around roles
    in particular seems well below the standard that anyone would consider
    committable today. On the other hand, even the parts of the code that
    are in reasonable shape from a code quality point of view don't
    actually do the things that we think users want done.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  140. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-11T15:27:52Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 5:14 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > This seems reasonable in isolation, but
    > >
    > > (1) it implies a persistent relationship between creating and created
    > > roles.  Whether you want to call that ownership or not, it sure walks
    > > and quacks like ownership.
    
    I agree that there would be a recorded relationship (that is, one that
    we write into the catalog and keep around until and unless it's removed
    by an admin) between creating and created roles and that's probably the
    default when CREATE ROLE is run but, unlike tables or such objects in
    the system, I don't agree that we should require this to exist at
    absolutely all times for every role (what would it be for the bootstrap
    superuser..?).  At least today, that's distinct from how ownership in
    the system works.  I also don't believe that this is necessarily an
    issue for Robert's use-case, as long as there are appropriate
    restrictions around who is allowed to remove or modify these
    relationships.
    
    > I agree. It's been obvious to me from the beginning that we needed
    > such a persistent relationship, and also that it needed to be a
    > relationship from which the created role couldn't simply walk away.
    > Yet, more than six months after the first discussions of this topic,
    > we still don't have any kind of agreement on what that thing should be
    > called. I like my TENANT idea best, but I'm perfectly willing to call
    > it ownership as you seem to prefer or WITH ADMIN OPTION as Stephen
    > seems to prefer if one of those ideas gains consensus. But we've
    > managed to waste all hope of making any significant progress here for
    > an entire release cycle for lack of ability to agree on spelling. I
    > think that's unfair to Mark, who put a lot of work into this area and
    > got nothing out of it, and I think it sucks for users of PostgreSQL,
    > too.
    
    Well ... one of those actually already exists and also happens to be in
    the SQL spec.  I don't necessarily agree that we should absolutely
    require that the system always enforce that this relationship exist (I'd
    like a superuser to be able to get rid of it and to be able to change it
    too if they want) and that seems a bit saner than having the bootstrap
    superuser be special in some way here as would seem to otherwise be
    required.  I also feel that it would be generally useful to have more
    than one of these relationships, if the user wishes, and that's
    something that ownership doesn't (directly) support today.  Further,
    that's supported and expected by the SQL spec too.  Even if we invented
    some concept of ownership of roles, it seems like we should make most of
    the other changes discussed here to bring us closer to what the spec
    says about CREATE ROLE, DROP ROLE, GRANT, REVOKE, etc.  At that point
    though, what's the point of having ownership?
    
    > > (2) it seems exactly contradictory to your later point that
    > >
    > > > Agree. I also think that it would be a good idea to attribute grants
    > > > performed by any superuser to the bootstrap superuser, or leave them
    > > > unattributed somehow. Because otherwise dropping superusers becomes a
    > > > pain in the tail for no good reason.
    > >
    > > Either there's a persistent relationship or there's not.  I don't
    > > think it's sensible to treat superusers differently here.
    > >
    > > I think that this argument about the difficulty of dropping superusers
    > > may in fact be the motivation for the existing behavior that object-
    > > permissions GRANTs done by superusers are attributed to the object
    > > owner; something you were unhappy about upthread.
    > >
    > > In the end these requirements seem mutually contradictory.  Either
    > > we can have a persistent ownership relationship or not, but I don't
    > > think we can have it apply in some cases and not others without
    > > creating worse problems than we solve.  I'm inclined to toss overboard
    > > the requirement that superusers need to be an easy thing to drop.
    > > Why is that important, anyway?
    > 
    > Well, I think you're looking at it the wrong way. Compared to getting
    > useful functionality, the relative ease of dropping users is
    > completely unimportant. I'm happy to surrender it in exchange for
    > something else. I just don't see why we should give it up for nothing.
    > If Alice creates non-superusers Bob and Charlie, and Charlie creates
    > Doug, we need the persistent relationship to know that Charlie is
    > allowed to drop Doug and Bob is not. But if Charlie is a superuser
    > anyway, then the persistent relationship is of no use. I don't see the
    > point of cluttering up the system with such dependencies. Will I do it
    > that way, if that's what it takes to get the patch accepted? Sure. But
    > I can't imagine any end-user actually liking it.
    
    We need to know that Charlie is allowed to drop Doug and Bob isn't but
    that doesn't make it absolutely required that this be tracked
    permanently or that Alice can't decide later to make it such that Doug
    can't be dropped by Charlie for whatever reason she has.  Also, I don't
    think it would be such an issue to have a CASCADE for DROP ROLE which
    would handle this case if we want it (and pg_auth_members is shared, so
    there isn't an issue with multi-database concerns).  We could also call
    it something else if people feel CASCADE would be confusing since it
    wouldn't cascade to owned objects.  Or we could consider extending GRANT
    to make this situation something that could be handled more easily.
    
    Thanks,
    
    Stephen
    
  141. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-11T15:27:56Z

    On Fri, Mar 11, 2022 at 6:55 AM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Thu, Mar 10, 2022 at 5:14 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > This seems reasonable in isolation, but
    > >
    > > (1) it implies a persistent relationship between creating and created
    > > roles.  Whether you want to call that ownership or not, it sure walks
    > > and quacks like ownership.
    >
    
    
    > I like my TENANT idea best, but I'm perfectly willing to call
    > it ownership as you seem to prefer or WITH ADMIN OPTION as Stephen
    > seems to prefer if one of those ideas gains consensus.
    
    
    If WITH ADMIN OPTION is sufficient to meet our immediate goals I do not see
    the benefit of adding an ownership concept where there is not one today.
    If added, I'd much rather have it be ownership as to fit in with the rest
    of the existing system rather than introduce an entirely new term.
    
    
    > If Alice creates non-superusers Bob and Charlie, and Charlie creates
    > Doug, we need the persistent relationship to know that Charlie is
    > allowed to drop Doug and Bob is not
    >
    
    The interesting question seems to be whether Alice can drop Doug, not
    whether Bob can.
    
    > It's more important
    > at this point to get agreement on the principles.
    >
    
    What are the principles you want to get agreement on and how do they differ
    from what we have in place today?  What are the proposed changes you would
    make to enforce the new principles.  Which principles are now obsolete and
    what do you want to do about the features that were built to enforce them
    (including backward compatibility concerns)?
    
    David J.
    
  142. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-11T15:32:07Z

    Greetings,
    
    * David G. Johnston (david.g.johnston@gmail.com) wrote:
    > On Thu, Mar 10, 2022 at 3:01 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > 
    > > On Thu, Mar 10, 2022 at 4:00 PM David G. Johnston
    > > <david.g.johnston@gmail.com> wrote:
    > > > I dislike changing the documented behavior of CREATEROLE to the degree
    > > suggested here.  However, there are three choices here, only one of which
    > > can be chosen:
    > > >
    > > > 1. Leave CREATEROLE alone entirely
    > > > 2. Make it so CREATEROLE cannot assign membership to the predefined
    > > roles or superuser (inheritance included), but leave the rest alone.  This
    > > would be the hard-coded version, not the role attribute one.
    > > > 3. Make it so CREATEROLE can only assign membership to roles for which
    > > it has been made an admin; as well as the other things mentioned
    > > >
    > > > Moving forward I'd prefer options 1 or 2, leaving the ability to
    > > create/alter/drop a role to be vested via predefined roles.
    > >
    > > It sounds like you prefer a behavior where CREATEROLE gives power over
    > > all non-superusers, but that seems pretty limiting to me.
    > 
    > Doh!  I edited out the part where I made clear I considered options 1 and 2
    > as basically being done for a limited period of time while deprecating the
    > CREATEROLE attribute altogether in favor of the fine-grained and predefined
    > role based permission granting.  I don't want to nerf CREATEROLE as part of
    > adding this new feature, instead leave it as close to status quo as
    > reasonable so as not to mess up existing setups that make use of it.  We
    > can note in the release notes and documentation that we consider CREATEROLE
    > to be deprecated and that the new predefined role should be used to give a
    > user the ability to create/alter/drop roles, etc...  DBAs should consider
    > revoking CREATEROLE from their users and granting them proper memberships
    > in the predefined roles and the groups those roles should be administering.
    
    I disagree entirely with the idea that we should push the out however
    many years it'd take to get through some deprecation period.  We are
    absolutely terrible when it comes to that and what we're talking about
    here, at this point anyway, is making changes that get us closer to what
    the spec says.  I agree that we can't back-patch these changes, but I
    don't think we need a deprecation period.  If we were just getting rid
    of CREATEROLE, I don't think we'd have a deprecation period.  If we need
    to get rid of CREATEROLE and introduce something new that more-or-less
    means the same thing, to make it so that people's scripts break in a
    more obvious way, maybe we can consider that, but I don't really think
    that's actually the case here.  Such scripts as will break will still
    break in a pretty clear way with a clear answer as to how to fix them
    and I don't think there's some kind of data corruption or something that
    would happen.
    
    Thanks,
    
    Stephen
    
  143. Re: role self-revocation

    David G. Johnston <david.g.johnston@gmail.com> — 2022-03-11T15:36:44Z

    On Fri, Mar 11, 2022 at 8:32 AM Stephen Frost <sfrost@snowman.net> wrote:
    
    >
    > Such scripts as will break will still
    > break in a pretty clear way with a clear answer as to how to fix them
    > and I don't think there's some kind of data corruption or something that
    > would happen.
    >
    >
    I largely agree and am perfectly fine with going with the majority on this
    point.  My vote would just fall on the conservative side.  But as so far no
    one else seems to be overly concerned, nerfing CREATEROLE seems to be the
    path forward.
    
    David J.
    
  144. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T15:41:17Z

    On Fri, Mar 11, 2022 at 10:27 AM Stephen Frost <sfrost@snowman.net> wrote:
    > I agree that there would be a recorded relationship (that is, one that
    > we write into the catalog and keep around until and unless it's removed
    > by an admin) between creating and created roles and that's probably the
    > default when CREATE ROLE is run but, unlike tables or such objects in
    > the system, I don't agree that we should require this to exist at
    > absolutely all times for every role (what would it be for the bootstrap
    > superuser..?).  At least today, that's distinct from how ownership in
    > the system works.  I also don't believe that this is necessarily an
    > issue for Robert's use-case, as long as there are appropriate
    > restrictions around who is allowed to remove or modify these
    > relationships.
    
    I agree.
    
    > > I agree. [ but we need to get consensus ]
    >
    > Well ... [ how about we do it my way? ]
    
    Repeating the same argument over again isn't necessarily going to help
    anything here. I read your argument and I can believe there could be a
    solution along those lines, although you haven't addressed my concern
    about NOINHERIT. Tom is apparently less convinced, and you know, I
    think that's OK. Not everybody has to agree with the way you want to
    do it.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  145. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-11T15:46:55Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Fri, Mar 11, 2022 at 10:27 AM Stephen Frost <sfrost@snowman.net> wrote:
    >> I agree that there would be a recorded relationship (that is, one that
    >> we write into the catalog and keep around until and unless it's removed
    >> by an admin) between creating and created roles and that's probably the
    >> default when CREATE ROLE is run but, unlike tables or such objects in
    >> the system, I don't agree that we should require this to exist at
    >> absolutely all times for every role (what would it be for the bootstrap
    >> superuser..?).  At least today, that's distinct from how ownership in
    >> the system works.  I also don't believe that this is necessarily an
    >> issue for Robert's use-case, as long as there are appropriate
    >> restrictions around who is allowed to remove or modify these
    >> relationships.
    
    > I agree.
    
    The bootstrap superuser clearly must be a special case in some way.
    I'm not convinced that that means there should be other special
    cases.  Maybe there is a use-case for other "unowned" roles, but in
    exactly what way would that be different from deeming such roles
    to be owned by the bootstrap superuser?
    
    			regards, tom lane
    
    
    
    
  146. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T15:58:18Z

    On Fri, Mar 11, 2022 at 10:37 AM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > I largely agree and am perfectly fine with going with the majority on this point.  My vote would just fall on the conservative side.  But as so far no one else seems to be overly concerned, nerfing CREATEROLE seems to be the path forward.
    
    This kind of thing is always a judgement call. If we were talking
    about breaking 'SELECT * from table', I'm sure it would be hard to
    convince anybody to agree to do that at all, let alone with no
    deprecation period. Fortunately, CREATEROLE is less used, so breaking
    it will inconvenience fewer people. Moreover, unlike 'SELECT * FROM
    table', CREATEROLE is kinda broken, and it's less scary to make
    changes to behavior that sucks in the first place than it is to make
    changes to the behavior of things that are working well. For all of
    that, there's no hard-and-fast rule that we couldn't keep the existing
    behavior around, introduce a substitute, and eventually drop the old
    thing. I'm just not clear that it's really worth it in this case. It'd
    certainly be interesting to hear from anyone who is finding some
    utility in the current system. It looks pretty crap to me, but it's
    easy to bring too much of one's own bias to such judgements.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  147. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-11T16:12:03Z

    
    > On Mar 11, 2022, at 7:58 AM, Robert Haas <robertmhaas@gmail.com> wrote:
    > 
    > This kind of thing is always a judgement call. If we were talking
    > about breaking 'SELECT * from table', I'm sure it would be hard to
    > convince anybody to agree to do that at all, let alone with no
    > deprecation period. Fortunately, CREATEROLE is less used, so breaking
    > it will inconvenience fewer people.
    
    This issue of how much backwards compatibility breakage we're willing to tolerate is just as important as questions about how we would want roles to work in a green-field development project.  The sense I got a year ago, on this list, was that changing CREATEROLE was acceptable, but changing other parts of the system, such as how ADMIN OPTION works, would go too far.
    
    Role ownership did not yet exist, and that was a big motivation in introducing that concept, because you couldn't credibly say it broke other existing features.  It introduces the new notion that when a superuser creates a role, the superuser owns it, which is identical to how things implicitly work today; and when a CREATEROLE non-superuser creates a role, that role owns the new role, which is different from how it works today, arguably breaking CREATEROLE's prior behavior.  *But it doesn't break anything else*.
    
    If we're going to change how ADMIN OPTION works, or how role membership works, or how inherit/noinherit works, let's first be clear that we are willing to accept whatever backwards incompatibility that entails.  This is not a green-field development project.  The constant spinning around with regard to how much compatibility we need to preserve is giving me vertigo.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  148. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T16:13:00Z

    On Fri, Mar 11, 2022 at 10:46 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > The bootstrap superuser clearly must be a special case in some way.
    > I'm not convinced that that means there should be other special
    > cases.  Maybe there is a use-case for other "unowned" roles, but in
    > exactly what way would that be different from deeming such roles
    > to be owned by the bootstrap superuser?
    
    I think that just boils down to how many useless catalog entries you
    want to make.
    
    If we implement the link between the creating role and the created
    role as role ownership, then we are surely just going to add a
    rolowner column to pg_authid, and when the role is owned by nobody, I
    think we should always just store a valid OID in it, rather than
    sometimes storing 0. It just seems simpler. Any time we would store 0,
    store the bootstrap superuser's pg_authid.oid value instead. That way
    the OID is always valid, which probably lets us get by with fewer
    special cases in the code.
    
    If we implement the link between the creating role and the created
    role as an automatically-granted WITH ADMIN OPTION, then we could
    choose to put (CREATOR_OID, CREATED_OID, whatever, TRUE) into
    pg_auth_members for the creating superuser or, indeed, every superuser
    in the system. Or we can leave it out. The result will be exactly the
    same. Here, I would favor leaving it out, because extra catalog
    entries that don't do anything are usually a thing that we do not
    want. See a49d081235997c67e8aab7a523b17e8d1cb93184, for example.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  149. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-11T16:34:36Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > If we implement the link between the creating role and the created
    > role as role ownership, then we are surely just going to add a
    > rolowner column to pg_authid, and when the role is owned by nobody, I
    > think we should always just store a valid OID in it, rather than
    > sometimes storing 0. It just seems simpler. Any time we would store 0,
    > store the bootstrap superuser's pg_authid.oid value instead. That way
    > the OID is always valid, which probably lets us get by with fewer
    > special cases in the code.
    
    +1.
    
    Note that either case would also involve making entries in pg_shdepend;
    although for the case of roles owned by/granted to the bootstrap
    superuser, we could omit those on the usual grounds that we don't need
    to record dependencies on pinned objects.
    
    			regards, tom lane
    
    
    
    
  150. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T16:36:21Z

    On Fri, Mar 11, 2022 at 11:12 AM Mark Dilger
    <mark.dilger@enterprisedb.com> wrote:
    > This issue of how much backwards compatibility breakage we're willing to tolerate is just as important as questions about how we would want roles to work in a green-field development project.  The sense I got a year ago, on this list, was that changing CREATEROLE was acceptable, but changing other parts of the system, such as how ADMIN OPTION works, would go too far.
    >
    > Role ownership did not yet exist, and that was a big motivation in introducing that concept, because you couldn't credibly say it broke other existing features.  It introduces the new notion that when a superuser creates a role, the superuser owns it, which is identical to how things implicitly work today; and when a CREATEROLE non-superuser creates a role, that role owns the new role, which is different from how it works today, arguably breaking CREATEROLE's prior behavior.  *But it doesn't break anything else*.
    >
    > If we're going to change how ADMIN OPTION works, or how role membership works, or how inherit/noinherit works, let's first be clear that we are willing to accept whatever backwards incompatibility that entails.  This is not a green-field development project.  The constant spinning around with regard to how much compatibility we need to preserve is giving me vertigo.
    
    I mean, I agree that the backward compatibility ramifications of every
    idea need to be considered, but I agree even more that the amount of
    spinning around here is pretty insane. My feeling is that neither role
    owners nor tenants introduce any real concerns about
    backward-compatibility or, for that matter, SQL standards compliance,
    nonwithstanding Stephen's argument to the contrary. Every vendor
    extends the standard with their own stuff, and we've done that as
    well, as we can do it in more places.
    
    On the other hand, changing ADMIN OPTION does have compatibility and
    spec-compliance ramifications. I think Stephen is arguing that we can
    solve this problem while coming closer to the spec, and I think we
    usually consider getting closer to the spec to be a sufficient reason
    for breaking backward compatibility (cf. standard_conforming_strings).
    But I don't know whether he is correct when he argues that the spec
    makes admin option on a role sufficient to drop the role. I've never
    had any luck understanding what the SQL specification is saying about
    any topic.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  151. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-11T16:41:05Z

    Greetings,
    
    * Tom Lane (tgl@sss.pgh.pa.us) wrote:
    > Robert Haas <robertmhaas@gmail.com> writes:
    > > If we implement the link between the creating role and the created
    > > role as role ownership, then we are surely just going to add a
    > > rolowner column to pg_authid, and when the role is owned by nobody, I
    > > think we should always just store a valid OID in it, rather than
    > > sometimes storing 0. It just seems simpler. Any time we would store 0,
    > > store the bootstrap superuser's pg_authid.oid value instead. That way
    > > the OID is always valid, which probably lets us get by with fewer
    > > special cases in the code.
    
    We haven't got any particularly special cases in the code today for what
    happens if we run up the role hierarchy to a point that it ends and so
    I'm not sure why adding in a whole new concept around role ownership,
    which doesn't exist in the spec, would somehow leave us with fewer such
    special cases.
    
    > +1.
    > 
    > Note that either case would also involve making entries in pg_shdepend;
    > although for the case of roles owned by/granted to the bootstrap
    > superuser, we could omit those on the usual grounds that we don't need
    > to record dependencies on pinned objects.
    
    That we aren't discussing the issues with the current GRANT ... WITH
    ADMIN OPTION and how we deviate from what the spec calls for when it
    comes to DROP ROLE, which seems to be the largest thing that's
    'solved' with this ownership concept, is concerning to me.
    
    If we go down the route of adding role ownership, are we going to
    document that we explicitly go against the SQL standard when it comes to
    how DROP ROLE works?  Or are we going to fix DROP ROLE?  I'd much prefer
    the latter, but doing so then largely negates the point of this role
    ownership concept.  I don't see how it makes sense to do both.
    
    Thanks,
    
    Stephen
    
  152. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-11T16:48:08Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Fri, Mar 11, 2022 at 11:12 AM Mark Dilger
    > <mark.dilger@enterprisedb.com> wrote:
    > > This issue of how much backwards compatibility breakage we're willing to tolerate is just as important as questions about how we would want roles to work in a green-field development project.  The sense I got a year ago, on this list, was that changing CREATEROLE was acceptable, but changing other parts of the system, such as how ADMIN OPTION works, would go too far.
    
    That we deviate as far as we do when it comes to the SQL spec is
    something that I don't feel like I had a good handle on when discussing
    this previously (that the spec doesn't talk about 'admin option' really
    but rather 'grantable authorization identifiers' or whatever it is
    doesn't help... but still, that's on me, sorry about that).
    
    > > Role ownership did not yet exist, and that was a big motivation in introducing that concept, because you couldn't credibly say it broke other existing features.  It introduces the new notion that when a superuser creates a role, the superuser owns it, which is identical to how things implicitly work today; and when a CREATEROLE non-superuser creates a role, that role owns the new role, which is different from how it works today, arguably breaking CREATEROLE's prior behavior.  *But it doesn't break anything else*.
    > >
    > > If we're going to change how ADMIN OPTION works, or how role membership works, or how inherit/noinherit works, let's first be clear that we are willing to accept whatever backwards incompatibility that entails.  This is not a green-field development project.  The constant spinning around with regard to how much compatibility we need to preserve is giving me vertigo.
    
    I agree that it would have an impact on backwards compatibility to
    change how WITH ADMIN works- but it would also get us more in line with
    what the SQL standard says for how WITH ADMIN is supposed to work and
    that seems worth the change to me.
    
    > On the other hand, changing ADMIN OPTION does have compatibility and
    > spec-compliance ramifications. I think Stephen is arguing that we can
    > solve this problem while coming closer to the spec, and I think we
    > usually consider getting closer to the spec to be a sufficient reason
    > for breaking backward compatibility (cf. standard_conforming_strings).
    
    Indeed.
    
    > But I don't know whether he is correct when he argues that the spec
    > makes admin option on a role sufficient to drop the role. I've never
    > had any luck understanding what the SQL specification is saying about
    > any topic.
    
    I'm happy to point you to what the spec says and to discuss it further
    if that would be helpful, or to get other folks to comment on it.  I
    agree that it's definitely hard to grok at times.  In this particular
    case what I'm looking at is, under DROP ROLE / Access Rules, there's
    only one sentence:
    
    There shall exist at least one grantable role authorization descriptor
    whose role name is R and whose grantee is an enabled authorization
    identifier.
    
    A bit of decoding: 'grantable role authorization descriptor' is a GRANT
    of a role WITH ADMIN OPTION.  The role name 'R' is the role specified.
    The 'grantee' is who that role R was GRANT'd to, and 'enabled
    authorization identifier' is basically "has_privs_of_role()" (note that
    you can in the spec hvae roles that you're a member of but which are
    *not* currently enabled).
    
    Hopefully that helps.
    
    Thanks,
    
    Stephen
    
  153. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-11T16:51:38Z

    On Fri, Mar 11, 2022 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Note that either case would also involve making entries in pg_shdepend;
    > although for the case of roles owned by/granted to the bootstrap
    > superuser, we could omit those on the usual grounds that we don't need
    > to record dependencies on pinned objects.
    
    That makes sense to me, but it still doesn't solve the problem of
    agreeing on role ownership vs. WITH ADMIN OPTION vs. something else.
    
    I find it ironic (and frustrating) that Mark implemented what I think
    is basically what you're arguing for, it got stuck because Stephen
    didn't like it, we then said OK so let's try to find out what Stephen
    would like, only to have you show up and say that it's right the way
    he already had it. I'm not saying that you're wrong, or for that
    matter that he's wrong. I'm just saying that if both of you are
    absolutely bent on having it the way you want it, either one of you is
    going to be sad, or we're not going to make any progress.
    
    Never mind the fact that neither of you seem interested in even giving
    a hearing to my preferred way of doing it. :-(
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  154. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-11T17:31:58Z

    
    > On Mar 11, 2022, at 8:48 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > I agree that it would have an impact on backwards compatibility to
    > change how WITH ADMIN works- but it would also get us more in line with
    > what the SQL standard says for how WITH ADMIN is supposed to work and
    > that seems worth the change to me.
    
    I'm fine with giving up some backwards compatibility to get some SQL standard compatibility, as long as we're clear that is what we're doing.  What you say about the SQL spec isn't great, though, because too much power is vested in "ADMIN".  I see "ADMIN" as at least three separate privileges together.  Maybe it would be spec compliant to implement "ADMIN" as a synonym for a set of separate privileges? 
    
    > On Mar 11, 2022, at 8:41 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > That we aren't discussing the issues with the current GRANT ... WITH
    > ADMIN OPTION and how we deviate from what the spec calls for when it
    > comes to DROP ROLE, which seems to be the largest thing that's
    > 'solved' with this ownership concept, is concerning to me.
    
    Sure, let's discuss that a bit more.  Here is my best interpretation of your post about the spec, when applied to postgres with an eye towards not doing any more damage than necessary:
    
    > On Mar 10, 2022, at 11:58 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > let's look at what the spec says:
    > 
    > CREATE ROLE
    >  - Who is allowed to run CREATE ROLE is implementation-defined
    
    This should be anyone with membership in pg_create_role.  
    
    >  - After creation, this is effictively run:
    >    GRANT new_role TO creator_role WITH ADMIN, GRANTOR "_SYSTEM"
    
    This should internally be implemented as three separate privileges, one which means you can grant the role, another which means you can drop the role, and a third that means you're a member of the role.  That way, they can be independently granted and revoked.  We could make "WITH ADMIN" a short-hand for "WITH G, D, M" where G, D, and M are whatever we name the independent privileges Grant, Drop, and Member-of.
    
    Splitting G and D helps with backwards compatibility, because it gives people who want the traditional postgres "admin" a way to get there, by granting "G+M".  Splitting M from G and D makes it simpler to implement the "bot" idea, since the bot shouldn't have M.  But it does raise a question about always granting G+D+M to the creator, since the bot is the creator and we don't want the bot to have M.  This isn't a problem I've invented from thin air, mind you, as G+D+M is just the definition of ADMIN per the SQL spec, if I've understood you correctly.  So we need to think a bit more about the pg_create_role built-in role and whether that needs to be further refined to distinguish those who can get membership in roles they create vs. those who cannot.  This line of reasoning takes me in the direction of what I think you were calling #5 upthread, but you'd have to elaborate on that, and how it interacts with the spec, for us to have a useful conversation about it.
    
    > DROP ROLE
    >  - Any user who has been GRANT'd a role with ADMIN option is able to
    >    DROP that role.
    
    Change this to "Any role who has D on the role".  That's spec compliant, because anyone granted ADMIN necessarily has D.
    
    > GRANT ROLE
    >  - No cycles allowed
    >  - A role must have ADMIN rights on the role to be able to GRANT it to
    >    another role.
    
    Change this to "Any role who has G on the role".  That's spec compliant, because anyone grant ADMIN necessarily has G.
    
    We should also fix the CREATE ROLE command to require the grantor have G on a role in order to give it to the new role as part of the command.  Changing the CREATEROLE, CREATEDB, REPLICATION, and BYPASSRLS attributes into pg_create_role, pg_create_db, pg_replication, and pg_bypassrls, the creator could only give them to the created role if the creator has G on the roles.  If we do this, we could keep the historical privilege bits and their syntax support for backward compatibility, or we could rip them out, but the decision between those two options is independent of the rest of the design.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  155. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-11T22:46:59Z

    Greetings,
    
    On Fri, Mar 11, 2022 at 12:32 Mark Dilger <mark.dilger@enterprisedb.com>
    wrote:
    
    >
    >
    > > On Mar 11, 2022, at 8:48 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > >
    > > I agree that it would have an impact on backwards compatibility to
    > > change how WITH ADMIN works- but it would also get us more in line with
    > > what the SQL standard says for how WITH ADMIN is supposed to work and
    > > that seems worth the change to me.
    >
    > I'm fine with giving up some backwards compatibility to get some SQL
    > standard compatibility, as long as we're clear that is what we're doing.
    > What you say about the SQL spec isn't great, though, because too much power
    > is vested in "ADMIN".  I see "ADMIN" as at least three separate privileges
    > together.  Maybe it would be spec compliant to implement "ADMIN" as a
    > synonym for a set of separate privileges?
    
    
    I do think that’s reasonable … and believe I suggested it about 3 messages
    ago in this thread. ;)  (step #3 I think it was?  Or maybe 4).
    
    > On Mar 11, 2022, at 8:41 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > >
    > > That we aren't discussing the issues with the current GRANT ... WITH
    > > ADMIN OPTION and how we deviate from what the spec calls for when it
    > > comes to DROP ROLE, which seems to be the largest thing that's
    > > 'solved' with this ownership concept, is concerning to me.
    >
    > Sure, let's discuss that a bit more.  Here is my best interpretation of
    > your post about the spec, when applied to postgres with an eye towards not
    > doing any more damage than necessary:
    >
    > > On Mar 10, 2022, at 11:58 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > >
    > > let's look at what the spec says:
    > >
    > > CREATE ROLE
    > >  - Who is allowed to run CREATE ROLE is implementation-defined
    >
    > This should be anyone with membership in pg_create_role.
    
    
    That could be the case if we wished to go that route. I’d think in such
    case we’d then also remove CREATEROLE as otherwise the documentation feels
    like it’d be quite confusing.
    
    >  - After creation, this is effictively run:
    > >    GRANT new_role TO creator_role WITH ADMIN, GRANTOR "_SYSTEM"
    >
    > This should internally be implemented as three separate privileges, one
    > which means you can grant the role, another which means you can drop the
    > role, and a third that means you're a member of the role.  That way, they
    > can be independently granted and revoked.  We could make "WITH ADMIN" a
    > short-hand for "WITH G, D, M" where G, D, and M are whatever we name the
    > independent privileges Grant, Drop, and Member-of.
    
    
    I mean, sure, we can get there, and possibly add more like if you’re
    allowed to change or reset that role’s password and other things, but I
    don’t see that this piece is required as part of the very first change in
    this area.  Further, WITH ADMIN already gives grant and member today, so
    you’re saying the only thing this change does that makes “WITH ADMIN” too
    powerful is adding DROP to it, yet that’s explicitly what the spec calls
    for.  In short, I disagree that moving the DROP ROLE right from CREATEROLE
    roles having that across the entire system (excluding superusers) to WITH
    ADMIN where the role who has that right can:
    
    A) already become that role and drop all their objects
    B) already GRANT that role to some other role
    
    is a big issue.
    
    Splitting G and D helps with backwards compatibility, because it gives
    > people who want the traditional postgres "admin" a way to get there, by
    > granting "G+M".  Splitting M from G and D makes it simpler to implement the
    > "bot" idea, since the bot shouldn't have M.  But it does raise a question
    > about always granting G+D+M to the creator, since the bot is the creator
    > and we don't want the bot to have M.  This isn't a problem I've invented
    > from thin air, mind you, as G+D+M is just the definition of ADMIN per the
    > SQL spec, if I've understood you correctly.  So we need to think a bit more
    > about the pg_create_role built-in role and whether that needs to be further
    > refined to distinguish those who can get membership in roles they create
    > vs. those who cannot.  This line of reasoning takes me in the direction of
    > what I think you were calling #5 upthread, but you'd have to elaborate on
    > that, and how it interacts with the spec, for us to have a useful
    > conversation about it.
    
    
    All that said, as I said before, I’m in favor of splitting things up and so
    if you want to do that as part of this initial work, sure. Idk that it’s
    absolutely required as part of this but I’m not going to complain if it’s
    included either.  I agree that would allow folks to get something similar
    to what they could get today if they want.
    
    I agree that the split up helps with the “bot” idea, as we could at least
    then create a security definer function that the bot runs and which creates
    roles that the bot then has G for but not M or D.  Even better would be to
    also provide a way for the “bot” to be able to create roles without the
    need for a security definer function where it doesn’t automatically get all
    three, and that was indeed what I was thinking about with the template
    idea. The general thought there being that an admin could define a template
    along the lines of:
    
    CREATE TEMPLATE employee_template
    CREATOR WITH ADMIN, NOMEMBERSHIP
    ROLE IN employee;
    
    And then provide a way for the bot to be given the right to use this
    template.  Thinking on it a bit further, I’m guessing that we wouldn’t
    actually give the bot pg_create_role in this case and instead would leave
    that to mean “able to create arbitrary roles and have all privs in that”
    similar to what we are talking about where ADMIN implies the full set of
    rights.
    
    > DROP ROLE
    > >  - Any user who has been GRANT'd a role with ADMIN option is able to
    > >    DROP that role.
    >
    > Change this to "Any role who has D on the role".  That's spec compliant,
    > because anyone granted ADMIN necessarily has D.
    
    
    Yeah.
    
    > GRANT ROLE
    > >  - No cycles allowed
    > >  - A role must have ADMIN rights on the role to be able to GRANT it to
    > >    another role.
    >
    > Change this to "Any role who has G on the role".  That's spec compliant,
    > because anyone grant ADMIN necessarily has G.
    
    
    Sure.
    
    We should also fix the CREATE ROLE command to require the grantor have G on
    > a role in order to give it to the new role as part of the command.
    
    
    … or just get rid of it, which seems saner to me.
    
    Changing the CREATEROLE, CREATEDB, REPLICATION, and BYPASSRLS attributes
    > into pg_create_role, pg_create_db, pg_replication, and pg_bypassrls, the
    > creator could only give them to the created role if the creator has G on
    > the roles.  If we do this, we could keep the historical privilege bits and
    > their syntax support for backward compatibility, or we could rip them out,
    > but the decision between those two options is independent of the rest of
    > the design.
    
    
    Yeah, turning those into predefined roles which an admin can then decide to
    give out (and to allow ADMIN on them to be given to folks who could then
    pass that along if they wanted) is another thought I’ve had though one
    that’s somewhat independent of the rest of this, but also shows how we
    could make those be things that a superuser could choose to give out, or
    not, to some set of roles who would then be able to create roles of their
    own with those privileges.
    
    On the whole, using predefined roles as the source of certain capabilities,
    and the options discussed here which would allow an admin to grant those
    capabilities out with or without the ability to grant them further, plus
    the splitting out of the individual role-relationship rights (membership,
    grantable, drop, etc) strikes me as being quite flexible and extendable and
    generally in the direction that we’ve been trending and which seems to be
    reasonably successful so far.
    
    Thanks,
    
    Stephen
    
    >
    
  156. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-12T00:03:06Z

    
    > On Mar 11, 2022, at 2:46 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > I do think that’s reasonable … and believe I suggested it about 3 messages ago in this thread. ;)  (step #3 I think it was?  Or maybe 4).
    
    Yes, and you mentioned it to me off-list.
    
    I'm soliciting a more concrete specification for what you are proposing.  To me, that means understanding how the SQL spec behavior that you champion translates into specific changes.  You specified some of this in steps #1 through #5, but I'd like a clearer indication of how many of those (#1 alone, both #1 and #2, or what?) constitute a competing idea to the idea of role ownership, and greater detail about how each of those steps translate into specific behavior changes in postgres.  Your initial five-step email seems to be claiming that #1 by itself is competitive, but to me it seems at least #1 and #2 would be required.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  157. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-12T00:56:19Z

    Greetings,
    
    On Fri, Mar 11, 2022 at 19:03 Mark Dilger <mark.dilger@enterprisedb.com>
    wrote:
    
    > > On Mar 11, 2022, at 2:46 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > >
    > > I do think that’s reasonable … and believe I suggested it about 3
    > messages ago in this thread. ;)  (step #3 I think it was?  Or maybe 4).
    >
    > Yes, and you mentioned it to me off-list.
    
    
    Indeed.
    
    I'm soliciting a more concrete specification for what you are proposing.
    > To me, that means understanding how the SQL spec behavior that you champion
    > translates into specific changes.  You specified some of this in steps #1
    > through #5, but I'd like a clearer indication of how many of those (#1
    > alone, both #1 and #2, or what?) constitute a competing idea to the idea of
    > role ownership, and greater detail about how each of those steps translate
    > into specific behavior changes in postgres.  Your initial five-step email
    > seems to be claiming that #1 by itself is competitive, but to me it seems
    > at least #1 and #2 would be required.
    
    
    First … I outlined a fair bit of further description in the message you
    just responded to but neglected to include in your response, which strikes
    me as odd that you’re now asking for further explanation.  When it comes to
    completing the idea of role ownership- I didn’t come up with that idea nor
    champion it and therefore I’m not really sure how many of the steps are
    required to fully support that concept..?  For my part, I would think that
    those steps necessary to satisfy the spec would get us pretty darn close to
    what true folks advocating for role ownership are asking for, but that
    doesn’t include the superuser-only alter role attributes piece.  Is that
    included in role ownership?  I wouldn’t think so, but some might argue
    otherwise, and I don’t know that it is actually useful to divert into a
    discussion about what is or isn’t in that.
    
    If we agree that the role attribute bits are independent then I think I
    agree that we need 1 and 2 to get the capabilities that the folks asking
    for role ownership want, as 2 is where we make sure that one admin of a
    role can’t revoke another admin’s rights over that role.  Perhaps 2 isn’t
    strictly necessary in a carefully managed environment where no one else is
    given admin rights over the mini-superuser roles, but I’d rather not have
    folks depending on that.  I’d still push back though and ask those
    advocating for this if it meets what they’re asking for.  I got the
    impression that it did but maybe I misunderstood.
    
    In terms of exactly how things would work with these changes… I thought I
    explained it pretty clearly, so it’s kind of hard to answer that further
    without a specific question to answer.  Did you have something specific in
    mind?  Perhaps I could answer a specific question and provide more clarity
    that way.
    
    Thanks,
    
    Stephen
    
    >
    
  158. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-12T02:08:36Z

    
    > On Mar 11, 2022, at 4:56 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > First … I outlined a fair bit of further description in the message you just responded to but neglected to include in your response, which strikes me as odd that you’re now asking for further explanation.
    
    
    
    >  When it comes to completing the idea of role ownership- I didn’t come up with that idea nor champion it
    
    Sorry, not "completing", but "competing".  It seems we're discussing different ways to fix how roles and CREATEROLE work, and we have several ideas competing against each other.  (This differs from *people* competing against each other, as I don't necessarily like the patch I wrote better than I like your idea.)
    
    > and therefore I’m not really sure how many of the steps are required to fully support that concept..?
    
    There are problems that the ownership concepts solve.  I strongly suspect that your proposal could also solve those same problems, and just trying to identify the specific portions of your proposal necessary to do so.
    
    >  For my part, I would think that those steps necessary to satisfy the spec would get us pretty darn close to what true folks advocating for role ownership are asking for
    
    I have little idea what "true folks" means in this context.  As for "advocating for role ownership", I'm not in that group.  Whether role ownership or something else, I just want some solution to a set of problems, mostly to do with needing superuser to do role management tasks.
    
    > , but that doesn’t include the superuser-only alter role attributes piece.  Is that included in role ownership?  I wouldn’t think so, but some might argue otherwise, and I don’t know that it is actually useful to divert into a discussion about what is or isn’t in that.
    
    Introducing the idea of role ownership doesn't fix that.  But a patch which introduces role ownership is useless unless CREATEROLE is also fixed.  There isn't any point having non-superusers create and own roles if, to do so, they need a privilege which can break into superuser.  But that argument is no different with a patch along the lines of what you are proposing.  CREATEROLE needs fixing either way.
    
    > If we agree that the role attribute bits are independent
    
    Yes.
    
    > then I think I agree that we need 1 and 2 to get the capabilities that the folks asking for role ownership want
    
    Yes.
    
    > as 2 is where we make sure that one admin of a role can’t revoke another admin’s rights over that role.
    
    Exactly, so #2 is part of the competing proposal.  (I get the sense you might not see these as competing proposals, but I find that framing useful for deciding which approach to pursue.)
    
    >  Perhaps 2 isn’t strictly necessary in a carefully managed environment where no one else is given admin rights over the mini-superuser roles, but I’d rather not have folks depending on that.
    
    I think it is necessary, and for the reason you say.
    
    >  I’d still push back though and ask those advocating for this if it meets what they’re asking for.  I got the impression that it did but maybe I misunderstood.
    > 
    > In terms of exactly how things would work with these changes… I thought I explained it pretty clearly, so it’s kind of hard to answer that further without a specific question to answer.  Did you have something specific in mind?  Perhaps I could answer a specific question and provide more clarity that way.
    
    Your emails contained a lot of "we could do this or that depending on what people want, and maybe this other thing, but that isn't really necessary, and ...." which left me unclear on the proposal.  I don't mean to disparage your communication style; it's just that when trying to distill technical details, high level conversation can be hard to grok.
    
    I have the sense that you aren't going to submit a patch, so I wanted this thread to contain enough detail for somebody else to do so.  Thanks.
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  159. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-14T14:38:10Z

    Greetings,
    
    * Mark Dilger (mark.dilger@enterprisedb.com) wrote:
    > > On Mar 11, 2022, at 4:56 PM, Stephen Frost <sfrost@snowman.net> wrote:
    > > First … I outlined a fair bit of further description in the message you just responded to but neglected to include in your response, which strikes me as odd that you’re now asking for further explanation.
    > 
    > >  When it comes to completing the idea of role ownership- I didn’t come up with that idea nor champion it
    > 
    > Sorry, not "completing", but "competing".  It seems we're discussing different ways to fix how roles and CREATEROLE work, and we have several ideas competing against each other.  (This differs from *people* competing against each other, as I don't necessarily like the patch I wrote better than I like your idea.)
    > 
    > > and therefore I’m not really sure how many of the steps are required to fully support that concept..?
    > 
    > There are problems that the ownership concepts solve.  I strongly suspect that your proposal could also solve those same problems, and just trying to identify the specific portions of your proposal necessary to do so.
    
    I'm happy to help try to identify those, but it seems we'd need to have
    the exact problems that ownership solves defined first.  Robert defined
    what he's looking for as:
    
    Robert Haas <robertmhaas@gmail.com> wrote:
    > Probably easier to just say it again: I want to have users that can
    > create roles and then have superuser-like powers with respect to those
    > roles. They can freely exercise the privileges of those roles, and
    > they can do all the things that a superuser can do but only with
    > respect to those roles. They cannot break out to the OS. I think it's
    > pretty similar to what you are describing, with a couple of possible
    > exceptions. For example, would you imagine that being an admin of a
    > login role would let you change that user's password? Because that
    > would be desirable behavior from where I sit.
    
    Which sure sounds like it's just about covered in step #1 of what I
    outlined before, except that the above description implies that one
    can't "get away" from the user who created their role, in which case we
    do need step #2 also.
    
    > >  For my part, I would think that those steps necessary to satisfy the spec would get us pretty darn close to what true folks advocating for role ownership are asking for
    > 
    > I have little idea what "true folks" means in this context.  As for "advocating for role ownership", I'm not in that group.  Whether role ownership or something else, I just want some solution to a set of problems, mostly to do with needing superuser to do role management tasks.
    
    ... I'm not entirely sure what I meant there either, my hunch is that
    'true' was actually just a leftover word from some other framing of that
    sentence and I had meant to remove it.  Apologies for that.  What I was
    trying to get at there is that steps #1 & #2 are the ones that I view as
    getting us closer to spec compliance and that doing so would get us to
    where Robert's ask above would be answered.
    
    > > , but that doesn’t include the superuser-only alter role attributes piece.  Is that included in role ownership?  I wouldn’t think so, but some might argue otherwise, and I don’t know that it is actually useful to divert into a discussion about what is or isn’t in that.
    > 
    > Introducing the idea of role ownership doesn't fix that.  But a patch which introduces role ownership is useless unless CREATEROLE is also fixed.  There isn't any point having non-superusers create and own roles if, to do so, they need a privilege which can break into superuser.  But that argument is no different with a patch along the lines of what you are proposing.  CREATEROLE needs fixing either way.
    
    There's a few ways to have the 'CREATEROLE' role attribute be fixed-
    
    - Remove it entirely (replacing with pg_create_role or such)
    - Remove its ability to GRANT out rights that the role running it
      doesn't have
    - Make it superfluous (leave it as-is, but add in pg_create_role which
      allows a role to create another role but doesn't include the magic
      GRANT whatever-role TO whatever-role that CREATEROLE has)
    
    I agree that we need to do something here to allow roles to create other
    roles while not having or being able to trivially get superuser
    themselves.
    
    > > If we agree that the role attribute bits are independent
    > 
    > Yes.
    
    Great.
    
    > > then I think I agree that we need 1 and 2 to get the capabilities that the folks asking for role ownership want
    > 
    > Yes.
    
    Ok.
    
    > > as 2 is where we make sure that one admin of a role can’t revoke another admin’s rights over that role.
    > 
    > Exactly, so #2 is part of the competing proposal.  (I get the sense you might not see these as competing proposals, but I find that framing useful for deciding which approach to pursue.)
    
    ... and is also part of getting us closer to the spec.
    
    > >  Perhaps 2 isn’t strictly necessary in a carefully managed environment where no one else is given admin rights over the mini-superuser roles, but I’d rather not have folks depending on that.
    > 
    > I think it is necessary, and for the reason you say.
    
    Great.
    
    > >  I’d still push back though and ask those advocating for this if it meets what they’re asking for.  I got the impression that it did but maybe I misunderstood.
    > > 
    > > In terms of exactly how things would work with these changes… I thought I explained it pretty clearly, so it’s kind of hard to answer that further without a specific question to answer.  Did you have something specific in mind?  Perhaps I could answer a specific question and provide more clarity that way.
    > 
    > Your emails contained a lot of "we could do this or that depending on what people want, and maybe this other thing, but that isn't really necessary, and ...." which left me unclear on the proposal.  I don't mean to disparage your communication style; it's just that when trying to distill technical details, high level conversation can be hard to grok.
    
    Feel free to quote me explicitly in such places that you're looking for
    clarification and I'd be happy to drill down on those.
    
    > I have the sense that you aren't going to submit a patch, so I wanted this thread to contain enough detail for somebody else to do so.  Thanks.
    
    So ... do you feel like that's now the case?  Or were you looking for
    more?
    
    Thanks,
    
    Stephen
    
  160. Re: role self-revocation

    Mark Dilger <mark.dilger@enterprisedb.com> — 2022-03-14T15:45:14Z

    
    > On Mar 14, 2022, at 7:38 AM, Stephen Frost <sfrost@snowman.net> wrote:
    > 
    > So ... do you feel like that's now the case?  Or were you looking for
    > more?
    
    I don't have any more questions at the moment.  Thanks!
    
    —
    Mark Dilger
    EnterpriseDB: http://www.enterprisedb.com
    The Enterprise PostgreSQL Company
    
    
    
    
    
    
    
  161. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-24T16:46:43Z

    On Fri, Mar 11, 2022 at 11:51 AM Robert Haas <robertmhaas@gmail.com> wrote:
    > On Fri, Mar 11, 2022 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > Note that either case would also involve making entries in pg_shdepend;
    > > although for the case of roles owned by/granted to the bootstrap
    > > superuser, we could omit those on the usual grounds that we don't need
    > > to record dependencies on pinned objects.
    >
    > That makes sense to me, but it still doesn't solve the problem of
    > agreeing on role ownership vs. WITH ADMIN OPTION vs. something else.
    
    Notwithstanding the lack of agreement on that point, I believe that
    what we should do for v15 is remove the session user
    self-administration exception. We have pretty much established that it
    was originally introduced in error. It later was found to be a
    security vulnerability, and that resulted in the exception being
    narrowed without removing it altogether. While there are differences
    of opinion on what the larger plan here ought to be, nobody's proposal
    plan involves retaining that exception. Neither has anyone offered a
    plausible use case for the current behavior, so there's no reason to
    think that removing it would break anything.
    
    However, it might. And if it does, I think it would be best if
    removing that exception were the *only* change in this area made by
    that release. If for v16 or v17 or v23 we implement Plan Tom or Plan
    Stephen or Plan Robert or something else, and along the way we remove
    that self-administration exception, we're going to have a real fire
    drill if it turns out that the self-administration exception was
    important for some reason we're not seeing right now. If, on the other
    hand, we remove that exception in v15, then if anything breaks, it'll
    be a lot easier to deal with. Worst case scenario we just revert the
    removal of that exception, which will be a very localized change if
    nothing else has been done that depends heavily on its having been
    removed.
    
    So I propose to commit something like what I posted here:
    
    http://postgr.es/m/CA+TgmobgeK0JraOwQVPqhSXcfBdFitXSomoebHMMMhmJ4gLonw@mail.gmail.com
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  162. Re: role self-revocation

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-03-24T17:10:26Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > Notwithstanding the lack of agreement on that point, I believe that
    > what we should do for v15 is remove the session user
    > self-administration exception. We have pretty much established that it
    > was originally introduced in error.
    
    Agreed.
    
    > However, it might. And if it does, I think it would be best if
    > removing that exception were the *only* change in this area made by
    > that release.
    
    Good idea, especially since it's getting to be too late to consider
    anything more invasive anyway.
    
    > So I propose to commit something like what I posted here:
    > http://postgr.es/m/CA+TgmobgeK0JraOwQVPqhSXcfBdFitXSomoebHMMMhmJ4gLonw@mail.gmail.com
    
    +1, although the comments might need some more work.  In particular,
    I'm not sure that this bit is well stated:
    
    +	 * A role cannot have WITH ADMIN OPTION on itself, because that would
    +	 * imply a membership loop.
    
    We already do consider a role to be a member of itself:
    
    regression=# create role r;
    CREATE ROLE
    regression=# grant r to r;
    ERROR:  role "r" is a member of role "r"
    regression=# grant r to r with admin option;
    ERROR:  role "r" is a member of role "r"
    
    It might be better to just say "By policy, a role cannot have WITH ADMIN
    OPTION on itself".  But if you want to write a defense of that policy,
    this isn't a very good one.
    
    			regards, tom lane
    
    
    
    
  163. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-24T17:26:48Z

    On Thu, Mar 24, 2022 at 1:10 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > However, it might. And if it does, I think it would be best if
    > > removing that exception were the *only* change in this area made by
    > > that release.
    >
    > Good idea, especially since it's getting to be too late to consider
    > anything more invasive anyway.
    
    I'd say it's definitely too late at this point.
    
    > > So I propose to commit something like what I posted here:
    > > http://postgr.es/m/CA+TgmobgeK0JraOwQVPqhSXcfBdFitXSomoebHMMMhmJ4gLonw@mail.gmail.com
    >
    > +1, although the comments might need some more work.  In particular,
    > I'm not sure that this bit is well stated:
    >
    > +        * A role cannot have WITH ADMIN OPTION on itself, because that would
    > +        * imply a membership loop.
    >
    > We already do consider a role to be a member of itself:
    >
    > regression=# create role r;
    > CREATE ROLE
    > regression=# grant r to r;
    > ERROR:  role "r" is a member of role "r"
    > regression=# grant r to r with admin option;
    > ERROR:  role "r" is a member of role "r"
    >
    > It might be better to just say "By policy, a role cannot have WITH ADMIN
    > OPTION on itself".  But if you want to write a defense of that policy,
    > this isn't a very good one.
    
    That sentence is present in the current code, along with a bunch of
    other sentences, which the patch renders irrelevant. So I just deleted
    all of the other stuff and kept the sentence that is still relevant to
    the revised code. I think your proposed replacement is an improvement,
    but let's be careful not to get sucked into too much of a wordsmithing
    exercise in a patch that's here to make a functional change.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  164. Re: role self-revocation

    Stephen Frost <sfrost@snowman.net> — 2022-03-28T14:51:37Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Thu, Mar 24, 2022 at 1:10 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > However, it might. And if it does, I think it would be best if
    > > > removing that exception were the *only* change in this area made by
    > > > that release.
    > >
    > > Good idea, especially since it's getting to be too late to consider
    > > anything more invasive anyway.
    > 
    > I'd say it's definitely too late at this point.
    
    Agreed.
    
    > > > So I propose to commit something like what I posted here:
    > > > http://postgr.es/m/CA+TgmobgeK0JraOwQVPqhSXcfBdFitXSomoebHMMMhmJ4gLonw@mail.gmail.com
    > >
    > > +1, although the comments might need some more work.  In particular,
    > > I'm not sure that this bit is well stated:
    
    Also +1 on this.
    
    Thanks,
    
    Stephen
    
  165. Re: role self-revocation

    Robert Haas <robertmhaas@gmail.com> — 2022-03-28T18:31:04Z

    On Mon, Mar 28, 2022 at 10:51 AM Stephen Frost <sfrost@snowman.net> wrote:
    > > > > So I propose to commit something like what I posted here:
    > > > > http://postgr.es/m/CA+TgmobgeK0JraOwQVPqhSXcfBdFitXSomoebHMMMhmJ4gLonw@mail.gmail.com
    > > >
    > > > +1, although the comments might need some more work.  In particular,
    > > > I'm not sure that this bit is well stated:
    >
    > Also +1 on this.
    
    OK, done using Tom's proposed wording.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  166. Re: CREATEROLE and role ownership hierarchies

    Greg Stark <stark@mit.edu> — 2022-04-01T14:46:07Z

    The cfbot is testing the last patch posted to this thread which is the
    remove-self-own patch which was already committed. I gather that
    there's still (at least one) patch under discussion.
    
    Could I suggest reposting the last version of the main patch, perhaps
    rebasing it. That way the cfbot would at least continue to test for
    conflicts.
    
    
    
    
  167. Re: CREATEROLE and role ownership hierarchies

    Robert Haas <robertmhaas@gmail.com> — 2022-04-01T14:56:35Z

    On Fri, Apr 1, 2022 at 10:46 AM Greg Stark <stark@mit.edu> wrote:
    > The cfbot is testing the last patch posted to this thread which is the
    > remove-self-own patch which was already committed. I gather that
    > there's still (at least one) patch under discussion.
    >
    > Could I suggest reposting the last version of the main patch, perhaps
    > rebasing it. That way the cfbot would at least continue to test for
    > conflicts.
    
    We should move this patch to the next CF or maybe even mark it
    returned with feedback. We're not going to get anything else done here
    for v15, and I'm not sure whether what we do beyond that will take
    this form or not.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  168. pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-02T18:31:24Z

    On Fri, Mar 4, 2022 at 4:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > If we are not tracking the grantors of role authorizations,
    > then we are doing it wrong and we ought to fix that.
    
    We are definitely doing it wrong. It's not that we aren't doing it at
    all, but we are doing it incorrectly. If user foo executes "GRANT foo
    TO bar GRANTED BY quux", pg_auth_members.grantor gets the OID of role
    "quux". Without the "GRANTED BY" clause, it gets the OID of role
    "foo". But no dependency is created; therefore, the OID of that column
    can point to a role that no longer exists, or potentially to a role
    that did exist at one point, was dropped, and was later replaced by
    some other role that happens to get the same OID. pg_dump handles this
    by dumping "GRANTED BY whoever" if pg_auth_members.grantor is a
    still-extant role and omitting the clause if not. This would be a
    security vulnerability if there were any logic in the backend that
    actually did anything with pg_auth_members.grantor, because if the
    original grantor is removed and replaced by another role with the same
    OID, a dump/restore could change the notional grantor. Since there is
    no such logic, I don't think it's insecure, but it's still really
    lame.
    
    So let's talk about how we could fix this. In a vacuum I'd say this is
    just a feature that never got finished and we should rip the whole
    thing out. That is, remove pg_auth_members.grantor entirely and at
    most keep some do-nothing syntax around for backward compatibility.
    However, what Tom is saying in the text quoted above is that we ought
    to have something that actually works, which is more challenging.
    Apparently, the desired behavior here is for this to work like grants
    on non-role objects, where executing "GRANT SELECT ON TABLE s1 TO foo"
    under two different user accounts bar and baz that both have
    permissions to grant that privilege creates two independent grants
    that can be independently revoked. To get there, we'll have to change
    a good few things -- not only will we need a dependency to prevent a
    grantor from being dropped without revoking the grant, but we need to
    change the primary key of pg_auth_members from (roleid, member) to
    (roleid, member, grantor). Then we'll also have to change the behavior
    of the GRANT and REVOKE commands at the SQL level, and also the
    behavior of pg_dump, which will need to dump and restore all grants.
    
    I'm open to other proposals, but my thought is that it might be
    simplest to try to clean this up in two steps. In step one, the only
    goal would be to make pg_auth_members.grantor reliably sane. In other
    words, we'd add a dependency on the grantor when a role is granted to
    another role. You could still only have one grant of role A to role B,
    but the notional grantor C would always be a user that actually
    exists. I suspect it would be a really good idea to also patch pg_dump
    to not ever dump the grantor when working from an older release,
    because the information is not necessarily reliable and I fear that
    propagating it forward could lead to broken stuff or maybe even
    security hazards as noted above. Then, in step two, we change things
    around to allow multiple grants of the same role to the same other
    role, one per grantor. Now you've achieved parity between the behavior
    we have for roles and the behavior we have for permissions on other
    kinds of SQL objects.
    
    There may be other improvements we want to make in this area -
    previous discussions have suggested various ideas - but it seems to me
    that making the behavior sane and consistent with other types of
    objects would be a good start. That way, if we decide we do want to
    change anything else, we will be starting from a firm foundation,
    rather than building on sand.
    
    Thoughts?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  169. Re: pg_auth_members.grantor is bunk

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-06-02T19:15:13Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Fri, Mar 4, 2022 at 4:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> If we are not tracking the grantors of role authorizations,
    >> then we are doing it wrong and we ought to fix that.
    
    > So let's talk about how we could fix this. In a vacuum I'd say this is
    > just a feature that never got finished and we should rip the whole
    > thing out. That is, remove pg_auth_members.grantor entirely and at
    > most keep some do-nothing syntax around for backward compatibility.
    > However, what Tom is saying in the text quoted above is that we ought
    > to have something that actually works, which is more challenging.
    > Apparently, the desired behavior here is for this to work like grants
    > on non-role objects, where executing "GRANT SELECT ON TABLE s1 TO foo"
    > under two different user accounts bar and baz that both have
    > permissions to grant that privilege creates two independent grants
    > that can be independently revoked.
    
    Maybe.  What I was pointing out is that this is SQL-standard syntax
    and there are SQL-standard semantics that it ought to be implementing.
    Probably those semantics match what you describe here, but we ought
    to dive into the spec and make sure before we spend a lot of effort.
    It's not quite clear to me whether the spec defines any particular
    unique key (identity) for the set of role authorizations.
    
    			regards, tom lane
    
    
    
    
  170. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-02T19:40:37Z

    On Thu, Jun 2, 2022 at 3:15 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Maybe.  What I was pointing out is that this is SQL-standard syntax
    > and there are SQL-standard semantics that it ought to be implementing.
    > Probably those semantics match what you describe here, but we ought
    > to dive into the spec and make sure before we spend a lot of effort.
    > It's not quite clear to me whether the spec defines any particular
    > unique key (identity) for the set of role authorizations.
    
    I sort of thought http://postgr.es/m/3981966.1646429663@sss.pgh.pa.us
    constituted a completed investigation of this sort. No?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  171. Re: pg_auth_members.grantor is bunk

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-06-02T19:50:58Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Thu, Jun 2, 2022 at 3:15 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> Maybe.  What I was pointing out is that this is SQL-standard syntax
    >> and there are SQL-standard semantics that it ought to be implementing.
    >> Probably those semantics match what you describe here, but we ought
    >> to dive into the spec and make sure before we spend a lot of effort.
    >> It's not quite clear to me whether the spec defines any particular
    >> unique key (identity) for the set of role authorizations.
    
    > I sort of thought http://postgr.es/m/3981966.1646429663@sss.pgh.pa.us
    > constituted a completed investigation of this sort. No?
    
    I didn't think so.  It's clear that the spec expects us to track the
    grantor, but I didn't chase down what it expects us to *do* with that
    information, nor what it thinks the rules are for merging multiple
    authorizations.
    
    			regards, tom lane
    
    
    
    
  172. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-02T20:44:02Z

    On Thu, Jun 2, 2022 at 3:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > I sort of thought http://postgr.es/m/3981966.1646429663@sss.pgh.pa.us
    > > constituted a completed investigation of this sort. No?
    >
    > I didn't think so.  It's clear that the spec expects us to track the
    > grantor, but I didn't chase down what it expects us to *do* with that
    > information, nor what it thinks the rules are for merging multiple
    > authorizations.
    
    Hmm, OK. Well, one problem is that I've never had any luck
    interpreting what the spec says about anything, and I've sort of given
    up. But even if that were not so, I'm a little unclear what other
    conclusion is possible here. The spec either wants the same behavior
    that we already have for other object types, which is what I am here
    proposing that we do, or it wants something different. If it wants
    something different, it probably wants that for all object types, not
    just roles. Since I doubt we would want the behavior for roles to be
    inconsistent with what we do for all other object types, in that case
    we would probably either change the behavior for all other object
    types to something new, and then clean up the role stuff afterwards,
    or else first do what I proposed here and then later change it all at
    once. In which case the proposal that I've made is as good a way to
    start as any.
    
    Now, if it happens to be the case that the spec proposes a different
    behavior for roles than for non-role objects, and if the behavior for
    roles is something other than the only we currently have for non-role
    objects, then I'd agree that the plan I propose here needs revision. I
    suspect that's unlikely but I can't make anything of the spec so ....
    maybe?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  173. Re: pg_auth_members.grantor is bunk

    Stephen Frost <sfrost@snowman.net> — 2022-06-06T23:41:02Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Thu, Jun 2, 2022 at 3:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > > I sort of thought http://postgr.es/m/3981966.1646429663@sss.pgh.pa.us
    > > > constituted a completed investigation of this sort. No?
    > >
    > > I didn't think so.  It's clear that the spec expects us to track the
    > > grantor, but I didn't chase down what it expects us to *do* with that
    > > information, nor what it thinks the rules are for merging multiple
    > > authorizations.
    > 
    > Hmm, OK. Well, one problem is that I've never had any luck
    > interpreting what the spec says about anything, and I've sort of given
    > up. But even if that were not so, I'm a little unclear what other
    > conclusion is possible here. The spec either wants the same behavior
    > that we already have for other object types, which is what I am here
    > proposing that we do, or it wants something different. If it wants
    > something different, it probably wants that for all object types, not
    > just roles. Since I doubt we would want the behavior for roles to be
    > inconsistent with what we do for all other object types, in that case
    > we would probably either change the behavior for all other object
    > types to something new, and then clean up the role stuff afterwards,
    > or else first do what I proposed here and then later change it all at
    > once. In which case the proposal that I've made is as good a way to
    > start as any.
    > 
    > Now, if it happens to be the case that the spec proposes a different
    > behavior for roles than for non-role objects, and if the behavior for
    > roles is something other than the only we currently have for non-role
    > objects, then I'd agree that the plan I propose here needs revision. I
    > suspect that's unlikely but I can't make anything of the spec so ....
    > maybe?
    
    Thankfully, at least from my reading, the spec isn't all that
    complicated on this particular point.  The spec talks about "role
    authorization descriptor"s and those are "created with role name,
    grantee, and grantor" and then further says "redundant duplicate role
    authorization descriptors are destroyed", presumably meaning that the
    entire thing has to be identical.  In other words, yeah, the PK should
    include the grantor.  There's a further comment that the 'set of
    involved grantees' is the union of all the 'grantees', clearly
    indicating that you can have multiple GRANT 'foo' to 'bar's with
    distinct grantees.
    
    In terms of how that's then used, yeah, it's during REVOKE because a
    REVOKE is only able to 'find' role authorization descriptors which match
    the triple of role revoked, grantee, grantor (though there's a caveat in
    that the 'grantor' role could be the current role, or the current user).
    
    Interestingly, at least in my looking it over today, it doesn't seem
    that the 'grantor' could be 'any applicable role' (which is what's
    usually used to indicate that it could be any role that the current role
    inherits), meaning you have to include the GRANTED BY in the REVOKE
    statement or do a SET ROLE first when doing a REVOKE if it's for a role
    that you aren't currently running as (but which you are a member of).
    
    Anyhow, in other words, I do think Robert's got it right here.  Happy to
    discuss further though if there are doubts.
    
    Thanks,
    
    Stephen
    
  174. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-24T20:19:34Z

    On Mon, Jun 6, 2022 at 7:41 PM Stephen Frost <sfrost@snowman.net> wrote:
    > Thankfully, at least from my reading, the spec isn't all that
    > complicated on this particular point.  The spec talks about "role
    > authorization descriptor"s and those are "created with role name,
    > grantee, and grantor" and then further says "redundant duplicate role
    > authorization descriptors are destroyed", presumably meaning that the
    > entire thing has to be identical.  In other words, yeah, the PK should
    > include the grantor.  There's a further comment that the 'set of
    > involved grantees' is the union of all the 'grantees', clearly
    > indicating that you can have multiple GRANT 'foo' to 'bar's with
    > distinct grantees.
    >
    > In terms of how that's then used, yeah, it's during REVOKE because a
    > REVOKE is only able to 'find' role authorization descriptors which match
    > the triple of role revoked, grantee, grantor (though there's a caveat in
    > that the 'grantor' role could be the current role, or the current user).
    
    What is supposed to happen if someone tries to execute DROP ROLE on a
    role that has previously been used as a grantor?
    
    Consider:
    
    create role foo;
    create role bar;
    create role baz;
    grant foo to bar granted by baz;
    drop role baz;
    
    Upthread, I proposed that "drop role baz" should fail here, but
    there's at least one other option: it could silently remove the grant,
    as we would do if either foo or bar were dropped. The situation is not
    quite comparable, though: a grant from foo to bar makes no logical
    sense if either of those roles cease to exist, but it does make at
    least some sense if baz ceases to exist. Therefore I think someone
    could argue either for an error or for removing the grant -- or
    possibly even for some other behavior, though the other behaviors that
    I can think of don't make much sense in a world where the primary key
    of pg_auth_members is (roleid, member, grantor).
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  175. Re: pg_auth_members.grantor is bunk

    David G. Johnston <david.g.johnston@gmail.com> — 2022-06-24T20:29:47Z

    On Fri, Jun 24, 2022 at 1:19 PM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Mon, Jun 6, 2022 at 7:41 PM Stephen Frost <sfrost@snowman.net> wrote:
    > >
    > > In terms of how that's then used, yeah, it's during REVOKE because a
    > > REVOKE is only able to 'find' role authorization descriptors which match
    > > the triple of role revoked, grantee, grantor (though there's a caveat in
    > > that the 'grantor' role could be the current role, or the current user).
    >
    > What is supposed to happen if someone tries to execute DROP ROLE on a
    > role that has previously been used as a grantor?
    >
    > Upthread, I proposed that "drop role baz" should fail here
    >
    
    I concur with this.
    
    I think that the grantor owns the grant, and that REASSIGNED OWNED should
    be able to move those grants to someone else.
    
    By extension, DROP OWNED should remove them.
    
    David J.
    
  176. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-24T20:46:42Z

    On Fri, Jun 24, 2022 at 4:30 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >> Upthread, I proposed that "drop role baz" should fail here
    >
    > I concur with this.
    >
    > I think that the grantor owns the grant, and that REASSIGNED OWNED should be able to move those grants to someone else.
    >
    > By extension, DROP OWNED should remove them.
    
    Interesting. I hadn't thought about changing the behavior of DROP
    OWNED BY and REASSIGN OWNED BY. A quick experiment supports your
    interpretation:
    
    rhaas=# grant select on table foo to bar;
    GRANT
    rhaas=# revoke select on table foo from bar;
    REVOKE
    rhaas=# grant select on table foo to bar with grant option;
    GRANT
    rhaas=# set role bar;
    SET
    rhaas=> grant select on table foo to baz;
    GRANT
    rhaas=> reset role;
    RESET
    rhaas=# drop role bar;
    ERROR:  role "bar" cannot be dropped because some objects depend on it
    DETAIL:  privileges for table foo
    rhaas=# drop owned by bar;
    DROP OWNED
    rhaas=# drop role bar;
    DROP ROLE
    
    So, privileges on tables (and presumably all other SQL objects)
    already work the way that you propose here. If we choose to make role
    memberships work in some other way then the two will be inconsistent.
    Probably we shouldn't do that. There is still the question of what the
    SQL specification says about this, but I would guess that it mandates
    the same behavior for all kinds of privileges rather than treating
    role memberships and table permissions in different ways. I could be
    wrong, though.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  177. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-06-30T15:23:19Z

    On Fri, Jun 24, 2022 at 4:46 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > Interesting. I hadn't thought about changing the behavior of DROP
    > OWNED BY and REASSIGN OWNED BY. A quick experiment supports your
    > interpretation:
    
    Here is a minimal patch fixing exactly $SUBJECT. Granting a role to
    another role now creates a dependency on the grantor, so if you try to
    drop the grantor you get an ERROR. You can resolve that by revoking
    the grant, or by using DROP OWNED BY or REASSIGN OWNED BY. To make
    this work, I had to make role memberships participate in the
    dependency system, which means pg_auth_members gains an OID column.
    The tricky part is that removing either of the two roles directly
    involved in a grant currently does, and should still, silently remove
    the grant. So, if you do "GRANT foo TO bar GRANTED BY baz", and then
    try to "DROP ROLE baz", that should fail, but if you instead try to
    "DROP ROLE baz, bar", that should work, because when bar is removed,
    the grant is silently removed, and then it's OK to drop baz. If these
    were database-local objects I think this could have all been sorted
    out quite easily by creating dependencies on all three roles involved
    in the GRANT and using the right deptype for each, but shared objects
    have their own set of deptypes which seemed to present no easy
    solution to this problem. I resolved the issue by having DropRole()
    make two loops over the list of roles to be dropped rather than one;
    see patch for details.
    
    There are several things that I think ought to be changed which this
    patch does not change. Most likely, I'll try to write separate patches
    for those things rather than making this one bigger.
    
    First, as discussed upthread, I think we ought to change things so
    that you can have multiple simultaneous grants of role A to role B
    each with a different grantor. That is what we do for other types of
    grants and Stephen at least thinks it's what the SQL standard
    specifies.
    
    Second, I think we ought to enforce that the grantor has to be a role
    which has the ability to perform the grant, just as we do for other
    object types. This is a little thorny, though, because we play some
    tricks with other types of objects that don't work for roles. If
    superuser alice executes "GRANT SELECT ON bobs_table TO fred" we
    record the owner of the grant as being the table owner and update the
    ownership of the grant each time the table owner is changed. That way,
    even if alice ceases to be a superuser, we maintain the invariant that
    the grantor of record must have privileges to perform the grant. But
    if superuser alice executes "GRANT accounting TO fred", we can't use
    the same trick, because the "accounting" role doesn't have an owner.
    If we attribute the grant to alice and she ceases to be a superuser
    (and also doesn't have CREATEROLE) then the invariant is violated.
    Attributing the grant to the bootstrap superuser doesn't help, as that
    user can also be made not a superuser. Attributing the grant to
    accounting is no good, as accounting doesn't and can't have ADMIN
    OPTION on itself; and fred doesn't have to have ADMIN OPTION on
    accounting either.
    
    One way to fix this problem would be to prohibit the removal of
    superuser privileges from the booststrap superuser. Then, we could
    attribute grants made by users who lack ADMIN OPTION on the granted
    role to the bootstrap superuser. Grants made by users who do possess
    ADMIN OPTION would be attributed to the actual grantor (unless GRANTED
    BY was used) and removing ADMIN OPTION from such a user could be made
    to fail if they had outstanding role grants. I think that's probably
    the nearest analogue of what we do for other object types, but if
    you've got another idea what to do here, I'd love to hear it.
    
    Thoughts on this patch would be great, too.
    
    Thanks,
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  178. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-07-20T19:11:09Z

    On Fri, Jun 24, 2022 at 4:46 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > On Fri, Jun 24, 2022 at 4:30 PM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > >> Upthread, I proposed that "drop role baz" should fail here
    > >
    > > I concur with this.
    > >
    > > I think that the grantor owns the grant, and that REASSIGNED OWNED should be able to move those grants to someone else.
    > >
    > > By extension, DROP OWNED should remove them.
    >
    > Interesting. I hadn't thought about changing the behavior of DROP
    > OWNED BY and REASSIGN OWNED BY. A quick experiment supports your
    > interpretation:
    
    This experiment was insufficiently thorough. I see now that, for other
    object types, DROP OWNED BY does work in the way that you propose, but
    REASSIGN OWNED BY does not. Here's a better test:
    
    rhaas=# create table foo();
    CREATE TABLE
    rhaas=# create role bar;
    CREATE ROLE
    rhaas=# create role baz;
    CREATE ROLE
    rhaas=# grant select on table foo to bar with grant option;
    GRANT
    rhaas=# set role bar;
    SET
    rhaas=> grant select on table foo to baz;
    GRANT
    rhaas=> reset role;
    RESET
    rhaas=# drop role bar;
    ERROR:  role "bar" cannot be dropped because some objects depend on it
    DETAIL:  privileges for table foo
    rhaas=# create role quux;
    CREATE ROLE
    rhaas=# reassign owned by bar to quux;
    REASSIGN OWNED
    rhaas=# drop role bar;
    ERROR:  role "bar" cannot be dropped because some objects depend on it
    DETAIL:  privileges for table foo
    rhaas=# drop owned by bar;
    DROP OWNED
    rhaas=# drop role bar;
    DROP ROLE
    
    This behavior might look somewhat bizarre, but there's actually a good
    reason for it: the system guarantees that whoever is listed as the
    grantor of a privilege has the *current* right to grant that
    privilege. It can't categorically change the grantor of every
    privilege given by bar to quux because quux might not and in fact does
    not have the right to grant select on table foo to baz. Now, you might
    be thinking, ah, but what if the superuser performed the grant? They
    could cease to be the superuser later, and then the rule would be
    violated! But actually not, because a grant by the superuser is
    imputed to the table owner, who always has the right to grant all
    rights on the table, and if the table owner is ever changed, all the
    grants imputed to the old table owner are changed to have their
    grantor as the new table owner. Similarly, trying to revoke select, or
    the grant option on it, from bar would fail. So it looks pretty
    intentional, and pretty tightly-enforced, that every role listed as a
    grantor must be one which is currently able to grant that privilege.
    
    And that means that REASSIGN OWNED can't just do a blanket change to
    the recorded grantor. It could try to do so, I suppose, and just throw
    an error if it doesn't work out, but that might make REASSIGN OWNED
    fail a lot more often, which could suck. In any event, the implemented
    behavior is that REASSIGN OWNED does nothing about permissions, but
    DROP OWNED cascades to grantors. This is SORT OF documented, although
    the documentation only mentions that DROP OWNED cascades to privileges
    granted *to* the target role, and does not mention that it also
    cascades to privileges granted *by* the target role.
    
    The previous version of the patch makes both DROP OWNED and REASSIGN
    OWNED cascade to grantors, but I now think that, for consistency, I'd
    better look into changing it so that only DROP OWNED cascades. I think
    perhaps I should be using SHARED_DEPENDENCY_ACL instead of
    SHARED_DEPENDENCY_OWNER.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  179. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-07-26T16:46:07Z

    On Wed, Jul 20, 2022 at 3:11 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > The previous version of the patch makes both DROP OWNED and REASSIGN
    > OWNED cascade to grantors, but I now think that, for consistency, I'd
    > better look into changing it so that only DROP OWNED cascades. I think
    > perhaps I should be using SHARED_DEPENDENCY_ACL instead of
    > SHARED_DEPENDENCY_OWNER.
    
    All right, here's a new patch set, now with a second patch added to the series.
    
    0001, as before, is a minimal fix for $SUBJECT, but it now uses
    SHARED_DEPENDENCY_ACL instead of SHARED_DEPENDENCY_OWNER, because that
    gives behavior which is more like what we do for other object types.
    However, it confines itself to making sure that
    pg_auth_members.grantor is a valid user, and that's it.
    
    0002 then revises the behavior substantially further to make role
    grants work like other grants. The grantor of record is required to be
    a user with ADMIN OPTION on the grant, or the bootstrap superuser,
    just as for other object types the grantor of record must have GRANT
    OPTION or be the object owner (but roles don't have owners). Dependent
    grants are tracked and must be revoked before the grants upon which
    they depend, but REVOKE .. CASCADE now works. Dependent grants must be
    acyclic: you can't have alice getting ADMIN OPTION from bob and bob
    getting it from alice; somebody's got to get it from the bootstrap
    superuser. This is all just by analogy with what we do for grants on
    object types, and making role grants do something similar instead of
    the completely random treatment we have at present.
    
    I believe that these patches are mostly complete, but I think that
    dumpRoleMembership() probably needs some more work. I don't know what
    exactly, but there's nothing to cause it to dump the role grants in an
    order that will create dependent grants after the things that they
    depend on, which seems essential.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  180. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-07-28T19:09:32Z

    On Tue, Jul 26, 2022 at 12:46 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > I believe that these patches are mostly complete, but I think that
    > dumpRoleMembership() probably needs some more work. I don't know what
    > exactly, but there's nothing to cause it to dump the role grants in an
    > order that will create dependent grants after the things that they
    > depend on, which seems essential.
    
    OK, so I fixed that, and also updated the documentation a bit more. I
    think these patches are basically done, and I'd like to get them
    committed before too much more time goes by, because I have other
    things that depend on this which I also want to get done for this
    release. Anybody object?
    
    I'm hoping not, because, while this is a behavior change, the current
    state of play in this area is just terrible. To my knowledge, this is
    the only place in the system where we allow a dangling OID reference
    in a catalog table to persist after the object to which it refers has
    been dropped. I believe it's also the object type where multiple
    grants by different grantors aren't tracked separately, and where the
    grantor need not themselves have the permission being granted. It
    doesn't really look like any of these things were intentional behavior
    so much as just ... nobody ever bothered to write the code to make it
    work properly. I'm hoping the fact that I have now done that will be
    viewed as a good thing, but maybe that won't turn out to be the case.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  181. Re: pg_auth_members.grantor is bunk

    David G. Johnston <david.g.johnston@gmail.com> — 2022-07-28T21:17:06Z

    On Thu, Jul 28, 2022 at 12:09 PM Robert Haas <robertmhaas@gmail.com> wrote:
    
    > On Tue, Jul 26, 2022 at 12:46 PM Robert Haas <robertmhaas@gmail.com>
    > wrote:
    > > I believe that these patches are mostly complete, but I think that
    > > dumpRoleMembership() probably needs some more work. I don't know what
    > > exactly, but there's nothing to cause it to dump the role grants in an
    > > order that will create dependent grants after the things that they
    > > depend on, which seems essential.
    >
    > OK, so I fixed that, and also updated the documentation a bit more. I
    > think these patches are basically done, and I'd like to get them
    > committed before too much more time goes by, because I have other
    > things that depend on this which I also want to get done for this
    > release. Anybody object?
    >
    > I'm hoping not, because, while this is a behavior change, the current
    > state of play in this area is just terrible. To my knowledge, this is
    > the only place in the system where we allow a dangling OID reference
    > in a catalog table to persist after the object to which it refers has
    > been dropped. I believe it's also the object type where multiple
    > grants by different grantors aren't tracked separately, and where the
    > grantor need not themselves have the permission being granted. It
    > doesn't really look like any of these things were intentional behavior
    > so much as just ... nobody ever bothered to write the code to make it
    > work properly. I'm hoping the fact that I have now done that will be
    > viewed as a good thing, but maybe that won't turn out to be the case.
    >
    >
    I suggest changing \du memberof to output something like this:
    
    select r.rolname,
    array(
      select format('%s:%s/%s',
        b.rolname,
        case when m.admin_option then 'admin' else 'member' end,
        g.rolname)
      from pg_catalog.pg_auth_members m
      join pg_catalog.pg_roles b on (m.roleid = b.oid)
      join pg_catalog.pg_roles g on (m.grantor = g.oid)
      where m.member = r.oid
    ) as memberof
    from pg_catalog.pg_roles r where r.rolname !~ '^pg_';
    
     rolname |              memberof
    ---------+------------------------------------
     vagrant | {}
     o       | {}
     a       | {o:admin/p,o:admin/vagrant}
     x       | {o:admin/a,p:member/vagrant}
     b       | {o:admin/a}
     p       | {o:admin/vagrant}
     y       | {x:member/vagrant}
     q       | {}
     r       | {q:admin/vagrant}
     s       | {}
     t       | {q:admin/vagrant,s:member/vagrant}
    
    
    (needs sorting, tried to model it after ACL - column privileges
    specifically)
    
    => \dp mytable
                                      Access privileges
     Schema |  Name   | Type  |   Access privileges   |   Column privileges   |
    Policies
    --------+---------+-------+-----------------------+-----------------------+----------
     public | mytable | table | miriam=arwdDxt/miriam+| col1:                +|
            |         |       | =r/miriam            +|   miriam_rw=rw/miriam |
            |         |       | admin=arw/miriam      |                       |
    (1 row)
    
    If we aren't dead set on having \du and \dg be aliases for each other I'd
    rather redesign \dg (or add a new meta-command) to be a group-centric view
    of this exact same data instead of user-centric one.  Namely it has a
    "members" column instead of "memberof" and have it output, one line per
    member:
    
    user=[admin|member]/grantor
    
    I looked over the rest of the patch and played with the circularity a bit,
    which motivated the expanded info in \du, and the confirmation that two
    separate admin grants that are not circular can exist.
    
    I don't have any meaningful insight as to breaking things with these
    changes but I am strongly in favor of tightening this up and formalizing it.
    
    David J.
    
  182. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-07-29T12:46:06Z

    On Thu, Jul 28, 2022 at 5:17 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    > I suggest changing \du memberof to output something like this:
    >
    >  rolname |              memberof
    > ---------+------------------------------------
    >  vagrant | {}
    >  r       | {q:admin/vagrant}
    >  t       | {q:admin/vagrant,s:member/vagrant}
    >
    > (needs sorting, tried to model it after ACL - column privileges specifically)
    
    I don't know. I agree with you that we should probably think about
    changing the \du output, but I'm not sure if I like this particular
    idea about how to do it. I mean, the ACL format that we use for tables
    and other objects is basically an internal format which we throw at
    the user, hoping they'll know how to interpret it. I don't know if
    it's what we should pick when we don't have that kind of internal
    format already. On the other hand, consistency is worth something, and
    I'm not sure that I have a better idea.
    
    https://commitfest.postgresql.org/38/3744/ might affect what we want
    to do here, too.
    
    > If we aren't dead set on having \du and \dg be aliases for each other I'd rather redesign \dg (or add a new meta-command) to be a group-centric view of this exact same data instead of user-centric one.  Namely it has a "members" column instead of "memberof" and have it output, one line per member:
    >
    > user=[admin|member]/grantor
    
    That seems like a topic for a separate thread, but I agree that a
    flipped view of this data would be more useful than using two letters
    of the alphabet for exactly the same thing, especially given that
    we're pretty short on unused letters.
    
    > I don't have any meaningful insight as to breaking things with these changes but I am strongly in favor of tightening this up and formalizing it.
    
    Cool.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  183. Re: pg_auth_members.grantor is bunk

    Stephen Frost <sfrost@snowman.net> — 2022-07-31T18:18:27Z

    Greetings,
    
    * Robert Haas (robertmhaas@gmail.com) wrote:
    > On Tue, Jul 26, 2022 at 12:46 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > > I believe that these patches are mostly complete, but I think that
    > > dumpRoleMembership() probably needs some more work. I don't know what
    > > exactly, but there's nothing to cause it to dump the role grants in an
    > > order that will create dependent grants after the things that they
    > > depend on, which seems essential.
    > 
    > OK, so I fixed that, and also updated the documentation a bit more. I
    > think these patches are basically done, and I'd like to get them
    > committed before too much more time goes by, because I have other
    > things that depend on this which I also want to get done for this
    > release. Anybody object?
    
    Thanks for working on this.
    
    Subject: [PATCH v3 1/2] Ensure that pg_auth_members.grantor is always valid.
    
    diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
    index 94135fdd6b..258943094a 100644
    --- a/src/backend/commands/user.c
    +++ b/src/backend/commands/user.c
    @@ -919,7 +920,7 @@ DropRole(DropRoleStmt *stmt)
     
     	/*
     	 * Scan the pg_authid relation to find the Oid of the role(s) to be
    -	 * deleted.
    +	 * deleted and perform prleliminary permissions and sanity checks.
    
    Should be preliminary, I'm guessing.
    
    Overall, this looks like a solid improvement.
    
    Subject: [PATCH v3 2/2] Make role grant system more consistent with other
     privileges.
    
    > Previously, only the superuser could specify GRANTED BY with a user
    > other than the current user. Relax that rule to allow the grantor
    > to be any role whose privileges the current user posseses. This
    > doesn't improve compatibility with what we do for other object types,
    > where support for GRANTED BY is entirely vestigial, but it makes this
    > feature more usable and seems to make sense to change at the same time
    > we're changing related behaviors.
    
    Presumably the GRANTED BY user in this case still has to have the
    ability to have performed the GRANT themselves?  Looks that way below
    and it's just the commit message, but was the first question that came
    to mind when I read through this.
    
    diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
    index f744b05b55..1f828d386a 100644
    --- a/doc/src/sgml/ref/grant.sgml
    +++ b/doc/src/sgml/ref/grant.sgml
    @@ -267,8 +267,14 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
     
       <para>
        If <literal>GRANTED BY</literal> is specified, the grant is recorded as
    -   having been done by the specified role.  Only database superusers may
    -   use this option, except when it names the same role executing the command.
    +   having been done by the specified role. A user can only attribute a grant
    +   to another role if they possess the privileges of that role. A role can
    +   only be recorded as a grantor if has <literal>ADMIN OPTION</literal> on
    
    Should be: if they have
    
    +   a role or is the bootstrap superuser. When a grant is recorded as having
    
    on *that* role seems like it'd be better.  And maybe 'or if they are the
    bootstrap superuser'?
    
    diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
    index 258943094a..8ab2fecf3a 100644
    --- a/src/backend/commands/user.c
    +++ b/src/backend/commands/user.c
    @@ -805,11 +842,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
     		if (stmt->action == +1) /* add members to role */
     			AddRoleMems(rolename, roleid,
     						rolemembers, roleSpecsToIds(rolemembers),
    -						GetUserId(), false);
    +						InvalidOid, false);
     		else if (stmt->action == -1)	/* drop members from role */
     			DelRoleMems(rolename, roleid,
     						rolemembers, roleSpecsToIds(rolemembers),
    -						false);
    +						InvalidOid, false, DROP_RESTRICT);	/* XXX sketchy - hint
    +															 * may mislead */
     	}
    
    This comment seems a little concerning..?  Also isn't very clear.
    
    @@ -1027,7 +1065,7 @@ DropRole(DropRoleStmt *stmt)
     
     		while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
     		{
    -			Form_pg_auth_members	authmem_form;
    +			Form_pg_auth_members authmem_form;
     
     			authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
     			deleteSharedDependencyRecordsFor(AuthMemRelationId,
    
    Some random whitespace changes that seems a bit odd given that they
    should have been already correct thanks to pgindent- will these end up
    just getting undone again?
    
    @@ -1543,14 +1578,94 @@ AddRoleMems(const char *rolename, Oid roleid,
     					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
     					 errmsg("role \"%s\" is a member of role \"%s\"",
     							rolename, get_rolespec_name(memberRole))));
    +	}
    +
    +	/*
    +	 * Disallow attempts to grant ADMIN OPTION back to a user who granted it
    +	 * to you, similar to what check_circularity does for ACLs. We want the
    +	 * chains of grants to remain acyclic, so that it's always possible to use
    +	 * REVOKE .. CASCADE to clean up all grants that depend on the one being
    +	 * revoked.
    +	 *
    +	 * NB: This check might look redundant with the check for membership
    +	 * loops above, but it isn't. That's checking for role-member loop (e.g.
    +	 * A is a member of B and B is a member of A) while this is checking for
    +	 * a member-grantor loop (e.g. A gave ADMIN OPTION to X to B and now B, who
    +	 * has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION
    +	 * on X back to A).
    +	 */
    
    With this exact scenario, wouldn't it just be a no-op as A must have
    ADMIN OPTION already on X?  The spec says that no cycles of role
    authorizations are allowed.  Presumably we'd continue this for other
    GRANT'able things which can be further GRANT'd (should we add them) in
    the future?  Just trying to think ahead a bit here in case it's
    worthwhile.  Those would likely be ABC WITH GRANT OPTION too, right?
    
    +	if (admin_opt && grantorId != BOOTSTRAP_SUPERUSERID)
    +	{
    +		CatCList   *memlist;
    +		RevokeRoleGrantAction *actions;
    +		int			i;
    +
    +		/* Get the list of members for this role. */
    +		memlist = SearchSysCacheList1(AUTHMEMROLEMEM,
    +									  ObjectIdGetDatum(roleid));
    +
    +		/*
    +		 * Figure out what would happen if we removed all existing grants to
    +		 * every role to which we've been asked to make a new grant.
    +		 */
    +		actions = initialize_revoke_actions(memlist);
    +		foreach(iditem, memberIds)
    +		{
    +			Oid			memberid = lfirst_oid(iditem);
    +
    +			if (memberid == BOOTSTRAP_SUPERUSERID)
    +				ereport(ERROR,
    +						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
    +						 errmsg("grants with admin options cannot be circular")));
    +			plan_member_revoke(memlist, actions, memberid);
    +		}
    
    I don't see a regression test added which produces the above error
    message.  The memberid == BOOTSTRAP_SUPERUSERID seems odd too?
    
    +		/*
    +		 * If the result would be that the grantor role would no longer have
    +		 * the ability to perform the grant, then the proposed grant would
    +		 * create a circularity.
    +		 */
    +		for (i = 0; i < memlist->n_members; ++i)
    +		{
    +			HeapTuple	authmem_tuple;
    +			Form_pg_auth_members authmem_form;
    +
    +			authmem_tuple = &memlist->members[i]->tuple;
    +			authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
    +
    +			if (actions[i] == RRG_NOOP &&
    +				authmem_form->member == grantorId &&
    +				authmem_form->admin_option)
    +				break;
    +		}
    +		if (i >= memlist->n_members)
    +			ereport(ERROR,
    +					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
    +					 errmsg("admin options cannot be granted back to your own grantor")));
    
    I do see this in the regression tests.  There though, the GRANTs are
    being performed by someone else, so saying 'your' isn't quite right.
    I'm trying to get this review out sooner than later and so I might be
    missing something, but looking at the regression test for this and these
    error messages, feels like the 'circular' error message makes more sense
    than the 'your own grantor' message that actually ends up being
    returned in that regression test.
    
    @@ -1637,17 +1737,22 @@ AddRoleMems(const char *rolename, Oid roleid,
      * roleid: OID of role to del from
      * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
      * memberIds: OIDs of roles to del
    + * grantorId: who is revoking the membership
      * admin_opt: remove admin option only?
      */
     static void
     DelRoleMems(const char *rolename, Oid roleid,
     			List *memberSpecs, List *memberIds,
    -			bool admin_opt)
    +			Oid grantorId, bool admin_opt, DropBehavior behavior)
    
    
    The comment above DropRoleMems missed adding a description for
    the 'behavior' parameter.
    
    @@ -1669,40 +1774,69 @@ DelRoleMems(const char *rolename, Oid roleid,
    +	/*
    +	 * We may need to recurse to dependent privileges if DROP_CASCADE was
    +	 * specified, or refuse to perform the operation if dependent privileges
    +	 * exist and DROP_RECURSE was specified. plan_single_revoke() will
    +	 * figure out what to do with each catalog tuple.
    +	 */
    
    Pretty sure that should be DROP_RESTRICT, not DROP_RECURSE.
    
    +/*
    + * Sanity-check, or infer, the grantor for a GRANT or REVOKE statement
    + * targeting a role.
    + *
    + * The grantor must always be either a role with ADMIN OPTION on the role in
    + * which membership is being granted, or the bootstrap superuser. This is
    + * similar to the restriction enforced by select_best_grantor, except that
    + * roles don't have owners, so we regard the bootstrap superuser as the
    + * implicit owner.
    + *
    + * The return value is the OID to be regarded as the grantor when executing
    + * the operation.
    + */
    +static Oid
    +check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant)
    
    As this also does some permission checks, it seems like it'd be good to
    mention that in the function description.  Maybe also in the places that
    call into this function with the expectation that the privilege check
    will be taken care of here.
    
    Indeed, I wonder if maybe we should really split this function into two
    as the "give me what the best grantor is" is a fair bit different from
    "check if this user has permission to grant as this role".  As noted in
    the comments, the current function also only does privilege checking in
    some case- when InvalidOid is passed in we've also already done
    permissions checks to make sure that the GRANT will succeed.
    
    +/*
    + * Initialize an array of RevokeRoleGrantAction objects.
    + *
    + * 'memlist' should be a list of all grants for the target role.
    + *
    + * We here construct an array indicating that no actions are to be performed;
    + * that is, every element is intiially RRG_NOOP.
    + */
    
    "We here construct" seems odd wording to me.  Maybe "Here we construct"?
    
    Thanks,
    
    Stephen
    
  184. Re: pg_auth_members.grantor is bunk

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-07-31T18:33:54Z

    Stephen Frost <sfrost@snowman.net> writes:
    > * Robert Haas (robertmhaas@gmail.com) wrote:
    >> OK, so I fixed that, and also updated the documentation a bit more. I
    >> think these patches are basically done, and I'd like to get them
    >> committed before too much more time goes by, because I have other
    >> things that depend on this which I also want to get done for this
    >> release. Anybody object?
    
    > Thanks for working on this.
    
    Indeed.  I've not read the patch, but I just wanted to mention that
    the cfbot shows it as failing regression tests on all platforms.
    Possibly a conflict with some recent commit?
    
    			regards, tom lane
    
    
    
    
  185. Re: pg_auth_members.grantor is bunk

    David G. Johnston <david.g.johnston@gmail.com> — 2022-07-31T18:43:48Z

    On Sun, Jul 31, 2022 at 11:18 AM Stephen Frost <sfrost@snowman.net> wrote:
    
    > Greetings,
    >
    > * Robert Haas (robertmhaas@gmail.com) wrote:
    > > On Tue, Jul 26, 2022 at 12:46 PM Robert Haas <robertmhaas@gmail.com>
    > wrote:
    >
    > +       }
    > +
    > +       /*
    > +        * Disallow attempts to grant ADMIN OPTION back to a user who
    > granted it
    > +        * to you, similar to what check_circularity does for ACLs. We
    > want the
    > +        * chains of grants to remain acyclic, so that it's always
    > possible to use
    > +        * REVOKE .. CASCADE to clean up all grants that depend on the one
    > being
    > +        * revoked.
    > +        *
    > +        * NB: This check might look redundant with the check for
    > membership
    > +        * loops above, but it isn't. That's checking for role-member loop
    > (e.g.
    > +        * A is a member of B and B is a member of A) while this is
    > checking for
    > +        * a member-grantor loop (e.g. A gave ADMIN OPTION to X to B and
    > now B, who
    > +        * has no other source of ADMIN OPTION on X, tries to give ADMIN
    > OPTION
    > +        * on X back to A).
    > +        */
    >
    > With this exact scenario, wouldn't it just be a no-op as A must have
    > ADMIN OPTION already on X?  The spec says that no cycles of role
    > authorizations are allowed.
    
    
    Role A must have admin option for X to grant membership in X (with or
    without admin option) to B.  But that doesn't preclude A from getting
    another admin option from someone else.  That someone else cannot be
    someone to whom they gave admin option to however. So B cannot grant admin
    option back to A but role P could if it was basically a sibling of A (i.e.,
    both getting their initial admin option from someone else).
    
    If they do have admin option twice it should be possible to drop one of
    them, the prohibition should be on dropping the only admin option
    permission a role has for some other role.  The commit message for 2
    contemplates this though I haven't gone through the revocation code in
    detail.
    
    
    > I'm trying to get this review out sooner than later and so I might be
    > missing something, but looking at the regression test for this and these
    > error messages, feels like the 'circular' error message makes more sense
    > than the 'your own grantor' message that actually ends up being
    > returned in that regression test.
    >
    
    Having a more specific error seems reasonable, faster to track down what
    the problem is.
    
    I think that the whole graph dynamic of this might need some presentation
    work (messages and/or psql and/or functions) ; but assuming the errors are
    handled improved messages and/or presentation of graphs can be a separate
    enhancement.
    
    David J.
    
  186. Re: pg_auth_members.grantor is bunk

    Stephen Frost <sfrost@snowman.net> — 2022-07-31T21:19:34Z

    Greetings,
    
    On Sun, Jul 31, 2022 at 11:44 David G. Johnston <david.g.johnston@gmail.com>
    wrote:
    
    > On Sun, Jul 31, 2022 at 11:18 AM Stephen Frost <sfrost@snowman.net> wrote:
    >
    >> Greetings,
    >>
    >> * Robert Haas (robertmhaas@gmail.com) wrote:
    >> > On Tue, Jul 26, 2022 at 12:46 PM Robert Haas <robertmhaas@gmail.com>
    >> wrote:
    >>
    >> +       }
    >> +
    >> +       /*
    >> +        * Disallow attempts to grant ADMIN OPTION back to a user who
    >> granted it
    >> +        * to you, similar to what check_circularity does for ACLs. We
    >> want the
    >> +        * chains of grants to remain acyclic, so that it's always
    >> possible to use
    >> +        * REVOKE .. CASCADE to clean up all grants that depend on the
    >> one being
    >> +        * revoked.
    >> +        *
    >> +        * NB: This check might look redundant with the check for
    >> membership
    >> +        * loops above, but it isn't. That's checking for role-member
    >> loop (e.g.
    >> +        * A is a member of B and B is a member of A) while this is
    >> checking for
    >> +        * a member-grantor loop (e.g. A gave ADMIN OPTION to X to B and
    >> now B, who
    >> +        * has no other source of ADMIN OPTION on X, tries to give ADMIN
    >> OPTION
    >> +        * on X back to A).
    >> +        */
    >>
    >> With this exact scenario, wouldn't it just be a no-op as A must have
    >> ADMIN OPTION already on X?  The spec says that no cycles of role
    >> authorizations are allowed.
    >
    >
    I’ve realized that what I hadn’t been contemplating here is actually that
    the GRANT from B to A for X wouldn’t be redundant because grantor is part
    of the key (A got the right from someone else, but this would be giving it
    to A from B and therefore would be distinct and would also create a loop
    which is no good).  Haven’t got a good idea on how to improve on the
    comment based off of that though it still feels like it could be clearer.
    If I think of something, I’ll share it.
    
    Role A must have admin option for X to grant membership in X (with or
    > without admin option) to B.  But that doesn't preclude A from getting
    > another admin option from someone else.  That someone else cannot be
    > someone to whom they gave admin option to however. So B cannot grant admin
    > option back to A but role P could if it was basically a sibling of A (i.e.,
    > both getting their initial admin option from someone else).
    >
    
    Right but that wasn’t what I had been trying to get at above.
    
    If they do have admin option twice it should be possible to drop one of
    > them, the prohibition should be on dropping the only admin option
    > permission a role has for some other role.  The commit message for 2
    > contemplates this though I haven't gone through the revocation code in
    > detail.
    >
    
    Yes, think I agree with this also- if A has been given the WITH ADMIN right
    from Q and P to GRANT X to other roles, and uses that to GRANT X to B, then
    the GRANT of X to B should be retained even if Q decides to revoke their
    GRANT as A still has the right from P. If both remove the right, however,
    either B should lose the right (if CASCADE was passed in) or an error
    should be returned saying that there’s a dependent GRANT and CASCADE wasn’t
    given.
    
    I'm trying to get this review out sooner than later and so I might be
    >> missing something, but looking at the regression test for this and these
    >> error messages, feels like the 'circular' error message makes more sense
    >> than the 'your own grantor' message that actually ends up being
    >> returned in that regression test.
    >>
    >
    > Having a more specific error seems reasonable, faster to track down what
    > the problem is.
    >
    
    Yeah, but also making sure that all the error messages we have in this area
    are in the regression test output would be good.
    
    Makes me wonder if we might try to figure out a way to globally check for
    that. I suppose one could review coverage.p.o for any ereport() calls that
    aren’t ever called.  I wonder what that would turn up.
    
    I think that the whole graph dynamic of this might need some presentation
    > work (messages and/or psql and/or functions) ; but assuming the errors are
    > handled improved messages and/or presentation of graphs can be a separate
    > enhancement.
    >
    
    Yes, we can further improve this later too but that doesn’t mean we should
    just commit this as-is when some deficiencies have been pointed out. If the
    only comments were “would be good to improve this error message but I
    haven’t got a great idea how”, then sure, but there were other items
    pointed out which were clear corrections and we should make sure to cover
    in the regression tests all these scenarios that we are checking for and
    erroring on, lest we end up breaking them unintentionally later.
    
    Thanks,
    
    Stephen
    
    >
    
  187. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-01T17:00:43Z

    On Sun, Jul 31, 2022 at 2:18 PM Stephen Frost <sfrost@snowman.net> wrote:
    > Thanks for working on this.
    
    Thanks for the review.
    
    > > Previously, only the superuser could specify GRANTED BY with a user
    > > other than the current user. Relax that rule to allow the grantor
    > > to be any role whose privileges the current user posseses. This
    > > doesn't improve compatibility with what we do for other object types,
    > > where support for GRANTED BY is entirely vestigial, but it makes this
    > > feature more usable and seems to make sense to change at the same time
    > > we're changing related behaviors.
    >
    > Presumably the GRANTED BY user in this case still has to have the
    > ability to have performed the GRANT themselves?  Looks that way below
    > and it's just the commit message, but was the first question that came
    > to mind when I read through this.
    
    Yes. The previous paragraph in this commit message seems to cover this
    point pretty thoroughly.
    
    >    <para>
    >     If <literal>GRANTED BY</literal> is specified, the grant is recorded as
    > -   having been done by the specified role.  Only database superusers may
    > -   use this option, except when it names the same role executing the command.
    > +   having been done by the specified role. A user can only attribute a grant
    > +   to another role if they possess the privileges of that role. A role can
    > +   only be recorded as a grantor if has <literal>ADMIN OPTION</literal> on
    >
    > Should be: if they have
    >
    > +   a role or is the bootstrap superuser. When a grant is recorded as having
    >
    > on *that* role seems like it'd be better.  And maybe 'or if they are the
    > bootstrap superuser'?
    
    Will fix.
    
    > diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
    > index 258943094a..8ab2fecf3a 100644
    > --- a/src/backend/commands/user.c
    > +++ b/src/backend/commands/user.c
    > @@ -805,11 +842,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
    >                 if (stmt->action == +1) /* add members to role */
    >                         AddRoleMems(rolename, roleid,
    >                                                 rolemembers, roleSpecsToIds(rolemembers),
    > -                                               GetUserId(), false);
    > +                                               InvalidOid, false);
    >                 else if (stmt->action == -1)    /* drop members from role */
    >                         DelRoleMems(rolename, roleid,
    >                                                 rolemembers, roleSpecsToIds(rolemembers),
    > -                                               false);
    > +                                               InvalidOid, false, DROP_RESTRICT);      /* XXX sketchy - hint
    > +                                                                                                                        * may mislead */
    >         }
    >
    > This comment seems a little concerning..?  Also isn't very clear.
    
    Oh right. That was a note to myself to look into that more. And then I
    didn't. I'll look into that more and report back.
    
    > @@ -1027,7 +1065,7 @@ DropRole(DropRoleStmt *stmt)
    >
    >                 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
    >                 {
    > -                       Form_pg_auth_members    authmem_form;
    > +                       Form_pg_auth_members authmem_form;
    >
    >                         authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
    >                         deleteSharedDependencyRecordsFor(AuthMemRelationId,
    >
    > Some random whitespace changes that seems a bit odd given that they
    > should have been already correct thanks to pgindent- will these end up
    > just getting undone again?
    
    Will fix.
    
    > @@ -1543,14 +1578,94 @@ AddRoleMems(const char *rolename, Oid roleid,
    >                                         (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    >                                          errmsg("role \"%s\" is a member of role \"%s\"",
    >                                                         rolename, get_rolespec_name(memberRole))));
    > +       }
    > +
    > +       /*
    > +        * Disallow attempts to grant ADMIN OPTION back to a user who granted it
    > +        * to you, similar to what check_circularity does for ACLs. We want the
    > +        * chains of grants to remain acyclic, so that it's always possible to use
    > +        * REVOKE .. CASCADE to clean up all grants that depend on the one being
    > +        * revoked.
    > +        *
    > +        * NB: This check might look redundant with the check for membership
    > +        * loops above, but it isn't. That's checking for role-member loop (e.g.
    > +        * A is a member of B and B is a member of A) while this is checking for
    > +        * a member-grantor loop (e.g. A gave ADMIN OPTION to X to B and now B, who
    > +        * has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION
    > +        * on X back to A).
    > +        */
    >
    > With this exact scenario, wouldn't it just be a no-op as A must have
    > ADMIN OPTION already on X?  The spec says that no cycles of role
    > authorizations are allowed.  Presumably we'd continue this for other
    > GRANT'able things which can be further GRANT'd (should we add them) in
    > the future?  Just trying to think ahead a bit here in case it's
    > worthwhile.  Those would likely be ABC WITH GRANT OPTION too, right?
    
    I don't believe there's anything novel here - at least there isn't
    supposed to be. Here's the equivalent with table privileges:
    
    rhaas=# create table t1();
    CREATE TABLE
    rhaas=# create role foo;
    CREATE ROLE
    rhaas=# create role bar;
    CREATE ROLE
    rhaas=# grant select on t1 to foo with grant option;
    GRANT
    rhaas=# set role foo;
    SET
    rhaas=> grant select on t1 to bar with grant option;
    GRANT
    rhaas=> set role bar;
    SET
    rhaas=> grant select on t1 to foo with grant option;
    ERROR:  grant options cannot be granted back to your own grantor
    
    
    > +       if (admin_opt && grantorId != BOOTSTRAP_SUPERUSERID)
    > +       {
    > +               CatCList   *memlist;
    > +               RevokeRoleGrantAction *actions;
    > +               int                     i;
    > +
    > +               /* Get the list of members for this role. */
    > +               memlist = SearchSysCacheList1(AUTHMEMROLEMEM,
    > +                                                                         ObjectIdGetDatum(roleid));
    > +
    > +               /*
    > +                * Figure out what would happen if we removed all existing grants to
    > +                * every role to which we've been asked to make a new grant.
    > +                */
    > +               actions = initialize_revoke_actions(memlist);
    > +               foreach(iditem, memberIds)
    > +               {
    > +                       Oid                     memberid = lfirst_oid(iditem);
    > +
    > +                       if (memberid == BOOTSTRAP_SUPERUSERID)
    > +                               ereport(ERROR,
    > +                                               (errcode(ERRCODE_INVALID_GRANT_OPERATION),
    > +                                                errmsg("grants with admin options cannot be circular")));
    > +                       plan_member_revoke(memlist, actions, memberid);
    > +               }
    >
    > I don't see a regression test added which produces the above error
    > message.  The memberid == BOOTSTRAP_SUPERUSERID seems odd too?
    
    Do we guarantee that the regression tests are running as the bootstrap
    superuser, or just as some superuser? I am a bit reluctant to add a
    regression test that assumes the former unless we're assuming it
    already. For 'make check' it doesn't matter but 'make installcheck' is
    another story.
    
    The memberid == BOOTSTRAP_SUPERUSERID case is very much intentional.
    The code will detect any loops in the catalog, but the implicit grant
    to the bootstrap superuser doesn't exist in the catalog
    representation, so it needs a separate check. I think I should sync
    the two error messages though, i.e. this should say "admin options
    cannot be granted back to your own grantor" like the other one just
    below.
    
    > I do see this in the regression tests.  There though, the GRANTs are
    > being performed by someone else, so saying 'your' isn't quite right.
    > I'm trying to get this review out sooner than later and so I might be
    > missing something, but looking at the regression test for this and these
    > error messages, feels like the 'circular' error message makes more sense
    > than the 'your own grantor' message that actually ends up being
    > returned in that regression test.
    
    I think it's a bit off too, but I didn't invent it. See check_circularity():
    
        if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_GRANT_OPERATION),
                     errmsg("grant options cannot be granted back to your
    own grantor")));
    
    Looks like Tom Lane, vintage 2004, 4b2dafcc0b1a579ef5daaa2728223006d1ff98e9.
    
    > The comment above DropRoleMems missed adding a description for
    > the 'behavior' parameter.
    
    Will fix.
    
    > @@ -1669,40 +1774,69 @@ DelRoleMems(const char *rolename, Oid roleid,
    > +       /*
    > +        * We may need to recurse to dependent privileges if DROP_CASCADE was
    > +        * specified, or refuse to perform the operation if dependent privileges
    > +        * exist and DROP_RECURSE was specified. plan_single_revoke() will
    > +        * figure out what to do with each catalog tuple.
    > +        */
    >
    > Pretty sure that should be DROP_RESTRICT, not DROP_RECURSE.
    
    I'm pretty sure you are right.
    
    > +/*
    > + * Sanity-check, or infer, the grantor for a GRANT or REVOKE statement
    > + * targeting a role.
    > + *
    > + * The grantor must always be either a role with ADMIN OPTION on the role in
    > + * which membership is being granted, or the bootstrap superuser. This is
    > + * similar to the restriction enforced by select_best_grantor, except that
    > + * roles don't have owners, so we regard the bootstrap superuser as the
    > + * implicit owner.
    > + *
    > + * The return value is the OID to be regarded as the grantor when executing
    > + * the operation.
    > + */
    > +static Oid
    > +check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant)
    >
    > As this also does some permission checks, it seems like it'd be good to
    > mention that in the function description.  Maybe also in the places that
    > call into this function with the expectation that the privilege check
    > will be taken care of here.
    >
    > Indeed, I wonder if maybe we should really split this function into two
    > as the "give me what the best grantor is" is a fair bit different from
    > "check if this user has permission to grant as this role".  As noted in
    > the comments, the current function also only does privilege checking in
    > some case- when InvalidOid is passed in we've also already done
    > permissions checks to make sure that the GRANT will succeed.
    
    I'll think about this some more, but I don't want to commit to
    changing it very much. IMHO, the whole split into AddRoleMems() and
    DelRoleMems() for what is basically the same operation seems pretty
    dubious, but this commit's intended purpose is to clean up the
    behavior rather than to rewrite the code. So I left the existing logic
    in AddRoleMems() and DelRoleMems() alone, and when I realized I needed
    something else that was mostly common to both, I made this function
    instead of duplicating the logic in two places. I realize there are
    other ways that it could be split up, and maybe some of those are
    better in theory, but they'd likely also expand the scope of the patch
    to things that it doesn't quite need to touch. I'm not real keen to go
    there. That can be done later, in a separate patch, or never, and I
    don't think we'll really be any the worse for it.
    
    > +/*
    > + * Initialize an array of RevokeRoleGrantAction objects.
    > + *
    > + * 'memlist' should be a list of all grants for the target role.
    > + *
    > + * We here construct an array indicating that no actions are to be performed;
    > + * that is, every element is intiially RRG_NOOP.
    > + */
    >
    > "We here construct" seems odd wording to me.  Maybe "Here we construct"?
    
    It seems completely fine to me, but I'll change it somehow to avoid
    annoying you. :-)
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  188. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-01T17:33:12Z

    On Sun, Jul 31, 2022 at 2:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Indeed.  I've not read the patch, but I just wanted to mention that
    > the cfbot shows it as failing regression tests on all platforms.
    > Possibly a conflict with some recent commit?
    
    I can't see this on cfbot - either I don't know how to use it
    properly, which is quite possible, or the results aren't showing up
    because of the close of the July CommitFest.
    
    I tried a rebase locally and it didn't seem to change anything
    material, not even context lines.
    
    Can you provide a link or something that I can look at?
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  189. Re: pg_auth_members.grantor is bunk

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-08-01T17:38:35Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Sun, Jul 31, 2022 at 2:34 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> Indeed.  I've not read the patch, but I just wanted to mention that
    >> the cfbot shows it as failing regression tests on all platforms.
    >> Possibly a conflict with some recent commit?
    
    > I can't see this on cfbot - either I don't know how to use it
    > properly, which is quite possible, or the results aren't showing up
    > because of the close of the July CommitFest.
    
    I think the latter --- the cfbot thinks the July CF is no longer relevant,
    but Jacob hasn't yet moved your patches forward.  You could wait for
    him to do that, or do it yourself.
    
    (Probably our nonexistent SOP manual for CFMs ought to say "don't
    close the old CF till you've moved everything forward".)
    
    			regards, tom lane
    
    
    
    
  190. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-01T19:51:03Z

    On Mon, Aug 1, 2022 at 1:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> I think the latter --- the cfbot thinks the July CF is no longer relevant,
    > but Jacob hasn't yet moved your patches forward.  You could wait for
    > him to do that, or do it yourself.
    
    Done. New patches attached.
    
    Changes in v4, for 0001:
    
    - Typo fix.
    - Whitespace fixes.
    
    Changes in v4, for 0002:
    
    - Remove "XXX sketchy" comment because the thing in question turns out
    not to be sketchy. It has to do with the behavior of ALTER GROUP ..
    DROP USER and, having investigated the situation, I think the
    messaging is clear enough.
    - But just to be sure, add a note to the ALTER GROUP documentation to
    try to make things more clear.
    - Wording fixes to the "If <literal>GRANTED BY</literal> is
    specified..." paragraph of the GRANT documentation. I reworded this a
    bit more extensively than what Stephen proposed. Hopefully this is
    clearer now, or at least no longer missing any words.
    - Change message to "admin option cannot be granted back to your own
    grantor". The choice of message is intended to be consistent with the
    existing message "grant options cannot be granted back to your own
    grantor," but while there's one grant option per privilege, there's
    only one admin option. Stephen suggested adopting a message that I had
    meant to take out of the version I posted, but which ended up
    surviving in one place, "grants with admin options cannot be
    circular". And we could still decide to do something like that, but my
    enthusiasm for that direction was considerably reduced when I realized
    that "circular" is not very clear at all, because there are multiple
    kinds of circularities (role-member, member-grantor).
    - Fix comment to say DROP_RESTRICT instead of DROP_RECURSE.
    - Make the comment for check_role_grantor() longer so that it can
    better explain itself.
    - Rephrase part of the header comment for initialize_revoke_actions()
    because Stephen found it awkward.
    - Whitespace fixes.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  191. Re: pg_auth_members.grantor is bunk

    Jacob Champion <jchampion@timescale.com> — 2022-08-02T17:48:53Z

    On Mon, Aug 1, 2022 at 10:38 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > > I can't see this on cfbot - either I don't know how to use it
    > > properly, which is quite possible, or the results aren't showing up
    > > because of the close of the July CommitFest.
    >
    > I think the latter --- the cfbot thinks the July CF is no longer relevant,
    > but Jacob hasn't yet moved your patches forward.  You could wait for
    > him to do that, or do it yourself.
    >
    > (Probably our nonexistent SOP manual for CFMs ought to say "don't
    > close the old CF till you've moved everything forward".)
    
    Sorry about that. I've made a note to add this to the manual later.
    
    --Jacob
    
    
    
    
  192. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-10T20:28:45Z

    On Mon, Aug 1, 2022 at 3:51 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > On Mon, Aug 1, 2022 at 1:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > >> I think the latter --- the cfbot thinks the July CF is no longer relevant,
    > > but Jacob hasn't yet moved your patches forward.  You could wait for
    > > him to do that, or do it yourself.
    >
    > Done. New patches attached.
    
    Well, CI isn't happy with this, and for good reason:
    
     ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate
    -NOTICE:  role "regress_priv_user2" has already been granted
    membership in role "regress_priv_group2" by role "rhaas"
    +NOTICE:  role "regress_priv_user2" has already been granted
    membership in role "regress_priv_group2" by role "postgres"
    
    The problem here is that I revised the error message to include the
    name of the grantor, since that's now a part of the identity of the
    grant. It would be misleading to say, as we did previously...
    
    NOTICE:  role "regress_priv_user2" is already a member of role
    "regress_priv_group2"
    
    ...because them being in the group isn't relevant so much as them
    being in the group by means of the same grantor. However, I suspect
    that I can't persuade all of you that we should hard-code the name of
    the bootstrap superuser as "rhaas", so this test case needs some
    alteration. I found, however, that the original intent of the test
    case couldn't be preserved with the patch as written, because when you
    grant membership in one role to another role as the superuser or a
    CREATEROLE user, the grant is attributed to the bootstrap superuser,
    whose name is variable, as this test failure shows. Therefore, to fix
    the test, I needed to use ALTER GROUP as a non-CREATEROLE user, some
    user created as part of the test, for the results to be stable. But
    that was impossible, because even though "GRANT user_name TO
    group_name" requires *either* CREATEROLE *or* ADMIN OPTION on the
    group, the equivalent command "ALTER GROUP group_name ADD USER
    user_name" requires specifically CREATEROLE.
    
    I debated whether to fix that inconsistency or just remove this test
    case and eventually came down on the side of fixing the inconsistency,
    so the attached version does it that way.
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com
    
  193. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-18T17:26:08Z

    On Wed, Aug 10, 2022 at 4:28 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > Well, CI isn't happy with this, and for good reason:
    
    CI is happier with this version, so I've committed 0001. If no major
    problems emerge, I'll proceed with 0002 as well.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  194. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-08-22T15:47:00Z

    On Thu, Aug 18, 2022 at 1:26 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > On Wed, Aug 10, 2022 at 4:28 PM Robert Haas <robertmhaas@gmail.com> wrote:
    > > Well, CI isn't happy with this, and for good reason:
    >
    > CI is happier with this version, so I've committed 0001. If no major
    > problems emerge, I'll proceed with 0002 as well.
    
    Done.
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  195. Re: pg_auth_members.grantor is bunk

    Jeff Davis <pgsql@j-davis.com> — 2022-09-01T20:34:06Z

    On Mon, 2022-08-22 at 11:47 -0400, Robert Haas wrote:
    > On Thu, Aug 18, 2022 at 1:26 PM Robert Haas <robertmhaas@gmail.com>
    > wrote:
    > > On Wed, Aug 10, 2022 at 4:28 PM Robert Haas <robertmhaas@gmail.com>
    > > wrote:
    > > > Well, CI isn't happy with this, and for good reason:
    > > 
    > > CI is happier with this version, so I've committed 0001. If no
    > > major
    > > problems emerge, I'll proceed with 0002 as well.
    > 
    > Done.
    
    It's still on the CF, so I took a look.
    
    There's still some weirdness around superusers:
    
    1. "GRANTED BY current_user" differs from not specifying "GRANTED BY"
    at all.
    
      a. With GRANTED BY current_user, weird because current_user is a
    superuser:
    
         CREATE USER su1 SUPERUSER;
         CREATE ROLE u1;
         CREATE ROLE u2;
         \c - su1
         GRANT u2 TO u1 GRANTED BY current_user;
         ERROR:  grantor must have ADMIN OPTION on "u2"
    
      b. Without GRANTED BY:
    
         CREATE USER su1 SUPERUSER;
         CREATE ROLE u1;
         CREATE ROLE u2;
         \c - su1
         GRANT u2 TO u1;
         -- grantor is bootstrap superuser
    
    2. Grantor can depend on the path to get there:
    
      a. Already superuser:
    
         CREATE USER su1 SUPERUSER;
         CREATE ROLE u1;
         CREATE ROLE u2;
         GRANT u2 TO su1 WITH ADMIN OPTION;
         \c - su1
         GRANT u2 TO u1;
         -- grantor is bootstrap superuser
    
      b. Becomes superuser after GRANT:
    
         CREATE USER su1;
         CREATE ROLE u1;
         CREATE ROLE u2;
         GRANT u2 TO su1 WITH ADMIN OPTION;
         \c - su1
         GRANT u2 TO u1;
         \c - bootstrap_superuser
         ALTER ROLE su1 SUPERUSER;
         -- grantor is su1
    
    3. Another case where "GRANTED BY current_user" differs from no
    "GRANTED BY" at all, with slightly different consequences:
    
      a. GRANTED BY current_user, throws error:
    
         CREATE USER su1 SUPERUSER;
         CREATE ROLE u1;
         CREATE ROLE u2;
         GRANT u2 TO su1 WITH ADMIN OPTION;
         \c - su1
         GRANT u2 TO u1 GRANTED BY current_user;
         -- grantor is su1
         \c - bootstrap_superuser
         REVOKE ADMIN OPTION FOR u2 FROM su1;
         ERROR:  dependent privileges exist
    
      b. No GRANTED BY, no error:
    
         CREATE USER su1 SUPERUSER;
         CREATE ROLE u1;
         CREATE ROLE u2;
         GRANT u2 TO su1 WITH ADMIN OPTION;
         \c - su1
         GRANT u2 TO u1;
         -- grantor is bootstrap superuser
         \c - boostrap_superuser
         REVOKE ADMIN OPTION FOR u2 FROM su1;
    
    
    
    We seem to be trying very hard to satisfy two things that seem
    impossible to satisfy:
    
       i. "ALTER ROLE ... NOSUPERUSER" must always succeed, and probably
    execute quickly, too.
       ii. We want to maintain catalog invariants that are based, in part,
    on roles having superuser privileges or not.
    
    The hacks we are using to try to make this work are just that: hacks.
    And it's all to satisfy a fairly rare case: removing superuser
    privileges and expecting the catalogs to be consistent.
    
    I think we'd be better off without these hacks. I'm not sure exactly
    how, but the benefit doesn't seem to be worth the cost. Some
    alternative ideas:
    
      * Have a "safe" version of removing superuser that can error or
    cascade, and an "unsafe" version that always succeeds but might leave
    inconsistent catalogs.
      * Ignore the problems with removing superuser, but issue a WARNING
      * Superusers would auto-grant themselves the privileges that a normal
    user would need to do something before doing it. For instance, if a
    superuser did "GRANT u2 TO u1", it would first automatically issue a
    "GRANT u2 TO current_user WITH ADMIN OPTION GRANTED BY
    bootstrap_superuser", then do the grant normally. If the superuser
    privileges are removed, then the catalogs would still be consistent.
    This is a new idea and I didn't think it through very carefully, but
    might be an interesting approach.
    
    Also, it would be nice to have REASSIGN OWNED work with grants, perhaps
    by adding a "WITH[OUT] GRANT" or something.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
    
    
    
    
  196. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-09-02T13:30:24Z

    Thanks for having a look.
    
    On Thu, Sep 1, 2022 at 4:34 PM Jeff Davis <pgsql@j-davis.com> wrote:
    > There's still some weirdness around superusers:
    >
    > 1. "GRANTED BY current_user" differs from not specifying "GRANTED BY"
    > at all.
    
    Yes. I figured that, when GRANTED BY is not specified, it is OK to
    infer a valid grantor, but if it is specified, it does not seem right
    to infer a grantor other than the one specified. Admittedly, this case
    is without precedent elsewhere in the system, because nobody has made
    GRANTED BY work for other object types, outside of trivial cases.
    Still, it seems like the right behavior to me.
    
    > 2. Grantor can depend on the path to get there:
    >
    >   a. Already superuser:
    >
    >      CREATE USER su1 SUPERUSER;
    >      CREATE ROLE u1;
    >      CREATE ROLE u2;
    >      GRANT u2 TO su1 WITH ADMIN OPTION;
    >      \c - su1
    >      GRANT u2 TO u1;
    >      -- grantor is bootstrap superuser
    >
    >   b. Becomes superuser after GRANT:
    >
    >      CREATE USER su1;
    >      CREATE ROLE u1;
    >      CREATE ROLE u2;
    >      GRANT u2 TO su1 WITH ADMIN OPTION;
    >      \c - su1
    >      GRANT u2 TO u1;
    >      \c - bootstrap_superuser
    >      ALTER ROLE su1 SUPERUSER;
    >      -- grantor is su1
    
    This also seems correct to me, and here I believe you could construct
    similar examples with other object types. We infer the grantor based
    on the state of the system at the time the grant was performed. We
    can't change our mind later even if things have changed that would
    cause us to make a different inference. In the case of a table, for
    example, consider:
    
    create role p1;
    create role p2;
    create role a;
    create table t1 (a int);
    create role b;
    grant select on table t1 to p1 with grant option;
    grant select on table t1 to p2 with grant option;
    grant p1 to a;
    set session authorization a;
    grant select on table t1 to b;
    
    At this point, b has SELECT permission on table t1 and the grantor of
    record is p1. But if you had done "GRANT p2 TO a" then the grantor of
    record would be p2 rather than p1. And you can still "REVOKE p1 FROM
    a;" and then "GRANT p2 to a;". As in your example, doing so won't
    change the grantor recorded for the grant already made.
    
    > 3. Another case where "GRANTED BY current_user" differs from no
    > "GRANTED BY" at all, with slightly different consequences:
    
    It's extremely difficult for me to imagine what other behavior would
    be sane here. In this example, the inferred best grantor is different
    from the current user, so forcing the grantor to be the current user
    changes the behavior. There are only two ways that anything different
    can happen: either we'd have to change the algorithm for inferring the
    best grantor, or we'd have to be willing to disregard the user's
    explicit specification that the grantor be the current user rather
    than somebody else.
    
    As to the first, the algorithm being used to select the best grantor
    here is analogous to the one we use for privileges on other object
    types, such as tables, namely, we prefer to create a grant that is not
    dependent on some other grant, rather than one that is. Maybe that's
    the best policy and maybe it isn't, but I can't see it being
    reasonable to have one policy for grants on tables, functions, etc.
    and another policy for grants on roles.
    
    As to the second, this is somewhat similar to the case you already
    raised in your example #1. However, in that case, the
    explicitly-specified grantor wasn't valid, so the grant failed. I
    don't think it's right to allow inference in the presence of an
    explicit specification, but if the consensus was that we really ought
    to make that case succeed, I suppose we could. Here, however, the
    explicitly-specified grantor *is a legal grantor*. I think it would be
    extremely surprising if we just ignored that and selected some other
    valid grantor instead.
    
    > We seem to be trying very hard to satisfy two things that seem
    > impossible to satisfy:
    >
    >    i. "ALTER ROLE ... NOSUPERUSER" must always succeed, and probably
    > execute quickly, too.
    >    ii. We want to maintain catalog invariants that are based, in part,
    > on roles having superuser privileges or not.
    >
    > The hacks we are using to try to make this work are just that: hacks.
    > And it's all to satisfy a fairly rare case: removing superuser
    > privileges and expecting the catalogs to be consistent.
    
    I guess I don't really agree with that view of it. The primary purpose
    of the patch was to make the handing of role grants consistent with
    the handling of grants on other object types. I did extend the
    existing functionality, because the GRANTED BY <whoever> clause works
    for role grants and does not work for other grants. However, that also
    worked for role grants before these patches, whereas it's never worked
    for other object types. So I chose to restrict that functionality as
    little as possible, and basically make it work, rather than removing
    it completely, which would have been the most consistent with what we
    do elsewhere.
    
    When you view this in the context of how other types of grants work,
    ALTER ROLE ... NOSUPERUSER isn't as much of a special case. Just as we
    want ALTER ROLE ... NOSUPERUSER to succeed quickly, we also insist
    that REVOKE role1 FROM role2 to succeed quickly. It isn't allowed to
    fail due to the existence of dependent privileges, because there
    aren't allowed to be any dependent privileges. GRANT role1 TO role2
    doesn't really give role2 the privileges of role1; what it does is
    allow role2 to act on behalf of role1. Similarly, ALTER ROLE ...
    SUPERUSER lets the target role act on behalf of any user at all,
    including the bootstrap superuser. In either case, actions are
    attributed to the user on behalf of whom they were performed, not the
    user who actually typed the command.
    
    As another example, consider a superuser (the bootstrap superuser or
    any other one) who executes GRANT SELECT ON some_random_table TO
    some_random_user. Who will be recorded as the grantor? The answer is
    that the table owner will be recorded as the grantor, because the
    table owner is the one who actually has permission to perform the
    operation. The superuser doesn't, except by virtue of their ability to
    act on behalf of any other user in the system. In most cases, that's
    just an academic distinction, because the question is only whether or
    not the operation can be performed, and not who has to perform it. But
    grants are different: it matters who does it, and when someone uses
    superuser powers or other special privileges to perform an operation,
    we have to ask on whose behalf they are acting.
    
    > I think we'd be better off without these hacks. I'm not sure exactly
    > how, but the benefit doesn't seem to be worth the cost. Some
    > alternative ideas:
    >
    >   * Have a "safe" version of removing superuser that can error or
    > cascade, and an "unsafe" version that always succeeds but might leave
    > inconsistent catalogs.
    >   * Ignore the problems with removing superuser, but issue a WARNING
    
    I don't like either of these. I think the fact that we have strong
    integrity constraints around who can be recorded as the grantor of a
    privilege is a good thing, and, again, the purpose of this patch was
    to bring role grants up to the level of other parts of the system.
    
    >   * Superusers would auto-grant themselves the privileges that a normal
    > user would need to do something before doing it. For instance, if a
    > superuser did "GRANT u2 TO u1", it would first automatically issue a
    > "GRANT u2 TO current_user WITH ADMIN OPTION GRANTED BY
    > bootstrap_superuser", then do the grant normally. If the superuser
    > privileges are removed, then the catalogs would still be consistent.
    > This is a new idea and I didn't think it through very carefully, but
    > might be an interesting approach.
    
    If we did this, we ought to do it for all object types, so that if a
    superuser grants privileges on a table they don't own, they implicitly
    grant themselves those privileges with grant option and then grant
    them to the requested recipient. I doubt that behavior change would be
    popular, and I bet somebody would complain about the SQL standard or
    something, but it seems more theoretically sound than the previous two
    ideas, because it doesn't just throw the idea of integrity constraints
    out the window.
    
    > Also, it would be nice to have REASSIGN OWNED work with grants, perhaps
    > by adding a "WITH[OUT] GRANT" or something.
    
    I thought about this, too. It's a bit tricky. Right now, DROP OWNED
    drops grants, but REASSIGN OWNED doesn't change their owner. On first
    glance, this seems inconsistent: either grants are a kind of object
    and DROP OWNED and REASSIGN OWNED ought to apply to them like anything
    else, or they are not a type of object and neither command should
    touch them. However, there's a pretty significant difference between
    (1) a table and (2) a grant of privileges on a table. Ownership on the
    table itself can be freely changed to any role in the system at any
    time. We rewrite the table's ACL on the fly to preserve the invariants
    about who can be listed as the grantor. But for the grant of
    privileges on the table, we can't freely change the grantor of record
    to an arbitrary user at any time: the set of valid grantors is
    constrained.
    
    What might be useful is a command that says "OK, for every existing
    grant that is attributed to user A, change the recorded grantor to
    user B, if that's allowable, for the others, do nothing". Or maybe
    there's some possible idea where we try to somehow make B into a valid
    grantor, but it's not clear to me what the algorithm would be.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  197. Re: pg_auth_members.grantor is bunk

    Jeff Davis <pgsql@j-davis.com> — 2022-09-02T22:01:10Z

    On Fri, 2022-09-02 at 09:30 -0400, Robert Haas wrote:
    > Thanks for having a look.
    
    Thanks for doing the work.
    
    > Yes. I figured that, when GRANTED BY is not specified, it is OK to
    > infer a valid grantor
    
    The spec is clear that the grantor should be either the current user or
    the current role. We also have a concept of INHERIT, which allows us to
    choose a role we're a member of if the current one does not suffice.
    
    But to choose a different role (the bootstrap superuser) even when the
    current (super) user role *does* suffice seems like an outright
    violation of both the spec and the principle of least surprise.
    > 
    
    > set session authorization a;
    > grant select on table t1 to b;
    > 
    > At this point, b has SELECT permission on table t1 and the grantor of
    > record is p1
    
    That's because "a" does not have permision to grant select on t1, so
    INHERIT kicks in to implicitly "SET ROLE p1". What keeps INHERIT sane
    is that it only kicks in when required (i.e. it would otherwise result
    in failure).
    
    But in the case I raised, the current user is an entirely valid
    grantor, so it doesn't make sense to me to infer a different grantor.
    
    > 
    
    > As to the first, the algorithm being used to select the best grantor
    > here is analogous to the one we use for privileges on other object
    > types, such as tables, namely, we prefer to create a grant that is
    > not
    > dependent on some other grant, rather than one that is.
    
    I don't quite follow. It seems like we're conflating a policy based on
    INHERIT with the policy around grants by superusers.
    
    In the case of role membership and INHERIT, our current behavior seems
    wise (and closer to the standard): to prefer a grantor that is closer
    to the current user/role, and therefore less dependent on other grants.
    
    But for the new policy around superusers, the current superuser is a
    completely valid grantor, and we instead preferring the bootstrap
    superuser. That doesn't seem consistent or wise to me.
    
    
    > 
    > The primary purpose
    > of the patch was to make the handing of role grants consistent with
    > the handling of grants on other object types.
    
    I certainly don't want to pin every weird thing about our privilege
    system on you just because you're the last one to touch it. But your
    changes did extend the behavior, and create some new analogous
    behavior, so it seems like a reasonable time to discuss whether those
    extensions are in the right direction.
    
    > When you view this in the context of how other types of grants work,
    > ALTER ROLE ... NOSUPERUSER isn't as much of a special case. Just as
    > we
    > want ALTER ROLE ... NOSUPERUSER to succeed quickly, we also insist
    > that REVOKE role1 FROM role2 to succeed quickly. It isn't allowed to
    > fail due to the existence of dependent privileges, because there
    > aren't allowed to be any dependent privileges.
    
      create user u1;
      create user u2;
      create user u3;
      grant u2 to u1 with admin option;
      \c - u1
      grant u2 to u3;
      \c - bootstrap_superuser
      revoke u2 from u1;
      ERROR:  dependent privileges exist
    
    > But
    > grants are different: it matters who does it, and when someone uses
    > superuser powers or other special privileges to perform an operation,
    > we have to ask on whose behalf they are acting.
    
    If superusers merely act on behalf of others, then:
    
       1. Why can the bootstrap superuser be a grantor?
       2. Why can non-bootstrap superusers specify themselves in GRANTED BY
    if they are not suitable grantors?
    
    > 
    > I think the fact that we have strong
    > integrity constraints around who can be recorded as the grantor of a
    > privilege is a good thing, and, again, the purpose of this patch was
    > to bring role grants up to the level of other parts of the system.
    
    I like integrity constriants, too. But it feels like we're recording
    the wrong information (losing the actual grantor) because it's easier
    to keep it "consistent", which doesn't necessarily seem like a win.
    
    And the whole reason we are jumping through all of these hoops is
    because we want to allow the removal of superuser privileges quickly
    without the possibility of failure. In other words, we don't have time
    to do the work of cascading to dependent objects, or erroring when we
    find them. I'm not entirely sure I agree that's a hard requirement,
    because dropping a superuser can fail. But even if it is a requirement,
    are we even meeting it if we preserve the grants that the former
    superuser created? I'd like to know more about this requirement, and
    whether we are still meeting it, and whether there are alternatives.
    
    It just feels like this edge case requirement about dropping superuser
    privileges is driving the whole design, and that feels wrong to me.
    
    > >   * Superusers would auto-grant themselves the privileges that a
    > > normal
    > > user would need to do something before doing it. For instance, if a
    > > superuser did "GRANT u2 TO u1", it would first automatically issue
    > > a
    > > "GRANT u2 TO current_user WITH ADMIN OPTION GRANTED BY
    > > bootstrap_superuser", then do the grant normally. 
    
    ...
    
    > it seems more theoretically sound than the previous two
    > ideas, because it doesn't just throw the idea of integrity
    > constraints
    > out the window.
    
    Perhaps it's worth considering further. Would be a separate patch, of
    course.
    
    > > Also, it would be nice to have REASSIGN OWNED work with grants,
    > > perhaps
    > > by adding a "WITH[OUT] GRANT" or something.
    
    ...
    
    > What might be useful is a command that says "OK, for every existing
    > grant that is attributed to user A, change the recorded grantor to
    > user B, if that's allowable, for the others, do nothing". Or maybe
    > there's some possible idea where we try to somehow make B into a
    > valid
    > grantor, but it's not clear to me what the algorithm would be.
    
    I was thinking that if the new grantor is not allowable, and "WITH
    GRANT" (or whatever) was specified, then it would throw an error.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  198. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-09-06T17:15:24Z

    On Fri, Sep 2, 2022 at 6:01 PM Jeff Davis <pgsql@j-davis.com> wrote:
    > > Yes. I figured that, when GRANTED BY is not specified, it is OK to
    > > infer a valid grantor
    >
    > The spec is clear that the grantor should be either the current user or
    > the current role. We also have a concept of INHERIT, which allows us to
    > choose a role we're a member of if the current one does not suffice.
    >
    > But to choose a different role (the bootstrap superuser) even when the
    > current (super) user role *does* suffice seems like an outright
    > violation of both the spec and the principle of least surprise.
    
    I don't think that the current superuser role suffices. For non-role
    objects, privileges originate in the table owner and can then be
    granted to others. Roles don't have an explicit owner, so I treated
    the bootstrap superuser as the implicit owner of every role. Perhaps
    there is some other way we could go here - e.g. it's been proposed by
    multiple people that maybe roles should have owners - but I do not
    think it is viable to regard the owner of a role as being anyone who
    happens to be a superuser right at the moment. To some extent that's
    related to your concern about whether ALTER USER .. NOSUPERUSER should
    be fast and immune to failure, but I also think that it is a good idea
    to have all of the privileges originating from a single owner. That
    ensures, for example, that anyone who can act as the object owner can
    revoke any privilege, which wouldn't necessarily be true if the object
    had multiple owners. Now if all of the owners are themselves
    superusers who all have the power to become any of the other owners
    then perhaps it wouldn't end up mattering too much, but it doesn't
    seem like a good idea to rely on that. In fact, part of my goal here
    is to get to a world where there's less need to rely on superuser
    powers to do system administration. I also just think it's less
    confusing if objects have single owners rather than nebulous groups of
    owners.
    
    > > set session authorization a;
    > > grant select on table t1 to b;
    > >
    > > At this point, b has SELECT permission on table t1 and the grantor of
    > > record is p1
    >
    > That's because "a" does not have permision to grant select on t1, so
    > INHERIT kicks in to implicitly "SET ROLE p1". What keeps INHERIT sane
    > is that it only kicks in when required (i.e. it would otherwise result
    > in failure).
    >
    > But in the case I raised, the current user is an entirely valid
    > grantor, so it doesn't make sense to me to infer a different grantor.
    
    See above, but also, see the first stanza of select_best_grantor(). If
    alice is a table owner, and grants permissions to bob WITH GRANT
    OPTION, and bob is a superuser and grants permissions on the table,
    the grantor will be alice, not bob.
    
    > > As to the first, the algorithm being used to select the best grantor
    > > here is analogous to the one we use for privileges on other object
    > > types, such as tables, namely, we prefer to create a grant that is
    > > not
    > > dependent on some other grant, rather than one that is.
    >
    > I don't quite follow. It seems like we're conflating a policy based on
    > INHERIT with the policy around grants by superusers.
    >
    > In the case of role membership and INHERIT, our current behavior seems
    > wise (and closer to the standard): to prefer a grantor that is closer
    > to the current user/role, and therefore less dependent on other grants.
    >
    > But for the new policy around superusers, the current superuser is a
    > completely valid grantor, and we instead preferring the bootstrap
    > superuser. That doesn't seem consistent or wise to me.
    
    I hope that the above comments on treating the bootstrap superuser as
    the object owner explain why it works this way.
    
    > I certainly don't want to pin every weird thing about our privilege
    > system on you just because you're the last one to touch it. But your
    > changes did extend the behavior, and create some new analogous
    > behavior, so it seems like a reasonable time to discuss whether those
    > extensions are in the right direction.
    
    Sure.
    
    > > When you view this in the context of how other types of grants work,
    > > ALTER ROLE ... NOSUPERUSER isn't as much of a special case. Just as
    > > we
    > > want ALTER ROLE ... NOSUPERUSER to succeed quickly, we also insist
    > > that REVOKE role1 FROM role2 to succeed quickly. It isn't allowed to
    > > fail due to the existence of dependent privileges, because there
    > > aren't allowed to be any dependent privileges.
    >
    >   create user u1;
    >   create user u2;
    >   create user u3;
    >   grant u2 to u1 with admin option;
    >   \c - u1
    >   grant u2 to u3;
    >   \c - bootstrap_superuser
    >   revoke u2 from u1;
    >   ERROR:  dependent privileges exist
    
    Hmm, I stand corrected. I was thinking of a case in which the grant
    was used to perform an action on behalf of an inherited role. Here the
    grant from u2 to u3 is performed as u1 and attributed to u1.
    
    > And the whole reason we are jumping through all of these hoops is
    > because we want to allow the removal of superuser privileges quickly
    > without the possibility of failure. In other words, we don't have time
    > to do the work of cascading to dependent objects, or erroring when we
    > find them. I'm not entirely sure I agree that's a hard requirement,
    > because dropping a superuser can fail. But even if it is a requirement,
    > are we even meeting it if we preserve the grants that the former
    > superuser created? I'd like to know more about this requirement, and
    > whether we are still meeting it, and whether there are alternatives.
    >
    > It just feels like this edge case requirement about dropping superuser
    > privileges is driving the whole design, and that feels wrong to me.
    
    I'm struggling to figure out how to reply to this exactly. I do agree
    that the way ALTER ROLE .. [NO]SUPERUSER thing works is something of a
    wart, and if we were designing SQL from scratch all over again in
    2022, I think it's reasonably likely that a lot of things would end up
    working quite a bit differently than they actually do. But, at the
    same time, it also seems to me that (1) the way ALTER ROLE ..
    [NO]SUPERUSER works is pretty firmly entrenched at this point and we
    can't easily get away with changing it; (2) I don't really see an easy
    way of changing it that wouldn't cause more problems than it solves;
    and (3) it all seems relatively unrelated to this patch.
    
    Like, the logic to infer the grantor in check_role_grantor() and
    select_best_admin() is intended to be, and as far as I know actually
    is, an exact clone of the logic in select_best_grantor(). It is
    different only in that we regard the bootstrap superuser as the object
    owner because there is no other owner stored in the catalogs; and in
    that we check CREATEROLE permission rather than SUPERUSER permission.
    Everything else is the same. To be unhappy with the patch, you have to
    think either that (1) treating the bootstrap superuser as the owner of
    every role is the wrong idea or (2) that role grants should not choose
    an implicit grantor in the same way that other types of grants do or
    (3) that the code has a bug.
    
    If you don't think any of those things but believe that the way we've
    made superusers interact with the grant system is lame in general, I
    somewhat agree, but if we came up with some new paradigm for how it
    should work, we'd have to explain why it was sufficiently better than
    the status quo to justify breaking backward compatibility, and I think
    that would be a hard argument to make. The current system feels kind
    of old-fashioned and awkward, but it's self-consistent on its terms
    and I bet a lot of people are relying on it to keep working. And I
    think if we were going to replace it with something that feels fresh
    and modern, focusing on the behavior of ALTER ROLE .. [NO]SUPERUSER
    would be the wrong place to start. That, to me, seems like it's a
    *mostly* a consequence of much broader design choices, like:
    
    - Having hard-coded superuser checks in many places instead of making
    everything a capability.
    - Having potentially any number of superusers, instead of just one root user.
    - Having granted privileges depend on the grantor continuing to hold
    the granted privilege, instead of existing independently.
    
    If I were designing a privilege system for a new piece of software
    that didn't need to comply with the SQL standard, I think I'd throw at
    least some and maybe all of those things right out the window. But I
    designed a system that had to work within that set of assumptions, I
    think I'd make it work pretty much the way it actually does.
    
    > > What might be useful is a command that says "OK, for every existing
    > > grant that is attributed to user A, change the recorded grantor to
    > > user B, if that's allowable, for the others, do nothing". Or maybe
    > > there's some possible idea where we try to somehow make B into a
    > > valid
    > > grantor, but it's not clear to me what the algorithm would be.
    >
    > I was thinking that if the new grantor is not allowable, and "WITH
    > GRANT" (or whatever) was specified, then it would throw an error.
    
    That could be done too, but then every grant attributed to the target
    role would have to be validly reattributable to the same new grantor.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  199. Re: pg_auth_members.grantor is bunk

    Jeff Davis <pgsql@j-davis.com> — 2022-09-06T23:26:12Z

    On Tue, 2022-09-06 at 13:15 -0400, Robert Haas wrote:
    
    
    > Like, the logic to infer the grantor in check_role_grantor() and
    > select_best_admin() is intended to be, and as far as I know actually
    > is, an exact clone of the logic in select_best_grantor(). It is
    > different only in that we regard the bootstrap superuser as the
    > object
    > owner because there is no other owner stored in the catalogs; and in
    > that we check CREATEROLE permission rather than SUPERUSER permission.
    
    There's at least one other difference: if you specify "GRANTED BY su1"
    for a table grant, it still selects the table owner as the grantor;
    whereas if you specify "GRANTED BY su1" for a role grant, it selects
    "su1".
    
       grant all privileges on schema public to public;
       create user su1 superuser;
       create user u1;
       create user u2;
       create user aa;
       grant u2 to su1 with admin option;
       \c - aa
       create table t_aa(i int);
       grant all privileges on t_aa to su1 with grant option;
       \c - su1
       grant select on t_aa to u1 granted by su1;
       -- grantor aa
       select relname, relacl from pg_class where relname='t_aa';
       grant u2 to u1 granted by su1; -- grantor su1
       -- grantor su1
       select grantor::regrole from pg_auth_members
         where member='u1'::regrole;
    
    [ If you run the same example but where su1 is not a superuser, then
    both select "su1" as the grantor because that's the only valid grantor
    that can be inferred. ]
    
    Now that I understand the underlying philosophy better, and I've
    experimented with more cases, I propose the following grantor inference
    behavior which I believe is in the spirit of your changes:
    
      * Let the granting user be the one specified in the GRANTED BY clause
    if it exists; otherwise the current user. In other words, omitting
    GRANTED BY is the same as specifying "GRANTED BY current_user".
      * If the granting user has privileges to be the grantor (ADMIN OPTION
    for roles, GRANT OPTION for other objects) then the granting user is
    the grantor.
      * Else if the granting user inherits from a user with the privileges
    to be the grantor, then it selects a role with the fewest inheritance
    hops as the grantor.
      * Else if the current user is any superuser:
        - If the grant is a role grant, it selects the bootstrap superuser
    as the grantor.
        - Else the object owner is the grantor.
      * Else error (or if an error would break important backwards
    compatibility, silently make it work like before or perhaps issue a
    WARNING).
    
    In other words, try to issue the grant normally if at all possible, and
    play the superuser card as a last resort. I believe that will lead to
    the fewest surprising cases, and make them easiest to explain, because
    superuser-ness doesn't influence the outcome in as many cases.
    
    It cements the idea that the bootstrap superuser is the "real"
    superuser, and must always remain so, and that all other superusers are
    temporary stand-ins (kind of but not quite the same as inheritance).
    And it leaves the ugliness that we lose the information about the
    "real" grantor when we play the superuser card, but, as I say above,
    that would be a last resort.
    
    The proposal would be a slight behavior change from v15 in the
    following case:
    
       grant all privileges on schema public to public;
       create user su1 superuser;
       create user u1;
       create user aa;
       \c - aa
       create table t_aa(i int);
       grant all privileges on t_aa to su1 with grant option;
       \c - su1
       grant select on t_aa to u1 granted by su1;
       -- grantor "aa" in v15, grantor "su1" after my proposal
       select relname, relacl from pg_class where relname='t_aa';
    
    Another change in behavior would be that the bootstrap superuser could
    be the grantor for table privileges, if the bootstrap superuser has
    WITH GRANT OPTION privileges.
    
    But those seems minor to me.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  200. Re: pg_auth_members.grantor is bunk

    Jeff Davis <pgsql@j-davis.com> — 2022-09-07T01:27:32Z

    On Tue, 2022-09-06 at 16:26 -0700, Jeff Davis wrote:
    > In other words, omitting
    > GRANTED BY is the same as specifying "GRANTED BY current_user".
    
    Let me correct this thinko to distinguish between specifying GRANTED BY
    and not:
    
      * Let the granting user be the one specified in the GRANTED BY clause
    if it exists; otherwise the current user.
      * If the granting user has privileges to be the grantor (ADMIN OPTION
    for roles, GRANT OPTION for other objects) then the granting user is
    the grantor.
      * Else if GRANTED BY was *not* specified, infer the grantor:
        - If the granting user inherits from a role with the privileges
    to be the grantor, then it selects a role with the fewest inheritance
    hops as the grantor.
        - Else if the current user is any superuser, the grantor is the top
    "owner" (bootstrap superuser for roles; object owner for other objects)
      * Else error (or if an error would break important backwards
    compatibility, silently make it work like before and perhaps issue a
    WARNING).
    
    The basic idea is to use superuser privileges as a last resort in order
    to maximize the cases that work normally (independent of superuser-
    ness).
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  201. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-09-07T13:39:05Z

    On Tue, Sep 6, 2022 at 7:26 PM Jeff Davis <pgsql@j-davis.com> wrote:
    > There's at least one other difference: if you specify "GRANTED BY su1"
    > for a table grant, it still selects the table owner as the grantor;
    > whereas if you specify "GRANTED BY su1" for a role grant, it selects
    > "su1".
    
    Right. Personally, I'm inclined to view that as a defect in the
    "GRANTED BY whoever" implementation for other object types, and I
    think it should be resolved by making other object types error out if
    the user explicitly mentioned in the "GRANTED BY" clause isn't a valid
    grantor. It also seems possible to view it as a defect in the new
    implementation, and argue that inference should always be performed
    starting at the named user. I find that a POLA violation, but someone
    could disagree.
    
    Parenthetically, I think we should also fix GRANTED BY for other
    object types so that it actually works, but that is a bit of headache
    because it doesn't seem like that code is relying as heavily on common
    infrastructure as some things, so I believe it's actually a fair
    amount of work to make that happen.
    
    > In other words, try to issue the grant normally if at all possible, and
    > play the superuser card as a last resort. I believe that will lead to
    > the fewest surprising cases, and make them easiest to explain, because
    > superuser-ness doesn't influence the outcome in as many cases.
    
    It seems to me that this policy would reverse select_best_grantor()'s
    decision about whether we should prefer to rely on superuser
    privileges or on privileges actually granted to the current user. I
    think either behavior is defensible, but the existing precedent is to
    prefer relying on superuser privileges. Like you, I found that a bit
    weird when I realized that's what it was doing, but it does have some
    advantages. In particular, it means that the privileges granted by a
    superuser don't depend on any other grants, which is something that a
    user might value.
    
    Now that is not to say that we couldn't decide that
    select_best_grantor() got it wrong and choose to break backward
    compatibility in order to fix it ... but I'm not even convinced that
    the alternative behavior you propose is clearly better, let alone that
    it's enough better to justify changing things. However, I don't
    personally have a strong preference about it one way or the other; if
    there's a strong consensus to change it, so be it.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  202. Re: pg_auth_members.grantor is bunk

    Jeff Davis <pgsql@j-davis.com> — 2022-09-07T14:56:28Z

    On Wed, 2022-09-07 at 09:39 -0400, Robert Haas wrote:
    > Now that is not to say that we couldn't decide that
    > select_best_grantor() got it wrong and choose to break backward
    > compatibility in order to fix it ... but I'm not even convinced that
    > the alternative behavior you propose is clearly better, let alone
    > that
    > it's enough better to justify changing things.
    
    OK. I suppose the best path forward is to just try to improve the
    ability to administer the system without relying as much on superusers,
    which will allow us to safely ignore some of the weirdness caused by
    superusers issuing grants.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  203. Re: pg_auth_members.grantor is bunk

    Robert Haas <robertmhaas@gmail.com> — 2022-09-07T17:00:39Z

    On Wed, Sep 7, 2022 at 10:56 AM Jeff Davis <pgsql@j-davis.com> wrote:
    > OK. I suppose the best path forward is to just try to improve the
    > ability to administer the system without relying as much on superusers,
    > which will allow us to safely ignore some of the weirdness caused by
    > superusers issuing grants.
    
    Yeah, and I think we might not even be that far away from making that
    happen. There are still a few thorny design issues to work out, I
    believe, and there's also some complexity that is introduced by the
    fact that different people want different things. For example, last
    release cycle, I believed that the NOINHERIT behavior was a weird wart
    that probably nobody cared about. That turned out to be false, really
    false.
    
    What I *personally* want most as an alternative to superuser is an
    account that inherits all the privileges of the other accounts that it
    manages, which might not be all the accounts on the system, and which
    can also SET ROLE to those accounts. If you're logged into such an
    account, you can do many of the things a superuser can do and in the
    same ways that a superuser can do them. For example, if you've got
    some pg_dump output, you could probably restore the dump using such an
    account and privilege restoration would work, provided that the
    required accounts exist and that they're among the accounts managed by
    your account.
    
    However, I think that other people want different things. For example,
    I think that Joshua Brindle mentioned wanting to have a user-creation
    bot that should be able to make new accounts but not access them in
    any way, and I think Stephen Frost was interested in semantics where
    you could make accounts and be able to SET ROLE into them but not
    inherit their privileges. Or maybe they were both proposing the same
    thing: not quite sure. Anyway, it will perhaps turn out to be
    impossible to give everybody 100% of everything they would like, but
    I'm thinking about a few ideas that might enable us to cater to a few
    different scenarios - and I'm hopeful that it will be possible to
    propose something in time for inclusion in v16, but my ideas aren't
    quite well enough formulated yet to make a concrete proposal just yet,
    and when I do make such a proposal I want to do it on a new thread for
    better visibility.
    
    In the meantime, I think that what has already been committed is
    clearly a step in the right direction. The patch which is the subject
    of this thread has basically brought the role grant code up to the
    level of other object types. I don't think it's an overstatement to
    say that the previous state of affairs was that this feature just
    didn't work properly and no one had cared enough to bother fixing it.
    That always makes discussions about future enhancements harder. The
    patch to add grant-level control to the INHERIT option also seems to
    me to be a step in the right direction, since, at least IMHO, it is
    really hard to reason about behavior when the heritability of a
    particular grant is a property of the grantee rather than something
    which can be controlled by the grantor, or the system. If we can reach
    agreement on some of the other things that I have proposed,
    specifically sorting out the issues under discussion on the
    "has_privs_of_role vs. is_member_of_role, redux" thread and adding the
    new capability discussed on the "allowing for control over SET ROLE"
    thread, I think will be a further, useful step.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  204. Re: pg_auth_members.grantor is bunk

    Tom Lane <tgl@sss.pgh.pa.us> — 2022-09-21T20:53:52Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Thu, Aug 18, 2022 at 1:26 PM Robert Haas <robertmhaas@gmail.com> wrote:
    >> CI is happier with this version, so I've committed 0001. If no major
    >> problems emerge, I'll proceed with 0002 as well.
    
    > Done.
    
    Shouldn't the CF entry [1] be closed as committed?
    
    			regards, tom lane
    
    [1] https://commitfest.postgresql.org/39/3745/