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.
This commit is contained in:
parent
448fee2e23
commit
f3ab5d4696
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue