mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-13 19:57:53 +08:00
Clean up some mess in row-security patches.
Fix unsafe coding around PG_TRY in RelationBuildRowSecurity: can't change a variable inside PG_TRY and then use it in PG_CATCH without marking it "volatile". In this case though it seems saner to avoid that by doing a single assignment before entering the TRY block. I started out just intending to fix that, but the more I looked at the row-security code the more distressed I got. This patch also fixes incorrect construction of the RowSecurityPolicy cache entries (there was not sufficient care taken to copy pass-by-ref data into the cache memory context) and a whole bunch of sloppiness around the definition and use of pg_policy.polcmd. You can't use nulls in that column because initdb will mark it NOT NULL --- and I see no particular reason why a null entry would be a good idea anyway, so changing initdb's behavior is not the right answer. The internal value of '\0' wouldn't be suitable in a "char" column either, so after a bit of thought I settled on using '*' to represent ALL. Chasing those changes down also revealed that somebody wasn't paying attention to what the underlying values of ACL_UPDATE_CHR etc really were, and there was a great deal of lackadaiscalness in the catalogs.sgml documentation for pg_policy and pg_policies too. This doesn't pretend to be a complete code review for the row-security stuff, it just fixes the things that were in my face while dealing with the bugs in RelationBuildRowSecurity.
This commit is contained in:
parent
f8a4dd2e14
commit
fd496129d1
@ -218,6 +218,11 @@
|
||||
<entry>template data for procedural languages</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link></entry>
|
||||
<entry>row-security policies</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link></entry>
|
||||
<entry>functions and procedures</entry>
|
||||
@ -238,11 +243,6 @@
|
||||
<entry>replication slot information</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link></entry>
|
||||
<entry>table policies</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
|
||||
<entry>security labels on database objects</entry>
|
||||
@ -1939,16 +1939,6 @@
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relrowsecurity</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
True if table has row level security enabled; see
|
||||
<link linkend="catalog-pg-policy"><structname>pg_policy</structname></link> catalog
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relhassubclass</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
@ -1956,6 +1946,16 @@
|
||||
<entry>True if table has (or once had) any inheritance children</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relrowsecurity</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
True if table has row-level security enabled; see
|
||||
<link linkend="catalog-pg-policy"><structname>pg_policy</structname></link> catalog
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>relispopulated</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
@ -4711,6 +4711,98 @@
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-policy">
|
||||
<title><structname>pg_policy</structname></title>
|
||||
|
||||
<indexterm zone="catalog-pg-policy">
|
||||
<primary>pg_policy</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
The catalog <structname>pg_policy</structname> stores row-level
|
||||
security policies for tables. A policy includes the kind of
|
||||
command that it applies to (possibly all commands), the roles that it
|
||||
applies to, the expression to be added as a security-barrier
|
||||
qualification to queries that include the table, and the expression
|
||||
to be added as a <literal>WITH CHECK</> option for queries that attempt to
|
||||
add new records to the table.
|
||||
</para>
|
||||
|
||||
<table>
|
||||
|
||||
<title><structname>pg_policy</structname> Columns</title>
|
||||
|
||||
<tgroup cols="4">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Name</entry>
|
||||
<entry>Type</entry>
|
||||
<entry>References</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><structfield>polname</structfield></entry>
|
||||
<entry><type>name</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The name of the policy</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polrelid</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
|
||||
<entry>The table to which the policy applies</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polcmd</structfield></entry>
|
||||
<entry><type>char</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The command type to which the policy is applied:
|
||||
<literal>r</> for <command>SELECT</>,
|
||||
<literal>a</> for <command>INSERT</>,
|
||||
<literal>w</> for <command>UPDATE</>,
|
||||
<literal>d</> for <command>DELETE</>,
|
||||
or <literal>*</> for all</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polroles</structfield></entry>
|
||||
<entry><type>oid[]</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
|
||||
<entry>The roles to which the policy is applied</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polqual</structfield></entry>
|
||||
<entry><type>pg_node_tree</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression tree to be added to the security barrier qualifications for queries that use the table</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polwithcheck</structfield></entry>
|
||||
<entry><type>pg_node_tree</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression tree to be added to the WITH CHECK qualifications for queries that attempt to add rows to the table</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
Policies stored in <structname>pg_policy</> are applied only when
|
||||
<structname>pg_class</>.<structfield>relrowsecurity</> is set for
|
||||
their table.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-proc">
|
||||
<title><structname>pg_proc</structname></title>
|
||||
@ -5342,94 +5434,6 @@
|
||||
</table>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-policy">
|
||||
<title><structname>pg_policy</structname></title>
|
||||
|
||||
<indexterm zone="catalog-pg-policy">
|
||||
<primary>pg_policy</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
The catalog <structname>pg_policy</structname> stores row-level
|
||||
security policies for each table. A policy includes the kind of
|
||||
command which it applies to (or all commands), the roles which it
|
||||
applies to, the expression to be added as a security-barrier
|
||||
qualification to queries which include the table and the expression
|
||||
to be added as a with-check option for queries which attempt to add
|
||||
new records to the table.
|
||||
</para>
|
||||
|
||||
<table>
|
||||
|
||||
<title><structname>pg_policy</structname> Columns</title>
|
||||
|
||||
<tgroup cols="4">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Name</entry>
|
||||
<entry>Type</entry>
|
||||
<entry>References</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><structfield>polname</structfield></entry>
|
||||
<entry><type>name</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The name of the policy</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polrelid</structfield></entry>
|
||||
<entry><type>oid</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
|
||||
<entry>The table to which the policy belongs</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polcmd</structfield></entry>
|
||||
<entry><type>char</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The command type to which the policy is applied.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polroles</structfield></entry>
|
||||
<entry><type>char</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The roles to which the policy is applied.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polqual</structfield></entry>
|
||||
<entry><type>pg_node_tree</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression tree to be added to the security barrier qualifications for queries which use the table.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>polwithcheck</structfield></entry>
|
||||
<entry><type>pg_node_tree</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression tree to be added to the with check qualifications for queries which attempt to add rows to the table.</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
<literal>pg_class.relrowsecurity</literal>
|
||||
True if the table has row security enabled. Policies will not be applied
|
||||
unless row security is enabled on the table.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="catalog-pg-seclabel">
|
||||
<title><structname>pg_seclabel</structname></title>
|
||||
|
||||
@ -8166,7 +8170,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
||||
|
||||
<para>
|
||||
The view <structname>pg_policies</structname> provides access to
|
||||
useful information about each policy in the database.
|
||||
useful information about each row-level security policy in the database.
|
||||
</para>
|
||||
|
||||
<table>
|
||||
@ -8197,34 +8201,34 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
||||
<row>
|
||||
<entry><structfield>policyname</structfield></entry>
|
||||
<entry><type>name</type></entry>
|
||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relname</literal></entry>
|
||||
<entry><literal><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link>.polname</literal></entry>
|
||||
<entry>Name of policy</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>cmd</structfield></entry>
|
||||
<entry><type>text</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The command type to which the policy is applied.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>roles</structfield></entry>
|
||||
<entry><type>name[]</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The roles to which this policy applies.</entry>
|
||||
<entry>The roles to which this policy applies</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>cmd</structfield></entry>
|
||||
<entry><type>text</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The command type to which the policy is applied</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>qual</structfield></entry>
|
||||
<entry><type>text</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression added to the security barrier qualifications for
|
||||
queries which this policy applies to.</entry>
|
||||
queries that this policy applies to</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><structfield>with_check</structfield></entry>
|
||||
<entry><type>text</type></entry>
|
||||
<entry></entry>
|
||||
<entry>The expression added to the with check qualifications for
|
||||
queries which attempt to add rows to this table.</entry>
|
||||
<entry>The expression added to the WITH CHECK qualifications for
|
||||
queries that attempt to add rows to this table</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
@ -79,13 +79,12 @@ CREATE VIEW pg_policies AS
|
||||
WHERE oid = ANY (pol.polroles) ORDER BY 1
|
||||
)
|
||||
END AS roles,
|
||||
CASE WHEN pol.polcmd IS NULL THEN 'ALL' ELSE
|
||||
CASE pol.polcmd
|
||||
WHEN 'r' THEN 'SELECT'
|
||||
WHEN 'a' THEN 'INSERT'
|
||||
WHEN 'u' THEN 'UPDATE'
|
||||
WHEN 'd' THEN 'DELETE'
|
||||
END
|
||||
CASE pol.polcmd
|
||||
WHEN 'r' THEN 'SELECT'
|
||||
WHEN 'a' THEN 'INSERT'
|
||||
WHEN 'w' THEN 'UPDATE'
|
||||
WHEN 'd' THEN 'DELETE'
|
||||
WHEN '*' THEN 'ALL'
|
||||
END AS cmd,
|
||||
pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS qual,
|
||||
pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
|
||||
|
@ -28,10 +28,10 @@
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_clause.h"
|
||||
#include "parser/parse_node.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "rewrite/rowsecurity.h"
|
||||
#include "storage/lock.h"
|
||||
#include "utils/acl.h"
|
||||
@ -109,10 +109,10 @@ parse_policy_command(const char *cmd_name)
|
||||
char cmd;
|
||||
|
||||
if (!cmd_name)
|
||||
elog(ERROR, "unrecognized command");
|
||||
elog(ERROR, "unrecognized policy command");
|
||||
|
||||
if (strcmp(cmd_name, "all") == 0)
|
||||
cmd = 0;
|
||||
cmd = '*';
|
||||
else if (strcmp(cmd_name, "select") == 0)
|
||||
cmd = ACL_SELECT_CHR;
|
||||
else if (strcmp(cmd_name, "insert") == 0)
|
||||
@ -122,7 +122,7 @@ parse_policy_command(const char *cmd_name)
|
||||
else if (strcmp(cmd_name, "delete") == 0)
|
||||
cmd = ACL_DELETE_CHR;
|
||||
else
|
||||
elog(ERROR, "unrecognized command");
|
||||
elog(ERROR, "unrecognized policy command");
|
||||
|
||||
return cmd;
|
||||
}
|
||||
@ -190,44 +190,54 @@ policy_role_list_to_array(List *roles)
|
||||
}
|
||||
|
||||
/*
|
||||
* Load row security policy from the catalog, and keep it in
|
||||
* the relation cache.
|
||||
* Load row security policy from the catalog, and store it in
|
||||
* the relation's relcache entry.
|
||||
*
|
||||
* We will always set up some kind of policy here. If no explicit policies
|
||||
* are found then an implicit default-deny policy is created.
|
||||
*/
|
||||
void
|
||||
RelationBuildRowSecurity(Relation relation)
|
||||
{
|
||||
Relation catalog;
|
||||
ScanKeyData skey;
|
||||
SysScanDesc sscan;
|
||||
HeapTuple tuple;
|
||||
MemoryContext oldcxt;
|
||||
MemoryContext rscxt = NULL;
|
||||
RowSecurityDesc *rsdesc = NULL;
|
||||
MemoryContext rscxt;
|
||||
MemoryContext oldcxt = CurrentMemoryContext;
|
||||
RowSecurityDesc * volatile rsdesc = NULL;
|
||||
|
||||
catalog = heap_open(PolicyRelationId, AccessShareLock);
|
||||
/*
|
||||
* Create a memory context to hold everything associated with this
|
||||
* relation's row security policy. This makes it easy to clean up
|
||||
* during a relcache flush.
|
||||
*/
|
||||
rscxt = AllocSetContextCreate(CacheMemoryContext,
|
||||
"row security descriptor",
|
||||
ALLOCSET_SMALL_MINSIZE,
|
||||
ALLOCSET_SMALL_INITSIZE,
|
||||
ALLOCSET_SMALL_MAXSIZE);
|
||||
|
||||
ScanKeyInit(&skey,
|
||||
Anum_pg_policy_polrelid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||
|
||||
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
|
||||
NULL, 1, &skey);
|
||||
/*
|
||||
* Since rscxt lives under CacheMemoryContext, it is long-lived. Use
|
||||
* a PG_TRY block to ensure it'll get freed if we fail partway through.
|
||||
*/
|
||||
PG_TRY();
|
||||
{
|
||||
/*
|
||||
* Set up our memory context- we will always set up some kind of
|
||||
* policy here. If no explicit policies are found then an implicit
|
||||
* default-deny policy is created.
|
||||
*/
|
||||
rscxt = AllocSetContextCreate(CacheMemoryContext,
|
||||
"row security descriptor",
|
||||
ALLOCSET_SMALL_MINSIZE,
|
||||
ALLOCSET_SMALL_INITSIZE,
|
||||
ALLOCSET_SMALL_MAXSIZE);
|
||||
Relation catalog;
|
||||
ScanKeyData skey;
|
||||
SysScanDesc sscan;
|
||||
HeapTuple tuple;
|
||||
|
||||
rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
|
||||
rsdesc->rscxt = rscxt;
|
||||
|
||||
catalog = heap_open(PolicyRelationId, AccessShareLock);
|
||||
|
||||
ScanKeyInit(&skey,
|
||||
Anum_pg_policy_polrelid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||
|
||||
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
|
||||
NULL, 1, &skey);
|
||||
|
||||
/*
|
||||
* Loop through the row level security policies for this relation, if
|
||||
* any.
|
||||
@ -236,7 +246,7 @@ RelationBuildRowSecurity(Relation relation)
|
||||
{
|
||||
Datum value_datum;
|
||||
char cmd_value;
|
||||
ArrayType *roles;
|
||||
Datum roles_datum;
|
||||
char *qual_value;
|
||||
Expr *qual_expr;
|
||||
char *with_check_value;
|
||||
@ -244,29 +254,33 @@ RelationBuildRowSecurity(Relation relation)
|
||||
char *policy_name_value;
|
||||
Oid policy_id;
|
||||
bool isnull;
|
||||
RowSecurityPolicy *policy = NULL;
|
||||
RowSecurityPolicy *policy;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(rscxt);
|
||||
/*
|
||||
* Note: all the pass-by-reference data we collect here is either
|
||||
* still stored in the tuple, or constructed in the caller's
|
||||
* short-lived memory context. We must copy it into rscxt
|
||||
* explicitly below.
|
||||
*/
|
||||
|
||||
/* Get policy command */
|
||||
value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
|
||||
RelationGetDescr(catalog), &isnull);
|
||||
if (isnull)
|
||||
cmd_value = 0;
|
||||
else
|
||||
cmd_value = DatumGetChar(value_datum);
|
||||
Assert(!isnull);
|
||||
cmd_value = DatumGetChar(value_datum);
|
||||
|
||||
/* Get policy name */
|
||||
value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
|
||||
RelationGetDescr(catalog), &isnull);
|
||||
Assert(!isnull);
|
||||
policy_name_value = DatumGetCString(value_datum);
|
||||
policy_name_value = NameStr(*(DatumGetName(value_datum)));
|
||||
|
||||
/* Get policy roles */
|
||||
value_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
|
||||
roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
|
||||
RelationGetDescr(catalog), &isnull);
|
||||
Assert(!isnull);
|
||||
roles = DatumGetArrayTypeP(value_datum);
|
||||
/* shouldn't be null, but initdb doesn't mark it so, so check */
|
||||
if (isnull)
|
||||
elog(ERROR, "unexpected null value in pg_policy.polroles");
|
||||
|
||||
/* Get policy qual */
|
||||
value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
|
||||
@ -282,7 +296,6 @@ RelationBuildRowSecurity(Relation relation)
|
||||
/* Get WITH CHECK qual */
|
||||
value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
|
||||
RelationGetDescr(catalog), &isnull);
|
||||
|
||||
if (!isnull)
|
||||
{
|
||||
with_check_value = TextDatumGetCString(value_datum);
|
||||
@ -293,27 +306,33 @@ RelationBuildRowSecurity(Relation relation)
|
||||
|
||||
policy_id = HeapTupleGetOid(tuple);
|
||||
|
||||
/* Now copy everything into the cache context */
|
||||
MemoryContextSwitchTo(rscxt);
|
||||
|
||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||
policy->policy_name = policy_name_value;
|
||||
policy->policy_name = pstrdup(policy_name_value);
|
||||
policy->policy_id = policy_id;
|
||||
policy->cmd = cmd_value;
|
||||
policy->roles = roles;
|
||||
policy->polcmd = cmd_value;
|
||||
policy->roles = DatumGetArrayTypePCopy(roles_datum);
|
||||
policy->qual = copyObject(qual_expr);
|
||||
policy->with_check_qual = copyObject(with_check_qual);
|
||||
policy->hassublinks = contain_subplans((Node *) qual_expr) ||
|
||||
contain_subplans((Node *) with_check_qual);
|
||||
policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
|
||||
checkExprHasSubLink((Node *) with_check_qual);
|
||||
|
||||
rsdesc->policies = lcons(policy, rsdesc->policies);
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
/* clean up some (not all) of the junk ... */
|
||||
if (qual_expr != NULL)
|
||||
pfree(qual_expr);
|
||||
|
||||
if (with_check_qual != NULL)
|
||||
pfree(with_check_qual);
|
||||
}
|
||||
|
||||
systable_endscan(sscan);
|
||||
heap_close(catalog, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Check if no policies were added
|
||||
*
|
||||
@ -324,17 +343,17 @@ RelationBuildRowSecurity(Relation relation)
|
||||
*/
|
||||
if (rsdesc->policies == NIL)
|
||||
{
|
||||
RowSecurityPolicy *policy = NULL;
|
||||
RowSecurityPolicy *policy;
|
||||
Datum role;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(rscxt);
|
||||
MemoryContextSwitchTo(rscxt);
|
||||
|
||||
role = ObjectIdGetDatum(ACL_ID_PUBLIC);
|
||||
|
||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||
policy->policy_name = pstrdup("default-deny policy");
|
||||
policy->policy_id = InvalidOid;
|
||||
policy->cmd = '\0';
|
||||
policy->polcmd = '*';
|
||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
||||
'i');
|
||||
policy->qual = (Expr *) makeConst(BOOLOID, -1, InvalidOid,
|
||||
@ -350,15 +369,14 @@ RelationBuildRowSecurity(Relation relation)
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
if (rscxt != NULL)
|
||||
MemoryContextDelete(rscxt);
|
||||
/* Delete rscxt, first making sure it isn't active */
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
MemoryContextDelete(rscxt);
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
systable_endscan(sscan);
|
||||
heap_close(catalog, AccessShareLock);
|
||||
|
||||
/* Success --- attach the policy descriptor to the relcache entry */
|
||||
relation->rd_rsdesc = rsdesc;
|
||||
}
|
||||
|
||||
@ -555,27 +573,20 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
||||
stmt->policy_name, RelationGetRelationName(target_table))));
|
||||
|
||||
values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
|
||||
values[Anum_pg_policy_polname - 1]
|
||||
= DirectFunctionCall1(namein, CStringGetDatum(stmt->policy_name));
|
||||
|
||||
if (polcmd)
|
||||
values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
|
||||
else
|
||||
isnull[Anum_pg_policy_polcmd - 1] = true;
|
||||
|
||||
values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(stmt->policy_name));
|
||||
values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
|
||||
values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
|
||||
|
||||
/* Add qual if present. */
|
||||
if (qual)
|
||||
values[Anum_pg_policy_polqual - 1]
|
||||
= CStringGetTextDatum(nodeToString(qual));
|
||||
values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));
|
||||
else
|
||||
isnull[Anum_pg_policy_polqual - 1] = true;
|
||||
|
||||
/* Add WITH CHECK qual if present */
|
||||
if (with_check_qual)
|
||||
values[Anum_pg_policy_polwithcheck - 1]
|
||||
= CStringGetTextDatum(nodeToString(with_check_qual));
|
||||
values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));
|
||||
else
|
||||
isnull[Anum_pg_policy_polwithcheck - 1] = true;
|
||||
|
||||
@ -738,10 +749,8 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
||||
cmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
|
||||
RelationGetDescr(pg_policy_rel),
|
||||
&polcmd_isnull);
|
||||
if (polcmd_isnull)
|
||||
polcmd = 0;
|
||||
else
|
||||
polcmd = DatumGetChar(cmd_datum);
|
||||
Assert(!polcmd_isnull);
|
||||
polcmd = DatumGetChar(cmd_datum);
|
||||
|
||||
/*
|
||||
* If the command is SELECT or DELETE then WITH CHECK should be NULL.
|
||||
|
@ -273,8 +273,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
|
||||
* query, then set hasSubLinks on the Query to force subLinks to be
|
||||
* properly expanded.
|
||||
*/
|
||||
if (hassublinks)
|
||||
root->hasSubLinks = hassublinks;
|
||||
root->hasSubLinks |= hassublinks;
|
||||
|
||||
/* If we got this far, we must have added quals */
|
||||
return true;
|
||||
@ -305,36 +304,36 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
|
||||
policy = (RowSecurityPolicy *) lfirst(item);
|
||||
|
||||
/* Always add ALL policies, if they exist. */
|
||||
if (policy->cmd == '\0' &&
|
||||
if (policy->polcmd == '*' &&
|
||||
check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
|
||||
/* Build the list of policies to return. */
|
||||
/* Add relevant command-specific policies to the list. */
|
||||
switch(cmd)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
if (policy->cmd == ACL_SELECT_CHR
|
||||
if (policy->polcmd == ACL_SELECT_CHR
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
/* If INSERT then only need to add the WITH CHECK qual */
|
||||
if (policy->cmd == ACL_INSERT_CHR
|
||||
if (policy->polcmd == ACL_INSERT_CHR
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
if (policy->cmd == ACL_UPDATE_CHR
|
||||
if (policy->polcmd == ACL_UPDATE_CHR
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
if (policy->cmd == ACL_DELETE_CHR
|
||||
if (policy->polcmd == ACL_DELETE_CHR
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized command type.");
|
||||
elog(ERROR, "unrecognized policy command type %d", (int) cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -354,7 +353,7 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
|
||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||
policy->policy_name = pstrdup("default-deny policy");
|
||||
policy->policy_id = InvalidOid;
|
||||
policy->cmd = '\0';
|
||||
policy->polcmd = '*';
|
||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
||||
'i');
|
||||
policy->qual = (Expr *) makeConst(BOOLOID, -1, InvalidOid,
|
||||
|
2
src/backend/utils/cache/relcache.c
vendored
2
src/backend/utils/cache/relcache.c
vendored
@ -868,7 +868,7 @@ equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
|
||||
|
||||
if (policy1->policy_id != policy2->policy_id)
|
||||
return false;
|
||||
if (policy1->cmd != policy2->cmd)
|
||||
if (policy1->polcmd != policy2->polcmd)
|
||||
return false;
|
||||
if (policy1->hassublinks != policy2->hassublinks)
|
||||
return false;
|
||||
|
@ -2851,9 +2851,9 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||
appendPQExpBuffer(query,
|
||||
"SELECT oid, tableoid, pol.polname, pol.polcmd, "
|
||||
"CASE WHEN pol.polroles = '{0}' THEN 'PUBLIC' ELSE "
|
||||
" array_to_string(ARRAY(SELECT rolname from pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
|
||||
"pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
|
||||
"pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
|
||||
" pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
|
||||
"pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
|
||||
"pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
|
||||
"FROM pg_catalog.pg_policy pol "
|
||||
"WHERE polrelid = '%u'",
|
||||
tbinfo->dobj.catId.oid);
|
||||
@ -2865,7 +2865,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||
{
|
||||
/*
|
||||
* No explicit policies to handle (only the default-deny policy,
|
||||
* which is handled as part of the table definition. Clean up and
|
||||
* which is handled as part of the table definition). Clean up and
|
||||
* return.
|
||||
*/
|
||||
PQclear(res);
|
||||
@ -2892,14 +2892,9 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||
polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
|
||||
polinfo[j].poltable = tbinfo;
|
||||
polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
|
||||
|
||||
polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
|
||||
|
||||
if (PQgetisnull(res, j, i_polcmd))
|
||||
polinfo[j].polcmd = NULL;
|
||||
else
|
||||
polinfo[j].polcmd = pg_strdup(PQgetvalue(res, j, i_polcmd));
|
||||
|
||||
polinfo[j].polcmd = pg_strdup(PQgetvalue(res, j, i_polcmd));
|
||||
polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
|
||||
|
||||
if (PQgetisnull(res, j, i_polqual))
|
||||
@ -2959,7 +2954,7 @@ dumpPolicy(Archive *fout, DumpOptions *dopt, PolicyInfo *polinfo)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!polinfo->polcmd)
|
||||
if (strcmp(polinfo->polcmd, "*") == 0)
|
||||
cmd = "ALL";
|
||||
else if (strcmp(polinfo->polcmd, "r") == 0)
|
||||
cmd = "SELECT";
|
||||
@ -2971,15 +2966,16 @@ dumpPolicy(Archive *fout, DumpOptions *dopt, PolicyInfo *polinfo)
|
||||
cmd = "DELETE";
|
||||
else
|
||||
{
|
||||
write_msg(NULL, "unexpected command type: '%s'\n", polinfo->polcmd);
|
||||
write_msg(NULL, "unexpected policy command type: \"%s\"\n",
|
||||
polinfo->polcmd);
|
||||
exit_nicely(1);
|
||||
}
|
||||
|
||||
query = createPQExpBuffer();
|
||||
delqry = createPQExpBuffer();
|
||||
|
||||
appendPQExpBuffer(query, "CREATE POLICY %s ON %s FOR %s",
|
||||
polinfo->polname, fmtId(tbinfo->dobj.name), cmd);
|
||||
appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
|
||||
appendPQExpBuffer(query, " ON %s FOR %s", fmtId(tbinfo->dobj.name), cmd);
|
||||
|
||||
if (polinfo->polroles != NULL)
|
||||
appendPQExpBuffer(query, " TO %s", polinfo->polroles);
|
||||
@ -2992,8 +2988,8 @@ dumpPolicy(Archive *fout, DumpOptions *dopt, PolicyInfo *polinfo)
|
||||
|
||||
appendPQExpBuffer(query, ";\n");
|
||||
|
||||
appendPQExpBuffer(delqry, "DROP POLICY %s ON %s;\n",
|
||||
polinfo->polname, fmtId(tbinfo->dobj.name));
|
||||
appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
|
||||
appendPQExpBuffer(delqry, " ON %s;\n", fmtId(tbinfo->dobj.name));
|
||||
|
||||
ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
|
||||
polinfo->dobj.name,
|
||||
|
@ -784,8 +784,8 @@ permissionsList(const char *pattern)
|
||||
appendPQExpBuffer(&buf,
|
||||
",\n pg_catalog.array_to_string(ARRAY(\n"
|
||||
" SELECT polname\n"
|
||||
" || CASE WHEN polcmd IS NOT NULL THEN\n"
|
||||
" E' (' || polcmd || E')'\n"
|
||||
" || CASE WHEN polcmd != '*' THEN\n"
|
||||
" E' (' || polcmd || E'):'\n"
|
||||
" ELSE E':' \n"
|
||||
" END\n"
|
||||
" || CASE WHEN polqual IS NOT NULL THEN\n"
|
||||
@ -2031,9 +2031,10 @@ describeOneTableDetails(const char *schemaname,
|
||||
"pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
|
||||
"CASE pol.polcmd \n"
|
||||
"WHEN 'r' THEN 'SELECT'\n"
|
||||
"WHEN 'u' THEN 'UPDATE'\n"
|
||||
"WHEN 'a' THEN 'INSERT'\n"
|
||||
"WHEN 'w' THEN 'UPDATE'\n"
|
||||
"WHEN 'd' THEN 'DELETE'\n"
|
||||
"WHEN '*' THEN 'ALL'\n"
|
||||
"END AS cmd\n"
|
||||
"FROM pg_catalog.pg_policy pol\n"
|
||||
"WHERE pol.polrelid = '%s' ORDER BY 1;",
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201412301
|
||||
#define CATALOG_VERSION_NO 201501241
|
||||
|
||||
#endif
|
||||
|
@ -22,10 +22,10 @@ CATALOG(pg_policy,3256)
|
||||
{
|
||||
NameData polname; /* Policy name. */
|
||||
Oid polrelid; /* Oid of the relation with policy. */
|
||||
char polcmd; /* One of ACL_*_CHR, or \0 for all */
|
||||
char polcmd; /* One of ACL_*_CHR, or '*' for all */
|
||||
|
||||
#ifdef CATALOG_VARLEN
|
||||
Oid polroles[1] /* Roles associated with policy, not-NULL */
|
||||
Oid polroles[1]; /* Roles associated with policy, not-NULL */
|
||||
pg_node_tree polqual; /* Policy quals. */
|
||||
pg_node_tree polwithcheck; /* WITH CHECK quals. */
|
||||
#endif
|
||||
|
@ -21,11 +21,11 @@ typedef struct RowSecurityPolicy
|
||||
{
|
||||
Oid policy_id; /* OID of the policy */
|
||||
char *policy_name; /* Name of the policy */
|
||||
char cmd; /* Type of command policy is for */
|
||||
char polcmd; /* Type of command policy is for */
|
||||
ArrayType *roles; /* Array of roles policy is for */
|
||||
Expr *qual; /* Expression to filter rows */
|
||||
Expr *with_check_qual; /* Expression to limit rows allowed */
|
||||
bool hassublinks; /* If expression has sublinks */
|
||||
bool hassublinks; /* If either expression has sublinks */
|
||||
} RowSecurityPolicy;
|
||||
|
||||
typedef struct RowSecurityDesc
|
||||
|
@ -1363,16 +1363,13 @@ pg_policies| SELECT n.nspname AS schemaname,
|
||||
WHERE (pg_authid.oid = ANY (pol.polroles))
|
||||
ORDER BY pg_authid.rolname)
|
||||
END AS roles,
|
||||
CASE
|
||||
WHEN (pol.polcmd IS NULL) THEN 'ALL'::text
|
||||
ELSE
|
||||
CASE pol.polcmd
|
||||
WHEN 'r'::"char" THEN 'SELECT'::text
|
||||
WHEN 'a'::"char" THEN 'INSERT'::text
|
||||
WHEN 'u'::"char" THEN 'UPDATE'::text
|
||||
WHEN 'd'::"char" THEN 'DELETE'::text
|
||||
ELSE NULL::text
|
||||
END
|
||||
CASE pol.polcmd
|
||||
WHEN 'r'::"char" THEN 'SELECT'::text
|
||||
WHEN 'a'::"char" THEN 'INSERT'::text
|
||||
WHEN 'w'::"char" THEN 'UPDATE'::text
|
||||
WHEN 'd'::"char" THEN 'DELETE'::text
|
||||
WHEN '*'::"char" THEN 'ALL'::text
|
||||
ELSE NULL::text
|
||||
END AS cmd,
|
||||
pg_get_expr(pol.polqual, pol.polrelid) AS qual,
|
||||
pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
|
||||
|
Loading…
x
Reference in New Issue
Block a user