]> granicus.if.org Git - postgresql/commitdiff
Switch user ID to the object owner when populating a materialized view.
authorNoah Misch <noah@leadboat.com>
Fri, 12 Jul 2013 22:21:22 +0000 (18:21 -0400)
committerNoah Misch <noah@leadboat.com>
Fri, 12 Jul 2013 22:25:41 +0000 (18:25 -0400)
This makes superuser-issued REFRESH MATERIALIZED VIEW safe regardless of
the object's provenance.  REINDEX is an earlier example of this pattern.
As a downside, functions called from materialized views must tolerate
running in a security-restricted operation.  CREATE MATERIALIZED VIEW
need not change user ID.  Nonetheless, avoid creation of materialized
views that will invariably fail REFRESH by making it, too, start a
security-restricted operation.

Back-patch to 9.3 so materialized views have this from the beginning.

Reviewed by Kevin Grittner.

doc/src/sgml/ref/create_materialized_view.sgml
src/backend/commands/createas.c
src/backend/commands/matview.c

index 0ed764b353394137afd73c9a013e031e197ae79c..b742e17ac828a64f816650e7c8c78c3f67bffd78 100644 (file)
@@ -105,7 +105,9 @@ CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable>
     <listitem>
      <para>
       A <xref linkend="sql-select">, <link linkend="sql-table">TABLE</link>,
-      or <xref linkend="sql-values"> command.
+      or <xref linkend="sql-values"> command.  This query will run within a
+      security-restricted operation; in particular, calls to functions that
+      themselves create temporary tables will fail.
      </para>
     </listitem>
    </varlistentry>
index 2bfe5fba8775631afb378b8591b536c3472b074b..a3509d8c2a3242efa7896fb0a777e5928b239d6e 100644 (file)
@@ -33,6 +33,7 @@
 #include "commands/prepare.h"
 #include "commands/tablecmds.h"
 #include "commands/view.h"
+#include "miscadmin.h"
 #include "parser/parse_clause.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/smgr.h"
@@ -69,7 +70,11 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
 {
        Query      *query = (Query *) stmt->query;
        IntoClause *into = stmt->into;
+       bool            is_matview = (into->viewQuery != NULL);
        DestReceiver *dest;
+       Oid                     save_userid = InvalidOid;
+       int                     save_sec_context = 0;
+       int                     save_nestlevel = 0;
        List       *rewritten;
        PlannedStmt *plan;
        QueryDesc  *queryDesc;
@@ -90,12 +95,28 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
        {
                ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
 
+               Assert(!is_matview);    /* excluded by syntax */
                ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
 
                return;
        }
        Assert(query->commandType == CMD_SELECT);
 
+       /*
+        * For materialized views, lock down security-restricted operations and
+        * arrange to make GUC variable changes local to this command.  This is
+        * not necessary for security, but this keeps the behavior similar to
+        * REFRESH MATERIALIZED VIEW.  Otherwise, one could create a materialized
+        * view not possible to refresh.
+        */
+       if (is_matview)
+       {
+               GetUserIdAndSecContext(&save_userid, &save_sec_context);
+               SetUserIdAndSecContext(save_userid,
+                                                  save_sec_context | SECURITY_RESTRICTED_OPERATION);
+               save_nestlevel = NewGUCNestLevel();
+       }
+
        /*
         * Parse analysis was done already, but we still have to run the rule
         * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
@@ -160,6 +181,15 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
        FreeQueryDesc(queryDesc);
 
        PopActiveSnapshot();
+
+       if (is_matview)
+       {
+               /* Roll back any GUC changes */
+               AtEOXact_GUC(false, save_nestlevel);
+
+               /* Restore userid and security context */
+               SetUserIdAndSecContext(save_userid, save_sec_context);
+       }
 }
 
 /*
index 2ffdca31f6ba35e2c95d75fcba5bf850f7a20aa3..1c383baf68750808320ebc5e9925b122c802a639 100644 (file)
@@ -122,6 +122,9 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
        RewriteRule *rule;
        List       *actions;
        Query      *dataQuery;
+       Oid                     save_userid;
+       int                     save_sec_context;
+       int                     save_nestlevel;
        Oid                     tableSpace;
        Oid                     OIDNewHeap;
        DestReceiver *dest;
@@ -191,6 +194,16 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
         */
        CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW");
 
+       /*
+        * Switch to the owner's userid, so that any functions are run as that
+        * user.  Also lock down security-restricted operations and arrange to
+        * make GUC variable changes local to this command.
+        */
+       GetUserIdAndSecContext(&save_userid, &save_sec_context);
+       SetUserIdAndSecContext(matviewRel->rd_rel->relowner,
+                                                  save_sec_context | SECURITY_RESTRICTED_OPERATION);
+       save_nestlevel = NewGUCNestLevel();
+
        /*
         * Tentatively mark the matview as populated or not (this will roll back
         * if we fail later).
@@ -217,6 +230,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
                                         RecentXmin, ReadNextMultiXactId());
 
        RelationCacheInvalidateEntry(matviewOid);
+
+       /* Roll back any GUC changes */
+       AtEOXact_GUC(false, save_nestlevel);
+
+       /* Restore userid and security context */
+       SetUserIdAndSecContext(save_userid, save_sec_context);
 }
 
 /*