Disallow converting a table to a view if row security is present.
When DefineQueryRewrite() is about to convert a table to a view, it checks the table for features unavailable to views. For example, it rejects tables having triggers. It omits to reject tables having relrowsecurity or a pg_policy record. Fix that. To faciliate the repair, invent relation_has_policies() which indicates the presence of policies on a relation even when row security is disabled for that relation. Reported by Noah Misch. Patch by me, review by Stephen Frost. Back-patch to 9.5 where RLS was introduced.
This commit is contained in:
parent
f781a0f1d8
commit
d824e2800f
@ -1037,3 +1037,32 @@ get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
|
|||||||
|
|
||||||
return policy_oid;
|
return policy_oid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* relation_has_policies - Determine if relation has any policies
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
relation_has_policies(Relation rel)
|
||||||
|
{
|
||||||
|
Relation catalog;
|
||||||
|
ScanKeyData skey;
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple policy_tuple;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
catalog = heap_open(PolicyRelationId, AccessShareLock);
|
||||||
|
ScanKeyInit(&skey,
|
||||||
|
Anum_pg_policy_polrelid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(RelationGetRelid(rel)));
|
||||||
|
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
|
||||||
|
NULL, 1, &skey);
|
||||||
|
policy_tuple = systable_getnext(sscan);
|
||||||
|
if (HeapTupleIsValid(policy_tuple))
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(catalog, AccessShareLock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "catalog/objectaccess.h"
|
#include "catalog/objectaccess.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/storage.h"
|
#include "catalog/storage.h"
|
||||||
|
#include "commands/policy.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "parser/parse_utilcmd.h"
|
#include "parser/parse_utilcmd.h"
|
||||||
@ -410,11 +411,12 @@ DefineQueryRewrite(char *rulename,
|
|||||||
*
|
*
|
||||||
* If so, check that the relation is empty because the storage for the
|
* If so, check that the relation is empty because the storage for the
|
||||||
* relation is going to be deleted. Also insist that the rel not have
|
* relation is going to be deleted. Also insist that the rel not have
|
||||||
* any triggers, indexes, or child tables. (Note: these tests are too
|
* any triggers, indexes, child tables, policies, or RLS enabled.
|
||||||
* strict, because they will reject relations that once had such but
|
* (Note: these tests are too strict, because they will reject
|
||||||
* don't anymore. But we don't really care, because this whole
|
* relations that once had such but don't anymore. But we don't
|
||||||
* business of converting relations to views is just a kluge to allow
|
* really care, because this whole business of converting relations
|
||||||
* dump/reload of views that participate in circular dependencies.)
|
* to views is just a kluge to allow dump/reload of views that
|
||||||
|
* participate in circular dependencies.)
|
||||||
*/
|
*/
|
||||||
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
|
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
|
||||||
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
|
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
|
||||||
@ -451,6 +453,18 @@ DefineQueryRewrite(char *rulename,
|
|||||||
errmsg("could not convert table \"%s\" to a view because it has child tables",
|
errmsg("could not convert table \"%s\" to a view because it has child tables",
|
||||||
RelationGetRelationName(event_relation))));
|
RelationGetRelationName(event_relation))));
|
||||||
|
|
||||||
|
if (event_relation->rd_rel->relrowsecurity)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
|
errmsg("could not convert table \"%s\" to a view because it has row security enabled",
|
||||||
|
RelationGetRelationName(event_relation))));
|
||||||
|
|
||||||
|
if (relation_has_policies(event_relation))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
|
errmsg("could not convert table \"%s\" to a view because it has row security policies",
|
||||||
|
RelationGetRelationName(event_relation))));
|
||||||
|
|
||||||
RelisBecomingView = true;
|
RelisBecomingView = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,6 @@ extern Oid get_relation_policy_oid(Oid relid, const char *policy_name,
|
|||||||
|
|
||||||
extern ObjectAddress rename_policy(RenameStmt *stmt);
|
extern ObjectAddress rename_policy(RenameStmt *stmt);
|
||||||
|
|
||||||
|
extern bool relation_has_policies(Relation rel);
|
||||||
|
|
||||||
#endif /* POLICY_H */
|
#endif /* POLICY_H */
|
||||||
|
@ -2997,6 +2997,29 @@ DROP ROLE bob; -- succeeds
|
|||||||
ROLLBACK TO q;
|
ROLLBACK TO q;
|
||||||
ROLLBACK; -- cleanup
|
ROLLBACK; -- cleanup
|
||||||
--
|
--
|
||||||
|
-- Converting table to view
|
||||||
|
--
|
||||||
|
BEGIN;
|
||||||
|
SET ROW_SECURITY = FORCE;
|
||||||
|
CREATE TABLE t (c int);
|
||||||
|
CREATE POLICY p ON t USING (c % 2 = 1);
|
||||||
|
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
|
||||||
|
SAVEPOINT q;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled
|
||||||
|
ERROR: could not convert table "t" to a view because it has row security enabled
|
||||||
|
ROLLBACK TO q;
|
||||||
|
ALTER TABLE t DISABLE ROW LEVEL SECURITY;
|
||||||
|
SAVEPOINT q;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
|
||||||
|
ERROR: could not convert table "t" to a view because it has row security policies
|
||||||
|
ROLLBACK TO q;
|
||||||
|
DROP POLICY p ON t;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- succeeds
|
||||||
|
ROLLBACK;
|
||||||
|
--
|
||||||
-- Clean up objects
|
-- Clean up objects
|
||||||
--
|
--
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
|
@ -1260,6 +1260,31 @@ ROLLBACK TO q;
|
|||||||
|
|
||||||
ROLLBACK; -- cleanup
|
ROLLBACK; -- cleanup
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Converting table to view
|
||||||
|
--
|
||||||
|
BEGIN;
|
||||||
|
SET ROW_SECURITY = FORCE;
|
||||||
|
CREATE TABLE t (c int);
|
||||||
|
CREATE POLICY p ON t USING (c % 2 = 1);
|
||||||
|
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
SAVEPOINT q;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled
|
||||||
|
ROLLBACK TO q;
|
||||||
|
|
||||||
|
ALTER TABLE t DISABLE ROW LEVEL SECURITY;
|
||||||
|
SAVEPOINT q;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
|
||||||
|
ROLLBACK TO q;
|
||||||
|
|
||||||
|
DROP POLICY p ON t;
|
||||||
|
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
|
||||||
|
SELECT * FROM generate_series(1,5) t0(c); -- succeeds
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Clean up objects
|
-- Clean up objects
|
||||||
--
|
--
|
||||||
|
Loading…
x
Reference in New Issue
Block a user