On 29/12/2019 17:31, Tom Lane wrote:
> Robert Haas <robertmh...@gmail.com> writes:
>> On Sat, Dec 28, 2019 at 2:02 PM Vik Fearing <vik.fear...@2ndquadrant.com> 
>> wrote:
>>> I'm all for this (and even suggested it during the IRC conversation that
>>> prompted this patch). It's rife with bikeshedding, though.  My original
>>> proposal was to use '&' and Andrew Gierth would have used ':'.
>> I think this is a good proposal regardless of which character we
>> decide to use. My order of preference from highest-to-lowest would
>> probably be :*&, but maybe that's just because I'm reading this on
>> Sunday rather than on Tuesday.
> I don't have any particular objection to '&' if people prefer that.


I wrote the patch so I got to decide. :-)  I will also volunteer to do
the grunt work of changing the symbol if consensus wants that, though.


It turns out that my original patch didn't really change, all the meat
is in the keywords patch.  The superuser patch is to be applied on top
of the keywords patch.

-- 

Vik Fearing

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 55694c4368..add25b2b43 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -8953,8 +8953,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
      <entry><structfield>text</structfield></entry>
      <entry>
       Host name or IP address, or one
-      of <literal>all</literal>, <literal>samehost</literal>,
-      or <literal>samenet</literal>, or null for local connections
+      of <literal>&amp;all</literal>, <literal>&amp;samehost</literal>,
+      or <literal>&amp;samenet</literal>, or null for local connections
      </entry>
     </row>
     <row>
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 5f1eec78fb..96fc4b01e9 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -81,11 +81,26 @@
    A record is made
    up of a number of fields which are separated by spaces and/or tabs.
    Fields can contain white space if the field value is double-quoted.
-   Quoting one of the keywords in a database, user, or address field (e.g.,
-   <literal>all</literal> or <literal>replication</literal>) makes the word lose its special
-   meaning, and just match a database, user, or host with that name.
   </para>
 
+  <note>
+   <para>
+    As of version 13, keywords are preceded by the character "&amp;".
+    For compatibility, the following legacy keywords are still accepted
+    on their own:
+    <literal>all</literal>, <literal>replication</literal>,
+    <literal>samegroup</literal>, <literal>samehost</literal>,
+    <literal>samenet</literal>, <literal>samerole</literal>,
+    <literal>sameuser</literal>.
+   </para>
+
+   <para>
+    Quoting one of these legacy keywords in a database, user, or address
+    field makes the word lose its special meaning, and just match a
+    database, user, or host with that name.
+   </para>
+  </note>
+
   <para>
    Each record specifies a connection type, a client IP address range
    (if relevant for the connection type), a database name, a user name,
@@ -221,18 +236,18 @@ hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable
      <listitem>
       <para>
        Specifies which database name(s) this record matches.  The value
-       <literal>all</literal> specifies that it matches all databases.
-       The value <literal>sameuser</literal> specifies that the record
+       <literal>&amp;all</literal> specifies that it matches all databases.
+       The value <literal>&amp;sameuser</literal> specifies that the record
        matches if the requested database has the same name as the
-       requested user.  The value <literal>samerole</literal> specifies that
+       requested user.  The value <literal>&amp;samerole</literal> specifies that
        the requested user must be a member of the role with the same
-       name as the requested database.  (<literal>samegroup</literal> is an
-       obsolete but still accepted spelling of <literal>samerole</literal>.)
+       name as the requested database.  (<literal>&amp;samegroup</literal> is an
+       obsolete but still accepted spelling of <literal>&amp;samerole</literal>.)
        Superusers are not considered to be members of a role for the
-       purposes of <literal>samerole</literal> unless they are explicitly
+       purposes of <literal>&amp;samerole</literal> unless they are explicitly
        members of the role, directly or indirectly, and not just by
        virtue of being a superuser.
-       The value <literal>replication</literal> specifies that the record
+       The value <literal>&amp;replication</literal> specifies that the record
        matches if a physical replication connection is requested (note that
        replication connections do not specify any particular database).
        Otherwise, this is the name of
@@ -249,7 +264,7 @@ hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable
      <listitem>
       <para>
        Specifies which database user name(s) this record
