From c1bf1cb39962690933e4a37af0ab8b00926a0020 Mon Sep 17 00:00:00 2001 From: Laurenz Albe Date: Thu, 9 Nov 2023 16:09:10 +0100 Subject: [PATCH] Allow WITH CKECK on FOR SELECT policies FOR SELECT or FOR ALL policies are used to check the new row after an UPDATE, but you could not define a WITH CHECK clause on FOR SELECT policies, so the USING clause was used. This patch adds the capability to define WITH CHECK clauses on FOR SELECT policies, which allows the user to specify different criteria for fetching rows from the table and for new rows after an UPDATE. Author: Laurenz Albe Discussion: https://postgr.es/m/aee893f1ec3ca8f62a0da2fc2f9f8b73920f9f9d.camel%40cybertec.at --- doc/src/sgml/ref/create_policy.sgml | 35 +++++++++++++++-------- src/backend/commands/policy.c | 14 ++++----- src/backend/rewrite/rowsecurity.c | 2 +- src/test/regress/expected/rowsecurity.out | 14 +++++++++ src/test/regress/sql/rowsecurity.sql | 11 +++++++ 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/doc/src/sgml/ref/create_policy.sgml b/doc/src/sgml/ref/create_policy.sgml index e76c342d3d..d473bb1094 100644 --- a/doc/src/sgml/ref/create_policy.sgml +++ b/doc/src/sgml/ref/create_policy.sgml @@ -82,7 +82,7 @@ CREATE POLICY name ON For policies that can have both USING - and WITH CHECK expressions (ALL + and WITH CHECK expressions (ALL, SELECT and UPDATE), if no WITH CHECK expression is defined, then the USING expression will be used both to determine which rows are visible (normal @@ -270,9 +270,11 @@ CREATE POLICY name ON SELECT permissions, such as UPDATE, will also only see those records that are allowed by the SELECT policy. - A SELECT policy cannot have a WITH - CHECK expression, as it only applies in cases where - records are being retrieved from the relation. + In addition, rows written by an INSERT or + UPDATE statement are checked against the + WITH CHECK expression of SELECT + policies on the table (or, if there is no WITH + CHECK expression, the USING expression). @@ -402,14 +404,17 @@ CREATE POLICY name ON Policies Applied by Command Type - - - + + + + + + Command - SELECT/ALL policy + SELECT/ALL policy INSERT/ALL policy UPDATE/ALL policy DELETE/ALL policy @@ -417,6 +422,7 @@ CREATE POLICY name ON USING expression WITH CHECK expression + WITH CHECK expression USING expression WITH CHECK expression USING expression @@ -430,11 +436,13 @@ CREATE POLICY name ON + SELECT FOR UPDATE/SHARE Existing row + Existing row @@ -442,6 +450,7 @@ CREATE POLICY name ON INSERT / MERGE ... THEN INSERT + New row @@ -449,6 +458,7 @@ CREATE POLICY name ON INSERT ... RETURNING + New row @@ -465,9 +475,8 @@ CREATE POLICY name ON UPDATE / MERGE ... THEN UPDATE - - Existing & new rows - + Existing row + New row Existing row New row @@ -481,11 +490,13 @@ CREATE POLICY name ON + Existing row ON CONFLICT DO UPDATE - Existing & new rows + Existing row + New row Existing row New row diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 76a45e56bf..e3e19167fe 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -597,13 +597,12 @@ CreatePolicy(CreatePolicyStmt *stmt) polcmd = parse_policy_command(stmt->cmd_name); /* - * If the command is SELECT or DELETE then WITH CHECK should be NULL. + * If the command is DELETE then WITH CHECK should be NULL. */ - if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR) - && stmt->with_check != NULL) + if (polcmd == ACL_DELETE_CHR && stmt->with_check != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("WITH CHECK cannot be applied to SELECT or DELETE"))); + errmsg("WITH CHECK cannot be applied to DELETE"))); /* * If the command is INSERT then WITH CHECK should be the only expression @@ -899,13 +898,12 @@ AlterPolicy(AlterPolicyStmt *stmt) polcmd = DatumGetChar(polcmd_datum); /* - * If the command is SELECT or DELETE then WITH CHECK should be NULL. + * If the command is DELETE then WITH CHECK should be NULL. */ - if ((polcmd == ACL_SELECT_CHR || polcmd == ACL_DELETE_CHR) - && stmt->with_check != NULL) + if (polcmd == ACL_DELETE_CHR && stmt->with_check != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("only USING expression allowed for SELECT, DELETE"))); + errmsg("only USING expression allowed for DELETE"))); /* * If the command is INSERT then WITH CHECK should be the only expression diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index b1620e4625..8df5422583 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -308,7 +308,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, select_restrictive_policies, withCheckOptions, hasSubLinks, - true); + false); } /* diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 6988128aa4..a43160cb0d 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -4308,6 +4308,20 @@ ERROR: new row violates row-level security policy for table "r1" INSERT INTO r1 VALUES (10) ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30; ERROR: new row violates row-level security policy for table "r1" +-- Add an explicit WITH CHECK clause to the FOR SELECT policy, so that the +-- UPDATE that failed before can succeed +ALTER POLICY p1 ON r1 WITH CHECK (true); +BEGIN; +UPDATE r1 SET a = 30 WHERE a = 10; +ROLLBACK; +BEGIN; +UPDATE r1 SET a = 30 RETURNING *; + a +---- + 30 +(1 row) + +ROLLBACK; DROP TABLE r1; -- Check dependency handling RESET SESSION AUTHORIZATION; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index dec7340538..7ebf89e2ce 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -2030,6 +2030,17 @@ INSERT INTO r1 VALUES (10) INSERT INTO r1 VALUES (10) ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30; +-- Add an explicit WITH CHECK clause to the FOR SELECT policy, so that the +-- UPDATE that failed before can succeed +ALTER POLICY p1 ON r1 WITH CHECK (true); + +BEGIN; +UPDATE r1 SET a = 30 WHERE a = 10; +ROLLBACK; +BEGIN; +UPDATE r1 SET a = 30 RETURNING *; +ROLLBACK; + DROP TABLE r1; -- Check dependency handling -- 2.41.0