mirror of https://github.com/postgres/postgres
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:
parent
3f6f9260e3
commit
6550b901fe
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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">.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -799,7 +799,7 @@ InsertPgClassTuple(Relation pg_class_desc,
|
|||
values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
|
||||
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
|
||||
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
|
||||
values[Anum_pg_class_relhasrowsecurity - 1] = BoolGetDatum(rd_rel->relhasrowsecurity);
|
||||
values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity);
|
||||
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
|
||||
values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
|
||||
values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
|
||||
|
|
|
@ -119,7 +119,7 @@ CREATE VIEW pg_tables AS
|
|||
C.relhasindex AS hasindexes,
|
||||
C.relhasrules AS hasrules,
|
||||
C.relhastriggers AS hastriggers,
|
||||
C.relhasrowsecurity AS hasrowsecurity
|
||||
C.relrowsecurity AS rowsecurity
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
|
||||
WHERE C.relkind = 'r';
|
||||
|
|
|
@ -108,7 +108,7 @@ parse_row_security_command(const char *cmd_name)
|
|||
char cmd;
|
||||
|
||||
if (!cmd_name)
|
||||
elog(ERROR, "Unregonized command.");
|
||||
elog(ERROR, "unregonized command");
|
||||
|
||||
if (strcmp(cmd_name, "all") == 0)
|
||||
cmd = 0;
|
||||
|
@ -121,8 +121,7 @@ parse_row_security_command(const char *cmd_name)
|
|||
else if (strcmp(cmd_name, "delete") == 0)
|
||||
cmd = ACL_DELETE_CHR;
|
||||
else
|
||||
elog(ERROR, "Unregonized command.");
|
||||
/* error unrecognized command */
|
||||
elog(ERROR, "unregonized command");
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
@ -422,8 +421,8 @@ RemovePolicyById(Oid policy_id)
|
|||
heap_close(rel, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* Note that, unlike some of the other flags in pg_class, relhasrowsecurity
|
||||
* is not just an indication of if policies exist. When relhasrowsecurity
|
||||
* Note that, unlike some of the other flags in pg_class, 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
|
||||
* a policy on the table), then all access to the relation must be through
|
||||
* a policy. If no policy is defined for the relation then a default-deny
|
||||
|
@ -484,7 +483,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
|
|||
if (rseccmd == ACL_INSERT_CHR && stmt->qual != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("Only WITH CHECK expression allowed for INSERT")));
|
||||
errmsg("only WITH CHECK expression allowed for INSERT")));
|
||||
|
||||
|
||||
/* Collect role ids */
|
||||
|
@ -731,7 +730,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
|
|||
if (!HeapTupleIsValid(rsec_tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("policy '%s' for does not exist on table %s",
|
||||
errmsg("policy \"%s\" on table \"%s\" does not exist",
|
||||
stmt->policy_name,
|
||||
RelationGetRelationName(target_table))));
|
||||
|
||||
|
@ -850,7 +849,7 @@ rename_policy(RenameStmt *stmt)
|
|||
|
||||
pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
|
||||
|
||||
/* First pass- check for conflict */
|
||||
/* First pass -- check for conflict */
|
||||
|
||||
/* Add key - row security relation id. */
|
||||
ScanKeyInit(&skey[0],
|
||||
|
@ -868,7 +867,7 @@ rename_policy(RenameStmt *stmt)
|
|||
RowSecurityRelidPolnameIndexId, true, NULL, 2,
|
||||
skey);
|
||||
|
||||
if (HeapTupleIsValid(rsec_tuple = systable_getnext(sscan)))
|
||||
if (HeapTupleIsValid(systable_getnext(sscan)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("row-policy \"%s\" for table \"%s\" already exists",
|
||||
|
|
|
@ -10647,7 +10647,7 @@ ATExecEnableRowSecurity(Relation rel)
|
|||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for relation %u", relid);
|
||||
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrowsecurity = true;
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
|
||||
simple_heap_update(pg_class, &tuple->t_self, tuple);
|
||||
|
||||
/* keep catalog indexes current */
|
||||
|
@ -10674,7 +10674,7 @@ ATExecDisableRowSecurity(Relation rel)
|
|||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for relation %u", relid);
|
||||
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrowsecurity = false;
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
|
||||
simple_heap_update(pg_class, &tuple->t_self, tuple);
|
||||
|
||||
/* keep catalog indexes current */
|
||||
|
|
|
@ -61,7 +61,7 @@ static void process_policies(List *policies, int rt_index,
|
|||
Expr **final_qual,
|
||||
Expr **final_with_check_qual,
|
||||
bool *hassublinks);
|
||||
static bool check_role_for_policy(RowSecurityPolicy *policy, Oid user_id);
|
||||
static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
|
||||
|
||||
/*
|
||||
* hook to allow extensions to apply their own security policy
|
||||
|
@ -177,7 +177,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
|
|||
* all of them OR'd together. However, to avoid the situation of an
|
||||
* extension granting more access to a table than the internal policies
|
||||
* would allow, the extension's policies are AND'd with the internal
|
||||
* policies. In other words- extensions can only provide further
|
||||
* policies. In other words - extensions can only provide further
|
||||
* filtering of the result set (or further reduce the set of records
|
||||
* allowed to be added).
|
||||
*
|
||||
|
@ -305,7 +305,8 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
|
|||
policy = (RowSecurityPolicy *) lfirst(item);
|
||||
|
||||
/* Always add ALL policies, if they exist. */
|
||||
if (policy->cmd == '\0' && check_role_for_policy(policy, user_id))
|
||||
if (policy->cmd == '\0' &&
|
||||
check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
|
||||
/* Build the list of policies to return. */
|
||||
|
@ -313,23 +314,23 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
|
|||
{
|
||||
case CMD_SELECT:
|
||||
if (policy->cmd == ACL_SELECT_CHR
|
||||
&& check_role_for_policy(policy, user_id))
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
/* If INSERT then only need to add the WITH CHECK qual */
|
||||
if (policy->cmd == ACL_INSERT_CHR
|
||||
&& check_role_for_policy(policy, user_id))
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
if (policy->cmd == ACL_UPDATE_CHR
|
||||
&& check_role_for_policy(policy, user_id))
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
if (policy->cmd == ACL_DELETE_CHR
|
||||
&& check_role_for_policy(policy, user_id))
|
||||
&& check_role_for_policy(policy->roles, user_id))
|
||||
policies = lcons(policy, policies);
|
||||
break;
|
||||
default:
|
||||
|
@ -473,7 +474,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
|
|||
{
|
||||
HeapTuple tuple;
|
||||
Form_pg_class classform;
|
||||
bool relhasrowsecurity;
|
||||
bool relrowsecurity;
|
||||
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
|
||||
|
||||
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
||||
|
@ -482,12 +483,12 @@ check_enable_rls(Oid relid, Oid checkAsUser)
|
|||
|
||||
classform = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
relhasrowsecurity = classform->relhasrowsecurity;
|
||||
relrowsecurity = classform->relrowsecurity;
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* Nothing to do if the relation does not have RLS */
|
||||
if (!relhasrowsecurity)
|
||||
if (!relrowsecurity)
|
||||
return RLS_NONE;
|
||||
|
||||
/*
|
||||
|
@ -537,19 +538,19 @@ check_enable_rls(Oid relid, Oid checkAsUser)
|
|||
* check_role_for_policy -
|
||||
* determines if the policy should be applied for the current role
|
||||
*/
|
||||
bool
|
||||
check_role_for_policy(RowSecurityPolicy *policy, Oid user_id)
|
||||
static bool
|
||||
check_role_for_policy(ArrayType *policy_roles, Oid user_id)
|
||||
{
|
||||
int i;
|
||||
Oid *roles = (Oid *) ARR_DATA_PTR(policy->roles);
|
||||
Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
|
||||
|
||||
/* Quick fall-thru for policies applied to all roles */
|
||||
if (roles[0] == ACL_ID_PUBLIC)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < ARR_DIMS(policy->roles)[0]; i++)
|
||||
for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
|
||||
{
|
||||
if (is_member_of_role(user_id, roles[i]))
|
||||
if (has_privs_of_role(user_id, roles[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2309,9 +2309,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|||
* have RLS enabled.
|
||||
*/
|
||||
if (!has_bypassrls_privilege(GetUserId()) &&
|
||||
((pk_rel->rd_rel->relhasrowsecurity &&
|
||||
((pk_rel->rd_rel->relrowsecurity &&
|
||||
!pg_class_ownercheck(pkrte->relid, GetUserId())) ||
|
||||
(fk_rel->rd_rel->relhasrowsecurity &&
|
||||
(fk_rel->rd_rel->relrowsecurity &&
|
||||
!pg_class_ownercheck(fkrte->relid, GetUserId()))))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -847,6 +847,87 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* equalPolicy
|
||||
*
|
||||
* Determine whether two policies are equivalent
|
||||
*/
|
||||
static bool
|
||||
equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
|
||||
{
|
||||
int i;
|
||||
Oid *r1,
|
||||
*r2;
|
||||
|
||||
if (policy1 != NULL)
|
||||
{
|
||||
if (policy2 == NULL)
|
||||
return false;
|
||||
|
||||
if (policy1->rsecid != policy2->rsecid)
|
||||
return false;
|
||||
if (policy1->cmd != policy2->cmd)
|
||||
return false;
|
||||
if (policy1->hassublinks != policy2->hassublinks);
|
||||
return false;
|
||||
if (strcmp(policy1->policy_name,policy2->policy_name) != 0)
|
||||
return false;
|
||||
if (ARR_DIMS(policy1->roles)[0] != ARR_DIMS(policy2->roles)[0])
|
||||
return false;
|
||||
|
||||
r1 = (Oid *) ARR_DATA_PTR(policy1->roles);
|
||||
r2 = (Oid *) ARR_DATA_PTR(policy2->roles);
|
||||
|
||||
for (i = 0; i < ARR_DIMS(policy1->roles)[0]; i++)
|
||||
{
|
||||
if (r1[i] != r2[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!equal(policy1->qual, policy1->qual))
|
||||
return false;
|
||||
if (!equal(policy1->with_check_qual, policy2->with_check_qual))
|
||||
return false;
|
||||
}
|
||||
else if (policy2 != NULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* equalRSDesc
|
||||
*
|
||||
* Determine whether two RowSecurityDesc's are equivalent
|
||||
*/
|
||||
static bool
|
||||
equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
|
||||
{
|
||||
ListCell *lc,
|
||||
*rc;
|
||||
|
||||
if (rsdesc1 == NULL && rsdesc2 == NULL)
|
||||
return true;
|
||||
|
||||
if ((rsdesc1 != NULL && rsdesc2 == NULL) ||
|
||||
(rsdesc1 == NULL && rsdesc2 != NULL))
|
||||
return false;
|
||||
|
||||
if (list_length(rsdesc1->policies) != list_length(rsdesc2->policies))
|
||||
return false;
|
||||
|
||||
/* RelationBuildRowSecurity should build policies in order */
|
||||
forboth(lc, rsdesc1->policies, rc, rsdesc2->policies)
|
||||
{
|
||||
RowSecurityPolicy *l = (RowSecurityPolicy *) lfirst(lc);
|
||||
RowSecurityPolicy *r = (RowSecurityPolicy *) lfirst(rc);
|
||||
|
||||
if (!equalPolicy(l,r))
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* RelationBuildDesc
|
||||
|
@ -967,7 +1048,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
|
|||
else
|
||||
relation->trigdesc = NULL;
|
||||
|
||||
if (relation->rd_rel->relhasrowsecurity)
|
||||
if (relation->rd_rel->relrowsecurity)
|
||||
RelationBuildRowSecurity(relation);
|
||||
else
|
||||
relation->rsdesc = NULL;
|
||||
|
@ -2104,6 +2185,7 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||
Oid save_relid = RelationGetRelid(relation);
|
||||
bool keep_tupdesc;
|
||||
bool keep_rules;
|
||||
bool keep_policies;
|
||||
|
||||
/* Build temporary entry, but don't link it into hashtable */
|
||||
newrel = RelationBuildDesc(save_relid, false);
|
||||
|
@ -2117,6 +2199,7 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||
|
||||
keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
|
||||
keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
|
||||
keep_policies = equalRSDesc(relation->rsdesc, newrel->rsdesc);
|
||||
|
||||
/*
|
||||
* Perform swapping of the relcache entry contents. Within this
|
||||
|
@ -2165,6 +2248,8 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||
SWAPFIELD(RuleLock *, rd_rules);
|
||||
SWAPFIELD(MemoryContext, rd_rulescxt);
|
||||
}
|
||||
if (keep_policies)
|
||||
SWAPFIELD(RowSecurityDesc *, rsdesc);
|
||||
/* toast OID override must be preserved */
|
||||
SWAPFIELD(Oid, rd_toastoid);
|
||||
/* pgstat_info must be preserved */
|
||||
|
@ -3345,11 +3430,11 @@ RelationCacheInitializePhase3(void)
|
|||
/*
|
||||
* Re-load the row security policies if the relation has them, since
|
||||
* they are not preserved in the cache. Note that we can never NOT
|
||||
* have a policy while relhasrowsecurity is true-
|
||||
* have a policy while relrowsecurity is true,
|
||||
* RelationBuildRowSecurity will create a single default-deny policy
|
||||
* if there is no policy defined in pg_rowsecurity.
|
||||
*/
|
||||
if (relation->rd_rel->relhasrowsecurity && relation->rsdesc == NULL)
|
||||
if (relation->rd_rel->relrowsecurity && relation->rsdesc == NULL)
|
||||
{
|
||||
RelationBuildRowSecurity(relation);
|
||||
|
||||
|
|
|
@ -376,10 +376,13 @@ RestoreArchive(Archive *AHX)
|
|||
/*
|
||||
* Enable row-security if necessary.
|
||||
*/
|
||||
if (!ropt->enable_row_security)
|
||||
ahprintf(AH, "SET row_security = off;\n");
|
||||
else
|
||||
ahprintf(AH, "SET row_security = on;\n");
|
||||
if (PQserverVersion(AH->connection) >= 90500)
|
||||
{
|
||||
if (!ropt->enable_row_security)
|
||||
ahprintf(AH, "SET row_security = off;\n");
|
||||
else
|
||||
ahprintf(AH, "SET row_security = on;\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Establish important parameter values right away.
|
||||
|
|
|
@ -2777,7 +2777,7 @@ dumpBlobs(Archive *fout, void *arg)
|
|||
void
|
||||
getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
||||
{
|
||||
PQExpBuffer query = createPQExpBuffer();
|
||||
PQExpBuffer query;
|
||||
PGresult *res;
|
||||
RowSecurityInfo *rsinfo;
|
||||
int i_oid;
|
||||
|
@ -2792,6 +2792,8 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||
if (fout->remoteVersion < 90500)
|
||||
return;
|
||||
|
||||
query = createPQExpBuffer();
|
||||
|
||||
for (i = 0; i < numTables; i++)
|
||||
{
|
||||
TableInfo *tbinfo = &tblinfo[i];
|
||||
|
@ -2809,7 +2811,7 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
|
|||
* We represent RLS enabled on a table by creating RowSecurityInfo
|
||||
* object with an empty policy.
|
||||
*/
|
||||
if (tbinfo->hasrowsec)
|
||||
if (tbinfo->rowsec)
|
||||
{
|
||||
/*
|
||||
* Note: use tableoid 0 so that this object won't be mistaken for
|
||||
|
@ -4534,7 +4536,7 @@ getTables(Archive *fout, int *numTables)
|
|||
int i_relhastriggers;
|
||||
int i_relhasindex;
|
||||
int i_relhasrules;
|
||||
int i_relhasrowsec;
|
||||
int i_relrowsec;
|
||||
int i_relhasoids;
|
||||
int i_relfrozenxid;
|
||||
int i_relminmxid;
|
||||
|
@ -4588,7 +4590,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"c.relhasrowsecurity, "
|
||||
"c.relrowsecurity, "
|
||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"tc.relminmxid AS tminmxid, "
|
||||
|
@ -4629,7 +4631,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"tc.relminmxid AS tminmxid, "
|
||||
|
@ -4670,7 +4672,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"tc.relminmxid AS tminmxid, "
|
||||
|
@ -4711,7 +4713,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"0 AS tminmxid, "
|
||||
|
@ -4750,7 +4752,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"0 AS tminmxid, "
|
||||
|
@ -4788,7 +4790,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, c.relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"0 AS tminmxid, "
|
||||
|
@ -4826,7 +4828,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s c.relowner) AS rolname, "
|
||||
"c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
|
||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||
"tc.relfrozenxid AS tfrozenxid, "
|
||||
"0 AS tminmxid, "
|
||||
|
@ -4864,7 +4866,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s relowner) AS rolname, "
|
||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||
"relhasindex, relhasrules, relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||
|
@ -4901,7 +4903,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s relowner) AS rolname, "
|
||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||
"relhasindex, relhasrules, relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||
|
@ -4934,7 +4936,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"(%s relowner) AS rolname, "
|
||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||
"relhasindex, relhasrules, relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||
|
@ -4962,7 +4964,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||
"relhasindex, relhasrules, "
|
||||
"'t'::bool AS relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||
|
@ -5000,7 +5002,7 @@ getTables(Archive *fout, int *numTables)
|
|||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||
"relhasindex, relhasrules, "
|
||||
"'t'::bool AS relhasoids, "
|
||||
"'f'::bool AS relhasrowsecurity, "
|
||||
"'f'::bool AS relrowsecurity, "
|
||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||
"0 AS toid, "
|
||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||
|
@ -5048,7 +5050,7 @@ getTables(Archive *fout, int *numTables)
|
|||
i_relhastriggers = PQfnumber(res, "relhastriggers");
|
||||
i_relhasindex = PQfnumber(res, "relhasindex");
|
||||
i_relhasrules = PQfnumber(res, "relhasrules");
|
||||
i_relhasrowsec = PQfnumber(res, "relhasrowsecurity");
|
||||
i_relrowsec = PQfnumber(res, "relrowsecurity");
|
||||
i_relhasoids = PQfnumber(res, "relhasoids");
|
||||
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
|
||||
i_relminmxid = PQfnumber(res, "relminmxid");
|
||||
|
@ -5100,7 +5102,7 @@ getTables(Archive *fout, int *numTables)
|
|||
tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
|
||||
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
|
||||
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
|
||||
tblinfo[i].hasrowsec = (strcmp(PQgetvalue(res, i, i_relhasrowsec), "t") == 0);
|
||||
tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
|
||||
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
|
||||
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
|
||||
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
|
||||
|
|
|
@ -246,7 +246,7 @@ typedef struct _tableInfo
|
|||
bool hasindex; /* does it have any indexes? */
|
||||
bool hasrules; /* does it have any rules? */
|
||||
bool hastriggers; /* does it have any triggers? */
|
||||
bool hasrowsec; /* does it have any row-security policy? */
|
||||
bool rowsec; /* does it have any row-security policy? */
|
||||
bool hasoids; /* does it have OIDs? */
|
||||
uint32 frozenxid; /* for restore frozen xid */
|
||||
uint32 minmxid; /* for restore min multi xid */
|
||||
|
|
|
@ -463,7 +463,7 @@ usage(const char *progname)
|
|||
printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n"));
|
||||
printf(_(" -1, --single-transaction restore as a single transaction\n"));
|
||||
printf(_(" --disable-triggers disable triggers during data-only restore\n"));
|
||||
printf(_(" --enable-row-security enable row level security\n"));
|
||||
printf(_(" --enable-row-security enable row level security\n"));
|
||||
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
|
||||
printf(_(" --no-data-for-failed-tables do not restore data of tables that could not be\n"
|
||||
" created\n"));
|
||||
|
|
|
@ -1204,7 +1204,7 @@ describeOneTableDetails(const char *schemaname,
|
|||
bool hasindex;
|
||||
bool hasrules;
|
||||
bool hastriggers;
|
||||
bool hasrowsecurity;
|
||||
bool rowsecurity;
|
||||
bool hasoids;
|
||||
Oid tablespace;
|
||||
char *reloptions;
|
||||
|
@ -1230,7 +1230,7 @@ describeOneTableDetails(const char *schemaname,
|
|||
{
|
||||
printfPQExpBuffer(&buf,
|
||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||
"c.relhastriggers, c.relhasrowsecurity, c.relhasoids, "
|
||||
"c.relhastriggers, c.relrowsecurity, c.relhasoids, "
|
||||
"%s, c.reltablespace, "
|
||||
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
||||
"c.relpersistence, c.relreplident\n"
|
||||
|
@ -1355,7 +1355,7 @@ describeOneTableDetails(const char *schemaname,
|
|||
tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
|
||||
tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
|
||||
tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
|
||||
tableinfo.hasrowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
|
||||
tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
|
||||
tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
|
||||
tableinfo.reloptions = (pset.sversion >= 80200) ?
|
||||
pg_strdup(PQgetvalue(res, 0, 7)) : NULL;
|
||||
|
@ -1998,18 +1998,17 @@ describeOneTableDetails(const char *schemaname,
|
|||
PQclear(result);
|
||||
}
|
||||
|
||||
|
||||
/* print any row-level policies */
|
||||
if (pset.sversion >= 90500)
|
||||
{
|
||||
appendPQExpBuffer(&buf,
|
||||
",\n pg_catalog.pg_get_expr(rs.rsecqual, c.oid) as \"%s\"",
|
||||
gettext_noop("Row-security"));
|
||||
if (verbose && pset.sversion >= 90500)
|
||||
appendPQExpBuffer(&buf,
|
||||
"\n LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
|
||||
|
||||
/* print any row-level policies */
|
||||
if (tableinfo.hasrowsecurity)
|
||||
{
|
||||
if (verbose)
|
||||
appendPQExpBuffer(&buf,
|
||||
"\n LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
|
||||
|
||||
printfPQExpBuffer(&buf,
|
||||
"SELECT rs.rsecpolname,\n"
|
||||
"CASE WHEN rs.rsecroles = '{0}' THEN NULL ELSE array(select rolname from pg_roles where oid = any (rs.rsecroles) order by 1) END,\n"
|
||||
|
@ -2019,41 +2018,53 @@ describeOneTableDetails(const char *schemaname,
|
|||
"FROM pg_catalog.pg_rowsecurity rs\n"
|
||||
"WHERE rs.rsecrelid = '%s' ORDER BY 1;",
|
||||
oid);
|
||||
|
||||
result = PSQLexec(buf.data, false);
|
||||
if (!result)
|
||||
goto error_return;
|
||||
else
|
||||
tuples = PQntuples(result);
|
||||
|
||||
if (tuples > 0)
|
||||
{
|
||||
/*
|
||||
* Handle cases where RLS is enabled and there are policies,
|
||||
* or there aren't policies, or RLS isn't enabled but there
|
||||
* are policies
|
||||
*/
|
||||
if (tableinfo.rowsecurity && tuples > 0)
|
||||
printTableAddFooter(&cont, _("Policies:"));
|
||||
for (i = 0; i < tuples; i++)
|
||||
|
||||
if (tableinfo.rowsecurity && tuples == 0)
|
||||
printTableAddFooter(&cont, _("Policies (Row Security Enabled): (None)"));
|
||||
|
||||
if (!tableinfo.rowsecurity && tuples > 0)
|
||||
printTableAddFooter(&cont, _("Policies (Row Security Disabled):"));
|
||||
|
||||
/* Might be an empty set - that's ok */
|
||||
for (i = 0; i < tuples; i++)
|
||||
{
|
||||
printfPQExpBuffer(&buf, " POLICY \"%s\"",
|
||||
PQgetvalue(result, i, 0));
|
||||
|
||||
if (!PQgetisnull(result, i, 4))
|
||||
appendPQExpBuffer(&buf, " (%s)",
|
||||
PQgetvalue(result, i, 4));
|
||||
|
||||
if (!PQgetisnull(result, i, 2))
|
||||
appendPQExpBuffer(&buf, " EXPRESSION %s",
|
||||
PQgetvalue(result, i, 2));
|
||||
|
||||
if (!PQgetisnull(result, i, 3))
|
||||
appendPQExpBuffer(&buf, " WITH CHECK %s",
|
||||
PQgetvalue(result, i, 3));
|
||||
|
||||
printTableAddFooter(&cont, buf.data);
|
||||
|
||||
if (!PQgetisnull(result, i, 1))
|
||||
{
|
||||
printfPQExpBuffer(&buf, " POLICY \"%s\"",
|
||||
PQgetvalue(result, i, 0));
|
||||
|
||||
if (!PQgetisnull(result, i, 4))
|
||||
appendPQExpBuffer(&buf, " (%s)",
|
||||
PQgetvalue(result, i, 4));
|
||||
|
||||
if (!PQgetisnull(result, i, 2))
|
||||
appendPQExpBuffer(&buf, " EXPRESSION %s",
|
||||
PQgetvalue(result, i, 2));
|
||||
|
||||
if (!PQgetisnull(result, i, 3))
|
||||
appendPQExpBuffer(&buf, " WITH CHECK %s",
|
||||
PQgetvalue(result, i, 3));
|
||||
printfPQExpBuffer(&buf, " APPLIED TO %s",
|
||||
PQgetvalue(result, i, 1));
|
||||
|
||||
printTableAddFooter(&cont, buf.data);
|
||||
|
||||
if (!PQgetisnull(result, i, 1))
|
||||
{
|
||||
printfPQExpBuffer(&buf, " APPLIED TO %s",
|
||||
PQgetvalue(result, i, 1));
|
||||
|
||||
printTableAddFooter(&cont, buf.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
PQclear(result);
|
||||
|
@ -2708,6 +2719,10 @@ describeRoles(const char *pattern, bool verbose)
|
|||
if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
|
||||
add_role_attribute(&buf, _("Replication"));
|
||||
|
||||
if (pset.sversion >= 90500)
|
||||
if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0)
|
||||
add_role_attribute(&buf, _("Bypass RLS"));
|
||||
|
||||
conns = atoi(PQgetvalue(res, i, 6));
|
||||
if (conns >= 0)
|
||||
{
|
||||
|
|
|
@ -1214,11 +1214,12 @@ psql_completion(const char *text, int start, int end)
|
|||
pg_strcasecmp(prev2_wd, "ROLE") == 0))
|
||||
{
|
||||
static const char *const list_ALTERUSER[] =
|
||||
{"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
|
||||
"ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
|
||||
"NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
|
||||
"NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
|
||||
"SUPERUSER", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
|
||||
{"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
|
||||
"CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS",
|
||||
"NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
|
||||
"NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "RENAME TO",
|
||||
"REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED",
|
||||
"VALID UNTIL", "WITH", NULL};
|
||||
|
||||
COMPLETE_WITH_LIST(list_ALTERUSER);
|
||||
}
|
||||
|
@ -1231,11 +1232,12 @@ psql_completion(const char *text, int start, int end)
|
|||
{
|
||||
/* Similar to the above, but don't complete "WITH" again. */
|
||||
static const char *const list_ALTERUSER_WITH[] =
|
||||
{"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
|
||||
"ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
|
||||
"NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
|
||||
"NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
|
||||
"SUPERUSER", "UNENCRYPTED", "VALID UNTIL", NULL};
|
||||
{"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
|
||||
"CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS",
|
||||
"NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
|
||||
"NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "RENAME TO",
|
||||
"REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED",
|
||||
"VALID UNTIL", NULL};
|
||||
|
||||
COMPLETE_WITH_LIST(list_ALTERUSER_WITH);
|
||||
}
|
||||
|
@ -2565,10 +2567,10 @@ psql_completion(const char *text, int start, int end)
|
|||
pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
|
||||
{
|
||||
static const char *const list_CREATEROLE[] =
|
||||
{"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
|
||||
"ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
|
||||
"NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
|
||||
"NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
|
||||
{"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
|
||||
"CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS",
|
||||
"NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
|
||||
"NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
|
||||
"SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
|
||||
|
||||
COMPLETE_WITH_LIST(list_CREATEROLE);
|
||||
|
@ -2583,10 +2585,10 @@ psql_completion(const char *text, int start, int end)
|
|||
{
|
||||
/* Similar to the above, but don't complete "WITH" again. */
|
||||
static const char *const list_CREATEROLE_WITH[] =
|
||||
{"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
|
||||
"ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
|
||||
"NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
|
||||
"NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
|
||||
{"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
|
||||
"CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS",
|
||||
"NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
|
||||
"NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
|
||||
"SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", NULL};
|
||||
|
||||
COMPLETE_WITH_LIST(list_CREATEROLE_WITH);
|
||||
|
|
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201409191
|
||||
#define CATALOG_VERSION_NO 201409241
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 relhastriggers; /* has (or has had) any TRIGGERs */
|
||||
bool relhassubclass; /* has (or has had) derived classes */
|
||||
bool relhasrowsecurity; /* has (or has had) row-security policy */
|
||||
bool relrowsecurity; /* row-security is enabled or not */
|
||||
bool relispopulated; /* matview currently holds query results */
|
||||
char relreplident; /* see REPLICA_IDENTITY_xxx constants */
|
||||
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
||||
|
@ -119,7 +119,7 @@ typedef FormData_pg_class *Form_pg_class;
|
|||
#define Anum_pg_class_relhasrules 21
|
||||
#define Anum_pg_class_relhastriggers 22
|
||||
#define Anum_pg_class_relhassubclass 23
|
||||
#define Anum_pg_class_relhasrowsecurity 24
|
||||
#define Anum_pg_class_relrowsecurity 24
|
||||
#define Anum_pg_class_relispopulated 25
|
||||
#define Anum_pg_class_relreplident 26
|
||||
#define Anum_pg_class_relfrozenxid 27
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#define POLICY_H
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "utils/relcache.h"
|
||||
|
||||
extern void RelationBuildRowSecurity(Relation relation);
|
||||
|
||||
|
@ -24,10 +25,10 @@ extern void RemovePolicyById(Oid policy_id);
|
|||
extern Oid CreatePolicy(CreatePolicyStmt *stmt);
|
||||
extern Oid AlterPolicy(AlterPolicyStmt *stmt);
|
||||
|
||||
Oid get_relation_policy_oid(Oid relid,
|
||||
const char *policy_name, bool missing_ok);
|
||||
extern Oid get_relation_policy_oid(Oid relid, const char *policy_name,
|
||||
bool missing_ok);
|
||||
|
||||
Oid rename_policy(RenameStmt *stmt);
|
||||
extern Oid rename_policy(RenameStmt *stmt);
|
||||
|
||||
|
||||
#endif /* POLICY_H */
|
||||
|
|
|
@ -2046,7 +2046,7 @@ pg_tables| SELECT n.nspname AS schemaname,
|
|||
c.relhasindex AS hasindexes,
|
||||
c.relhasrules AS hasrules,
|
||||
c.relhastriggers AS hastriggers,
|
||||
c.relhasrowsecurity AS hasrowsecurity
|
||||
c.relrowsecurity AS rowsecurity
|
||||
FROM ((pg_class c
|
||||
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
|
||||
LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace)))
|
||||
|
|
Loading…
Reference in New Issue