Allow GRANTED BY clause in normal GRANT and REVOKE statements
authorPeter Eisentraut <[email protected]>
Sat, 30 Jan 2021 08:41:44 +0000 (09:41 +0100)
committerPeter Eisentraut <[email protected]>
Sat, 30 Jan 2021 08:45:11 +0000 (09:45 +0100)
The SQL standard allows a GRANTED BY clause on GRANT and
REVOKE (privilege) statements that can specify CURRENT_USER or
CURRENT_ROLE.  In PostgreSQL, both of these are the default behavior.
Since we already have all the parsing support for this for the
GRANT (role) statement, we might as well add basic support for this
for the privilege variant as well.  This allows us to check off SQL
feature T332.  In the future, perhaps more interesting things could be
done with this, too.

Reviewed-by: Simon Riggs <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/f2feac44-b4c5-f38f-3699-2851d6a76dc9@2ndquadrant.com

doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/revoke.sgml
src/backend/catalog/aclchk.c
src/backend/catalog/sql_features.txt
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/include/nodes/parsenodes.h
src/test/regress/expected/privileges.out
src/test/regress/sql/privileges.sql

index c3db393bdea70278dc1b8ace5b5a7bbf0134da14..a897712de2e5cdc3b2d6f73cd4b987657257ab67 100644 (file)
@@ -26,58 +26,71 @@ GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
     ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
          | ALL TABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( <replaceable class="parameter">column_name</replaceable> [, ...] )
     [, ...] | ALL [ PRIVILEGES ] ( <replaceable class="parameter">column_name</replaceable> [, ...] ) }
     ON [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { { USAGE | SELECT | UPDATE }
     [, ...] | ALL [ PRIVILEGES ] }
     ON { SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [, ...]
          | ALL SEQUENCES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
     ON DATABASE <replaceable>database_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON DOMAIN <replaceable>domain_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON FOREIGN DATA WRAPPER <replaceable>fdw_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON FOREIGN SERVER <replaceable>server_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { EXECUTE | ALL [ PRIVILEGES ] }
     ON { { FUNCTION | PROCEDURE | ROUTINE } <replaceable>routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
          | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON LANGUAGE <replaceable>lang_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
     ON LARGE OBJECT <replaceable class="parameter">loid</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
     ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { CREATE | ALL [ PRIVILEGES ] }
     ON TABLESPACE <replaceable>tablespace_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
     ON TYPE <replaceable>type_name</replaceable> [, ...]
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
 
 GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replaceable class="parameter">role_specification</replaceable> [, ...]
     [ WITH ADMIN OPTION ]
@@ -133,6 +146,12 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
    to <literal>PUBLIC</literal>.
   </para>
 
+  <para>
+   If <literal>GRANTED BY</literal> is specified, the specified grantor must
+   be the current user.  This clause is currently present in this form only
+   for SQL compatibility.
+  </para>
+
   <para>
    There is no need to grant privileges to the owner of an object
    (usually the user that created it),
@@ -410,9 +429,9 @@ GRANT admins TO joe;
 
    <para>
     The SQL standard allows the <literal>GRANTED BY</literal> option to
-    be used in all forms of <command>GRANT</command>.  PostgreSQL only
-    supports it when granting role membership, and even then only superusers
-    may use it in nontrivial ways.
+    specify only <literal>CURRENT_USER</literal> or
+    <literal>CURRENT_ROLE</literal>.  The other variants are PostgreSQL
+    extensions.
    </para>
 
    <para>
index 35ff87a4f5e26c5a8bc30c8fe50d5c0df6e127e7..3014c864ea3ca42233b0dcfe540fc76b2a6d9a16 100644 (file)
@@ -27,6 +27,7 @@ REVOKE [ GRANT OPTION FOR ]
     ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
          | ALL TABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
@@ -34,6 +35,7 @@ REVOKE [ GRANT OPTION FOR ]
     [, ...] | ALL [ PRIVILEGES ] ( <replaceable class="parameter">column_name</replaceable> [, ...] ) }
     ON [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
@@ -42,30 +44,35 @@ REVOKE [ GRANT OPTION FOR ]
     ON { SEQUENCE <replaceable class="parameter">sequence_name</replaceable> [, ...]
          | ALL SEQUENCES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
     ON DATABASE <replaceable>database_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { USAGE | ALL [ PRIVILEGES ] }
     ON DOMAIN <replaceable>domain_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { USAGE | ALL [ PRIVILEGES ] }
     ON FOREIGN DATA WRAPPER <replaceable>fdw_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { USAGE | ALL [ PRIVILEGES ] }
     ON FOREIGN SERVER <replaceable>server_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
@@ -73,36 +80,42 @@ REVOKE [ GRANT OPTION FOR ]
     ON { { FUNCTION | PROCEDURE | ROUTINE } <replaceable>function_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
          | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { USAGE | ALL [ PRIVILEGES ] }
     ON LANGUAGE <replaceable>lang_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
     ON LARGE OBJECT <replaceable class="parameter">loid</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
     ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { CREATE | ALL [ PRIVILEGES ] }
     ON TABLESPACE <replaceable>tablespace_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ GRANT OPTION FOR ]
     { USAGE | ALL [ PRIVILEGES ] }
     ON TYPE <replaceable>type_name</replaceable> [, ...]
     FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+    [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
     [ CASCADE | RESTRICT ]
 
 REVOKE [ ADMIN OPTION FOR ]
index f3c1ca18ae7476ec623bb7265afd3e08d947f5f7..add3d147e766cde6ee64af335cc1f6988f5c66f6 100644 (file)
@@ -363,6 +363,22 @@ ExecuteGrantStmt(GrantStmt *stmt)
    const char *errormsg;
    AclMode     all_privileges;
 
+   if (stmt->grantor)
+   {
+       Oid         grantor;
+
+       grantor = get_rolespec_oid(stmt->grantor, false);
+
+       /*
+        * Currently, this clause is only for SQL compatibility, not very
+        * interesting otherwise.
+        */
+       if (grantor != GetUserId())
+           ereport(ERROR,
+                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("grantor must be current user")));
+   }
+
    /*
     * Turn the regular GrantStmt into the InternalGrant form.
     */
index caa971c4356acd39e3903350cd30ae5cb6b8711a..86519ad2974a0693d6cda856483f2571c46420bf 100644 (file)
@@ -475,7 +475,7 @@ T324    Explicit security for SQL routines          NO
 T325   Qualified SQL parameter references          YES 
 T326   Table functions         NO  
 T331   Basic roles         YES 
-T332   Extended roles          NO  mostly supported
+T332   Extended roles          YES 
 T341   Overloading of SQL-invoked functions and procedures         YES 
 T351   Bracketed SQL comments (/*...*/ comments)           YES 
 T431   Extended grouping capabilities          YES 
index ba3ccc712c8887869a54ff47310b00fcbc12c830..21e09c667a369fa6636b981f45f233de9bddc808 100644 (file)
@@ -3270,6 +3270,7 @@ _copyGrantStmt(const GrantStmt *from)
    COPY_NODE_FIELD(privileges);
    COPY_NODE_FIELD(grantees);
    COPY_SCALAR_FIELD(grant_option);
+   COPY_NODE_FIELD(grantor);
    COPY_SCALAR_FIELD(behavior);
 
    return newnode;
index a2ef853dc2a029dec844022b88a266c2646f214f..5a5237c6c3099e51f38004ed7ba90d289de2d1ea 100644 (file)
@@ -1145,6 +1145,7 @@ _equalGrantStmt(const GrantStmt *a, const GrantStmt *b)
    COMPARE_NODE_FIELD(privileges);
    COMPARE_NODE_FIELD(grantees);
    COMPARE_SCALAR_FIELD(grant_option);
+   COMPARE_NODE_FIELD(grantor);
    COMPARE_SCALAR_FIELD(behavior);
 
    return true;
index 7574d545e0e0218f8a1d3670abbad9e3860a5c06..b2f447bf9a277745452f29e48ff4a81272fef0c9 100644 (file)
@@ -6772,7 +6772,7 @@ opt_from_in:  from_in
  *****************************************************************************/
 
 GrantStmt: GRANT privileges ON privilege_target TO grantee_list
-           opt_grant_grant_option
+           opt_grant_grant_option opt_granted_by
                {
                    GrantStmt *n = makeNode(GrantStmt);
                    n->is_grant = true;
@@ -6782,13 +6782,14 @@ GrantStmt:  GRANT privileges ON privilege_target TO grantee_list
                    n->objects = ($4)->objs;
                    n->grantees = $6;
                    n->grant_option = $7;
+                   n->grantor = $8;
                    $$ = (Node*)n;
                }
        ;
 
 RevokeStmt:
            REVOKE privileges ON privilege_target
-           FROM grantee_list opt_drop_behavior
+           FROM grantee_list opt_granted_by opt_drop_behavior
                {
                    GrantStmt *n = makeNode(GrantStmt);
                    n->is_grant = false;
@@ -6798,11 +6799,12 @@ RevokeStmt:
                    n->objtype = ($4)->objtype;
                    n->objects = ($4)->objs;
                    n->grantees = $6;
-                   n->behavior = $7;
+                   n->grantor = $7;
+                   n->behavior = $8;
                    $$ = (Node *)n;
                }
            | REVOKE GRANT OPTION FOR privileges ON privilege_target
-           FROM grantee_list opt_drop_behavior
+           FROM grantee_list opt_granted_by opt_drop_behavior
                {
                    GrantStmt *n = makeNode(GrantStmt);
                    n->is_grant = false;
@@ -6812,7 +6814,8 @@ RevokeStmt:
                    n->objtype = ($7)->objtype;
                    n->objects = ($7)->objs;
                    n->grantees = $9;
-                   n->behavior = $10;
+                   n->grantor = $10;
+                   n->behavior = $11;
                    $$ = (Node *)n;
                }
        ;
index dc2bb40926a4abaef299e40877d8f26dc0779ccf..068c6ec440135ca7eb2a256b16527260662aecb1 100644 (file)
@@ -1949,6 +1949,7 @@ typedef struct GrantStmt
    /* privileges == NIL denotes ALL PRIVILEGES */
    List       *grantees;       /* list of RoleSpec nodes */
    bool        grant_option;   /* grant or revoke grant option */
+   RoleSpec   *grantor;
    DropBehavior behavior;      /* drop behavior (for REVOKE) */
 } GrantStmt;
 
index 8f2fc89851bc19f5ddaa18e932d07b2e1d412a01..ed98fa8376ff1640318b3139e75b6f0fa4703e23 100644 (file)
@@ -70,8 +70,10 @@ SELECT * FROM atest1;
 CREATE TABLE atest2 (col1 varchar(10), col2 boolean);
 GRANT SELECT ON atest2 TO regress_priv_user2;
 GRANT UPDATE ON atest2 TO regress_priv_user3;
-GRANT INSERT ON atest2 TO regress_priv_user4;
-GRANT TRUNCATE ON atest2 TO regress_priv_user5;
+GRANT INSERT ON atest2 TO regress_priv_user4 GRANTED BY CURRENT_USER;
+GRANT TRUNCATE ON atest2 TO regress_priv_user5 GRANTED BY CURRENT_ROLE;
+GRANT TRUNCATE ON atest2 TO regress_priv_user4 GRANTED BY regress_priv_user5;  -- error
+ERROR:  grantor must be current user
 SET SESSION AUTHORIZATION regress_priv_user2;
 SELECT session_user, current_user;
     session_user    |    current_user    
index 1c250a11fe1ac9471b3fb6fd21906d9b40ecddda..becbc196713a03c23c1c53cc66638b586e38949d 100644 (file)
@@ -64,8 +64,10 @@ SELECT * FROM atest1;
 CREATE TABLE atest2 (col1 varchar(10), col2 boolean);
 GRANT SELECT ON atest2 TO regress_priv_user2;
 GRANT UPDATE ON atest2 TO regress_priv_user3;
-GRANT INSERT ON atest2 TO regress_priv_user4;
-GRANT TRUNCATE ON atest2 TO regress_priv_user5;
+GRANT INSERT ON atest2 TO regress_priv_user4 GRANTED BY CURRENT_USER;
+GRANT TRUNCATE ON atest2 TO regress_priv_user5 GRANTED BY CURRENT_ROLE;
+
+GRANT TRUNCATE ON atest2 TO regress_priv_user4 GRANTED BY regress_priv_user5;  -- error
 
 
 SET SESSION AUTHORIZATION regress_priv_user2;