1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Code review for row security.

Buildfarm member tick identified an issue where the policies in the
relcache for a relation were were being replaced underneath a running
query, leading to segfaults while processing the policies to be added
to a query.  Similar to how TupleDesc RuleLocks are handled, add in a
equalRSDesc() function to check if the policies have actually changed
and, if not, swap back the rsdesc field (using the original instead of
the temporairly built one; the whole structure is swapped and then
specific fields swapped back).  This now passes a CLOBBER_CACHE_ALWAYS
for me and should resolve the buildfarm error.

In addition to addressing this, add a new chapter in Data Definition
under Privileges which explains row security and provides examples of
its usage, change \d to always list policies (even if row security is
disabled- but note that it is disabled, or enabled with no policies),
rework check_role_for_policy (it really didn't need the entire policy,
but it did need to be using has_privs_of_role()), and change the field
in pg_class to relrowsecurity from relhasrowsecurity, based on
Heikki's suggestion.  Also from Heikki, only issue SET ROW_SECURITY in
pg_restore when talking to a 9.5+ server, list Bypass RLS in \du, and
document --enable-row-security options for pg_dump and pg_restore.

Lastly, fix a number of minor whitespace and typo issues from Heikki,
Dimitri, add a missing #include, per Peter E, fix a few minor
variable-assigned-but-not-used and resource leak issues from Coverity
and add tab completion for role attribute bypassrls as well.
This commit is contained in:
Stephen Frost
2014-09-24 16:32:22 -04:00
parent 3f6f9260e3
commit 6550b901fe
24 changed files with 442 additions and 125 deletions

View File

@ -1941,8 +1941,9 @@
</row>
<row>
<entry><structfield>relhasrowsecurity</structfield></entry>
<entry><structfield>relrowsecurity</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
True if table has row-security enabled; see
<link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link> catalog
@ -5415,7 +5416,7 @@
<note>
<para>
<literal>pg_class.relhasrowsecurity</literal>
<literal>pg_class.relrowsecurity</literal>
True if the table has row-security enabled.
Must be true if the table has a row-security policy in this catalog.
</para>
@ -9228,10 +9229,10 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<entry>True if table has (or once had) triggers</entry>
</row>
<row>
<entry><structfield>hasrowsecurity</structfield></entry>
<entry><structfield>rowsecurity</structfield></entry>
<entry><type>boolean</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relhasrowsecurity</literal></entry>
<entry>True if table has row security enabled</entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relrowsecurity</literal></entry>
<entry>True if row security is enabled on the table</entry>
</row>
</tbody>
</tgroup>

View File

@ -5457,9 +5457,9 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<para>
The allowed values of <varname>row_security</> are
<literal>on</> (apply normally- not to superuser or table owner),
<literal>on</> (apply normally - not to superuser or table owner),
<literal>off</> (fail if row security would be applied), and
<literal>force</> (apply always- even to superuser and table owner).
<literal>force</> (apply always - even to superuser and table owner).
</para>
<para>

View File

