mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
Rename pg_rowsecurity -> pg_policy and other fixes
As pointed out by Robert, we should really have named pg_rowsecurity pg_policy, as the objects stored in that catalog are policies. This patch fixes that and updates the column names to start with 'pol' to match the new catalog name. The security consideration for COPY with row level security, also pointed out by Robert, has also been addressed by remembering and re-checking the OID of the relation initially referenced during COPY processing, to make sure it hasn't changed under us by the time we finish planning out the query which has been built. Robert and Alvaro also commented on missing OCLASS and OBJECT entries for POLICY (formerly ROWSECURITY or POLICY, depending) in various places. This patch fixes that too, which also happens to add the ability to COMMENT on policies. In passing, attempt to improve the consistency of messages, comments, and documentation as well. This removes various incarnations of 'row-security', 'row-level security', 'Row-security', etc, in favor of 'policy', 'row level security' or 'row_security' as appropriate. Happy Thanksgiving!
This commit is contained in:
parent
1812ee5767
commit
143b39c185
@ -239,8 +239,8 @@
|
|||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link></entry>
|
<entry><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link></entry>
|
||||||
<entry>table row-level security policies</entry>
|
<entry>table policies</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
@ -1944,8 +1944,8 @@
|
|||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>
|
<entry>
|
||||||
True if table has row-security enabled; see
|
True if table has row level security enabled; see
|
||||||
<link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link> catalog
|
<link linkend="catalog-pg-policy"><structname>pg_policy</structname></link> catalog
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
@ -5342,15 +5342,15 @@
|
|||||||
</table>
|
</table>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="catalog-pg-rowsecurity">
|
<sect1 id="catalog-pg-policy">
|
||||||
<title><structname>pg_rowsecurity</structname></title>
|
<title><structname>pg_policy</structname></title>
|
||||||
|
|
||||||
<indexterm zone="catalog-pg-rowsecurity">
|
<indexterm zone="catalog-pg-policy">
|
||||||
<primary>pg_rowsecurity</primary>
|
<primary>pg_policy</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The catalog <structname>pg_rowsecurity</structname> stores row-level
|
The catalog <structname>pg_policy</structname> stores row-level
|
||||||
security policies for each table. A policy includes the kind of
|
security policies for each table. A policy includes the kind of
|
||||||
command which it applies to (or all commands), the roles which it
|
command which it applies to (or all commands), the roles which it
|
||||||
applies to, the expression to be added as a security-barrier
|
applies to, the expression to be added as a security-barrier
|
||||||
@ -5361,7 +5361,7 @@
|
|||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|
||||||
<title><structname>pg_rowsecurity</structname> Columns</title>
|
<title><structname>pg_policy</structname> Columns</title>
|
||||||
|
|
||||||
<tgroup cols="4">
|
<tgroup cols="4">
|
||||||
<thead>
|
<thead>
|
||||||
@ -5375,42 +5375,42 @@
|
|||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rsecpolname</structfield></entry>
|
<entry><structfield>polname</structfield></entry>
|
||||||
<entry><type>name</type></entry>
|
<entry><type>name</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The name of the row-security policy</entry>
|
<entry>The name of the policy</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rsecrelid</structfield></entry>
|
<entry><structfield>polrelid</structfield></entry>
|
||||||
<entry><type>oid</type></entry>
|
<entry><type>oid</type></entry>
|
||||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
|
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
|
||||||
<entry>The table to which the row-security policy belongs</entry>
|
<entry>The table to which the policy belongs</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rseccmd</structfield></entry>
|
<entry><structfield>polcmd</structfield></entry>
|
||||||
<entry><type>char</type></entry>
|
<entry><type>char</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The command type to which the row-security policy is applied.</entry>
|
<entry>The command type to which the policy is applied.</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rsecroles</structfield></entry>
|
<entry><structfield>polroles</structfield></entry>
|
||||||
<entry><type>char</type></entry>
|
<entry><type>char</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The roles to which the row-security policy is applied.</entry>
|
<entry>The roles to which the policy is applied.</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rsecqual</structfield></entry>
|
<entry><structfield>polqual</structfield></entry>
|
||||||
<entry><type>pg_node_tree</type></entry>
|
<entry><type>pg_node_tree</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>The expression tree to be added to the security barrier qualifications for queries which use the table.</entry>
|
<entry>The expression tree to be added to the security barrier qualifications for queries which use the table.</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>rsecwithcheck</structfield></entry>
|
<entry><structfield>polwithcheck</structfield></entry>
|
||||||
<entry><type>pg_node_tree</type></entry>
|
<entry><type>pg_node_tree</type></entry>
|
||||||
<entry></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>
|
<entry>The expression tree to be added to the with check qualifications for queries which attempt to add rows to the table.</entry>
|
||||||
@ -5423,8 +5423,8 @@
|
|||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
<literal>pg_class.relrowsecurity</literal>
|
<literal>pg_class.relrowsecurity</literal>
|
||||||
True if the table has row-security enabled. Policies will not be applied
|
True if the table has row security enabled. Policies will not be applied
|
||||||
unless row-security is enabled on the table.
|
unless row security is enabled on the table.
|
||||||
</para>
|
</para>
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
|
@ -1558,8 +1558,8 @@ REVOKE ALL ON accounts FROM PUBLIC;
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
To specify which rows are visible and what rows can be added to the
|
To specify which rows are visible and what rows can be added to the
|
||||||
table with row security, an expression is required which returns a
|
table with row level security, an expression is required which returns
|
||||||
boolean result. This expression will be evaluated for each row prior
|
a boolean result. This expression will be evaluated for each row prior
|
||||||
to other conditionals or functions which are part of the query. The
|
to other conditionals or functions which are part of the query. The
|
||||||
one exception to this rule are <literal>leakproof</literal> functions,
|
one exception to this rule are <literal>leakproof</literal> functions,
|
||||||
which are guaranteed to not leak information. Two expressions may be
|
which are guaranteed to not leak information. Two expressions may be
|
||||||
|
@ -16,7 +16,7 @@ PostgreSQL documentation
|
|||||||
|
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
<refname>ALTER POLICY</refname>
|
<refname>ALTER POLICY</refname>
|
||||||
<refpurpose>change the definition of a row-security policy</refpurpose>
|
<refpurpose>change the definition of a policy</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
@ -34,7 +34,7 @@ ALTER POLICY <replaceable class="parameter">name</replaceable> ON <replaceable c
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>ALTER POLICY</command> changes the <replaceable class="parameter">
|
<command>ALTER POLICY</command> changes the <replaceable class="parameter">
|
||||||
definition</replaceable> of an existing row-security policy.
|
definition</replaceable> of an existing policy.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -16,7 +16,7 @@ PostgreSQL documentation
|
|||||||
|
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
<refname>CREATE POLICY</refname>
|
<refname>CREATE POLICY</refname>
|
||||||
<refpurpose>define a new row-security policy for a table</refpurpose>
|
<refpurpose>define a new policy for a table</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
@ -33,14 +33,13 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
|
|||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The <command>CREATE POLICY</command> command defines a new row-security
|
The <command>CREATE POLICY</command> command defines a new policy for a
|
||||||
policy for a table. Note that row-security must also be enabled on the
|
table. Note that row level security must also be enabled on the table using
|
||||||
table using <command>ALTER TABLE</command> in order for created policies
|
<command>ALTER TABLE</command> in order for created policies to be applied.
|
||||||
to be applied.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
A row-security policy is an expression which is added to the security-barrier
|
A policy is an expression which is added to the security-barrier
|
||||||
qualifications of queries which are run against the table the policy is on,
|
qualifications of queries which are run against the table the policy is on,
|
||||||
or an expression which is added to the with-check options for a table and
|
or an expression which is added to the with-check options for a table and
|
||||||
which is applied to rows which would be added to the table.
|
which is applied to rows which would be added to the table.
|
||||||
@ -49,7 +48,7 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
|
|||||||
expression will be evaluated against the rows which are going to be added to
|
expression will be evaluated against the rows which are going to be added to
|
||||||
the table. By adding policies to a table, a user can limit the rows which a
|
the table. By adding policies to a table, a user can limit the rows which a
|
||||||
given user can select, insert, update, or delete. This capability is also
|
given user can select, insert, update, or delete. This capability is also
|
||||||
known as Row-Level Security or RLS.
|
known as Row Level Security or RLS.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -66,22 +65,22 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Note that while row-security policies will be applied for explicit queries
|
Note that while policies will be applied for explicit queries against tables
|
||||||
against tables in the system, they are not applied when the system is
|
in the system, they are not applied when the system is performing internal
|
||||||
performing internal referential integrity checks or validating constraints.
|
referential integrity checks or validating constraints. This means there are
|
||||||
This means there are indirect ways to determine that a given value exists.
|
indirect ways to determine that a given value exists. An example of this is
|
||||||
An example of this is attempting to insert a duplicate value
|
attempting to insert a duplicate value into a column which is the primary key
|
||||||
into a column which is the primary key or has a unique constraint. If the
|
or has a unique constraint. If the insert fails then the user can infer that
|
||||||
insert fails then the user can infer that the value already exists (this
|
the value already exists (this example assumes that the user is permitted by
|
||||||
example assumes that the user is permitted by policy to insert
|
policy to insert records which they are not allowed to see). Another example
|
||||||
records which they are not allowed to see). Another example is where a user
|
is where a user is allowed to insert into a table which references another,
|
||||||
is allowed to insert into a table which references another, otherwise hidden
|
otherwise hidden table. Existence can be determined by the user inserting
|
||||||
table. Existence can be determined by the user inserting values into the
|
values into the referencing table, where success would indicate that the
|
||||||
referencing table, where success would indicate that the value exists in the
|
value exists in the referenced table. These issues can be addressed by
|
||||||
referenced table. These issues can be addressed by carefully crafting
|
carefully crafting policies which prevent users from being able to insert,
|
||||||
policies which prevent users from being able to insert, delete, or update
|
delete, or update records at all which might possibly indicate a value they
|
||||||
records at all which might possibly indicate a value they are not otherwise
|
are not otherwise able to see, or by using generated values (e.g.: surrogate
|
||||||
able to see, or by using generated values (e.g.: surrogate keys) instead.
|
keys) instead.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -291,8 +290,8 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
In order to maintain <firstterm>referential integrity</firstterm> between
|
In order to maintain <firstterm>referential integrity</firstterm> between
|
||||||
two related tables, row-security policies are not applied when the system
|
two related tables, policies are not applied when the system performs
|
||||||
performs checks on foreign key constraints.
|
checks on foreign key constraints.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -16,7 +16,7 @@ PostgreSQL documentation
|
|||||||
|
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
<refname>DROP POLICY</refname>
|
<refname>DROP POLICY</refname>
|
||||||
<refpurpose>remove a row-security policy from a table</refpurpose>
|
<refpurpose>remove a policy from a table</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
@ -29,11 +29,11 @@ DROP POLICY [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ON <
|
|||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>DROP POLICY</command> removes the specified row-security policy
|
<command>DROP POLICY</command> removes the specified policy from the table.
|
||||||
from the table. Note that if the last policy is removed for a table and
|
Note that if the last policy is removed for a table and the table still has
|
||||||
the table still has ROW POLICY enabled via <command>ALTER TABLE</command>,
|
row level security enabled via <command>ALTER TABLE</command>, then the
|
||||||
then the default-deny policy will be used. <command>ALTER TABLE</command>
|
default-deny policy will be used. <command>ALTER TABLE</command> can be used
|
||||||
can be used to disable row security for a table using
|
to disable row level security for a table using
|
||||||
<literal>DISABLE ROW SECURITY</literal>, whether policies for the table
|
<literal>DISABLE ROW SECURITY</literal>, whether policies for the table
|
||||||
exist or not.
|
exist or not.
|
||||||
</para>
|
</para>
|
||||||
@ -80,8 +80,8 @@ DROP POLICY [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ON <
|
|||||||
<title>Examples</title>
|
<title>Examples</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To drop the row-security policy called <literal>p1</literal> on the
|
To drop the policy called <literal>p1</literal> on the table named
|
||||||
table named <literal>my_table</literal>:
|
<literal>my_table</literal>:
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
DROP POLICY p1 ON my_table;
|
DROP POLICY p1 ON my_table;
|
||||||
|
@ -2133,7 +2133,7 @@ SELECT * FROM phone_number WHERE tricky(person, phone);
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
When it is necessary for a view to provide row-level security, the
|
When it is necessary for a view to provide row level security, the
|
||||||
<literal>security_barrier</literal> attribute should be applied to
|
<literal>security_barrier</literal> attribute should be applied to
|
||||||
the view. This prevents maliciously-chosen functions and operators from
|
the view. This prevents maliciously-chosen functions and operators from
|
||||||
being invoked on rows until after the view has done its work. For
|
being invoked on rows until after the view has done its work. For
|
||||||
|
@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
|
|||||||
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
|
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
|
||||||
pg_ts_parser.h pg_ts_template.h pg_extension.h \
|
pg_ts_parser.h pg_ts_template.h pg_extension.h \
|
||||||
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
|
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
|
||||||
pg_foreign_table.h pg_rowsecurity.h \
|
pg_foreign_table.h pg_policy.h \
|
||||||
pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
|
pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
|
||||||
toasting.h indexing.h \
|
toasting.h indexing.h \
|
||||||
)
|
)
|
||||||
|
@ -43,9 +43,9 @@
|
|||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_opfamily.h"
|
#include "catalog/pg_opfamily.h"
|
||||||
|
#include "catalog/pg_policy.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/pg_rowsecurity.h"
|
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_ts_config.h"
|
#include "catalog/pg_ts_config.h"
|
||||||
@ -156,7 +156,8 @@ static const Oid object_classes[MAX_OCLASS] = {
|
|||||||
UserMappingRelationId, /* OCLASS_USER_MAPPING */
|
UserMappingRelationId, /* OCLASS_USER_MAPPING */
|
||||||
DefaultAclRelationId, /* OCLASS_DEFACL */
|
DefaultAclRelationId, /* OCLASS_DEFACL */
|
||||||
ExtensionRelationId, /* OCLASS_EXTENSION */
|
ExtensionRelationId, /* OCLASS_EXTENSION */
|
||||||
EventTriggerRelationId /* OCLASS_EVENT_TRIGGER */
|
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
|
||||||
|
PolicyRelationId /* OCLASS_POLICY */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1251,7 +1252,7 @@ doDeletion(const ObjectAddress *object, int flags)
|
|||||||
RemoveEventTriggerById(object->objectId);
|
RemoveEventTriggerById(object->objectId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCLASS_ROWSECURITY:
|
case OCLASS_POLICY:
|
||||||
RemovePolicyById(object->objectId);
|
RemovePolicyById(object->objectId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2361,8 +2362,8 @@ getObjectClass(const ObjectAddress *object)
|
|||||||
case EventTriggerRelationId:
|
case EventTriggerRelationId:
|
||||||
return OCLASS_EVENT_TRIGGER;
|
return OCLASS_EVENT_TRIGGER;
|
||||||
|
|
||||||
case RowSecurityRelationId:
|
case PolicyRelationId:
|
||||||
return OCLASS_ROWSECURITY;
|
return OCLASS_POLICY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shouldn't get here */
|
/* shouldn't get here */
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
#include "catalog/pg_opfamily.h"
|
#include "catalog/pg_opfamily.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_rowsecurity.h"
|
#include "catalog/pg_policy.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
@ -346,11 +346,11 @@ static const ObjectPropertyType ObjectProperty[] =
|
|||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
RowSecurityRelationId,
|
PolicyRelationId,
|
||||||
RowSecurityOidIndexId,
|
PolicyOidIndexId,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
InvalidAttrNumber,
|
InvalidAttrNumber,
|
||||||
InvalidAttrNumber,
|
InvalidAttrNumber,
|
||||||
InvalidAttrNumber,
|
InvalidAttrNumber,
|
||||||
@ -998,7 +998,7 @@ get_object_address_relobject(ObjectType objtype, List *objname,
|
|||||||
address.objectSubId = 0;
|
address.objectSubId = 0;
|
||||||
break;
|
break;
|
||||||
case OBJECT_POLICY:
|
case OBJECT_POLICY:
|
||||||
address.classId = RowSecurityRelationId;
|
address.classId = PolicyRelationId;
|
||||||
address.objectId = relation ?
|
address.objectId = relation ?
|
||||||
get_relation_policy_oid(reloid, depname, missing_ok) :
|
get_relation_policy_oid(reloid, depname, missing_ok) :
|
||||||
InvalidOid;
|
InvalidOid;
|
||||||
@ -2189,38 +2189,38 @@ getObjectDescription(const ObjectAddress *object)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OCLASS_ROWSECURITY:
|
case OCLASS_POLICY:
|
||||||
{
|
{
|
||||||
Relation rsec_rel;
|
Relation policy_rel;
|
||||||
ScanKeyData skey[1];
|
ScanKeyData skey[1];
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Form_pg_rowsecurity form_rsec;
|
Form_pg_policy form_policy;
|
||||||
|
|
||||||
rsec_rel = heap_open(RowSecurityRelationId, AccessShareLock);
|
policy_rel = heap_open(PolicyRelationId, AccessShareLock);
|
||||||
|
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
ObjectIdAttributeNumber,
|
ObjectIdAttributeNumber,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(object->objectId));
|
ObjectIdGetDatum(object->objectId));
|
||||||
|
|
||||||
sscan = systable_beginscan(rsec_rel, RowSecurityOidIndexId,
|
sscan = systable_beginscan(policy_rel, PolicyOidIndexId,
|
||||||
true, NULL, 1, skey);
|
true, NULL, 1, skey);
|
||||||
|
|
||||||
tuple = systable_getnext(sscan);
|
tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "cache lookup failed for row-security relation %u",
|
elog(ERROR, "cache lookup failed for policy %u",
|
||||||
object->objectId);
|
object->objectId);
|
||||||
|
|
||||||
form_rsec = (Form_pg_rowsecurity) GETSTRUCT(tuple);
|
form_policy = (Form_pg_policy) GETSTRUCT(tuple);
|
||||||
|
|
||||||
appendStringInfo(&buffer, _("policy %s on "),
|
appendStringInfo(&buffer, _("policy %s on "),
|
||||||
NameStr(form_rsec->rsecpolname));
|
NameStr(form_policy->polname));
|
||||||
getRelationDescription(&buffer, form_rsec->rsecrelid);
|
getRelationDescription(&buffer, form_policy->polrelid);
|
||||||
|
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
heap_close(rsec_rel, AccessShareLock);
|
heap_close(policy_rel, AccessShareLock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2635,6 +2635,10 @@ getObjectTypeDescription(const ObjectAddress *object)
|
|||||||
appendStringInfoString(&buffer, "event trigger");
|
appendStringInfoString(&buffer, "event trigger");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OCLASS_POLICY:
|
||||||
|
appendStringInfoString(&buffer, "policy");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
appendStringInfo(&buffer, "unrecognized %u", object->classId);
|
appendStringInfo(&buffer, "unrecognized %u", object->classId);
|
||||||
break;
|
break;
|
||||||
@ -3119,6 +3123,30 @@ getObjectIdentity(const ObjectAddress *object)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OCLASS_POLICY:
|
||||||
|
{
|
||||||
|
Relation polDesc;
|
||||||
|
HeapTuple tup;
|
||||||
|
Form_pg_policy policy;
|
||||||
|
|
||||||
|
polDesc = heap_open(PolicyRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
tup = get_catalog_object_by_oid(polDesc, object->objectId);
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(tup))
|
||||||
|
elog(ERROR, "could not find tuple for policy %u",
|
||||||
|
object->objectId);
|
||||||
|
|
||||||
|
policy = (Form_pg_policy) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
appendStringInfo(&buffer, "%s on ",
|
||||||
|
quote_identifier(NameStr(policy->polname)));
|
||||||
|
getRelationIdentity(&buffer, policy->polrelid);
|
||||||
|
|
||||||
|
heap_close(polDesc, AccessShareLock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OCLASS_SCHEMA:
|
case OCLASS_SCHEMA:
|
||||||
{
|
{
|
||||||
char *nspname;
|
char *nspname;
|
||||||
|
@ -67,30 +67,30 @@ CREATE VIEW pg_policies AS
|
|||||||
SELECT
|
SELECT
|
||||||
N.nspname AS schemaname,
|
N.nspname AS schemaname,
|
||||||
C.relname AS tablename,
|
C.relname AS tablename,
|
||||||
rs.rsecpolname AS policyname,
|
pol.polname AS policyname,
|
||||||
CASE
|
CASE
|
||||||
WHEN rs.rsecroles = '{0}' THEN
|
WHEN pol.polroles = '{0}' THEN
|
||||||
string_to_array('public', '')
|
string_to_array('public', '')
|
||||||
ELSE
|
ELSE
|
||||||
ARRAY
|
ARRAY
|
||||||
(
|
(
|
||||||
SELECT rolname
|
SELECT rolname
|
||||||
FROM pg_catalog.pg_authid
|
FROM pg_catalog.pg_authid
|
||||||
WHERE oid = ANY (rs.rsecroles) ORDER BY 1
|
WHERE oid = ANY (pol.polroles) ORDER BY 1
|
||||||
)
|
)
|
||||||
END AS roles,
|
END AS roles,
|
||||||
CASE WHEN rs.rseccmd IS NULL THEN 'ALL' ELSE
|
CASE WHEN pol.polcmd IS NULL THEN 'ALL' ELSE
|
||||||
CASE rs.rseccmd
|
CASE pol.polcmd
|
||||||
WHEN 'r' THEN 'SELECT'
|
WHEN 'r' THEN 'SELECT'
|
||||||
WHEN 'a' THEN 'INSERT'
|
WHEN 'a' THEN 'INSERT'
|
||||||
WHEN 'u' THEN 'UPDATE'
|
WHEN 'u' THEN 'UPDATE'
|
||||||
WHEN 'd' THEN 'DELETE'
|
WHEN 'd' THEN 'DELETE'
|
||||||
END
|
END
|
||||||
END AS cmd,
|
END AS cmd,
|
||||||
pg_catalog.pg_get_expr(rs.rsecqual, rs.rsecrelid) AS qual,
|
pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS qual,
|
||||||
pg_catalog.pg_get_expr(rs.rsecwithcheck, rs.rsecrelid) AS with_check
|
pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
|
||||||
FROM pg_catalog.pg_rowsecurity rs
|
FROM pg_catalog.pg_policy pol
|
||||||
JOIN pg_catalog.pg_class C ON (C.oid = rs.rsecrelid)
|
JOIN pg_catalog.pg_class C ON (C.oid = pol.polrelid)
|
||||||
LEFT JOIN pg_catalog.pg_namespace N ON (N.oid = C.relnamespace);
|
LEFT JOIN pg_catalog.pg_namespace N ON (N.oid = C.relnamespace);
|
||||||
|
|
||||||
CREATE VIEW pg_rules AS
|
CREATE VIEW pg_rules AS
|
||||||
|
@ -282,12 +282,13 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
|
|||||||
|
|
||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query,
|
static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query,
|
||||||
const char *queryString, List *attnamelist, List *options);
|
const char *queryString, const Oid queryRelId, List *attnamelist,
|
||||||
|
List *options);
|
||||||
static void EndCopy(CopyState cstate);
|
static void EndCopy(CopyState cstate);
|
||||||
static void ClosePipeToProgram(CopyState cstate);
|
static void ClosePipeToProgram(CopyState cstate);
|
||||||
static CopyState BeginCopyTo(Relation rel, Node *query, const char *queryString,
|
static CopyState BeginCopyTo(Relation rel, Node *query, const char *queryString,
|
||||||
const char *filename, bool is_program, List *attnamelist,
|
const Oid queryRelId, const char *filename, bool is_program,
|
||||||
List *options);
|
List *attnamelist, List *options);
|
||||||
static void EndCopyTo(CopyState cstate);
|
static void EndCopyTo(CopyState cstate);
|
||||||
static uint64 DoCopyTo(CopyState cstate);
|
static uint64 DoCopyTo(CopyState cstate);
|
||||||
static uint64 CopyTo(CopyState cstate);
|
static uint64 CopyTo(CopyState cstate);
|
||||||
@ -843,7 +844,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
|
|||||||
ExecCheckRTPerms(list_make1(rte), true);
|
ExecCheckRTPerms(list_make1(rte), true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Permission check for row security.
|
* Permission check for row security policies.
|
||||||
*
|
*
|
||||||
* check_enable_rls will ereport(ERROR) if the user has requested
|
* check_enable_rls will ereport(ERROR) if the user has requested
|
||||||
* something invalid and will otherwise indicate if we should enable
|
* something invalid and will otherwise indicate if we should enable
|
||||||
@ -866,7 +867,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
|
|||||||
if (is_from)
|
if (is_from)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("COPY FROM not supported with row security."),
|
errmsg("COPY FROM not supported with row level security."),
|
||||||
errhint("Use direct INSERT statements instead.")));
|
errhint("Use direct INSERT statements instead.")));
|
||||||
|
|
||||||
/* Build target list */
|
/* Build target list */
|
||||||
@ -886,7 +887,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
|
|||||||
target->location = 1;
|
target->location = 1;
|
||||||
|
|
||||||
/* Build FROM clause */
|
/* Build FROM clause */
|
||||||
from = makeRangeVar(NULL, RelationGetRelationName(rel), 1);
|
from = stmt->relation;
|
||||||
|
|
||||||
/* Build query */
|
/* Build query */
|
||||||
select = makeNode(SelectStmt);
|
select = makeNode(SelectStmt);
|
||||||
@ -895,8 +896,6 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
|
|||||||
|
|
||||||
query = (Node*) select;
|
query = (Node*) select;
|
||||||
|
|
||||||
relid = InvalidOid;
|
|
||||||
|
|
||||||
/* Close the handle to the relation as it is no longer needed. */
|
/* Close the handle to the relation as it is no longer needed. */
|
||||||
heap_close(rel, (is_from ? RowExclusiveLock : AccessShareLock));
|
heap_close(rel, (is_from ? RowExclusiveLock : AccessShareLock));
|
||||||
rel = NULL;
|
rel = NULL;
|
||||||
@ -926,7 +925,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cstate = BeginCopyTo(rel, query, queryString,
|
cstate = BeginCopyTo(rel, query, queryString, relid,
|
||||||
stmt->filename, stmt->is_program,
|
stmt->filename, stmt->is_program,
|
||||||
stmt->attlist, stmt->options);
|
stmt->attlist, stmt->options);
|
||||||
*processed = DoCopyTo(cstate); /* copy from database to file */
|
*processed = DoCopyTo(cstate); /* copy from database to file */
|
||||||
@ -1304,6 +1303,7 @@ BeginCopy(bool is_from,
|
|||||||
Relation rel,
|
Relation rel,
|
||||||
Node *raw_query,
|
Node *raw_query,
|
||||||
const char *queryString,
|
const char *queryString,
|
||||||
|
const Oid queryRelId,
|
||||||
List *attnamelist,
|
List *attnamelist,
|
||||||
List *options)
|
List *options)
|
||||||
{
|
{
|
||||||
@ -1394,6 +1394,30 @@ BeginCopy(bool is_from,
|
|||||||
/* plan the query */
|
/* plan the query */
|
||||||
plan = planner(query, 0, NULL);
|
plan = planner(query, 0, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we were passed in a relid, make sure we got the same one back
|
||||||
|
* after planning out the query. It's possible that it changed between
|
||||||
|
* when we checked the policies on the table and decided to use a query
|
||||||
|
* and now.
|
||||||
|
*/
|
||||||
|
if (queryRelId != InvalidOid)
|
||||||
|
{
|
||||||
|
Oid relid = linitial_oid(plan->relationOids);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There should only be one relationOid in this case, since we will
|
||||||
|
* only get here when we have changed the command for the user from
|
||||||
|
* a "COPY relation TO" to "COPY (SELECT * FROM relation) TO", to
|
||||||
|
* allow row level security policies to be applied.
|
||||||
|
*/
|
||||||
|
Assert(list_length(plan->relationOids) == 1);
|
||||||
|
|
||||||
|
if (relid != queryRelId)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
|
errmsg("relation referenced by COPY statement has changed")));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use a snapshot with an updated command ID to ensure this query sees
|
* Use a snapshot with an updated command ID to ensure this query sees
|
||||||
* results of any previously executed queries.
|
* results of any previously executed queries.
|
||||||
@ -1595,6 +1619,7 @@ static CopyState
|
|||||||
BeginCopyTo(Relation rel,
|
BeginCopyTo(Relation rel,
|
||||||
Node *query,
|
Node *query,
|
||||||
const char *queryString,
|
const char *queryString,
|
||||||
|
const Oid queryRelId,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
bool is_program,
|
bool is_program,
|
||||||
List *attnamelist,
|
List *attnamelist,
|
||||||
@ -1636,7 +1661,8 @@ BeginCopyTo(Relation rel,
|
|||||||
RelationGetRelationName(rel))));
|
RelationGetRelationName(rel))));
|
||||||
}
|
}
|
||||||
|
|
||||||
cstate = BeginCopy(false, rel, query, queryString, attnamelist, options);
|
cstate = BeginCopy(false, rel, query, queryString, queryRelId, attnamelist,
|
||||||
|
options);
|
||||||
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
|
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
|
||||||
|
|
||||||
if (pipe)
|
if (pipe)
|
||||||
@ -2565,7 +2591,7 @@ BeginCopyFrom(Relation rel,
|
|||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
bool volatile_defexprs;
|
bool volatile_defexprs;
|
||||||
|
|
||||||
cstate = BeginCopy(true, rel, NULL, NULL, attnamelist, options);
|
cstate = BeginCopy(true, rel, NULL, NULL, InvalidOid, attnamelist, options);
|
||||||
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
|
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
|
||||||
|
|
||||||
/* Initialize state variables */
|
/* Initialize state variables */
|
||||||
|
@ -997,7 +997,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
|
|||||||
case OCLASS_USER_MAPPING:
|
case OCLASS_USER_MAPPING:
|
||||||
case OCLASS_DEFACL:
|
case OCLASS_DEFACL:
|
||||||
case OCLASS_EXTENSION:
|
case OCLASS_EXTENSION:
|
||||||
case OCLASS_ROWSECURITY:
|
case OCLASS_POLICY:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MAX_OCLASS:
|
case MAX_OCLASS:
|
||||||
|
@ -904,9 +904,9 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
ReleaseSysCache(languageTuple);
|
ReleaseSysCache(languageTuple);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only superuser is allowed to create leakproof functions because it
|
* Only superuser is allowed to create leakproof functions because leakproof
|
||||||
* possibly allows unprivileged users to reference invisible tuples to be
|
* functions can see tuples which have not yet been filtered out by security
|
||||||
* filtered out using views for row-level security.
|
* barrier views or row level security policies.
|
||||||
*/
|
*/
|
||||||
if (isLeakProof && !superuser())
|
if (isLeakProof && !superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/objectaccess.h"
|
#include "catalog/objectaccess.h"
|
||||||
#include "catalog/pg_rowsecurity.h"
|
#include "catalog/pg_policy.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/policy.h"
|
#include "commands/policy.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -46,8 +46,8 @@
|
|||||||
|
|
||||||
static void RangeVarCallbackForPolicy(const RangeVar *rv,
|
static void RangeVarCallbackForPolicy(const RangeVar *rv,
|
||||||
Oid relid, Oid oldrelid, void *arg);
|
Oid relid, Oid oldrelid, void *arg);
|
||||||
static char parse_row_security_command(const char *cmd_name);
|
static char parse_policy_command(const char *cmd_name);
|
||||||
static ArrayType* rls_role_list_to_array(List *roles);
|
static ArrayType* policy_role_list_to_array(List *roles);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback to RangeVarGetRelidExtended().
|
* Callback to RangeVarGetRelidExtended().
|
||||||
@ -95,7 +95,7 @@ RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse_row_security_command -
|
* parse_policy_command -
|
||||||
* helper function to convert full command strings to their char
|
* helper function to convert full command strings to their char
|
||||||
* representation.
|
* representation.
|
||||||
*
|
*
|
||||||
@ -104,7 +104,7 @@ RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static char
|
static char
|
||||||
parse_row_security_command(const char *cmd_name)
|
parse_policy_command(const char *cmd_name)
|
||||||
{
|
{
|
||||||
char cmd;
|
char cmd;
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ parse_row_security_command(const char *cmd_name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rls_role_list_to_array
|
* policy_role_list_to_array
|
||||||
* helper function to convert a list of role names in to an array of
|
* helper function to convert a list of role names in to an array of
|
||||||
* role ids.
|
* role ids.
|
||||||
*
|
*
|
||||||
@ -138,7 +138,7 @@ parse_row_security_command(const char *cmd_name)
|
|||||||
* roles - the list of role names to convert.
|
* roles - the list of role names to convert.
|
||||||
*/
|
*/
|
||||||
static ArrayType *
|
static ArrayType *
|
||||||
rls_role_list_to_array(List *roles)
|
policy_role_list_to_array(List *roles)
|
||||||
{
|
{
|
||||||
ArrayType *role_ids;
|
ArrayType *role_ids;
|
||||||
Datum *temp_array;
|
Datum *temp_array;
|
||||||
@ -190,7 +190,7 @@ rls_role_list_to_array(List *roles)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load row-security policy from the catalog, and keep it in
|
* Load row security policy from the catalog, and keep it in
|
||||||
* the relation cache.
|
* the relation cache.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
@ -204,14 +204,14 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
MemoryContext rscxt = NULL;
|
MemoryContext rscxt = NULL;
|
||||||
RowSecurityDesc *rsdesc = NULL;
|
RowSecurityDesc *rsdesc = NULL;
|
||||||
|
|
||||||
catalog = heap_open(RowSecurityRelationId, AccessShareLock);
|
catalog = heap_open(PolicyRelationId, AccessShareLock);
|
||||||
|
|
||||||
ScanKeyInit(&skey,
|
ScanKeyInit(&skey,
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||||
|
|
||||||
sscan = systable_beginscan(catalog, RowSecurityRelidPolnameIndexId, true,
|
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
|
||||||
NULL, 1, &skey);
|
NULL, 1, &skey);
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
@ -221,7 +221,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
* default-deny policy is created.
|
* default-deny policy is created.
|
||||||
*/
|
*/
|
||||||
rscxt = AllocSetContextCreate(CacheMemoryContext,
|
rscxt = AllocSetContextCreate(CacheMemoryContext,
|
||||||
"Row-security descriptor",
|
"row security descriptor",
|
||||||
ALLOCSET_SMALL_MINSIZE,
|
ALLOCSET_SMALL_MINSIZE,
|
||||||
ALLOCSET_SMALL_INITSIZE,
|
ALLOCSET_SMALL_INITSIZE,
|
||||||
ALLOCSET_SMALL_MAXSIZE);
|
ALLOCSET_SMALL_MAXSIZE);
|
||||||
@ -229,7 +229,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
rsdesc->rscxt = rscxt;
|
rsdesc->rscxt = rscxt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through the row-level security entries for this relation, if
|
* Loop through the row level security policies for this relation, if
|
||||||
* any.
|
* any.
|
||||||
*/
|
*/
|
||||||
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
|
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
|
||||||
@ -249,7 +249,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
oldcxt = MemoryContextSwitchTo(rscxt);
|
oldcxt = MemoryContextSwitchTo(rscxt);
|
||||||
|
|
||||||
/* Get policy command */
|
/* Get policy command */
|
||||||
value_datum = heap_getattr(tuple, Anum_pg_rowsecurity_rseccmd,
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
cmd_value = 0;
|
cmd_value = 0;
|
||||||
@ -257,19 +257,19 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
cmd_value = DatumGetChar(value_datum);
|
cmd_value = DatumGetChar(value_datum);
|
||||||
|
|
||||||
/* Get policy name */
|
/* Get policy name */
|
||||||
value_datum = heap_getattr(tuple, Anum_pg_rowsecurity_rsecpolname,
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
Assert(!isnull);
|
Assert(!isnull);
|
||||||
policy_name_value = DatumGetCString(value_datum);
|
policy_name_value = DatumGetCString(value_datum);
|
||||||
|
|
||||||
/* Get policy roles */
|
/* Get policy roles */
|
||||||
value_datum = heap_getattr(tuple, Anum_pg_rowsecurity_rsecroles,
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
Assert(!isnull);
|
Assert(!isnull);
|
||||||
roles = DatumGetArrayTypeP(value_datum);
|
roles = DatumGetArrayTypeP(value_datum);
|
||||||
|
|
||||||
/* Get policy qual */
|
/* Get policy qual */
|
||||||
value_datum = heap_getattr(tuple, Anum_pg_rowsecurity_rsecqual,
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
{
|
{
|
||||||
@ -280,7 +280,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
qual_expr = NULL;
|
qual_expr = NULL;
|
||||||
|
|
||||||
/* Get WITH CHECK qual */
|
/* Get WITH CHECK qual */
|
||||||
value_datum = heap_getattr(tuple, Anum_pg_rowsecurity_rsecwithcheck,
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
|
||||||
RelationGetDescr(catalog), &isnull);
|
RelationGetDescr(catalog), &isnull);
|
||||||
|
|
||||||
if (!isnull)
|
if (!isnull)
|
||||||
@ -295,7 +295,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
|
|
||||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||||
policy->policy_name = policy_name_value;
|
policy->policy_name = policy_name_value;
|
||||||
policy->rsecid = policy_id;
|
policy->policy_id = policy_id;
|
||||||
policy->cmd = cmd_value;
|
policy->cmd = cmd_value;
|
||||||
policy->roles = roles;
|
policy->roles = roles;
|
||||||
policy->qual = copyObject(qual_expr);
|
policy->qual = copyObject(qual_expr);
|
||||||
@ -317,7 +317,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
/*
|
/*
|
||||||
* Check if no policies were added
|
* Check if no policies were added
|
||||||
*
|
*
|
||||||
* If no policies exist in pg_rowsecurity for this relation, then we
|
* If no policies exist in pg_policy for this relation, then we
|
||||||
* need to create a single default-deny policy. We use InvalidOid for
|
* need to create a single default-deny policy. We use InvalidOid for
|
||||||
* the Oid to indicate that this is the default-deny policy (we may
|
* the Oid to indicate that this is the default-deny policy (we may
|
||||||
* decide to ignore the default policy if an extension adds policies).
|
* decide to ignore the default policy if an extension adds policies).
|
||||||
@ -333,7 +333,7 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
|
|
||||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||||
policy->policy_name = pstrdup("default-deny policy");
|
policy->policy_name = pstrdup("default-deny policy");
|
||||||
policy->rsecid = InvalidOid;
|
policy->policy_id = InvalidOid;
|
||||||
policy->cmd = '\0';
|
policy->cmd = '\0';
|
||||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
||||||
'i');
|
'i');
|
||||||
@ -364,22 +364,22 @@ RelationBuildRowSecurity(Relation relation)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* RemovePolicyById -
|
* RemovePolicyById -
|
||||||
* remove a row-security policy by its OID. If a policy does not exist with
|
* remove a policy by its OID. If a policy does not exist with the provided
|
||||||
* the provided oid, then an error is raised.
|
* oid, then an error is raised.
|
||||||
*
|
*
|
||||||
* policy_id - the oid of the row-security policy.
|
* policy_id - the oid of the policy.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RemovePolicyById(Oid policy_id)
|
RemovePolicyById(Oid policy_id)
|
||||||
{
|
{
|
||||||
Relation pg_rowsecurity_rel;
|
Relation pg_policy_rel;
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
ScanKeyData skey[1];
|
ScanKeyData skey[1];
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Oid relid;
|
Oid relid;
|
||||||
Relation rel;
|
Relation rel;
|
||||||
|
|
||||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
|
pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the policy to delete.
|
* Find the policy to delete.
|
||||||
@ -389,19 +389,19 @@ RemovePolicyById(Oid policy_id)
|
|||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(policy_id));
|
ObjectIdGetDatum(policy_id));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel, RowSecurityOidIndexId, true,
|
sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
|
||||||
NULL, 1, skey);
|
NULL, 1, skey);
|
||||||
|
|
||||||
tuple = systable_getnext(sscan);
|
tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
/* If the policy exists, then remove it, otherwise raise an error. */
|
/* If the policy exists, then remove it, otherwise raise an error. */
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
elog(ERROR, "could not find tuple for row-security %u", policy_id);
|
elog(ERROR, "could not find tuple for policy %u", policy_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open and exclusive-lock the relation the policy belong to.
|
* Open and exclusive-lock the relation the policy belong to.
|
||||||
*/
|
*/
|
||||||
relid = ((Form_pg_rowsecurity) GETSTRUCT(tuple))->rsecrelid;
|
relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
|
||||||
|
|
||||||
rel = heap_open(relid, AccessExclusiveLock);
|
rel = heap_open(relid, AccessExclusiveLock);
|
||||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
@ -416,7 +416,7 @@ RemovePolicyById(Oid policy_id)
|
|||||||
errmsg("permission denied: \"%s\" is a system catalog",
|
errmsg("permission denied: \"%s\" is a system catalog",
|
||||||
RelationGetRelationName(rel))));
|
RelationGetRelationName(rel))));
|
||||||
|
|
||||||
simple_heap_delete(pg_rowsecurity_rel, &tuple->t_self);
|
simple_heap_delete(pg_policy_rel, &tuple->t_self);
|
||||||
|
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
heap_close(rel, AccessExclusiveLock);
|
heap_close(rel, AccessExclusiveLock);
|
||||||
@ -424,9 +424,8 @@ RemovePolicyById(Oid policy_id)
|
|||||||
/*
|
/*
|
||||||
* Note that, unlike some of the other flags in pg_class, relrowsecurity
|
* Note that, unlike some of the other flags in pg_class, relrowsecurity
|
||||||
* is not just an indication of if policies exist. When relrowsecurity
|
* is not just an indication of if policies exist. When relrowsecurity
|
||||||
* is set (which can be done directly by the user or indirectly by creating
|
* is set by a user, then all access to the relation must be through a
|
||||||
* a policy on the table), then all access to the relation must be through
|
* policy. If no policy is defined for the relation then a default-deny
|
||||||
* a policy. If no policy is defined for the relation then a default-deny
|
|
||||||
* policy is created and all records are filtered (except for queries from
|
* policy is created and all records are filtered (except for queries from
|
||||||
* the owner).
|
* the owner).
|
||||||
*/
|
*/
|
||||||
@ -434,7 +433,7 @@ RemovePolicyById(Oid policy_id)
|
|||||||
CacheInvalidateRelcache(rel);
|
CacheInvalidateRelcache(rel);
|
||||||
|
|
||||||
/* Clean up */
|
/* Clean up */
|
||||||
heap_close(pg_rowsecurity_rel, RowExclusiveLock);
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -446,11 +445,11 @@ RemovePolicyById(Oid policy_id)
|
|||||||
Oid
|
Oid
|
||||||
CreatePolicy(CreatePolicyStmt *stmt)
|
CreatePolicy(CreatePolicyStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation pg_rowsecurity_rel;
|
Relation pg_policy_rel;
|
||||||
Oid rowsec_id;
|
Oid policy_id;
|
||||||
Relation target_table;
|
Relation target_table;
|
||||||
Oid table_id;
|
Oid table_id;
|
||||||
char rseccmd;
|
char polcmd;
|
||||||
ArrayType *role_ids;
|
ArrayType *role_ids;
|
||||||
ParseState *qual_pstate;
|
ParseState *qual_pstate;
|
||||||
ParseState *with_check_pstate;
|
ParseState *with_check_pstate;
|
||||||
@ -459,19 +458,19 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||||||
Node *with_check_qual;
|
Node *with_check_qual;
|
||||||
ScanKeyData skey[2];
|
ScanKeyData skey[2];
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple rsec_tuple;
|
HeapTuple policy_tuple;
|
||||||
Datum values[Natts_pg_rowsecurity];
|
Datum values[Natts_pg_policy];
|
||||||
bool isnull[Natts_pg_rowsecurity];
|
bool isnull[Natts_pg_policy];
|
||||||
ObjectAddress target;
|
ObjectAddress target;
|
||||||
ObjectAddress myself;
|
ObjectAddress myself;
|
||||||
|
|
||||||
/* Parse command */
|
/* Parse command */
|
||||||
rseccmd = parse_row_security_command(stmt->cmd);
|
polcmd = parse_policy_command(stmt->cmd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the command is SELECT or DELETE then WITH CHECK should be NULL.
|
* If the command is SELECT or DELETE then WITH CHECK should be NULL.
|
||||||
*/
|
*/
|
||||||
if ((rseccmd == ACL_SELECT_CHR || rseccmd == ACL_DELETE_CHR)
|
if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
|
||||||
&& stmt->with_check != NULL)
|
&& stmt->with_check != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
@ -481,14 +480,14 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||||||
* If the command is INSERT then WITH CHECK should be the only expression
|
* If the command is INSERT then WITH CHECK should be the only expression
|
||||||
* provided.
|
* provided.
|
||||||
*/
|
*/
|
||||||
if (rseccmd == ACL_INSERT_CHR && stmt->qual != NULL)
|
if (polcmd == ACL_INSERT_CHR && stmt->qual != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("only WITH CHECK expression allowed for INSERT")));
|
errmsg("only WITH CHECK expression allowed for INSERT")));
|
||||||
|
|
||||||
|
|
||||||
/* Collect role ids */
|
/* Collect role ids */
|
||||||
role_ids = rls_role_list_to_array(stmt->roles);
|
role_ids = policy_role_list_to_array(stmt->roles);
|
||||||
|
|
||||||
/* Parse the supplied clause */
|
/* Parse the supplied clause */
|
||||||
qual_pstate = make_parsestate(NULL);
|
qual_pstate = make_parsestate(NULL);
|
||||||
@ -527,74 +526,74 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||||||
EXPR_KIND_WHERE,
|
EXPR_KIND_WHERE,
|
||||||
"POLICY");
|
"POLICY");
|
||||||
|
|
||||||
/* Open pg_rowsecurity catalog */
|
/* Open pg_policy catalog */
|
||||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
|
pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/* Set key - row security relation id. */
|
/* Set key - policy's relation id. */
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(table_id));
|
ObjectIdGetDatum(table_id));
|
||||||
|
|
||||||
/* Set key - row security policy name. */
|
/* Set key - policy's name. */
|
||||||
ScanKeyInit(&skey[1],
|
ScanKeyInit(&skey[1],
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
BTEqualStrategyNumber, F_NAMEEQ,
|
BTEqualStrategyNumber, F_NAMEEQ,
|
||||||
CStringGetDatum(stmt->policy_name));
|
CStringGetDatum(stmt->policy_name));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel,
|
sscan = systable_beginscan(pg_policy_rel,
|
||||||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
PolicyPolrelidPolnameIndexId, true, NULL, 2,
|
||||||
skey);
|
skey);
|
||||||
|
|
||||||
rsec_tuple = systable_getnext(sscan);
|
policy_tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
/* Complain if the policy name already exists for the table */
|
/* Complain if the policy name already exists for the table */
|
||||||
if (HeapTupleIsValid(rsec_tuple))
|
if (HeapTupleIsValid(policy_tuple))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||||
errmsg("policy \"%s\" for relation \"%s\" already exists",
|
errmsg("policy \"%s\" for relation \"%s\" already exists",
|
||||||
stmt->policy_name, RelationGetRelationName(target_table))));
|
stmt->policy_name, RelationGetRelationName(target_table))));
|
||||||
|
|
||||||
values[Anum_pg_rowsecurity_rsecrelid - 1] = ObjectIdGetDatum(table_id);
|
values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);
|
||||||
values[Anum_pg_rowsecurity_rsecpolname - 1]
|
values[Anum_pg_policy_polname - 1]
|
||||||
= DirectFunctionCall1(namein, CStringGetDatum(stmt->policy_name));
|
= DirectFunctionCall1(namein, CStringGetDatum(stmt->policy_name));
|
||||||
|
|
||||||
if (rseccmd)
|
if (polcmd)
|
||||||
values[Anum_pg_rowsecurity_rseccmd - 1] = CharGetDatum(rseccmd);
|
values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);
|
||||||
else
|
else
|
||||||
isnull[Anum_pg_rowsecurity_rseccmd - 1] = true;
|
isnull[Anum_pg_policy_polcmd - 1] = true;
|
||||||
|
|
||||||
values[Anum_pg_rowsecurity_rsecroles - 1] = PointerGetDatum(role_ids);
|
values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
|
||||||
|
|
||||||
/* Add qual if present. */
|
/* Add qual if present. */
|
||||||
if (qual)
|
if (qual)
|
||||||
values[Anum_pg_rowsecurity_rsecqual - 1]
|
values[Anum_pg_policy_polqual - 1]
|
||||||
= CStringGetTextDatum(nodeToString(qual));
|
= CStringGetTextDatum(nodeToString(qual));
|
||||||
else
|
else
|
||||||
isnull[Anum_pg_rowsecurity_rsecqual - 1] = true;
|
isnull[Anum_pg_policy_polqual - 1] = true;
|
||||||
|
|
||||||
/* Add WITH CHECK qual if present */
|
/* Add WITH CHECK qual if present */
|
||||||
if (with_check_qual)
|
if (with_check_qual)
|
||||||
values[Anum_pg_rowsecurity_rsecwithcheck - 1]
|
values[Anum_pg_policy_polwithcheck - 1]
|
||||||
= CStringGetTextDatum(nodeToString(with_check_qual));
|
= CStringGetTextDatum(nodeToString(with_check_qual));
|
||||||
else
|
else
|
||||||
isnull[Anum_pg_rowsecurity_rsecwithcheck - 1] = true;
|
isnull[Anum_pg_policy_polwithcheck - 1] = true;
|
||||||
|
|
||||||
rsec_tuple = heap_form_tuple(RelationGetDescr(pg_rowsecurity_rel), values,
|
policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,
|
||||||
isnull);
|
isnull);
|
||||||
|
|
||||||
rowsec_id = simple_heap_insert(pg_rowsecurity_rel, rsec_tuple);
|
policy_id = simple_heap_insert(pg_policy_rel, policy_tuple);
|
||||||
|
|
||||||
/* Update Indexes */
|
/* Update Indexes */
|
||||||
CatalogUpdateIndexes(pg_rowsecurity_rel, rsec_tuple);
|
CatalogUpdateIndexes(pg_policy_rel, policy_tuple);
|
||||||
|
|
||||||
/* Record Dependencies */
|
/* Record Dependencies */
|
||||||
target.classId = RelationRelationId;
|
target.classId = RelationRelationId;
|
||||||
target.objectId = table_id;
|
target.objectId = table_id;
|
||||||
target.objectSubId = 0;
|
target.objectSubId = 0;
|
||||||
|
|
||||||
myself.classId = RowSecurityRelationId;
|
myself.classId = PolicyRelationId;
|
||||||
myself.objectId = rowsec_id;
|
myself.objectId = policy_id;
|
||||||
myself.objectSubId = 0;
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
|
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
|
||||||
@ -609,14 +608,14 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||||||
CacheInvalidateRelcache(target_table);
|
CacheInvalidateRelcache(target_table);
|
||||||
|
|
||||||
/* Clean up. */
|
/* Clean up. */
|
||||||
heap_freetuple(rsec_tuple);
|
heap_freetuple(policy_tuple);
|
||||||
free_parsestate(qual_pstate);
|
free_parsestate(qual_pstate);
|
||||||
free_parsestate(with_check_pstate);
|
free_parsestate(with_check_pstate);
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
relation_close(target_table, NoLock);
|
relation_close(target_table, NoLock);
|
||||||
heap_close(pg_rowsecurity_rel, RowExclusiveLock);
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
||||||
|
|
||||||
return rowsec_id;
|
return policy_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -628,8 +627,8 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||||||
Oid
|
Oid
|
||||||
AlterPolicy(AlterPolicyStmt *stmt)
|
AlterPolicy(AlterPolicyStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation pg_rowsecurity_rel;
|
Relation pg_policy_rel;
|
||||||
Oid rowsec_id;
|
Oid policy_id;
|
||||||
Relation target_table;
|
Relation target_table;
|
||||||
Oid table_id;
|
Oid table_id;
|
||||||
ArrayType *role_ids = NULL;
|
ArrayType *role_ids = NULL;
|
||||||
@ -639,20 +638,20 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
Node *with_check_qual = NULL;
|
Node *with_check_qual = NULL;
|
||||||
ScanKeyData skey[2];
|
ScanKeyData skey[2];
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple rsec_tuple;
|
HeapTuple policy_tuple;
|
||||||
HeapTuple new_tuple;
|
HeapTuple new_tuple;
|
||||||
Datum values[Natts_pg_rowsecurity];
|
Datum values[Natts_pg_policy];
|
||||||
bool isnull[Natts_pg_rowsecurity];
|
bool isnull[Natts_pg_policy];
|
||||||
bool replaces[Natts_pg_rowsecurity];
|
bool replaces[Natts_pg_policy];
|
||||||
ObjectAddress target;
|
ObjectAddress target;
|
||||||
ObjectAddress myself;
|
ObjectAddress myself;
|
||||||
Datum cmd_datum;
|
Datum cmd_datum;
|
||||||
char rseccmd;
|
char polcmd;
|
||||||
bool rseccmd_isnull;
|
bool polcmd_isnull;
|
||||||
|
|
||||||
/* Parse role_ids */
|
/* Parse role_ids */
|
||||||
if (stmt->roles != NULL)
|
if (stmt->roles != NULL)
|
||||||
role_ids = rls_role_list_to_array(stmt->roles);
|
role_ids = policy_role_list_to_array(stmt->roles);
|
||||||
|
|
||||||
/* Get id of table. Also handles permissions checks. */
|
/* Get id of table. Also handles permissions checks. */
|
||||||
table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
|
table_id = RangeVarGetRelidExtended(stmt->table, AccessExclusiveLock,
|
||||||
@ -662,7 +661,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
|
|
||||||
target_table = relation_open(table_id, NoLock);
|
target_table = relation_open(table_id, NoLock);
|
||||||
|
|
||||||
/* Parse the row-security clause */
|
/* Parse the using policy clause */
|
||||||
if (stmt->qual)
|
if (stmt->qual)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
@ -675,13 +674,13 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
|
|
||||||
qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
|
qual = transformWhereClause(qual_pstate, copyObject(stmt->qual),
|
||||||
EXPR_KIND_WHERE,
|
EXPR_KIND_WHERE,
|
||||||
"ROW SECURITY");
|
"POLICY");
|
||||||
|
|
||||||
qual_parse_rtable = qual_pstate->p_rtable;
|
qual_parse_rtable = qual_pstate->p_rtable;
|
||||||
free_parsestate(qual_pstate);
|
free_parsestate(qual_pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse the with-check row-security clause */
|
/* Parse the with-check policy clause */
|
||||||
if (stmt->with_check)
|
if (stmt->with_check)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
@ -695,7 +694,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
with_check_qual = transformWhereClause(with_check_pstate,
|
with_check_qual = transformWhereClause(with_check_pstate,
|
||||||
copyObject(stmt->with_check),
|
copyObject(stmt->with_check),
|
||||||
EXPR_KIND_WHERE,
|
EXPR_KIND_WHERE,
|
||||||
"ROW SECURITY");
|
"POLICY");
|
||||||
|
|
||||||
with_check_parse_rtable = with_check_pstate->p_rtable;
|
with_check_parse_rtable = with_check_pstate->p_rtable;
|
||||||
free_parsestate(with_check_pstate);
|
free_parsestate(with_check_pstate);
|
||||||
@ -707,28 +706,28 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
memset(isnull, 0, sizeof(isnull));
|
memset(isnull, 0, sizeof(isnull));
|
||||||
|
|
||||||
/* Find policy to update. */
|
/* Find policy to update. */
|
||||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
|
pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/* Set key - row security relation id. */
|
/* Set key - policy's relation id. */
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(table_id));
|
ObjectIdGetDatum(table_id));
|
||||||
|
|
||||||
/* Set key - row security policy name. */
|
/* Set key - policy's name. */
|
||||||
ScanKeyInit(&skey[1],
|
ScanKeyInit(&skey[1],
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
BTEqualStrategyNumber, F_NAMEEQ,
|
BTEqualStrategyNumber, F_NAMEEQ,
|
||||||
CStringGetDatum(stmt->policy_name));
|
CStringGetDatum(stmt->policy_name));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel,
|
sscan = systable_beginscan(pg_policy_rel,
|
||||||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
PolicyPolrelidPolnameIndexId, true, NULL, 2,
|
||||||
skey);
|
skey);
|
||||||
|
|
||||||
rsec_tuple = systable_getnext(sscan);
|
policy_tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
/* Check that the policy is found, raise an error if not. */
|
/* Check that the policy is found, raise an error if not. */
|
||||||
if (!HeapTupleIsValid(rsec_tuple))
|
if (!HeapTupleIsValid(policy_tuple))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
errmsg("policy \"%s\" on table \"%s\" does not exist",
|
errmsg("policy \"%s\" on table \"%s\" does not exist",
|
||||||
@ -736,18 +735,18 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
RelationGetRelationName(target_table))));
|
RelationGetRelationName(target_table))));
|
||||||
|
|
||||||
/* Get policy command */
|
/* Get policy command */
|
||||||
cmd_datum = heap_getattr(rsec_tuple, Anum_pg_rowsecurity_rseccmd,
|
cmd_datum = heap_getattr(policy_tuple, Anum_pg_policy_polcmd,
|
||||||
RelationGetDescr(pg_rowsecurity_rel),
|
RelationGetDescr(pg_policy_rel),
|
||||||
&rseccmd_isnull);
|
&polcmd_isnull);
|
||||||
if (rseccmd_isnull)
|
if (polcmd_isnull)
|
||||||
rseccmd = 0;
|
polcmd = 0;
|
||||||
else
|
else
|
||||||
rseccmd = DatumGetChar(cmd_datum);
|
polcmd = DatumGetChar(cmd_datum);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the command is SELECT or DELETE then WITH CHECK should be NULL.
|
* If the command is SELECT or DELETE then WITH CHECK should be NULL.
|
||||||
*/
|
*/
|
||||||
if ((rseccmd == ACL_SELECT_CHR || rseccmd == ACL_DELETE_CHR)
|
if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR)
|
||||||
&& stmt->with_check != NULL)
|
&& stmt->with_check != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
@ -757,52 +756,52 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
* If the command is INSERT then WITH CHECK should be the only
|
* If the command is INSERT then WITH CHECK should be the only
|
||||||
* expression provided.
|
* expression provided.
|
||||||
*/
|
*/
|
||||||
if ((rseccmd == ACL_INSERT_CHR)
|
if ((polcmd == ACL_INSERT_CHR)
|
||||||
&& stmt->qual != NULL)
|
&& stmt->qual != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("only WITH CHECK expression allowed for INSERT")));
|
errmsg("only WITH CHECK expression allowed for INSERT")));
|
||||||
|
|
||||||
rowsec_id = HeapTupleGetOid(rsec_tuple);
|
policy_id = HeapTupleGetOid(policy_tuple);
|
||||||
|
|
||||||
if (role_ids != NULL)
|
if (role_ids != NULL)
|
||||||
{
|
{
|
||||||
replaces[Anum_pg_rowsecurity_rsecroles - 1] = true;
|
replaces[Anum_pg_policy_polroles - 1] = true;
|
||||||
values[Anum_pg_rowsecurity_rsecroles - 1] = PointerGetDatum(role_ids);
|
values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qual != NULL)
|
if (qual != NULL)
|
||||||
{
|
{
|
||||||
replaces[Anum_pg_rowsecurity_rsecqual - 1] = true;
|
replaces[Anum_pg_policy_polqual - 1] = true;
|
||||||
values[Anum_pg_rowsecurity_rsecqual - 1]
|
values[Anum_pg_policy_polqual - 1]
|
||||||
= CStringGetTextDatum(nodeToString(qual));
|
= CStringGetTextDatum(nodeToString(qual));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (with_check_qual != NULL)
|
if (with_check_qual != NULL)
|
||||||
{
|
{
|
||||||
replaces[Anum_pg_rowsecurity_rsecwithcheck - 1] = true;
|
replaces[Anum_pg_policy_polwithcheck - 1] = true;
|
||||||
values[Anum_pg_rowsecurity_rsecwithcheck - 1]
|
values[Anum_pg_policy_polwithcheck - 1]
|
||||||
= CStringGetTextDatum(nodeToString(with_check_qual));
|
= CStringGetTextDatum(nodeToString(with_check_qual));
|
||||||
}
|
}
|
||||||
|
|
||||||
new_tuple = heap_modify_tuple(rsec_tuple,
|
new_tuple = heap_modify_tuple(policy_tuple,
|
||||||
RelationGetDescr(pg_rowsecurity_rel),
|
RelationGetDescr(pg_policy_rel),
|
||||||
values, isnull, replaces);
|
values, isnull, replaces);
|
||||||
simple_heap_update(pg_rowsecurity_rel, &new_tuple->t_self, new_tuple);
|
simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple);
|
||||||
|
|
||||||
/* Update Catalog Indexes */
|
/* Update Catalog Indexes */
|
||||||
CatalogUpdateIndexes(pg_rowsecurity_rel, new_tuple);
|
CatalogUpdateIndexes(pg_policy_rel, new_tuple);
|
||||||
|
|
||||||
/* Update Dependencies. */
|
/* Update Dependencies. */
|
||||||
deleteDependencyRecordsFor(RowSecurityRelationId, rowsec_id, false);
|
deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
|
||||||
|
|
||||||
/* Record Dependencies */
|
/* Record Dependencies */
|
||||||
target.classId = RelationRelationId;
|
target.classId = RelationRelationId;
|
||||||
target.objectId = table_id;
|
target.objectId = table_id;
|
||||||
target.objectSubId = 0;
|
target.objectSubId = 0;
|
||||||
|
|
||||||
myself.classId = RowSecurityRelationId;
|
myself.classId = PolicyRelationId;
|
||||||
myself.objectId = rowsec_id;
|
myself.objectId = policy_id;
|
||||||
myself.objectSubId = 0;
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
|
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
|
||||||
@ -820,9 +819,9 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
/* Clean up. */
|
/* Clean up. */
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
relation_close(target_table, NoLock);
|
relation_close(target_table, NoLock);
|
||||||
heap_close(pg_rowsecurity_rel, RowExclusiveLock);
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
||||||
|
|
||||||
return rowsec_id;
|
return policy_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -832,13 +831,13 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||||||
Oid
|
Oid
|
||||||
rename_policy(RenameStmt *stmt)
|
rename_policy(RenameStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation pg_rowsecurity_rel;
|
Relation pg_policy_rel;
|
||||||
Relation target_table;
|
Relation target_table;
|
||||||
Oid table_id;
|
Oid table_id;
|
||||||
Oid opoloid;
|
Oid opoloid;
|
||||||
ScanKeyData skey[2];
|
ScanKeyData skey[2];
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple rsec_tuple;
|
HeapTuple policy_tuple;
|
||||||
|
|
||||||
/* Get id of table. Also handles permissions checks. */
|
/* Get id of table. Also handles permissions checks. */
|
||||||
table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
|
table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
|
||||||
@ -848,74 +847,74 @@ rename_policy(RenameStmt *stmt)
|
|||||||
|
|
||||||
target_table = relation_open(table_id, NoLock);
|
target_table = relation_open(table_id, NoLock);
|
||||||
|
|
||||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
|
pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/* First pass -- check for conflict */
|
/* First pass -- check for conflict */
|
||||||
|
|
||||||
/* Add key - row security relation id. */
|
/* Add key - policy's relation id. */
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(table_id));
|
ObjectIdGetDatum(table_id));
|
||||||
|
|
||||||
/* Add key - row security policy name. */
|
/* Add key - policy's name. */
|
||||||
ScanKeyInit(&skey[1],
|
ScanKeyInit(&skey[1],
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
BTEqualStrategyNumber, F_NAMEEQ,
|
BTEqualStrategyNumber, F_NAMEEQ,
|
||||||
CStringGetDatum(stmt->newname));
|
CStringGetDatum(stmt->newname));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel,
|
sscan = systable_beginscan(pg_policy_rel,
|
||||||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
PolicyPolrelidPolnameIndexId, true, NULL, 2,
|
||||||
skey);
|
skey);
|
||||||
|
|
||||||
if (HeapTupleIsValid(systable_getnext(sscan)))
|
if (HeapTupleIsValid(systable_getnext(sscan)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||||
errmsg("row-policy \"%s\" for table \"%s\" already exists",
|
errmsg("policy \"%s\" for table \"%s\" already exists",
|
||||||
stmt->newname, RelationGetRelationName(target_table))));
|
stmt->newname, RelationGetRelationName(target_table))));
|
||||||
|
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
|
|
||||||
/* Second pass -- find existing policy and update */
|
/* Second pass -- find existing policy and update */
|
||||||
/* Add key - row security relation id. */
|
/* Add key - policy's relation id. */
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(table_id));
|
ObjectIdGetDatum(table_id));
|
||||||
|
|
||||||
/* Add key - row security policy name. */
|
/* Add key - policy's name. */
|
||||||
ScanKeyInit(&skey[1],
|
ScanKeyInit(&skey[1],
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
BTEqualStrategyNumber, F_NAMEEQ,
|
BTEqualStrategyNumber, F_NAMEEQ,
|
||||||
CStringGetDatum(stmt->subname));
|
CStringGetDatum(stmt->subname));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel,
|
sscan = systable_beginscan(pg_policy_rel,
|
||||||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
PolicyPolrelidPolnameIndexId, true, NULL, 2,
|
||||||
skey);
|
skey);
|
||||||
|
|
||||||
rsec_tuple = systable_getnext(sscan);
|
policy_tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
/* Complain if we did not find the policy */
|
/* Complain if we did not find the policy */
|
||||||
if (!HeapTupleIsValid(rsec_tuple))
|
if (!HeapTupleIsValid(policy_tuple))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
errmsg("row-policy \"%s\" for table \"%s\" does not exist",
|
errmsg("policy \"%s\" for table \"%s\" does not exist",
|
||||||
stmt->subname, RelationGetRelationName(target_table))));
|
stmt->subname, RelationGetRelationName(target_table))));
|
||||||
|
|
||||||
opoloid = HeapTupleGetOid(rsec_tuple);
|
opoloid = HeapTupleGetOid(policy_tuple);
|
||||||
|
|
||||||
rsec_tuple = heap_copytuple(rsec_tuple);
|
policy_tuple = heap_copytuple(policy_tuple);
|
||||||
|
|
||||||
namestrcpy(&((Form_pg_rowsecurity) GETSTRUCT(rsec_tuple))->rsecpolname,
|
namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname,
|
||||||
stmt->newname);
|
stmt->newname);
|
||||||
|
|
||||||
simple_heap_update(pg_rowsecurity_rel, &rsec_tuple->t_self, rsec_tuple);
|
simple_heap_update(pg_policy_rel, &policy_tuple->t_self, policy_tuple);
|
||||||
|
|
||||||
/* keep system catalog indexes current */
|
/* keep system catalog indexes current */
|
||||||
CatalogUpdateIndexes(pg_rowsecurity_rel, rsec_tuple);
|
CatalogUpdateIndexes(pg_policy_rel, policy_tuple);
|
||||||
|
|
||||||
InvokeObjectPostAlterHook(RowSecurityRelationId,
|
InvokeObjectPostAlterHook(PolicyRelationId,
|
||||||
HeapTupleGetOid(rsec_tuple), 0);
|
HeapTupleGetOid(policy_tuple), 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalidate relation's relcache entry so that other backends (and
|
* Invalidate relation's relcache entry so that other backends (and
|
||||||
@ -926,7 +925,7 @@ rename_policy(RenameStmt *stmt)
|
|||||||
|
|
||||||
/* Clean up. */
|
/* Clean up. */
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
heap_close(pg_rowsecurity_rel, RowExclusiveLock);
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
||||||
relation_close(target_table, NoLock);
|
relation_close(target_table, NoLock);
|
||||||
|
|
||||||
return opoloid;
|
return opoloid;
|
||||||
@ -941,33 +940,33 @@ rename_policy(RenameStmt *stmt)
|
|||||||
Oid
|
Oid
|
||||||
get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
|
get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
|
||||||
{
|
{
|
||||||
Relation pg_rowsecurity_rel;
|
Relation pg_policy_rel;
|
||||||
ScanKeyData skey[2];
|
ScanKeyData skey[2];
|
||||||
SysScanDesc sscan;
|
SysScanDesc sscan;
|
||||||
HeapTuple rsec_tuple;
|
HeapTuple policy_tuple;
|
||||||
Oid policy_oid;
|
Oid policy_oid;
|
||||||
|
|
||||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, AccessShareLock);
|
pg_policy_rel = heap_open(PolicyRelationId, AccessShareLock);
|
||||||
|
|
||||||
/* Add key - row security relation id. */
|
/* Add key - policy's relation id. */
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_rowsecurity_rsecrelid,
|
Anum_pg_policy_polrelid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(relid));
|
ObjectIdGetDatum(relid));
|
||||||
|
|
||||||
/* Add key - row security policy name. */
|
/* Add key - policy's name. */
|
||||||
ScanKeyInit(&skey[1],
|
ScanKeyInit(&skey[1],
|
||||||
Anum_pg_rowsecurity_rsecpolname,
|
Anum_pg_policy_polname,
|
||||||
BTEqualStrategyNumber, F_NAMEEQ,
|
BTEqualStrategyNumber, F_NAMEEQ,
|
||||||
CStringGetDatum(policy_name));
|
CStringGetDatum(policy_name));
|
||||||
|
|
||||||
sscan = systable_beginscan(pg_rowsecurity_rel,
|
sscan = systable_beginscan(pg_policy_rel,
|
||||||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
PolicyPolrelidPolnameIndexId, true, NULL, 2,
|
||||||
skey);
|
skey);
|
||||||
|
|
||||||
rsec_tuple = systable_getnext(sscan);
|
policy_tuple = systable_getnext(sscan);
|
||||||
|
|
||||||
if (!HeapTupleIsValid(rsec_tuple))
|
if (!HeapTupleIsValid(policy_tuple))
|
||||||
{
|
{
|
||||||
if (!missing_ok)
|
if (!missing_ok)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -978,11 +977,11 @@ get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
|
|||||||
policy_oid = InvalidOid;
|
policy_oid = InvalidOid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
policy_oid = HeapTupleGetOid(rsec_tuple);
|
policy_oid = HeapTupleGetOid(policy_tuple);
|
||||||
|
|
||||||
/* Clean up. */
|
/* Clean up. */
|
||||||
systable_endscan(sscan);
|
systable_endscan(sscan);
|
||||||
heap_close(pg_rowsecurity_rel, AccessShareLock);
|
heap_close(pg_policy_rel, AccessShareLock);
|
||||||
|
|
||||||
return policy_oid;
|
return policy_oid;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
#include "catalog/pg_inherits_fn.h"
|
#include "catalog/pg_inherits_fn.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_rowsecurity.h"
|
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
@ -7986,6 +7985,24 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
|
|||||||
colName)));
|
colName)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OCLASS_POLICY:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A policy can depend on a column because the column is
|
||||||
|
* specified in the policy's USING or WITH CHECK qual
|
||||||
|
* expressions. It might be possible to rewrite and recheck
|
||||||
|
* the policy expression, but punt for now. It's certainly
|
||||||
|
* easy enough to remove and recreate the policy; still,
|
||||||
|
* FIXME someday.
|
||||||
|
*/
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot alter type of a column used in a policy definition"),
|
||||||
|
errdetail("%s depends on column \"%s\"",
|
||||||
|
getObjectDescription(&foundObject),
|
||||||
|
colName)));
|
||||||
|
break;
|
||||||
|
|
||||||
case OCLASS_DEFAULT:
|
case OCLASS_DEFAULT:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -502,7 +502,7 @@ ExecutorRewind(QueryDesc *queryDesc)
|
|||||||
* Returns true if permissions are adequate. Otherwise, throws an appropriate
|
* Returns true if permissions are adequate. Otherwise, throws an appropriate
|
||||||
* error if ereport_on_violation is true, or simply returns false otherwise.
|
* error if ereport_on_violation is true, or simply returns false otherwise.
|
||||||
*
|
*
|
||||||
* Note that this does NOT address row-level security policies (aka: RLS). If
|
* Note that this does NOT address row level security policies (aka: RLS). If
|
||||||
* rows will be returned to the user as a result of this permission check
|
* rows will be returned to the user as a result of this permission check
|
||||||
* passing, then RLS also needs to be consulted (and check_enable_rls()).
|
* passing, then RLS also needs to be consulted (and check_enable_rls()).
|
||||||
*
|
*
|
||||||
|
@ -1171,7 +1171,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the subquery has the "security_barrier" flag, it means the subquery
|
* If the subquery has the "security_barrier" flag, it means the subquery
|
||||||
* originated from a view that must enforce row-level security. Then we
|
* originated from a view that must enforce row level security. Then we
|
||||||
* must not push down quals that contain leaky functions. (Ideally this
|
* must not push down quals that contain leaky functions. (Ideally this
|
||||||
* would be checked inside subquery_is_pushdown_safe, but since we don't
|
* would be checked inside subquery_is_pushdown_safe, but since we don't
|
||||||
* currently pass the RTE to that function, we must do it here.)
|
* currently pass the RTE to that function, we must do it here.)
|
||||||
|
@ -177,7 +177,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
|
|||||||
glob->lastPHId = 0;
|
glob->lastPHId = 0;
|
||||||
glob->lastRowMarkId = 0;
|
glob->lastRowMarkId = 0;
|
||||||
glob->transientPlan = false;
|
glob->transientPlan = false;
|
||||||
glob->has_rls = false;
|
glob->hasRowSecurity = false;
|
||||||
|
|
||||||
/* Determine what fraction of the plan is likely to be scanned */
|
/* Determine what fraction of the plan is likely to be scanned */
|
||||||
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
|
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
|
||||||
@ -255,7 +255,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
|
|||||||
result->relationOids = glob->relationOids;
|
result->relationOids = glob->relationOids;
|
||||||
result->invalItems = glob->invalItems;
|
result->invalItems = glob->invalItems;
|
||||||
result->nParamExec = glob->nParamExec;
|
result->nParamExec = glob->nParamExec;
|
||||||
result->has_rls = glob->has_rls;
|
result->hasRowSecurity = glob->hasRowSecurity;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1208,7 +1208,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
|||||||
* This may add new security barrier subquery RTEs to the rangetable.
|
* This may add new security barrier subquery RTEs to the rangetable.
|
||||||
*/
|
*/
|
||||||
expand_security_quals(root, tlist);
|
expand_security_quals(root, tlist);
|
||||||
root->glob->has_rls = parse->hasRowSecurity;
|
root->glob->hasRowSecurity = parse->hasRowSecurity;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Locate any window functions in the tlist. (We don't need to look
|
* Locate any window functions in the tlist. (We don't need to look
|
||||||
|
@ -2109,7 +2109,7 @@ extract_query_dependencies(Node *query,
|
|||||||
glob.type = T_PlannerGlobal;
|
glob.type = T_PlannerGlobal;
|
||||||
glob.relationOids = NIL;
|
glob.relationOids = NIL;
|
||||||
glob.invalItems = NIL;
|
glob.invalItems = NIL;
|
||||||
glob.has_rls = false;
|
glob.hasRowSecurity = false;
|
||||||
|
|
||||||
MemSet(&root, 0, sizeof(root));
|
MemSet(&root, 0, sizeof(root));
|
||||||
root.type = T_PlannerInfo;
|
root.type = T_PlannerInfo;
|
||||||
@ -2119,7 +2119,7 @@ extract_query_dependencies(Node *query,
|
|||||||
|
|
||||||
*relationOids = glob.relationOids;
|
*relationOids = glob.relationOids;
|
||||||
*invalItems = glob.invalItems;
|
*invalItems = glob.invalItems;
|
||||||
*hasRowSecurity = glob.has_rls;
|
*hasRowSecurity = glob.hasRowSecurity;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -2135,8 +2135,8 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
|
|||||||
Query *query = (Query *) node;
|
Query *query = (Query *) node;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
/* Collect row-security information */
|
/* Collect row security information */
|
||||||
context->glob->has_rls = query->hasRowSecurity;
|
context->glob->hasRowSecurity = query->hasRowSecurity;
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (query->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
|
@ -5535,7 +5535,7 @@ opt_restart_seqs:
|
|||||||
* COMMENT ON [ [ CONVERSION | COLLATION | DATABASE | DOMAIN |
|
* COMMENT ON [ [ CONVERSION | COLLATION | DATABASE | DOMAIN |
|
||||||
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
|
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
|
||||||
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
|
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
|
||||||
* MATERIALIZED VIEW | ROLE | SCHEMA | SEQUENCE |
|
* MATERIALIZED VIEW | POLICY | ROLE | SCHEMA | SEQUENCE |
|
||||||
* SERVER | TABLE | TABLESPACE |
|
* SERVER | TABLE | TABLESPACE |
|
||||||
* TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY |
|
* TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY |
|
||||||
* TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE |
|
* TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE |
|
||||||
@ -5601,6 +5601,15 @@ CommentStmt:
|
|||||||
n->comment = $8;
|
n->comment = $8;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
|
| COMMENT ON POLICY name ON any_name IS comment_text
|
||||||
|
{
|
||||||
|
CommentStmt *n = makeNode(CommentStmt);
|
||||||
|
n->objtype = OBJECT_POLICY;
|
||||||
|
n->objname = lappend($6, makeString($4));
|
||||||
|
n->objargs = NIL;
|
||||||
|
n->comment = $8;
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
| COMMENT ON RULE name ON any_name IS comment_text
|
| COMMENT ON RULE name ON any_name IS comment_text
|
||||||
{
|
{
|
||||||
CommentStmt *n = makeNode(CommentStmt);
|
CommentStmt *n = makeNode(CommentStmt);
|
||||||
|
@ -1715,7 +1715,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If the RTE has row-security quals, apply them and recurse into the
|
* If the RTE has row security quals, apply them and recurse into the
|
||||||
* securityQuals.
|
* securityQuals.
|
||||||
*/
|
*/
|
||||||
if (prepend_row_security_policies(parsetree, rte, rt_index))
|
if (prepend_row_security_policies(parsetree, rte, rt_index))
|
||||||
@ -1727,7 +1727,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
|
|||||||
if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
|
if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("infinite recursion detected in row-security policy for relation \"%s\"",
|
errmsg("infinite recursion detected in policy for relation \"%s\"",
|
||||||
RelationGetRelationName(rel))));
|
RelationGetRelationName(rel))));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* rewrite/rowsecurity.c
|
* rewrite/rowsecurity.c
|
||||||
* Routines to support policies for row-level security.
|
* Routines to support policies for row level security (aka RLS).
|
||||||
*
|
*
|
||||||
* Policies in PostgreSQL provide a mechanism to limit what records are
|
* Policies in PostgreSQL provide a mechanism to limit what records are
|
||||||
* returned to a user and what records a user is permitted to add to a table.
|
* returned to a user and what records a user is permitted to add to a table.
|
||||||
@ -38,7 +38,7 @@
|
|||||||
#include "access/sysattr.h"
|
#include "access/sysattr.h"
|
||||||
#include "catalog/pg_class.h"
|
#include "catalog/pg_class.h"
|
||||||
#include "catalog/pg_inherits_fn.h"
|
#include "catalog/pg_inherits_fn.h"
|
||||||
#include "catalog/pg_rowsecurity.h"
|
#include "catalog/pg_policy.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
@ -72,8 +72,8 @@ static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
|
|||||||
row_security_policy_hook_type row_security_policy_hook = NULL;
|
row_security_policy_hook_type row_security_policy_hook = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the given RTE to see whether it's already had row-security quals
|
* Check the given RTE to see whether it's already had row security quals
|
||||||
* expanded and, if not, prepend any row-security rules from built-in or
|
* expanded and, if not, prepend any row security rules from built-in or
|
||||||
* plug-in sources to the securityQuals. The security quals are rewritten (for
|
* plug-in sources to the securityQuals. The security quals are rewritten (for
|
||||||
* view expansion, etc) before being added to the RTE.
|
* view expansion, etc) before being added to the RTE.
|
||||||
*
|
*
|
||||||
@ -154,14 +154,14 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
|
|||||||
/*
|
/*
|
||||||
* Check if this is only the default-deny policy.
|
* Check if this is only the default-deny policy.
|
||||||
*
|
*
|
||||||
* Normally, if the table has row-security enabled but there are
|
* Normally, if the table has row security enabled but there are
|
||||||
* no policies, we use a default-deny policy and not allow anything.
|
* no policies, we use a default-deny policy and not allow anything.
|
||||||
* However, when an extension uses the hook to add their own
|
* However, when an extension uses the hook to add their own
|
||||||
* policies, we don't want to include the default deny policy or
|
* policies, we don't want to include the default deny policy or
|
||||||
* there won't be any way for a user to use an extension exclusively
|
* there won't be any way for a user to use an extension exclusively
|
||||||
* for the policies to be used.
|
* for the policies to be used.
|
||||||
*/
|
*/
|
||||||
if (((RowSecurityPolicy *) linitial(rowsec_policies))->rsecid
|
if (((RowSecurityPolicy *) linitial(rowsec_policies))->policy_id
|
||||||
== InvalidOid)
|
== InvalidOid)
|
||||||
defaultDeny = true;
|
defaultDeny = true;
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
|
|||||||
|
|
||||||
policy = palloc0(sizeof(RowSecurityPolicy));
|
policy = palloc0(sizeof(RowSecurityPolicy));
|
||||||
policy->policy_name = pstrdup("default-deny policy");
|
policy->policy_name = pstrdup("default-deny policy");
|
||||||
policy->rsecid = InvalidOid;
|
policy->policy_id = InvalidOid;
|
||||||
policy->cmd = '\0';
|
policy->cmd = '\0';
|
||||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true,
|
||||||
'i');
|
'i');
|
||||||
|
8
src/backend/utils/cache/plancache.c
vendored
8
src/backend/utils/cache/plancache.c
vendored
@ -207,7 +207,7 @@ CreateCachedPlan(Node *raw_parse_tree,
|
|||||||
plansource->generic_cost = -1;
|
plansource->generic_cost = -1;
|
||||||
plansource->total_custom_cost = 0;
|
plansource->total_custom_cost = 0;
|
||||||
plansource->num_custom_plans = 0;
|
plansource->num_custom_plans = 0;
|
||||||
plansource->has_rls = false;
|
plansource->hasRowSecurity = false;
|
||||||
plansource->rowSecurityDisabled
|
plansource->rowSecurityDisabled
|
||||||
= (security_context & SECURITY_ROW_LEVEL_DISABLED) != 0;
|
= (security_context & SECURITY_ROW_LEVEL_DISABLED) != 0;
|
||||||
plansource->row_security_env = row_security;
|
plansource->row_security_env = row_security;
|
||||||
@ -383,7 +383,7 @@ CompleteCachedPlan(CachedPlanSource *plansource,
|
|||||||
extract_query_dependencies((Node *) querytree_list,
|
extract_query_dependencies((Node *) querytree_list,
|
||||||
&plansource->relationOids,
|
&plansource->relationOids,
|
||||||
&plansource->invalItems,
|
&plansource->invalItems,
|
||||||
&plansource->has_rls);
|
&plansource->hasRowSecurity);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Also save the current search_path in the query_context. (This
|
* Also save the current search_path in the query_context. (This
|
||||||
@ -617,7 +617,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
|
|||||||
*/
|
*/
|
||||||
if (plansource->is_valid
|
if (plansource->is_valid
|
||||||
&& !plansource->rowSecurityDisabled
|
&& !plansource->rowSecurityDisabled
|
||||||
&& plansource->has_rls
|
&& plansource->hasRowSecurity
|
||||||
&& (plansource->planUserId != GetUserId()
|
&& (plansource->planUserId != GetUserId()
|
||||||
|| plansource->row_security_env != row_security))
|
|| plansource->row_security_env != row_security))
|
||||||
plansource->is_valid = false;
|
plansource->is_valid = false;
|
||||||
@ -766,7 +766,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
|
|||||||
extract_query_dependencies((Node *) qlist,
|
extract_query_dependencies((Node *) qlist,
|
||||||
&plansource->relationOids,
|
&plansource->relationOids,
|
||||||
&plansource->invalItems,
|
&plansource->invalItems,
|
||||||
&plansource->has_rls);
|
&plansource->hasRowSecurity);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Also save the current search_path in the query_context. (This should
|
* Also save the current search_path in the query_context. (This should
|
||||||
|
4
src/backend/utils/cache/relcache.c
vendored
4
src/backend/utils/cache/relcache.c
vendored
@ -866,7 +866,7 @@ equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
|
|||||||
if (policy2 == NULL)
|
if (policy2 == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (policy1->rsecid != policy2->rsecid)
|
if (policy1->policy_id != policy2->policy_id)
|
||||||
return false;
|
return false;
|
||||||
if (policy1->cmd != policy2->cmd)
|
if (policy1->cmd != policy2->cmd)
|
||||||
return false;
|
return false;
|
||||||
@ -3439,7 +3439,7 @@ RelationCacheInitializePhase3(void)
|
|||||||
* they are not preserved in the cache. Note that we can never NOT
|
* they are not preserved in the cache. Note that we can never NOT
|
||||||
* have a policy while relrowsecurity is true,
|
* have a policy while relrowsecurity is true,
|
||||||
* RelationBuildRowSecurity will create a single default-deny policy
|
* RelationBuildRowSecurity will create a single default-deny policy
|
||||||
* if there is no policy defined in pg_rowsecurity.
|
* if there is no policy defined in pg_policy.
|
||||||
*/
|
*/
|
||||||
if (relation->rd_rel->relrowsecurity && relation->rd_rsdesc == NULL)
|
if (relation->rd_rel->relrowsecurity && relation->rd_rsdesc == NULL)
|
||||||
{
|
{
|
||||||
|
@ -248,8 +248,8 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
|
|||||||
getRules(fout, &numRules);
|
getRules(fout, &numRules);
|
||||||
|
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
write_msg(NULL, "reading row-security policies\n");
|
write_msg(NULL, "reading policies\n");
|
||||||
getRowSecurity(fout, tblinfo, numTables);
|
getPolicies(fout, tblinfo, numTables);
|
||||||
|
|
||||||
*numTablesPtr = numTables;
|
*numTablesPtr = numTables;
|
||||||
return tblinfo;
|
return tblinfo;
|
||||||
|
@ -3328,6 +3328,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
|
|||||||
strcmp(te->desc, "RULE") == 0 ||
|
strcmp(te->desc, "RULE") == 0 ||
|
||||||
strcmp(te->desc, "TRIGGER") == 0 ||
|
strcmp(te->desc, "TRIGGER") == 0 ||
|
||||||
strcmp(te->desc, "ROW SECURITY") == 0 ||
|
strcmp(te->desc, "ROW SECURITY") == 0 ||
|
||||||
|
strcmp(te->desc, "POLICY") == 0 ||
|
||||||
strcmp(te->desc, "USER MAPPING") == 0)
|
strcmp(te->desc, "USER MAPPING") == 0)
|
||||||
{
|
{
|
||||||
/* these object types don't have separate owners */
|
/* these object types don't have separate owners */
|
||||||
|
@ -233,7 +233,7 @@ static char *myFormatType(const char *typname, int32 typmod);
|
|||||||
static void getBlobs(Archive *fout);
|
static void getBlobs(Archive *fout);
|
||||||
static void dumpBlob(Archive *fout, DumpOptions *dopt, BlobInfo *binfo);
|
static void dumpBlob(Archive *fout, DumpOptions *dopt, BlobInfo *binfo);
|
||||||
static int dumpBlobs(Archive *fout, DumpOptions *dopt, void *arg);
|
static int dumpBlobs(Archive *fout, DumpOptions *dopt, void *arg);
|
||||||
static void dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo);
|
static void dumpPolicy(Archive *fout, DumpOptions *dopt, PolicyInfo *polinfo);
|
||||||
static void dumpDatabase(Archive *AH, DumpOptions *dopt);
|
static void dumpDatabase(Archive *AH, DumpOptions *dopt);
|
||||||
static void dumpEncoding(Archive *AH);
|
static void dumpEncoding(Archive *AH);
|
||||||
static void dumpStdStrings(Archive *AH);
|
static void dumpStdStrings(Archive *AH);
|
||||||
@ -2765,22 +2765,22 @@ dumpBlobs(Archive *fout, DumpOptions *dopt, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getRowSecurity
|
* getPolicies
|
||||||
* get information about every row-security policy on a dumpable table.
|
* get information about policies on a dumpable table.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||||
{
|
{
|
||||||
PQExpBuffer query;
|
PQExpBuffer query;
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
RowSecurityInfo *rsinfo;
|
PolicyInfo *polinfo;
|
||||||
int i_oid;
|
int i_oid;
|
||||||
int i_tableoid;
|
int i_tableoid;
|
||||||
int i_rsecpolname;
|
int i_polname;
|
||||||
int i_rseccmd;
|
int i_polcmd;
|
||||||
int i_rsecroles;
|
int i_polroles;
|
||||||
int i_rsecqual;
|
int i_polqual;
|
||||||
int i_rsecwithcheck;
|
int i_polwithcheck;
|
||||||
int i,
|
int i,
|
||||||
j,
|
j,
|
||||||
ntups;
|
ntups;
|
||||||
@ -2794,18 +2794,18 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
{
|
{
|
||||||
TableInfo *tbinfo = &tblinfo[i];
|
TableInfo *tbinfo = &tblinfo[i];
|
||||||
|
|
||||||
/* Ignore row-security on tables not to be dumped */
|
/* Ignore row security on tables not to be dumped */
|
||||||
if (!tbinfo->dobj.dump)
|
if (!tbinfo->dobj.dump)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
write_msg(NULL, "reading row-security enabled for table \"%s\".\"%s\"\n",
|
write_msg(NULL, "reading row security enabled for table \"%s\".\"%s\"\n",
|
||||||
tbinfo->dobj.namespace->dobj.name,
|
tbinfo->dobj.namespace->dobj.name,
|
||||||
tbinfo->dobj.name);
|
tbinfo->dobj.name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get row-security enabled information for the table. We represent
|
* Get row security enabled information for the table. We represent
|
||||||
* RLS enabled on a table by creating RowSecurityInfo object with an
|
* RLS enabled on a table by creating PolicyInfo object with an
|
||||||
* empty policy.
|
* empty policy.
|
||||||
*/
|
*/
|
||||||
if (tbinfo->rowsec)
|
if (tbinfo->rowsec)
|
||||||
@ -2814,23 +2814,23 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
* Note: use tableoid 0 so that this object won't be mistaken for
|
* Note: use tableoid 0 so that this object won't be mistaken for
|
||||||
* something that pg_depend entries apply to.
|
* something that pg_depend entries apply to.
|
||||||
*/
|
*/
|
||||||
rsinfo = pg_malloc(sizeof(RowSecurityInfo));
|
polinfo = pg_malloc(sizeof(PolicyInfo));
|
||||||
rsinfo->dobj.objType = DO_ROW_SECURITY;
|
polinfo->dobj.objType = DO_POLICY;
|
||||||
rsinfo->dobj.catId.tableoid = 0;
|
polinfo->dobj.catId.tableoid = 0;
|
||||||
rsinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
|
polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
|
||||||
AssignDumpId(&rsinfo->dobj);
|
AssignDumpId(&polinfo->dobj);
|
||||||
rsinfo->dobj.namespace = tbinfo->dobj.namespace;
|
polinfo->dobj.namespace = tbinfo->dobj.namespace;
|
||||||
rsinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
|
polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
|
||||||
rsinfo->rstable = tbinfo;
|
polinfo->poltable = tbinfo;
|
||||||
rsinfo->rsecpolname = NULL;
|
polinfo->polname = NULL;
|
||||||
rsinfo->rseccmd = NULL;
|
polinfo->polcmd = NULL;
|
||||||
rsinfo->rsecroles = NULL;
|
polinfo->polroles = NULL;
|
||||||
rsinfo->rsecqual = NULL;
|
polinfo->polqual = NULL;
|
||||||
rsinfo->rsecwithcheck = NULL;
|
polinfo->polwithcheck = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
write_msg(NULL, "reading row-security policies for table \"%s\".\"%s\"\n",
|
write_msg(NULL, "reading policies for table \"%s\".\"%s\"\n",
|
||||||
tbinfo->dobj.namespace->dobj.name,
|
tbinfo->dobj.namespace->dobj.name,
|
||||||
tbinfo->dobj.name);
|
tbinfo->dobj.name);
|
||||||
|
|
||||||
@ -2843,13 +2843,13 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
|
|
||||||
/* Get the policies for the table. */
|
/* Get the policies for the table. */
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query,
|
||||||
"SELECT oid, tableoid, s.rsecpolname, s.rseccmd, "
|
"SELECT oid, tableoid, pol.polname, pol.polcmd, "
|
||||||
"CASE WHEN s.rsecroles = '{0}' THEN 'PUBLIC' ELSE "
|
"CASE WHEN pol.polroles = '{0}' THEN 'PUBLIC' ELSE "
|
||||||
" array_to_string(ARRAY(SELECT rolname from pg_roles WHERE oid = ANY(s.rsecroles)), ', ') END AS rsecroles, "
|
" array_to_string(ARRAY(SELECT rolname from pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
|
||||||
"pg_get_expr(s.rsecqual, s.rsecrelid) AS rsecqual, "
|
"pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
|
||||||
"pg_get_expr(s.rsecwithcheck, s.rsecrelid) AS rsecwithcheck "
|
"pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
|
||||||
"FROM pg_catalog.pg_rowsecurity s "
|
"FROM pg_catalog.pg_policy pol "
|
||||||
"WHERE rsecrelid = '%u'",
|
"WHERE polrelid = '%u'",
|
||||||
tbinfo->dobj.catId.oid);
|
tbinfo->dobj.catId.oid);
|
||||||
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
|
||||||
|
|
||||||
@ -2868,45 +2868,44 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
|
|
||||||
i_oid = PQfnumber(res, "oid");
|
i_oid = PQfnumber(res, "oid");
|
||||||
i_tableoid = PQfnumber(res, "tableoid");
|
i_tableoid = PQfnumber(res, "tableoid");
|
||||||
i_rsecpolname = PQfnumber(res, "rsecpolname");
|
i_polname = PQfnumber(res, "polname");
|
||||||
i_rseccmd = PQfnumber(res, "rseccmd");
|
i_polcmd = PQfnumber(res, "polcmd");
|
||||||
i_rsecroles = PQfnumber(res, "rsecroles");
|
i_polroles = PQfnumber(res, "polroles");
|
||||||
i_rsecqual = PQfnumber(res, "rsecqual");
|
i_polqual = PQfnumber(res, "polqual");
|
||||||
i_rsecwithcheck = PQfnumber(res, "rsecwithcheck");
|
i_polwithcheck = PQfnumber(res, "polwithcheck");
|
||||||
|
|
||||||
rsinfo = pg_malloc(ntups * sizeof(RowSecurityInfo));
|
polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
|
||||||
|
|
||||||
for (j = 0; j < ntups; j++)
|
for (j = 0; j < ntups; j++)
|
||||||
{
|
{
|
||||||
rsinfo[j].dobj.objType = DO_ROW_SECURITY;
|
polinfo[j].dobj.objType = DO_POLICY;
|
||||||
rsinfo[j].dobj.catId.tableoid =
|
polinfo[j].dobj.catId.tableoid =
|
||||||
atooid(PQgetvalue(res, j, i_tableoid));
|
atooid(PQgetvalue(res, j, i_tableoid));
|
||||||
rsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
|
polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
|
||||||
AssignDumpId(&rsinfo[j].dobj);
|
AssignDumpId(&polinfo[j].dobj);
|
||||||
rsinfo[j].dobj.namespace = tbinfo->dobj.namespace;
|
polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
|
||||||
rsinfo[j].rstable = tbinfo;
|
polinfo[j].poltable = tbinfo;
|
||||||
rsinfo[j].rsecpolname = pg_strdup(PQgetvalue(res, j,
|
polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
|
||||||
i_rsecpolname));
|
|
||||||
|
|
||||||
rsinfo[j].dobj.name = pg_strdup(rsinfo[j].rsecpolname);
|
polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
|
||||||
|
|
||||||
if (PQgetisnull(res, j, i_rseccmd))
|
if (PQgetisnull(res, j, i_polcmd))
|
||||||
rsinfo[j].rseccmd = NULL;
|
polinfo[j].polcmd = NULL;
|
||||||
else
|
else
|
||||||
rsinfo[j].rseccmd = pg_strdup(PQgetvalue(res, j, i_rseccmd));
|
polinfo[j].polcmd = pg_strdup(PQgetvalue(res, j, i_polcmd));
|
||||||
|
|
||||||
rsinfo[j].rsecroles = pg_strdup(PQgetvalue(res, j, i_rsecroles));
|
polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
|
||||||
|
|
||||||
if (PQgetisnull(res, j, i_rsecqual))
|
if (PQgetisnull(res, j, i_polqual))
|
||||||
rsinfo[j].rsecqual = NULL;
|
polinfo[j].polqual = NULL;
|
||||||
else
|
else
|
||||||
rsinfo[j].rsecqual = pg_strdup(PQgetvalue(res, j, i_rsecqual));
|
polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
|
||||||
|
|
||||||
if (PQgetisnull(res, j, i_rsecwithcheck))
|
if (PQgetisnull(res, j, i_polwithcheck))
|
||||||
rsinfo[j].rsecwithcheck = NULL;
|
polinfo[j].polwithcheck = NULL;
|
||||||
else
|
else
|
||||||
rsinfo[j].rsecwithcheck
|
polinfo[j].polwithcheck
|
||||||
= pg_strdup(PQgetvalue(res, j, i_rsecwithcheck));
|
= pg_strdup(PQgetvalue(res, j, i_polwithcheck));
|
||||||
}
|
}
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
@ -2914,13 +2913,13 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dumpRowSecurity
|
* dumpPolicy
|
||||||
* dump the definition of the given row-security policy
|
* dump the definition of the given policy
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo)
|
dumpPolicy(Archive *fout, DumpOptions *dopt, PolicyInfo *polinfo)
|
||||||
{
|
{
|
||||||
TableInfo *tbinfo = rsinfo->rstable;
|
TableInfo *tbinfo = polinfo->poltable;
|
||||||
PQExpBuffer query;
|
PQExpBuffer query;
|
||||||
PQExpBuffer delqry;
|
PQExpBuffer delqry;
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
@ -2929,23 +2928,23 @@ dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If rsecpolname is NULL, then this record is just indicating that ROW
|
* If polname is NULL, then this record is just indicating that ROW
|
||||||
* LEVEL SECURITY is enabled for the table. Dump as ALTER TABLE <table>
|
* LEVEL SECURITY is enabled for the table. Dump as ALTER TABLE <table>
|
||||||
* ENABLE ROW LEVEL SECURITY.
|
* ENABLE ROW LEVEL SECURITY.
|
||||||
*/
|
*/
|
||||||
if (rsinfo->rsecpolname == NULL)
|
if (polinfo->polname == NULL)
|
||||||
{
|
{
|
||||||
query = createPQExpBuffer();
|
query = createPQExpBuffer();
|
||||||
|
|
||||||
appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
|
appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
|
||||||
fmtId(rsinfo->dobj.name));
|
fmtId(polinfo->dobj.name));
|
||||||
|
|
||||||
ArchiveEntry(fout, rsinfo->dobj.catId, rsinfo->dobj.dumpId,
|
ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
|
||||||
rsinfo->dobj.name,
|
polinfo->dobj.name,
|
||||||
rsinfo->dobj.namespace->dobj.name,
|
polinfo->dobj.namespace->dobj.name,
|
||||||
NULL,
|
NULL,
|
||||||
tbinfo->rolname, false,
|
tbinfo->rolname, false,
|
||||||
"ROW SECURITY", SECTION_NONE,
|
"ROW SECURITY", SECTION_POST_DATA,
|
||||||
query->data, "", NULL,
|
query->data, "", NULL,
|
||||||
NULL, 0,
|
NULL, 0,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
@ -2954,19 +2953,19 @@ dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rsinfo->rseccmd)
|
if (!polinfo->polcmd)
|
||||||
cmd = "ALL";
|
cmd = "ALL";
|
||||||
else if (strcmp(rsinfo->rseccmd, "r") == 0)
|
else if (strcmp(polinfo->polcmd, "r") == 0)
|
||||||
cmd = "SELECT";
|
cmd = "SELECT";
|
||||||
else if (strcmp(rsinfo->rseccmd, "a") == 0)
|
else if (strcmp(polinfo->polcmd, "a") == 0)
|
||||||
cmd = "INSERT";
|
cmd = "INSERT";
|
||||||
else if (strcmp(rsinfo->rseccmd, "w") == 0)
|
else if (strcmp(polinfo->polcmd, "w") == 0)
|
||||||
cmd = "UPDATE";
|
cmd = "UPDATE";
|
||||||
else if (strcmp(rsinfo->rseccmd, "d") == 0)
|
else if (strcmp(polinfo->polcmd, "d") == 0)
|
||||||
cmd = "DELETE";
|
cmd = "DELETE";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write_msg(NULL, "unexpected command type: '%s'\n", rsinfo->rseccmd);
|
write_msg(NULL, "unexpected command type: '%s'\n", polinfo->polcmd);
|
||||||
exit_nicely(1);
|
exit_nicely(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2974,28 +2973,28 @@ dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo)
|
|||||||
delqry = createPQExpBuffer();
|
delqry = createPQExpBuffer();
|
||||||
|
|
||||||
appendPQExpBuffer(query, "CREATE POLICY %s ON %s FOR %s",
|
appendPQExpBuffer(query, "CREATE POLICY %s ON %s FOR %s",
|
||||||
rsinfo->rsecpolname, fmtId(tbinfo->dobj.name), cmd);
|
polinfo->polname, fmtId(tbinfo->dobj.name), cmd);
|
||||||
|
|
||||||
if (rsinfo->rsecroles != NULL)
|
if (polinfo->polroles != NULL)
|
||||||
appendPQExpBuffer(query, " TO %s", rsinfo->rsecroles);
|
appendPQExpBuffer(query, " TO %s", polinfo->polroles);
|
||||||
|
|
||||||
if (rsinfo->rsecqual != NULL)
|
if (polinfo->polqual != NULL)
|
||||||
appendPQExpBuffer(query, " USING %s", rsinfo->rsecqual);
|
appendPQExpBuffer(query, " USING %s", polinfo->polqual);
|
||||||
|
|
||||||
if (rsinfo->rsecwithcheck != NULL)
|
if (polinfo->polwithcheck != NULL)
|
||||||
appendPQExpBuffer(query, " WITH CHECK %s", rsinfo->rsecwithcheck);
|
appendPQExpBuffer(query, " WITH CHECK %s", polinfo->polwithcheck);
|
||||||
|
|
||||||
appendPQExpBuffer(query, ";\n");
|
appendPQExpBuffer(query, ";\n");
|
||||||
|
|
||||||
appendPQExpBuffer(delqry, "DROP POLICY %s ON %s;\n",
|
appendPQExpBuffer(delqry, "DROP POLICY %s ON %s;\n",
|
||||||
rsinfo->rsecpolname, fmtId(tbinfo->dobj.name));
|
polinfo->polname, fmtId(tbinfo->dobj.name));
|
||||||
|
|
||||||
ArchiveEntry(fout, rsinfo->dobj.catId, rsinfo->dobj.dumpId,
|
ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
|
||||||
rsinfo->dobj.name,
|
polinfo->dobj.name,
|
||||||
rsinfo->dobj.namespace->dobj.name,
|
polinfo->dobj.namespace->dobj.name,
|
||||||
NULL,
|
NULL,
|
||||||
tbinfo->rolname, false,
|
tbinfo->rolname, false,
|
||||||
"ROW SECURITY", SECTION_POST_DATA,
|
"POLICY", SECTION_POST_DATA,
|
||||||
query->data, delqry->data, NULL,
|
query->data, delqry->data, NULL,
|
||||||
NULL, 0,
|
NULL, 0,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
@ -8232,8 +8231,8 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
|
|||||||
NULL, 0,
|
NULL, 0,
|
||||||
dumpBlobs, NULL);
|
dumpBlobs, NULL);
|
||||||
break;
|
break;
|
||||||
case DO_ROW_SECURITY:
|
case DO_POLICY:
|
||||||
dumpRowSecurity(fout, dopt, (RowSecurityInfo *) dobj);
|
dumpPolicy(fout, dopt, (PolicyInfo *) dobj);
|
||||||
break;
|
break;
|
||||||
case DO_PRE_DATA_BOUNDARY:
|
case DO_PRE_DATA_BOUNDARY:
|
||||||
case DO_POST_DATA_BOUNDARY:
|
case DO_POST_DATA_BOUNDARY:
|
||||||
@ -15631,7 +15630,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
|
|||||||
case DO_TRIGGER:
|
case DO_TRIGGER:
|
||||||
case DO_EVENT_TRIGGER:
|
case DO_EVENT_TRIGGER:
|
||||||
case DO_DEFAULT_ACL:
|
case DO_DEFAULT_ACL:
|
||||||
case DO_ROW_SECURITY:
|
case DO_POLICY:
|
||||||
/* Post-data objects: must come after the post-data boundary */
|
/* Post-data objects: must come after the post-data boundary */
|
||||||
addObjectDependency(dobj, postDataBound->dumpId);
|
addObjectDependency(dobj, postDataBound->dumpId);
|
||||||
break;
|
break;
|
||||||
|
@ -76,7 +76,7 @@ typedef enum
|
|||||||
DO_POST_DATA_BOUNDARY,
|
DO_POST_DATA_BOUNDARY,
|
||||||
DO_EVENT_TRIGGER,
|
DO_EVENT_TRIGGER,
|
||||||
DO_REFRESH_MATVIEW,
|
DO_REFRESH_MATVIEW,
|
||||||
DO_ROW_SECURITY
|
DO_POLICY
|
||||||
} DumpableObjectType;
|
} DumpableObjectType;
|
||||||
|
|
||||||
typedef struct _dumpableObject
|
typedef struct _dumpableObject
|
||||||
@ -210,7 +210,7 @@ typedef struct _tableInfo
|
|||||||
bool hasindex; /* does it have any indexes? */
|
bool hasindex; /* does it have any indexes? */
|
||||||
bool hasrules; /* does it have any rules? */
|
bool hasrules; /* does it have any rules? */
|
||||||
bool hastriggers; /* does it have any triggers? */
|
bool hastriggers; /* does it have any triggers? */
|
||||||
bool rowsec; /* is row-security enabled? */
|
bool rowsec; /* is row security enabled? */
|
||||||
bool hasoids; /* does it have OIDs? */
|
bool hasoids; /* does it have OIDs? */
|
||||||
uint32 frozenxid; /* for restore frozen xid */
|
uint32 frozenxid; /* for restore frozen xid */
|
||||||
uint32 minmxid; /* for restore min multi xid */
|
uint32 minmxid; /* for restore min multi xid */
|
||||||
@ -453,21 +453,21 @@ typedef struct _blobInfo
|
|||||||
} BlobInfo;
|
} BlobInfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The RowSecurityInfo struct is used to represent row policies on a table and
|
* The PolicyInfo struct is used to represent policies on a table and
|
||||||
* to indicate if a table has RLS enabled (ENABLE ROW SECURITY). If
|
* to indicate if a table has RLS enabled (ENABLE ROW SECURITY). If
|
||||||
* rsecpolname is NULL, then the record indicates ENABLE ROW SECURITY, while if
|
* polname is NULL, then the record indicates ENABLE ROW SECURITY, while if
|
||||||
* it's non-NULL then this is a regular policy definition.
|
* it's non-NULL then this is a regular policy definition.
|
||||||
*/
|
*/
|
||||||
typedef struct _rowSecurityInfo
|
typedef struct _policyInfo
|
||||||
{
|
{
|
||||||
DumpableObject dobj;
|
DumpableObject dobj;
|
||||||
TableInfo *rstable;
|
TableInfo *poltable;
|
||||||
char *rsecpolname; /* null indicates RLS is enabled on rel */
|
char *polname; /* null indicates RLS is enabled on rel */
|
||||||
char *rseccmd;
|
char *polcmd;
|
||||||
char *rsecroles;
|
char *polroles;
|
||||||
char *rsecqual;
|
char *polqual;
|
||||||
char *rsecwithcheck;
|
char *polwithcheck;
|
||||||
} RowSecurityInfo;
|
} PolicyInfo;
|
||||||
|
|
||||||
/* global decls */
|
/* global decls */
|
||||||
extern bool force_quotes; /* double-quotes for identifiers flag */
|
extern bool force_quotes; /* double-quotes for identifiers flag */
|
||||||
@ -549,6 +549,6 @@ extern DefaultACLInfo *getDefaultACLs(Archive *fout, DumpOptions *dopt, int *num
|
|||||||
extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionInfo extinfo[],
|
extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionInfo extinfo[],
|
||||||
int numExtensions);
|
int numExtensions);
|
||||||
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
|
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
|
||||||
extern void getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables);
|
extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
|
||||||
|
|
||||||
#endif /* PG_DUMP_H */
|
#endif /* PG_DUMP_H */
|
||||||
|
@ -28,8 +28,8 @@ static const char *modulename = gettext_noop("sorter");
|
|||||||
* by OID. (This is a relatively crude hack to provide semi-reasonable
|
* by OID. (This is a relatively crude hack to provide semi-reasonable
|
||||||
* behavior for old databases without full dependency info.) Note: collations,
|
* behavior for old databases without full dependency info.) Note: collations,
|
||||||
* extensions, text search, foreign-data, materialized view, event trigger,
|
* extensions, text search, foreign-data, materialized view, event trigger,
|
||||||
* and default ACL objects can't really happen here, so the rather bogus
|
* policies, and default ACL objects can't really happen here, so the rather
|
||||||
* priorities for them don't matter.
|
* bogus priorities for them don't matter.
|
||||||
*
|
*
|
||||||
* NOTE: object-type priorities must match the section assignments made in
|
* NOTE: object-type priorities must match the section assignments made in
|
||||||
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
|
* pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
|
||||||
@ -73,7 +73,7 @@ static const int oldObjectTypePriority[] =
|
|||||||
13, /* DO_POST_DATA_BOUNDARY */
|
13, /* DO_POST_DATA_BOUNDARY */
|
||||||
20, /* DO_EVENT_TRIGGER */
|
20, /* DO_EVENT_TRIGGER */
|
||||||
15, /* DO_REFRESH_MATVIEW */
|
15, /* DO_REFRESH_MATVIEW */
|
||||||
21 /* DO_ROW_SECURITY */
|
21 /* DO_POLICY */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -122,7 +122,7 @@ static const int newObjectTypePriority[] =
|
|||||||
25, /* DO_POST_DATA_BOUNDARY */
|
25, /* DO_POST_DATA_BOUNDARY */
|
||||||
32, /* DO_EVENT_TRIGGER */
|
32, /* DO_EVENT_TRIGGER */
|
||||||
33, /* DO_REFRESH_MATVIEW */
|
33, /* DO_REFRESH_MATVIEW */
|
||||||
34 /* DO_ROW_SECURITY */
|
34 /* DO_POLICY */
|
||||||
};
|
};
|
||||||
|
|
||||||
static DumpId preDataBoundId;
|
static DumpId preDataBoundId;
|
||||||
@ -1438,9 +1438,9 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
|
|||||||
"BLOB DATA (ID %d)",
|
"BLOB DATA (ID %d)",
|
||||||
obj->dumpId);
|
obj->dumpId);
|
||||||
return;
|
return;
|
||||||
case DO_ROW_SECURITY:
|
case DO_POLICY:
|
||||||
snprintf(buf, bufsize,
|
snprintf(buf, bufsize,
|
||||||
"ROW-SECURITY POLICY (ID %d OID %u)",
|
"POLICY (ID %d OID %u)",
|
||||||
obj->dumpId, obj->catId.oid);
|
obj->dumpId, obj->catId.oid);
|
||||||
return;
|
return;
|
||||||
case DO_PRE_DATA_BOUNDARY:
|
case DO_PRE_DATA_BOUNDARY:
|
||||||
|
@ -783,31 +783,31 @@ permissionsList(const char *pattern)
|
|||||||
if (pset.sversion >= 90500)
|
if (pset.sversion >= 90500)
|
||||||
appendPQExpBuffer(&buf,
|
appendPQExpBuffer(&buf,
|
||||||
",\n pg_catalog.array_to_string(ARRAY(\n"
|
",\n pg_catalog.array_to_string(ARRAY(\n"
|
||||||
" SELECT rsecpolname\n"
|
" SELECT polname\n"
|
||||||
" || CASE WHEN rseccmd IS NOT NULL THEN\n"
|
" || CASE WHEN polcmd IS NOT NULL THEN\n"
|
||||||
" E' (' || rseccmd || E')'\n"
|
" E' (' || polcmd || E')'\n"
|
||||||
" ELSE E':' \n"
|
" ELSE E':' \n"
|
||||||
" END\n"
|
" END\n"
|
||||||
" || CASE WHEN rs.rsecqual IS NOT NULL THEN\n"
|
" || CASE WHEN polqual IS NOT NULL THEN\n"
|
||||||
" E'\\n (u): ' || pg_catalog.pg_get_expr(rsecqual, rsecrelid)\n"
|
" E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
|
||||||
" ELSE E''\n"
|
" ELSE E''\n"
|
||||||
" END\n"
|
" END\n"
|
||||||
" || CASE WHEN rsecwithcheck IS NOT NULL THEN\n"
|
" || CASE WHEN polwithcheck IS NOT NULL THEN\n"
|
||||||
" E'\\n (c): ' || pg_catalog.pg_get_expr(rsecwithcheck, rsecrelid)\n"
|
" E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
|
||||||
" ELSE E''\n"
|
" ELSE E''\n"
|
||||||
" END"
|
" END"
|
||||||
" || CASE WHEN rs.rsecroles <> '{0}' THEN\n"
|
" || CASE WHEN polroles <> '{0}' THEN\n"
|
||||||
" E'\\n to: ' || pg_catalog.array_to_string(\n"
|
" E'\\n to: ' || pg_catalog.array_to_string(\n"
|
||||||
" ARRAY(\n"
|
" ARRAY(\n"
|
||||||
" SELECT rolname\n"
|
" SELECT rolname\n"
|
||||||
" FROM pg_catalog.pg_roles\n"
|
" FROM pg_catalog.pg_roles\n"
|
||||||
" WHERE oid = ANY (rs.rsecroles)\n"
|
" WHERE oid = ANY (polroles)\n"
|
||||||
" ORDER BY 1\n"
|
" ORDER BY 1\n"
|
||||||
" ), E', ')\n"
|
" ), E', ')\n"
|
||||||
" ELSE E''\n"
|
" ELSE E''\n"
|
||||||
" END\n"
|
" END\n"
|
||||||
" FROM pg_catalog.pg_rowsecurity rs\n"
|
" FROM pg_catalog.pg_policy pol\n"
|
||||||
" WHERE rsecrelid = c.oid), E'\\n')\n"
|
" WHERE polrelid = c.oid), E'\\n')\n"
|
||||||
" AS \"%s\"",
|
" AS \"%s\"",
|
||||||
gettext_noop("Policies"));
|
gettext_noop("Policies"));
|
||||||
|
|
||||||
@ -2001,27 +2001,19 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
/* print any row-level policies */
|
/* print any row-level policies */
|
||||||
if (pset.sversion >= 90500)
|
if (pset.sversion >= 90500)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(&buf,
|
|
||||||
",\n pg_catalog.pg_get_expr(rs.rsecqual, c.oid) as \"%s\"",
|
|
||||||
gettext_noop("Row-security"));
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
appendPQExpBuffer(&buf,
|
|
||||||
"\n LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
|
|
||||||
|
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT rs.rsecpolname,\n"
|
"SELECT pol.polname,\n"
|
||||||
"CASE WHEN rs.rsecroles = '{0}' THEN NULL ELSE array_to_string(array(select rolname from pg_roles where oid = any (rs.rsecroles) order by 1),',') END,\n"
|
"CASE WHEN pol.polroles = '{0}' THEN NULL ELSE array_to_string(array(select rolname from pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
|
||||||
"pg_catalog.pg_get_expr(rs.rsecqual, rs.rsecrelid),\n"
|
"pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
|
||||||
"pg_catalog.pg_get_expr(rs.rsecwithcheck, rs.rsecrelid),\n"
|
"pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
|
||||||
"CASE rs.rseccmd \n"
|
"CASE pol.polcmd \n"
|
||||||
"WHEN 'r' THEN 'SELECT'\n"
|
"WHEN 'r' THEN 'SELECT'\n"
|
||||||
"WHEN 'u' THEN 'UPDATE'\n"
|
"WHEN 'u' THEN 'UPDATE'\n"
|
||||||
"WHEN 'a' THEN 'INSERT'\n"
|
"WHEN 'a' THEN 'INSERT'\n"
|
||||||
"WHEN 'd' THEN 'DELETE'\n"
|
"WHEN 'd' THEN 'DELETE'\n"
|
||||||
"END AS cmd\n"
|
"END AS cmd\n"
|
||||||
"FROM pg_catalog.pg_rowsecurity rs\n"
|
"FROM pg_catalog.pg_policy pol\n"
|
||||||
"WHERE rs.rsecrelid = '%s' ORDER BY 1;",
|
"WHERE pol.polrelid = '%s' ORDER BY 1;",
|
||||||
oid);
|
oid);
|
||||||
|
|
||||||
result = PSQLexec(buf.data);
|
result = PSQLexec(buf.data);
|
||||||
|
@ -2069,7 +2069,7 @@ psql_completion(const char *text, int start, int end)
|
|||||||
static const char *const list_COMMENT[] =
|
static const char *const list_COMMENT[] =
|
||||||
{"CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION",
|
{"CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION",
|
||||||
"FOREIGN DATA WRAPPER", "FOREIGN TABLE",
|
"FOREIGN DATA WRAPPER", "FOREIGN TABLE",
|
||||||
"SERVER", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE",
|
"SERVER", "INDEX", "LANGUAGE", "POLICY", "RULE", "SCHEMA", "SEQUENCE",
|
||||||
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
|
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
|
||||||
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
|
"OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
|
||||||
"TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
|
"TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201411251
|
#define CATALOG_VERSION_NO 201411271
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -147,7 +147,7 @@ typedef enum ObjectClass
|
|||||||
OCLASS_DEFACL, /* pg_default_acl */
|
OCLASS_DEFACL, /* pg_default_acl */
|
||||||
OCLASS_EXTENSION, /* pg_extension */
|
OCLASS_EXTENSION, /* pg_extension */
|
||||||
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
|
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
|
||||||
OCLASS_ROWSECURITY, /* pg_rowsecurity */
|
OCLASS_POLICY, /* pg_policy */
|
||||||
MAX_OCLASS /* MUST BE LAST */
|
MAX_OCLASS /* MUST BE LAST */
|
||||||
} ObjectClass;
|
} ObjectClass;
|
||||||
|
|
||||||
|
@ -299,11 +299,11 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
|
|||||||
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
|
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
|
||||||
#define RangeTypidIndexId 3542
|
#define RangeTypidIndexId 3542
|
||||||
|
|
||||||
DECLARE_UNIQUE_INDEX(pg_rowsecurity_oid_index, 3257, on pg_rowsecurity using btree(oid oid_ops));
|
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
|
||||||
#define RowSecurityOidIndexId 3257
|
#define PolicyOidIndexId 3257
|
||||||
|
|
||||||
DECLARE_UNIQUE_INDEX(pg_rowsecurity_polname_relid_index, 3258, on pg_rowsecurity using btree(rsecrelid oid_ops, rsecpolname name_ops));
|
DECLARE_UNIQUE_INDEX(pg_policy_polrelid_polname_index, 3258, on pg_policy using btree(polrelid oid_ops, polname name_ops));
|
||||||
#define RowSecurityRelidPolnameIndexId 3258
|
#define PolicyPolrelidPolnameIndexId 3258
|
||||||
|
|
||||||
/* last step of initialization script: build the indexes declared above */
|
/* last step of initialization script: build the indexes declared above */
|
||||||
BUILD_INDICES
|
BUILD_INDICES
|
||||||
|
@ -65,7 +65,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
|
|||||||
bool relhasrules; /* has (or has had) any rules */
|
bool relhasrules; /* has (or has had) any rules */
|
||||||
bool relhastriggers; /* has (or has had) any TRIGGERs */
|
bool relhastriggers; /* has (or has had) any TRIGGERs */
|
||||||
bool relhassubclass; /* has (or has had) derived classes */
|
bool relhassubclass; /* has (or has had) derived classes */
|
||||||
bool relrowsecurity; /* row-security is enabled or not */
|
bool relrowsecurity; /* row security is enabled or not */
|
||||||
bool relispopulated; /* matview currently holds query results */
|
bool relispopulated; /* matview currently holds query results */
|
||||||
char relreplident; /* see REPLICA_IDENTITY_xxx constants */
|
char relreplident; /* see REPLICA_IDENTITY_xxx constants */
|
||||||
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
||||||
|
53
src/include/catalog/pg_policy.h
Normal file
53
src/include/catalog/pg_policy.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* pg_policy.h
|
||||||
|
* definition of the system "policy" relation (pg_policy)
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef PG_POLICY_H
|
||||||
|
#define PG_POLICY_H
|
||||||
|
|
||||||
|
#include "catalog/genbki.h"
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* pg_policy definition. cpp turns this into
|
||||||
|
* typedef struct FormData_pg_policy
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
#define PolicyRelationId 3256
|
||||||
|
|
||||||
|
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 */
|
||||||
|
|
||||||
|
#ifdef CATALOG_VARLEN
|
||||||
|
Oid polroles[1] /* Roles associated with policy, not-NULL */
|
||||||
|
pg_node_tree polqual; /* Policy quals. */
|
||||||
|
pg_node_tree polwithcheck; /* WITH CHECK quals. */
|
||||||
|
#endif
|
||||||
|
} FormData_pg_policy;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* Form_pg_policy corresponds to a pointer to a row with
|
||||||
|
* the format of pg_policy relation.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef FormData_pg_policy *Form_pg_policy;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* compiler constants for pg_policy
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
#define Natts_pg_policy 6
|
||||||
|
#define Anum_pg_policy_polname 1
|
||||||
|
#define Anum_pg_policy_polrelid 2
|
||||||
|
#define Anum_pg_policy_polcmd 3
|
||||||
|
#define Anum_pg_policy_polroles 4
|
||||||
|
#define Anum_pg_policy_polqual 5
|
||||||
|
#define Anum_pg_policy_polwithcheck 6
|
||||||
|
|
||||||
|
#endif /* PG_POLICY_H */
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* pg_rowsecurity.h
|
|
||||||
* definition of the system catalog for row-security policy (pg_rowsecurity)
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef PG_ROWSECURITY_H
|
|
||||||
#define PG_ROWSECURITY_H
|
|
||||||
|
|
||||||
#include "catalog/genbki.h"
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* pg_rowsecurity definition. cpp turns this into
|
|
||||||
* typedef struct FormData_pg_rowsecurity
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
#define RowSecurityRelationId 3256
|
|
||||||
|
|
||||||
CATALOG(pg_rowsecurity,3256)
|
|
||||||
{
|
|
||||||
NameData rsecpolname; /* Policy name. */
|
|
||||||
Oid rsecrelid; /* Oid of the relation with policy. */
|
|
||||||
char rseccmd; /* One of ACL_*_CHR, or \0 for all */
|
|
||||||
|
|
||||||
#ifdef CATALOG_VARLEN
|
|
||||||
Oid rsecroles[1] /* Roles associated with policy, not-NULL */
|
|
||||||
pg_node_tree rsecqual; /* Policy quals. */
|
|
||||||
pg_node_tree rsecwithcheck; /* WITH CHECK quals. */
|
|
||||||
#endif
|
|
||||||
} FormData_pg_rowsecurity;
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* Form_pg_rowsecurity corresponds to a pointer to a row with
|
|
||||||
* the format of pg_rowsecurity relation.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
typedef FormData_pg_rowsecurity *Form_pg_rowsecurity;
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* compiler constants for pg_rowsecurity
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
#define Natts_pg_rowsecurity 6
|
|
||||||
#define Anum_pg_rowsecurity_rsecpolname 1
|
|
||||||
#define Anum_pg_rowsecurity_rsecrelid 2
|
|
||||||
#define Anum_pg_rowsecurity_rseccmd 3
|
|
||||||
#define Anum_pg_rowsecurity_rsecroles 4
|
|
||||||
#define Anum_pg_rowsecurity_rsecqual 5
|
|
||||||
#define Anum_pg_rowsecurity_rsecwithcheck 6
|
|
||||||
|
|
||||||
#endif /* PG_ROWSECURITY_H */
|
|
@ -121,7 +121,7 @@ typedef struct Query
|
|||||||
bool hasRecursive; /* WITH RECURSIVE was specified */
|
bool hasRecursive; /* WITH RECURSIVE was specified */
|
||||||
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
|
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
|
||||||
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
|
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
|
||||||
bool hasRowSecurity; /* Row-security policy is applied */
|
bool hasRowSecurity; /* row security applied? */
|
||||||
|
|
||||||
List *cteList; /* WITH list (of CommonTableExpr's) */
|
List *cteList; /* WITH list (of CommonTableExpr's) */
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ typedef struct PlannedStmt
|
|||||||
|
|
||||||
int nParamExec; /* number of PARAM_EXEC Params used */
|
int nParamExec; /* number of PARAM_EXEC Params used */
|
||||||
|
|
||||||
bool has_rls; /* row-security applied? */
|
bool hasRowSecurity; /* row security applied? */
|
||||||
|
|
||||||
} PlannedStmt;
|
} PlannedStmt;
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ typedef struct PlannerGlobal
|
|||||||
|
|
||||||
bool transientPlan; /* redo plan when TransactionXmin changes? */
|
bool transientPlan; /* redo plan when TransactionXmin changes? */
|
||||||
|
|
||||||
bool has_rls; /* row-security is applied? */
|
bool hasRowSecurity; /* row security applied? */
|
||||||
|
|
||||||
} PlannerGlobal;
|
} PlannerGlobal;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
typedef struct RowSecurityPolicy
|
typedef struct RowSecurityPolicy
|
||||||
{
|
{
|
||||||
Oid rsecid; /* OID of the policy */
|
Oid policy_id; /* OID of the policy */
|
||||||
char *policy_name; /* Name of the policy */
|
char *policy_name; /* Name of the policy */
|
||||||
char cmd; /* Type of command policy is for */
|
char cmd; /* Type of command policy is for */
|
||||||
ArrayType *roles; /* Array of roles policy is for */
|
ArrayType *roles; /* Array of roles policy is for */
|
||||||
@ -30,8 +30,8 @@ typedef struct RowSecurityPolicy
|
|||||||
|
|
||||||
typedef struct RowSecurityDesc
|
typedef struct RowSecurityDesc
|
||||||
{
|
{
|
||||||
MemoryContext rscxt; /* row-security memory context */
|
MemoryContext rscxt; /* row security memory context */
|
||||||
List *policies; /* list of row-security policies */
|
List *policies; /* list of row security policies */
|
||||||
} RowSecurityDesc;
|
} RowSecurityDesc;
|
||||||
|
|
||||||
/* GUC variable */
|
/* GUC variable */
|
||||||
|
@ -109,9 +109,9 @@ typedef struct CachedPlanSource
|
|||||||
double generic_cost; /* cost of generic plan, or -1 if not known */
|
double generic_cost; /* cost of generic plan, or -1 if not known */
|
||||||
double total_custom_cost; /* total cost of custom plans so far */
|
double total_custom_cost; /* total cost of custom plans so far */
|
||||||
int num_custom_plans; /* number of plans included in total */
|
int num_custom_plans; /* number of plans included in total */
|
||||||
bool has_rls; /* planned with row-security? */
|
bool hasRowSecurity; /* planned with row security? */
|
||||||
int row_security_env; /* row security setting when planned */
|
int row_security_env; /* row security setting when planned */
|
||||||
bool rowSecurityDisabled; /* is row-security disabled? */
|
bool rowSecurityDisabled; /* is row security disabled? */
|
||||||
} CachedPlanSource;
|
} CachedPlanSource;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -106,7 +106,7 @@ typedef struct RelationData
|
|||||||
MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */
|
MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */
|
||||||
TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */
|
TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */
|
||||||
/* use "struct" here to avoid needing to include rowsecurity.h: */
|
/* use "struct" here to avoid needing to include rowsecurity.h: */
|
||||||
struct RowSecurityDesc *rd_rsdesc; /* Row-security policies, or NULL */
|
struct RowSecurityDesc *rd_rsdesc; /* row security policies, or NULL */
|
||||||
|
|
||||||
/* data managed by RelationGetIndexList: */
|
/* data managed by RelationGetIndexList: */
|
||||||
List *rd_indexlist; /* list of OIDs of indexes on relation */
|
List *rd_indexlist; /* list of OIDs of indexes on relation */
|
||||||
|
@ -173,7 +173,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dt
|
|||||||
Index Cond: (pguser = "current_user"())
|
Index Cond: (pguser = "current_user"())
|
||||||
(11 rows)
|
(11 rows)
|
||||||
|
|
||||||
-- only owner can change row-level security
|
-- only owner can change policies
|
||||||
ALTER POLICY p1 ON document USING (true); --fail
|
ALTER POLICY p1 ON document USING (true); --fail
|
||||||
ERROR: must be owner of relation document
|
ERROR: must be owner of relation document
|
||||||
DROP POLICY p1 ON document; --fail
|
DROP POLICY p1 ON document; --fail
|
||||||
@ -724,7 +724,7 @@ CREATE TABLE dependent (x integer, y integer);
|
|||||||
CREATE POLICY d1 ON dependent FOR ALL
|
CREATE POLICY d1 ON dependent FOR ALL
|
||||||
TO PUBLIC
|
TO PUBLIC
|
||||||
USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
|
USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
|
||||||
DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row-security qual?
|
DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual?
|
||||||
ERROR: cannot drop table dependee because other objects depend on it
|
ERROR: cannot drop table dependee because other objects depend on it
|
||||||
DETAIL: policy d1 on table dependent depends on table dependee
|
DETAIL: policy d1 on table dependent depends on table dependee
|
||||||
HINT: Use DROP ... CASCADE to drop the dependent objects too.
|
HINT: Use DROP ... CASCADE to drop the dependent objects too.
|
||||||
@ -746,7 +746,7 @@ CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y));
|
|||||||
ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY;
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
SELECT * FROM rec1; -- fail, direct recursion
|
SELECT * FROM rec1; -- fail, direct recursion
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "rec1"
|
ERROR: infinite recursion detected in policy for relation "rec1"
|
||||||
--
|
--
|
||||||
-- Mutual recursion
|
-- Mutual recursion
|
||||||
--
|
--
|
||||||
@ -757,7 +757,7 @@ CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b));
|
|||||||
ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY;
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
SELECT * FROM rec1; -- fail, mutual recursion
|
SELECT * FROM rec1; -- fail, mutual recursion
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "rec1"
|
ERROR: infinite recursion detected in policy for relation "rec1"
|
||||||
--
|
--
|
||||||
-- Mutual recursion via views
|
-- Mutual recursion via views
|
||||||
--
|
--
|
||||||
@ -769,7 +769,7 @@ ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
|
|||||||
ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
|
ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
SELECT * FROM rec1; -- fail, mutual recursion via views
|
SELECT * FROM rec1; -- fail, mutual recursion via views
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "rec1"
|
ERROR: infinite recursion detected in policy for relation "rec1"
|
||||||
--
|
--
|
||||||
-- Mutual recursion via .s.b views
|
-- Mutual recursion via .s.b views
|
||||||
--
|
--
|
||||||
@ -785,7 +785,7 @@ CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
|
|||||||
CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
|
CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
SELECT * FROM rec1; -- fail, mutual recursion via s.b. views
|
SELECT * FROM rec1; -- fail, mutual recursion via s.b. views
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "rec1"
|
ERROR: infinite recursion detected in policy for relation "rec1"
|
||||||
--
|
--
|
||||||
-- recursive RLS and VIEWs in policy
|
-- recursive RLS and VIEWs in policy
|
||||||
--
|
--
|
||||||
@ -803,9 +803,9 @@ ALTER TABLE s2 ENABLE ROW LEVEL SECURITY;
|
|||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%';
|
CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%';
|
||||||
SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion)
|
SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion)
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "s1"
|
ERROR: infinite recursion detected in policy for relation "s1"
|
||||||
INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion)
|
INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion)
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "s1"
|
ERROR: infinite recursion detected in policy for relation "s1"
|
||||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||||
DROP POLICY p3 on s1;
|
DROP POLICY p3 on s1;
|
||||||
ALTER POLICY p2 ON s2 USING (x % 2 = 0);
|
ALTER POLICY p2 ON s2 USING (x % 2 = 0);
|
||||||
@ -897,7 +897,7 @@ SET SESSION AUTHORIZATION rls_regress_user0;
|
|||||||
ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%'));
|
ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%'));
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view)
|
SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view)
|
||||||
ERROR: infinite recursion detected in row-security policy for relation "s1"
|
ERROR: infinite recursion detected in policy for relation "s1"
|
||||||
-- prepared statement with rls_regress_user0 privilege
|
-- prepared statement with rls_regress_user0 privilege
|
||||||
PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1;
|
PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1;
|
||||||
EXECUTE p1(2);
|
EXECUTE p1(2);
|
||||||
@ -1714,24 +1714,24 @@ WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM c
|
|||||||
--
|
--
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
ALTER POLICY p1 ON t1 RENAME TO p1; --fail
|
ALTER POLICY p1 ON t1 RENAME TO p1; --fail
|
||||||
ERROR: row-policy "p1" for table "t1" already exists
|
ERROR: policy "p1" for table "t1" already exists
|
||||||
SELECT rsecpolname, relname
|
SELECT polname, relname
|
||||||
FROM pg_rowsecurity rs
|
FROM pg_policy pol
|
||||||
JOIN pg_class pc ON (pc.oid = rs.rsecrelid)
|
JOIN pg_class pc ON (pc.oid = pol.polrelid)
|
||||||
WHERE relname = 't1';
|
WHERE relname = 't1';
|
||||||
rsecpolname | relname
|
polname | relname
|
||||||
-------------+---------
|
---------+---------
|
||||||
p1 | t1
|
p1 | t1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
ALTER POLICY p1 ON t1 RENAME TO p2; --ok
|
ALTER POLICY p1 ON t1 RENAME TO p2; --ok
|
||||||
SELECT rsecpolname, relname
|
SELECT polname, relname
|
||||||
FROM pg_rowsecurity rs
|
FROM pg_policy pol
|
||||||
JOIN pg_class pc ON (pc.oid = rs.rsecrelid)
|
JOIN pg_class pc ON (pc.oid = pol.polrelid)
|
||||||
WHERE relname = 't1';
|
WHERE relname = 't1';
|
||||||
rsecpolname | relname
|
polname | relname
|
||||||
-------------+---------
|
---------+---------
|
||||||
p2 | t1
|
p2 | t1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -2161,7 +2161,7 @@ SET row_security TO ON;
|
|||||||
COPY copy_t FROM STDIN; --ok
|
COPY copy_t FROM STDIN; --ok
|
||||||
SET row_security TO FORCE;
|
SET row_security TO FORCE;
|
||||||
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
||||||
ERROR: COPY FROM not supported with row security.
|
ERROR: COPY FROM not supported with row level security.
|
||||||
HINT: Use direct INSERT statements instead.
|
HINT: Use direct INSERT statements instead.
|
||||||
-- Check COPY FROM as user with permissions.
|
-- Check COPY FROM as user with permissions.
|
||||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||||
@ -2170,11 +2170,11 @@ COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
|
|||||||
ERROR: insufficient privilege to bypass row security.
|
ERROR: insufficient privilege to bypass row security.
|
||||||
SET row_security TO ON;
|
SET row_security TO ON;
|
||||||
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
||||||
ERROR: COPY FROM not supported with row security.
|
ERROR: COPY FROM not supported with row level security.
|
||||||
HINT: Use direct INSERT statements instead.
|
HINT: Use direct INSERT statements instead.
|
||||||
SET row_security TO FORCE;
|
SET row_security TO FORCE;
|
||||||
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
||||||
ERROR: COPY FROM not supported with row security.
|
ERROR: COPY FROM not supported with row level security.
|
||||||
HINT: Use direct INSERT statements instead.
|
HINT: Use direct INSERT statements instead.
|
||||||
-- Check COPY TO as user with permissions and BYPASSRLS
|
-- Check COPY TO as user with permissions and BYPASSRLS
|
||||||
SET SESSION AUTHORIZATION rls_regress_exempt_user;
|
SET SESSION AUTHORIZATION rls_regress_exempt_user;
|
||||||
@ -2182,11 +2182,11 @@ SET row_security TO OFF;
|
|||||||
COPY copy_t FROM STDIN; --ok
|
COPY copy_t FROM STDIN; --ok
|
||||||
SET row_security TO ON;
|
SET row_security TO ON;
|
||||||
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
||||||
ERROR: COPY FROM not supported with row security.
|
ERROR: COPY FROM not supported with row level security.
|
||||||
HINT: Use direct INSERT statements instead.
|
HINT: Use direct INSERT statements instead.
|
||||||
SET row_security TO FORCE;
|
SET row_security TO FORCE;
|
||||||
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
|
||||||
ERROR: COPY FROM not supported with row security.
|
ERROR: COPY FROM not supported with row level security.
|
||||||
HINT: Use direct INSERT statements instead.
|
HINT: Use direct INSERT statements instead.
|
||||||
-- Check COPY FROM as user without permissions.
|
-- Check COPY FROM as user without permissions.
|
||||||
SET SESSION AUTHORIZATION rls_regress_user2;
|
SET SESSION AUTHORIZATION rls_regress_user2;
|
||||||
|
@ -1355,18 +1355,18 @@ pg_matviews| SELECT n.nspname AS schemaname,
|
|||||||
WHERE (c.relkind = 'm'::"char");
|
WHERE (c.relkind = 'm'::"char");
|
||||||
pg_policies| SELECT n.nspname AS schemaname,
|
pg_policies| SELECT n.nspname AS schemaname,
|
||||||
c.relname AS tablename,
|
c.relname AS tablename,
|
||||||
rs.rsecpolname AS policyname,
|
pol.polname AS policyname,
|
||||||
CASE
|
CASE
|
||||||
WHEN (rs.rsecroles = '{0}'::oid[]) THEN (string_to_array('public'::text, ''::text))::name[]
|
WHEN (pol.polroles = '{0}'::oid[]) THEN (string_to_array('public'::text, ''::text))::name[]
|
||||||
ELSE ARRAY( SELECT pg_authid.rolname
|
ELSE ARRAY( SELECT pg_authid.rolname
|
||||||
FROM pg_authid
|
FROM pg_authid
|
||||||
WHERE (pg_authid.oid = ANY (rs.rsecroles))
|
WHERE (pg_authid.oid = ANY (pol.polroles))
|
||||||
ORDER BY pg_authid.rolname)
|
ORDER BY pg_authid.rolname)
|
||||||
END AS roles,
|
END AS roles,
|
||||||
CASE
|
CASE
|
||||||
WHEN (rs.rseccmd IS NULL) THEN 'ALL'::text
|
WHEN (pol.polcmd IS NULL) THEN 'ALL'::text
|
||||||
ELSE
|
ELSE
|
||||||
CASE rs.rseccmd
|
CASE pol.polcmd
|
||||||
WHEN 'r'::"char" THEN 'SELECT'::text
|
WHEN 'r'::"char" THEN 'SELECT'::text
|
||||||
WHEN 'a'::"char" THEN 'INSERT'::text
|
WHEN 'a'::"char" THEN 'INSERT'::text
|
||||||
WHEN 'u'::"char" THEN 'UPDATE'::text
|
WHEN 'u'::"char" THEN 'UPDATE'::text
|
||||||
@ -1374,10 +1374,10 @@ pg_policies| SELECT n.nspname AS schemaname,
|
|||||||
ELSE NULL::text
|
ELSE NULL::text
|
||||||
END
|
END
|
||||||
END AS cmd,
|
END AS cmd,
|
||||||
pg_get_expr(rs.rsecqual, rs.rsecrelid) AS qual,
|
pg_get_expr(pol.polqual, pol.polrelid) AS qual,
|
||||||
pg_get_expr(rs.rsecwithcheck, rs.rsecrelid) AS with_check
|
pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
|
||||||
FROM ((pg_rowsecurity rs
|
FROM ((pg_policy pol
|
||||||
JOIN pg_class c ON ((c.oid = rs.rsecrelid)))
|
JOIN pg_class c ON ((c.oid = pol.polrelid)))
|
||||||
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
|
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
|
||||||
pg_prepared_statements| SELECT p.name,
|
pg_prepared_statements| SELECT p.name,
|
||||||
p.statement,
|
p.statement,
|
||||||
|
@ -118,10 +118,10 @@ pg_opclass|t
|
|||||||
pg_operator|t
|
pg_operator|t
|
||||||
pg_opfamily|t
|
pg_opfamily|t
|
||||||
pg_pltemplate|t
|
pg_pltemplate|t
|
||||||
|
pg_policy|t
|
||||||
pg_proc|t
|
pg_proc|t
|
||||||
pg_range|t
|
pg_range|t
|
||||||
pg_rewrite|t
|
pg_rewrite|t
|
||||||
pg_rowsecurity|t
|
|
||||||
pg_seclabel|t
|
pg_seclabel|t
|
||||||
pg_shdepend|t
|
pg_shdepend|t
|
||||||
pg_shdescription|t
|
pg_shdescription|t
|
||||||
|
@ -102,7 +102,7 @@ SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;
|
|||||||
EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
|
EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
|
||||||
EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
|
EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
|
||||||
|
|
||||||
-- only owner can change row-level security
|
-- only owner can change policies
|
||||||
ALTER POLICY p1 ON document USING (true); --fail
|
ALTER POLICY p1 ON document USING (true); --fail
|
||||||
DROP POLICY p1 ON document; --fail
|
DROP POLICY p1 ON document; --fail
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ CREATE POLICY d1 ON dependent FOR ALL
|
|||||||
TO PUBLIC
|
TO PUBLIC
|
||||||
USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
|
USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
|
||||||
|
|
||||||
DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row-security qual?
|
DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual?
|
||||||
|
|
||||||
DROP TABLE dependee CASCADE;
|
DROP TABLE dependee CASCADE;
|
||||||
|
|
||||||
@ -659,16 +659,16 @@ WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM c
|
|||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
ALTER POLICY p1 ON t1 RENAME TO p1; --fail
|
ALTER POLICY p1 ON t1 RENAME TO p1; --fail
|
||||||
|
|
||||||
SELECT rsecpolname, relname
|
SELECT polname, relname
|
||||||
FROM pg_rowsecurity rs
|
FROM pg_policy pol
|
||||||
JOIN pg_class pc ON (pc.oid = rs.rsecrelid)
|
JOIN pg_class pc ON (pc.oid = pol.polrelid)
|
||||||
WHERE relname = 't1';
|
WHERE relname = 't1';
|
||||||
|
|
||||||
ALTER POLICY p1 ON t1 RENAME TO p2; --ok
|
ALTER POLICY p1 ON t1 RENAME TO p2; --ok
|
||||||
|
|
||||||
SELECT rsecpolname, relname
|
SELECT polname, relname
|
||||||
FROM pg_rowsecurity rs
|
FROM pg_policy pol
|
||||||
JOIN pg_class pc ON (pc.oid = rs.rsecrelid)
|
JOIN pg_class pc ON (pc.oid = pol.polrelid)
|
||||||
WHERE relname = 't1';
|
WHERE relname = 't1';
|
||||||
|
|
||||||
--
|
--
|
||||||
|
Loading…
x
Reference in New Issue
Block a user