Skip to content

Commit f3ab5d4

Browse files
committed
Switch user ID to the object owner when populating a materialized view.
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.
1 parent 448fee2 commit f3ab5d4

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

doc/src/sgml/ref/create_materialized_view.sgml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ CREATE MATERIALIZED VIEW <replaceable>table_name</replaceable>
105105
<listitem>
106106
<para>
107107
A <xref linkend="sql-select">, <link linkend="sql-table">TABLE</link>,
108-
or <xref linkend="sql-values"> command.
108+
or <xref linkend="sql-values"> command. This query will run within a
109+
security-restricted operation; in particular, calls to functions that
110+
themselves create temporary tables will fail.
109111
</para>
110112
</listitem>
111113
</varlistentry>

src/backend/commands/createas.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "commands/prepare.h"
3434
#include "commands/tablecmds.h"
3535
#include "commands/view.h"
36+
#include "miscadmin.h"
3637
#include "parser/parse_clause.h"
3738
#include "rewrite/rewriteHandler.h"
3839
#include "storage/smgr.h"
@@ -69,7 +70,11 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
6970
{
7071
Query *query = (Query *) stmt->query;
7172
IntoClause *into = stmt->into;
73+
bool is_matview = (into->viewQuery != NULL);
7274
DestReceiver *dest;
75+
Oid save_userid = InvalidOid;
76+
int save_sec_context = 0;
77+
int save_nestlevel = 0;
7378
List *rewritten;
7479
PlannedStmt *plan;
7580
QueryDesc *queryDesc;
@@ -90,12 +95,28 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
9095
{
9196
ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
9297

98+
Assert(!is_matview); /* excluded by syntax */
9399
ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
94100

95101
return;
96102
}
97103
Assert(query->commandType == CMD_SELECT);
98104

105+
/*
106+
* For materialized views, lock down security-restricted operations and
107+
* arrange to make GUC variable changes local to this command. This is
108+
* not necessary for security, but this keeps the behavior similar to
109+
* REFRESH MATERIALIZED VIEW. Otherwise, one could create a materialized
110+
* view not possible to refresh.
111+
*/
112+
if (is_matview)
113+
{
114+
GetUserIdAndSecContext(&save_userid, &save_sec_context);
115+
SetUserIdAndSecContext(save_userid,
116+
save_sec_context | SECURITY_RESTRICTED_OPERATION);
117+
save_nestlevel = NewGUCNestLevel();
118+
}
119+
99120
/*
100121
* Parse analysis was done already, but we still have to run the rule
101122
* rewriter. We do not do AcquireRewriteLocks: we assume the query either
@@ -160,6 +181,15 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
160181
FreeQueryDesc(queryDesc);
161182

162183
PopActiveSnapshot();
184+
185+
if (is_matview)
186+
{
187+
/* Roll back any GUC changes */
188+
AtEOXact_GUC(false, save_nestlevel);
189+
190+
/* Restore userid and security context */
191+
SetUserIdAndSecContext(save_userid, save_sec_context);
192+
}
163193
}
164194

165195
/*

src/backend/commands/matview.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
122122
RewriteRule *rule;
123123
List *actions;
124124
Query *dataQuery;
125+
Oid save_userid;
126+
int save_sec_context;
127+
int save_nestlevel;
125128
Oid tableSpace;
126129
Oid OIDNewHeap;
127130
DestReceiver *dest;
@@ -191,6 +194,16 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
191194
*/
192195
CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW");
193196

197+
/*
198+
* Switch to the owner's userid, so that any functions are run as that
199+
* user. Also lock down security-restricted operations and arrange to
200+
* make GUC variable changes local to this command.
201+
*/
202+
GetUserIdAndSecContext(&save_userid, &save_sec_context);
203+
SetUserIdAndSecContext(matviewRel->rd_rel->relowner,
204+
save_sec_context | SECURITY_RESTRICTED_OPERATION);
205+
save_nestlevel = NewGUCNestLevel();
206+
194207
/*
195208
* Tentatively mark the matview as populated or not (this will roll back
196209
* if we fail later).
@@ -217,6 +230,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
217230
RecentXmin, ReadNextMultiXactId());
218231

219232
RelationCacheInvalidateEntry(matviewOid);
233+
234+
/* Roll back any GUC changes */
235+
AtEOXact_GUC(false, save_nestlevel);
236+
237+
/* Restore userid and security context */
238+
SetUserIdAndSecContext(save_userid, save_sec_context);
220239
}
221240

222241
/*

0 commit comments

Comments
 (0)