-       matches. The value <literal>all</literal> specifies that it
+       matches. The value <literal>&amp;all</literal> specifies that it
        matches all users.  Otherwise, this is either the name of a specific
        database user, or a group name preceded by <literal>+</literal>.
        (Recall that there is no real distinction between users and groups
@@ -312,9 +327,9 @@ hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable
       </para>
 
       <para>
-       You can also write <literal>all</literal> to match any IP address,
-       <literal>samehost</literal> to match any of the server's own IP
-       addresses, or <literal>samenet</literal> to match any address in any
+       You can also write <literal>&amp;all</literal> to match any IP address,
+       <literal>&amp;samehost</literal> to match any of the server's own IP
+       addresses, or <literal>&amp;samenet</literal> to match any address in any
        subnet that the server is directly connected to.
       </para>
 
@@ -699,40 +714,40 @@ hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable
 # connections).
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-local   all             all                                     trust
+local   &amp;all            &amp;all                                    trust
 
 # The same using local loopback TCP/IP connections.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             all             127.0.0.1/32            trust
+host    &amp;all            &amp;all            127.0.0.1/32            trust
 
 # The same as the previous line, but using a separate netmask column
 #
 # TYPE  DATABASE        USER            IP-ADDRESS      IP-MASK             METHOD
-host    all             all             127.0.0.1       255.255.255.255     trust
+host    &amp;all            &amp;all            127.0.0.1       255.255.255.255     trust
 
 # The same over IPv6.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             all             ::1/128                 trust
+host    &amp;all            &amp;all            ::1/128                 trust
 
 # The same using a host name (would typically cover both IPv4 and IPv6).
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             all             localhost               trust
+host    &amp;all            &amp;all            localhost               trust
 
 # Allow any user from any host with IP address 192.168.93.x to connect
 # to database "postgres" as the same user name that ident reports for
 # the connection (typically the operating system user name).
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    postgres        all             192.168.93.0/24         ident
+host    postgres        &amp;all            192.168.93.0/24         ident
 
 # Allow any user from host 192.168.12.10 to connect to database
 # "postgres" if the user's password is correctly supplied.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    postgres        all             192.168.12.10/32        scram-sha-256
+host    postgres        &amp;all            192.168.12.10/32        scram-sha-256
 
 # Allow any user from hosts in the example.com domain to connect to
 # any database if the user's password is correctly supplied.
@@ -742,8 +757,8 @@ host    postgres        all             192.168.12.10/32        scram-sha-256
 # authentication.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             mike            .example.com            md5
-host    all             all             .example.com            scram-sha-256
+host    &amp;all            mike            .example.com            md5
+host    &amp;all            &amp;all            .example.com            scram-sha-256
 
 # In the absence of preceding "host" lines, these three lines will
 # reject all connections from 192.168.54.1 (since that entry will be
@@ -754,9 +769,9 @@ host    all             all             .example.com            scram-sha-256
 # encrypted GSSAPI connections) are allowed, but only from 192.168.12.10.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             all             192.168.54.1/32         reject
-hostgssenc all          all             0.0.0.0/0               gss
-host    all             all             192.168.12.10/32        gss
+host    &amp;all            &amp;all            192.168.54.1/32         reject
+hostgssenc &amp;all         &amp;all            0.0.0.0/0               gss
+host    &amp;all            &amp;all            192.168.12.10/32        gss
 
 # Allow users from 192.168.x.x hosts to connect to any database, if
 # they pass the ident check.  If, for example, ident says the user is
@@ -765,7 +780,7 @@ host    all             all             192.168.12.10/32        gss
 # "omicron" that says "bryanh" is allowed to connect as "guest1".
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-host    all             all             192.168.0.0/16          ident map=omicron
+host    &amp;all            &amp;all            192.168.0.0/16          ident map=omicron
 
 # If these are the only three lines for local connections, they will
 # allow local users to connect only to their own databases (databases
@@ -775,15 +790,15 @@ host    all             all             192.168.0.0/16          ident map=omicro
 # are required in all cases.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
-local   sameuser        all                                     md5
-local   all             @admins                                 md5
-local   all             +support                                md5
+local   &amp;sameuser       &amp;all                                    md5
+local   &amp;all            @admins                                 md5
+local   &amp;all            +support                                md5
 
 # The last two lines above can be combined into a single line:
-local   all             @admins,+support                        md5
+local   &amp;all            @admins,+support                        md5
 
 # The database column can also use lists and file names:
-local   db1,db2,@demodbs  all                                   md5
+local   db1,db2,@demodbs  &amp;all                                  md5
 </programlisting>
    </example>
  </sect1>
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index b6de92783a..da18c389e5 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -64,8 +64,20 @@ typedef struct check_network_data
 	bool		result;			/* set to true if match */
 } check_network_data;
 
+/*
+ * The following keywords are accepted without the keyword sigil for legacy
+ * reasons.  They may become normal words at some point in the future.
+ */
+#define token_is_legacy_keyword(t, k)  (!t->quoted && ( \
+			strcmp(t->string, "all") == 0 || \
+			strcmp(t->string, "replication") == 0 || \
+			strcmp(t->string, "samegroup") == 0 || \
+			strcmp(t->string, "samehost") == 0 || \
+			strcmp(t->string, "samenet") == 0 || \
+			strcmp(t->string, "samerole") == 0 || \
+			strcmp(t->string, "sameuser") == 0))
 
