英文:
INSERT statement returns a policy violation (USING expression)
问题
我正在使用AWS Aurora Postgres 14(14.5),并创建了以下表格(借助Rust中的Diesel) ->
create table contacts (
email TEXT NOT NULL,
user_id TEXT NOT NULL,
contact_data JSONB,
user_groups TEXT[],
tenant_groups TEXT[],
tags TEXT[],
PRIMARY KEY (email, user_id)
);
附带的策略 ->
ALTER TABLE contacts FORCE ROW LEVEL SECURITY;
CREATE POLICY select_contacts_policy ON contacts
FOR SELECT
USING (user_id = current_setting('myapp.user_id'));
ALTER TABLE contacts ENABLE ROW LEVEL SECURITY;
CREATE POLICY insert_contacts_policy ON contacts
FOR INSERT
WITH CHECK (true);
ALTER TABLE contacts ENABLE ROW LEVEL SECURITY;
在向此表插入新数据时,首先设置一个名为user_id
的会话参数(一个字符串)(使用Diesel的sql_query函数) ->
sql_query(format!("SET SESSION myapp.user_id = '{}';", user_id)).execute(pg_conn)?;
然后,使用Diesel进行插入 ->
diesel::insert_into(contacts::table)
.values(&contacts) // 一个自定义结构体
.on_conflict((contacts::email, contacts::user_id))
.do_update()
.set(&contacts)
.execute(pg_conn)?;
在这个查询之后,user_id
会被恢复为默认值 ->
sql_query("SET SESSION myapp.user_id = -1;").execute(pg_conn)?;
Diesel函数调用转换为以下SQL查询(从postgres日志中获取) ->
INSERT INTO "contacts" ("email", "user_id", "contact_data", "user_groups", "tenant_groups", "tags") VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT ("email", "user_id") DO UPDATE SET "email" = $7, "user_id" = $8, "contact_data" = $9, "user_groups" = $10, "tenant_groups" = $11, "tags" = $12
运行此查询时,如果发生冲突并触发了更新分支,将会收到以下错误消息 ->
new row violates row-level security policy (USING expression) for table "contacts"
我在本地也有一个类似的Postgres安装(v 14.8),它能够正常进行插入操作,尽管没有多用户但模式相同。
额外的信息 ->
正在使用名为backend
的不同用户进行此操作
访问权限 -> contacts表的访问权限快照
我尝试运行一个EXPLAIN ANALYZE
查询并记录它,以查看错误发生在何处,但该查询在完成之前导致策略违规。如果我不使用ANALYZE
标志运行它,它只会打印查询计划(但我想知道是什么导致了该违规)。
英文:
I am using AWS Aurora Postgres 14 (14.5) and have this table set-up (with the help of Diesel in Rust) ->
create table contacts (
email TEXT NOT NULL,
user_id TEXT NOT NULL,
contact_data JSONB,
user_groups TEXT[],
tenant_groups TEXT[],
tags TEXT[],
PRIMARY KEY (email, user_id)
);
With policies ->
ALTER TABLE contacts FORCE ROW LEVEL SECURITY;
CREATE POLICY select_contacts_policy ON contacts
FOR SELECT
USING (user_id = current_setting('myapp.user_id'));
ALTER TABLE contacts ENABLE ROW LEVEL SECURITY;
CREATE POLICY insert_contacts_policy ON contacts
FOR INSERT
WITH CHECK (true);
ALTER TABLE contacts ENABLE ROW LEVEL SECURITY;
While inserting new data to this table, I first set a session parameter called user_id
(a string) (using Diesel's sql_query function)->
sql_query(format!("SET SESSION myapp.user_id = '{user_id}';")).execute(pg_conn)?;
Then I insert using diesel ->
diesel::insert_into(contacts::table)
.values(&contacts) # a custom struct
.on_conflict((contacts::email, contacts::user_id))
.do_update()
.set(&contacts)
.execute(pg_conn)?;
After this query, the user_id
is reverted to a default ->
sql_query("SET SESSION myapp.user_id = -1;").execute(pg_conn)?;
The diesel function call translates to this SQL Query (got it from postgres logs) ->
INSERT INTO "contacts" ("email", "user_id", "contact_data", "user_groups", "tenant_groups", "tags") VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT ("email", "user_id") DO UPDATE SET "email" = $7, "user_id" = $8, "contact_data" = $9, "user_groups" = $10, "tenant_groups" = $11, "tags" = $12
Upon running this query, I get this error message when there is a conflict and the update branch is triggered ->
new row violates row-level security policy (USING expression) for table "contacts"
I have a somewhat similar (without multiple users but the same schema) postgres installation (v 14.8) on my local which is able to upsert without any issues.
Additional information ->
The user working on this is a different one called backend
user
The access privileges -> Snapshot of access privileges for the contacts table
I tried to run an EXPLAIN ANALYZE
query and log it to see where the error occurs but that query results in policy violation before completion. If I run without the ANALYZE
flag, it just prints the query plan (but I want to know where and what is resulting in that violation)
答案1
得分: 2
问题出在 ON CONFLICT ... DO UPDATE
部分。一个简单的 INSERT
或者 INSERT ... ON CONFLICT DO NOTHING
将会成功。
你只能更新你能看到的行,也就是你具有 FOR SELECT
策略的行,正如文档所描述:
> [...] 需要 SELECT
权限的查询,比如 UPDATE
,只能看到被 SELECT
策略允许的记录。
所以是 FOR SELECT
策略和(缺失的)FOR UPDATE
策略在这里引发了问题。这有点令人吃惊,因为可以争论只有在 INSERT
过程中出现冲突时才应该收到该错误。然而,要确定是否存在冲突,你已经需要能够从表中进行 SELECT
。添加一个 FOR SELECT
策略将使语句在没有冲突时成功执行。如果确实存在冲突,你需要额外的 FOR UPDATE
策略。
你需要添加允许插入角色无条件更新和选择的策略:
CREATE POLICY inserter_may_select ON contacts FOR SELECT TO inserter
USING (TRUE);
CREATE POLICY inserter_may_update ON contacts FOR UPDATE TO inserter
USING (TRUE);
英文:
The problem here is the ON CONFLICT ... DO UPDATE
. A plain INSERT
or an INSERT ... ON CONFLICT DO NOTHING
would succeed.
You can only update rows that you can see, that is, rows for which you have a FOR SELECT
policy, as the documentation describes:
> [...] queries that require SELECT
permissions, such as UPDATE
, will also only see those records that are allowed by the SELECT
policy.
So it is the FOR SELECT
policy and the (missing) FOR UPDATE
policy that give you trouble here. This is somewhat surprising, since one could argue that you should only get that error if there is a conflict during the INSERT
. However, to determine whether there is a conflict or not, you already need to be able to SELECT
from the table. Adding a FOR SELECT
policy would make the statement succeed if there is no conflict. If there is a conflict, you need an additional FOR UPDATE
policy.
You'll have to add policies that allow the inserting role unconditional updates and selects:
CREATE POLICY inserter_may_select ON contacts FOR SELECT TO inserter
USING (TRUE);
CREATE POLICY inserter_may_update ON contacts FOR UPDATE TO inserter
USING (TRUE);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论