即使通过 INSERT 在 postgres 控制台中工作,由于 RLS Supabase 插入失败

Supabase insert fails due to RLS even through INSERT works in postgres console

我正在 supabase 中实现一个身份验证框架,它支持具有各种权限的多个角色,因此为不同的场景创建多个策略。

我的所有策略都包含一组集成测试以确保连续性。

我面临的一个特殊问题是来自 supabase JS API 的 INSERT 语句由于行级验证而失败,即使通过与 Posgres 控制台中的 SQL 语句相同的 INSERT 也是如此。


我有一个tableclients大致定义为

CREATE TABLE clients (
  id serial primary key,
  name varchar
);

ALTER TABLE clients ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Allow client_admin to create new record if none exist" ON clients
    FOR INSERT TO authenticated
    WITH CHECK (
        auth.is_user_in_group(auth.uid(), 'client_admin')
        AND auth.client_ids() IS NULL
    );

我的测试如下所示

// Given
const newUser = await createUser({
  email: 'new_client+test@example.com',
})
await logInAs(newUser)

// When
const { data, error } = await anonClient
  .from('clients')
  .insert([generateClient({}).record])

注意:generateClient(...)函数只生成一个带有随机数据的对象,不做任何插入。

插入语句失败

{"code": "42501", "details": null, "hint": null, "message": "new row violates row-level security policy for table \"clients\""}

即使我在 DataGrip 中打开控制台并执行

SET "request.jwt.claim.sub" TO '6aab88c8-6cc9-48fa-a90a-d45bfdc4a055';
SET ROLE authenticated;

INSERT INTO clients(name) VALUES (gen_random_uuid());
INSERT INTO clients(name) VALUES (gen_random_uuid());

第一次插入有效,而第二次插入失败。

为了让这个实验干净利落,我什至尝试了以下策略

CREATE POLICY "Allow client_admin to create new record if none exist" ON clients
    FOR INSERT TO authenticated
    WITH CHECK (
      true
    );

我什至尝试删除所有其他政策以确保它们不会以某种方式干扰。

关于我可能在哪里犯错的任何想法?

就像经常发生的那样,在 SO 上发帖后我几乎立即找到了答案。

我一直在阅读 SQL 查询日志,发现 supabase 也会在插入后立即尝试 SELECT 数据。

大致像

INSERT INTO clients(name) VALUES (gen_random_uuid()) RETURNING clients.*;

我有一个触发器可以将新记录插入 client_users table 并影响 SELECT 策略。

显然这里发生的是:

TRANSACTION STARTS
INSERT to clients
RETURNING clients.* <--- FAILS RLS AND REVERTS TRANSACTION
INSERT to client_users <--- Should let SELECT RLS work, but fails due to order of operations

对于遇到同样问题的人来说,似乎有两种选择:

  • 配置SELECT策略
  • 做没有return任何信息的supabase插入
const { data, error } = await anonClient
  .from('clients')
  .insert(generateClient({}).record, {
    returning: 'minimal',
  })