@ -1508,6 +1508,174 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</sect1>
<sect1 id="ddl-rowsecurity">
<title>Row Security Policies</title>
<indexterm zone="ddl-rowsecurity">
<primary>rowsecurity</primary>
</indexterm>
<indexterm zone="ddl-rowsecurity">
<primary>rls</primary>
</indexterm>
<indexterm>
<primary>policies</primary>
<see>policy</see>
</indexterm>
<indexterm zone="ddl-rowsecurity">
<primary>POLICY</primary>
</indexterm>
<para>
In addition to the <xref linkend="ddl-priv"> system available through
<xref linkend="sql-grant">, tables can have row security policies
which limit the rows returned for normal queries and rows which can
be added through data modification commands. By default, tables do
not have any policies and all rows are visible and able to be added,
subject to the regular <xref linkend="ddl-priv"> system. This is
also known to as Row Level Security.
</para>
<para>
When row security is enabled on a table with
<xref linkend="sql-altertable">, all normal access to the table
(excluding the owner) for selecting rows or adding rows must be through
a policy. If no policy exists for the table, a default-deny policy is
used and no rows are visible or can be added. Privileges which operate
at the whole table level such as <literal>TRUNCATE</>, and
<literal>REFERENCES</> are not subject to row security.
</para>
<para>
Row security policies can be specific to commands, or to roles, or to
both. The commands available are <literal>SELECT</>, <literal>INSERT</>,
<literal>UPDATE</>, and <literal>DELETE</>. Multiple roles can be
assigned to a given policy and normal role membership and inheiritance
rules apply.
</para>
<para>
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
boolean result. This expression will be evaluated for each row prior
to other conditionals or functions which are part of the query. The
one exception to this rule are <literal>leakproof</literal> functions,
which are guaranteed to not leak information. Two expressions may be
specified to provide independent control over the rows which are
visible and the rows which are allowed to be added. The expression
is run as part of the query and with the privileges of the user
running the query, however, security definer functions can be used in
the expression.
</para>
<para>
Enabling and disabling row security, as well as adding policies to a
table, is always the privilege of the owner only.
</para>
<para>
Policies are created using the <xref linkend="sql-createpolicy">
command, altered using the <xref linkend="sql-alterpolicy"> command,
and dropped using the <xref linkend="sql-droppolicy"> command. To
enable and disable row security for a given table, use the
<xref linkend="sql-altertable"> command.
</para>
<para>
The table owners and superusers bypass the row security system when
querying a table, by default. Row security can be enabled for
superusers and table owners by setting
<xref linkend="guc-row-security"> to <literal>force</literal>. Any
user can request that row security be bypassed by setting
<xref linkend="guc-row-security"> to <literal>off</literal>. If
the user does not have privileges to bypass row security when
querying a given table then an error will be returned instead. Other
users can be granted the ability to bypass the row security system
with the <literal>BYPASSRLS</literal> role attribute. This
attribute can only be set by a superuser.
</para>
<para>
Each policy has a name and multiple policies can be defined for a
table. As policies are table-specific, each policy for a table must
have a unique name. Different tables may have policies with the
same name.
</para>
<para>
When multiple policies apply to a given query, they are combined using
<literal>OR</literal>, similar to how a given role has the privileges
of all roles which they are a member of.
</para>
<para>
Referential integrity checks, such as unique or primary key constraints
and foreign key references, will bypass row security to ensure that
data integrity is maintained. Care must be taken when developing
schemas and row level policies to avoid a "covert channel" leak of
information through these referntial integrity checks.
</para>
<para>
To enable row security for a table,
the <command>ALTER TABLE</command> is used. For example, to enable
row level security for the table accounts, use:
</para>
<programlisting>
-- Create the table first
CREATE TABLE accounts (manager text, company text, contact_email text);
ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
</programlisting>
<para>
To create a policy on the account relation to allow the managers role
to view the rows of their accounts, the <command>CREATE POLICY</command>
command can be used:
</para>
<programlisting>
CREATE POLICY account_managers ON accounts TO managers
USING (manager = current_user);
</programlisting>
<para>
If no role is specified, or the special <quote>user</quote> name
<literal>PUBLIC</literal> is used, then the policy applies to all
users on the system. To allow all users to view their own row in
a user table, a simple policy can be used:
</para>
<programlisting>
CREATE POLICY user_policy ON users
USING (user = current_user);
</programlisting>
<para>
To use a different policy for rows which are being added to the
table from those rows which are visible, the WITH CHECK clause
can be used. This would allow all users to view all rows in the
users table, but only modify their own:
</para>
<programlisting>
CREATE POLICY user_policy ON users
USING (true)
WITH CHECK (user = current_user);
</programlisting>
<para>
Row security can be disabled with the <command>ALTER TABLE</command>
also. Note that disabling row security does not remove the
policies which are defined on the table, they are simply ignored
and all rows are visible and able to be added, subject to the
normal privileges system.
</para>
</sect1>
<sect1 id="ddl-schemas">
<title>Schemas</title>

View File

@ -429,7 +429,7 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
These forms control the application of row security policies belonging
to the table. If enabled and no policies exist for the table, then a
default-deny policy is applied. Note that policies can exist for a table
even if row level security is disabled- in this case, the policies will
even if row level security is disabled - in this case, the policies will
NOT be applied and the policies will be ignored.
See also
<xref linkend="SQL-CREATEPOLICY">.

View File

@ -240,7 +240,7 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
</varlistentry>
<varlistentry id="SQL-CREATEPOLICY-UPDATE">
<term><literal>DELETE</></term>
<term><literal>UPDATE</></term>
<listitem>
<para>
Using <literal>UPDATE</literal> for a policy means that it will apply

View File

@ -687,6 +687,23 @@ PostgreSQL documentation
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable-row-security</></term>
<listitem>
<para>
This option is relevant only when dumping the contents of a table
which has row security. By default, pg_dump will set
<literal>ROW_SECURITY</literal> to <literal>OFF</literal>, to ensure
that all data is dumped from the table. If the user does not have
sufficient privileges to bypass row security, then an error is thrown.
This parameter instructs <application>pg_dump</application> to set
row_security to 'ON' instead, allowing the user to dump the contents
of the table which they have access to.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--exclude-table-data=<replaceable class="parameter">table</replaceable></option></term>
<listitem>

View File

@ -490,6 +490,29 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable-row-security</></term> <listitem>
<para>
This option is relevant only when restoring the contents of a table
which has row security. By default, pg_restore will set
<literal>ROW_SECURITY</literal> to <literal>OFF</literal>, to ensure
that all data is restored in to the table. If the user does not have
sufficient privileges to bypass row security, then an error is thrown.
This parameter instructs <application>pg_restore</application> to set
row_security to 'ON' instead, allowing the user to attempt to restore
the contents of the table with row security enabled. This may still
fail if the user does not have the right to insert the rows from the
dump into the table.
</para>
<para>
Note that this option currently also requires the dump be in INSERT
format as COPY TO does not support row security.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--if-exists</option></term>
<listitem>