@@ -165,18 +165,58 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
165
165
commandType = rt_index == root -> resultRelation ?
166
166
root -> commandType : CMD_SELECT ;
167
167
168
- get_policies_for_relation (rel , commandType , user_id , & permissive_policies ,
169
- & restrictive_policies );
168
+ /*
169
+ * In some cases, we need to apply USING policies (which control the
170
+ * visibility of records) associated with multiple command types (see
171
+ * specific cases below).
172
+ *
173
+ * When considering the order in which to apply these USING policies,
174
+ * we prefer to apply higher privileged policies, those which allow the
175
+ * user to lock records (UPDATE and DELETE), first, followed by policies
176
+ * which don't (SELECT).
177
+ *
178
+ * Note that the optimizer is free to push down and reorder quals which
179
+ * use leakproof functions.
180
+ *
181
+ * In all cases, if there are no policy clauses allowing access to rows in
182
+ * the table for the specific type of operation, then a single always-false
183
+ * clause (a default-deny policy) will be added (see add_security_quals).
184
+ */
185
+
186
+ /*
187
+ * For a SELECT, if UPDATE privileges are required (eg: the user has
188
+ * specified FOR [KEY] UPDATE/SHARE), then add the UPDATE USING quals first.
189
+ *
190
+ * This way, we filter out any records from the SELECT FOR SHARE/UPDATE
191
+ * which the user does not have access to via the UPDATE USING policies,
192
+ * similar to how we require normal UPDATE rights for these queries.
193
+ */
194
+ if (commandType == CMD_SELECT && rte -> requiredPerms & ACL_UPDATE )
195
+ {
196
+ List * update_permissive_policies ;
197
+ List * update_restrictive_policies ;
198
+
199
+ get_policies_for_relation (rel , CMD_UPDATE , user_id ,
200
+ & update_permissive_policies ,
201
+ & update_restrictive_policies );
202
+
203
+ add_security_quals (rt_index ,
204
+ update_permissive_policies ,
205
+ update_restrictive_policies ,
206
+ securityQuals ,
207
+ hasSubLinks );
208
+ }
170
209
171
210
/*
172
- * For SELECT, UPDATE and DELETE, add security quals to enforce these
211
+ * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
173
212
* policies. These security quals control access to existing table rows.
174
213
* Restrictive policies are "AND"d together, and permissive policies are
175
214
* "OR"d together.
176
- *
177
- * If there are no policy clauses controlling access to the table, this
178
- * will add a single always-false clause (a default-deny policy).
179
215
*/
216
+
217
+ get_policies_for_relation (rel , commandType , user_id , & permissive_policies ,
218
+ & restrictive_policies );
219
+
180
220
if (commandType == CMD_SELECT ||
181
221
commandType == CMD_UPDATE ||
182
222
commandType == CMD_DELETE )
@@ -187,28 +227,27 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
187
227
hasSubLinks );
188
228
189
229
/*
190
- * For the target relation, when there is a returning list, we need to
191
- * collect up CMD_SELECT policies and add them via add_security_quals.
192
- * This is because, for the RETURNING case , we have to filter any records
193
- * which are not visible through an ALL or SELECT USING policy .
230
+ * Similar to above, during an UPDATE or DELETE, if SELECT rights are also
231
+ * required (eg: when a RETURNING clause exists, or the user has provided
232
+ * a WHERE clause which involves columns from the relation) , we collect up
233
+ * CMD_SELECT policies and add them via add_security_quals first .
194
234
*
195
- * We don't need to worry about the non-target relation case because we are
196
- * checking the ALL and SELECT policies for those relations anyway (see
197
- * above).
235
+ * This way, we filter out any records which are not visible through an ALL
236
+ * or SELECT USING policy.
198
237
*/
199
- if (root -> returningList != NIL &&
200
- ( commandType == CMD_UPDATE || commandType == CMD_DELETE ) )
238
+ if (( commandType == CMD_UPDATE || commandType == CMD_DELETE ) &&
239
+ rte -> requiredPerms & ACL_SELECT )
201
240
{
202
- List * returning_permissive_policies ;
203
- List * returning_restrictive_policies ;
241
+ List * select_permissive_policies ;
242
+ List * select_restrictive_policies ;
204
243
205
244
get_policies_for_relation (rel , CMD_SELECT , user_id ,
206
- & returning_permissive_policies ,
207
- & returning_restrictive_policies );
245
+ & select_permissive_policies ,
246
+ & select_restrictive_policies );
208
247
209
248
add_security_quals (rt_index ,
210
- returning_permissive_policies ,
211
- returning_restrictive_policies ,
249
+ select_permissive_policies ,
250
+ select_restrictive_policies ,
212
251
securityQuals ,
213
252
hasSubLinks );
214
253
}
@@ -261,21 +300,22 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
261
300
hasSubLinks );
262
301
263
302
/*
264
- * Get and add ALL/SELECT policies, if there is a RETURNING clause,
265
- * also as WCO policies, again, to avoid silently dropping data.
303
+ * Get and add ALL/SELECT policies, if SELECT rights are required
304
+ * for this relation, also as WCO policies, again, to avoid
305
+ * silently dropping data. See above.
266
306
*/
267
- if (root -> returningList != NIL )
307
+ if (rte -> requiredPerms & ACL_SELECT )
268
308
{
269
- List * conflict_returning_permissive_policies = NIL ;
270
- List * conflict_returning_restrictive_policies = NIL ;
309
+ List * conflict_select_permissive_policies = NIL ;
310
+ List * conflict_select_restrictive_policies = NIL ;
271
311
272
312
get_policies_for_relation (rel , CMD_SELECT , user_id ,
273
- & conflict_returning_permissive_policies ,
274
- & conflict_returning_restrictive_policies );
313
+ & conflict_select_permissive_policies ,
314
+ & conflict_select_restrictive_policies );
275
315
add_with_check_options (rel , rt_index ,
276
316
WCO_RLS_CONFLICT_CHECK ,
277
- conflict_returning_permissive_policies ,
278
- conflict_returning_restrictive_policies ,
317
+ conflict_select_permissive_policies ,
318
+ conflict_select_restrictive_policies ,
279
319
withCheckOptions ,
280
320
hasSubLinks );
281
321
}
@@ -524,7 +564,7 @@ add_security_quals(int rt_index,
524
564
qual = copyObject (policy -> qual );
525
565
ChangeVarNodes ((Node * ) qual , 1 , rt_index , 0 );
526
566
527
- * securityQuals = lappend (* securityQuals , qual );
567
+ * securityQuals = list_append_unique (* securityQuals , qual );
528
568
* hasSubLinks |= policy -> hassublinks ;
529
569
}
530
570
}
@@ -539,7 +579,7 @@ add_security_quals(int rt_index,
539
579
rowsec_expr = makeBoolExpr (OR_EXPR , permissive_quals , -1 );
540
580
541
581
ChangeVarNodes ((Node * ) rowsec_expr , 1 , rt_index , 0 );
542
- * securityQuals = lappend (* securityQuals , rowsec_expr );
582
+ * securityQuals = list_append_unique (* securityQuals , rowsec_expr );
543
583
}
544
584
else
545
585
/*
@@ -631,7 +671,7 @@ add_with_check_options(Relation rel,
631
671
632
672
ChangeVarNodes (wco -> qual , 1 , rt_index , 0 );
633
673
634
- * withCheckOptions = lappend (* withCheckOptions , wco );
674
+ * withCheckOptions = list_append_unique (* withCheckOptions , wco );
635
675
636
676
/*
637
677
* Now add WithCheckOptions for each of the restrictive policy clauses
@@ -657,7 +697,7 @@ add_with_check_options(Relation rel,
657
697
wco -> qual = (Node * ) qual ;
658
698
wco -> cascaded = false;
659
699
660
- * withCheckOptions = lappend (* withCheckOptions , wco );
700
+ * withCheckOptions = list_append_unique (* withCheckOptions , wco );
661
701
* hasSubLinks |= policy -> hassublinks ;
662
702
}
663
703
}
0 commit comments