-#define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
+#define token_is_keyword(t, k)  ((t->string[0] == '&' && strcmp(t->string+1, k) == 0) || token_is_legacy_keyword(t, k))
 #define token_matches(t, k)  (strcmp(t->string, k) == 0)
 
 /*
@@ -2472,7 +2484,7 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
 			 * Flatten HbaToken list to string list.  It might seem that we
 			 * should re-quote any quoted tokens, but that has been rejected
 			 * on the grounds that it makes it harder to compare the array
-			 * elements to other system catalogs.  That makes entries like
+			 * elements to other system catalogs.  That makes entries with legacy keywords like
 			 * "all" or "samerole" formally ambiguous ... but users who name
 			 * databases/roles that way are inflicting their own pain.
 			 */
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index c853e36232..d08aba497f 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -21,12 +21,12 @@
 # "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
 # plain TCP/IP socket.
 #
-# DATABASE can be "all", "sameuser", "samerole", "replication", a
-# database name, or a comma-separated list thereof. The "all"
-# keyword does not match "replication". Access to replication
+# DATABASE can be &all, &sameuser, &samerole, &replication, a
+# database name, or a comma-separated list thereof. The &all
+# keyword does not match &replication. Access to replication
 # must be enabled in a separate record (see example below).
 #
-# USER can be "all", a user name, a group name prefixed with "+", or a
+# USER can be &all, a user name, a group name prefixed with "+", or a
 # comma-separated list thereof.  In both the DATABASE and USER fields
 # you can also write a file name prefixed with "@" to include names
 # from a separate file.
@@ -53,11 +53,18 @@
 # section in the documentation for a list of which options are
 # available for which authentication methods.
 #
+# As of version 13, keywords are preceded by the character "&".
+# For compatibility, the following legacy keywords are still accepted on
+# their own:
+#     all,
+#     replication,
+#     sameuser, samegroup, samerole,
+#     samehost, samenet
+#
 # Database and user names containing spaces, commas, quotes and other
-# special characters must be quoted.  Quoting one of the keywords
-# "all", "sameuser", "samerole" or "replication" makes the name lose
-# its special character, and just match a database or username with
-# that name.
+# special characters must be quoted.  Quoting one of the legacy keywords
+# from the previous paragraph makes the name lose its special character,
+# and just match a database or username with that name.
 #
 # This file is read on server startup and when the server receives a
 # SIGHUP signal.  If you edit the file on a running system, you have to
@@ -77,13 +84,13 @@
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
 
 @remove-line-for-nolocal@# "local" is for Unix domain socket connections only
-@remove-line-for-nolocal@local   all             all                                     @authmethodlocal@
+@remove-line-for-nolocal@local   &all            &all                                    @authmethodlocal@
 # IPv4 local connections:
-host    all             all             127.0.0.1/32            @authmethodhost@
+host    &all            &all            127.0.0.1/32            @authmethodhost@
 # IPv6 local connections:
-host    all             all             ::1/128                 @authmethodhost@
+host    &all            &all            ::1/128                 @authmethodhost@
 # Allow replication connections from localhost, by a user with the
 # replication privilege.
-@remove-line-for-nolocal@local   replication     all                                     @authmethodlocal@
-host    replication     all             127.0.0.1/32            @authmethodhost@
-host    replication     all             ::1/128                 @authmethodhost@
+@remove-line-for-nolocal@local   &replication    &all                                    @authmethodlocal@
+host    &replication    &all            127.0.0.1/32            @authmethodhost@
+host    &replication    &all            ::1/128                 @authmethodhost@
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 96fc4b01e9..36cdf180c4 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -265,7 +265,11 @@ hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable
       <para>
        Specifies which database user name(s) this record
        matches. The value <literal>&amp;all</literal> specifies that it
-       matches all users.  Otherwise, this is either the name of a specific
+       matches all users.
+       The value <literal>&amp;superuser</literal> specifies that it matches all
+       superusers.  The value <literal>&amp;nonsuperuser</literal> specifies that it
+       matches no superusers.
+       Otherwise, this is either the name of a specific
        database user, or a group name preceded by <literal>+</literal>.
        (Recall that there is no real distinction between users and groups
        in <productname>PostgreSQL</productname>; a <literal>+</literal> mark really means
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index da18c389e5..087680be64 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -608,6 +608,16 @@ check_role(const char *role, Oid roleid, List *tokens)
 			if (is_member(roleid, tok->string + 1))
 				return true;
 		}
+		else if (token_is_keyword(tok, "superuser"))
+		{
+			if (superuser_arg(roleid))
+				return true;
+		}
+		else if (token_is_keyword(tok, "nonsuperuser"))
+		{
+			if (!superuser_arg(roleid))
+				return true;
+		}
 		else if (token_matches(tok, role) ||
 				 token_is_keyword(tok, "all"))
 			return true;

Reply via email to