ข้ามไปยังเนื้อหา

Row Level Security Fundamentals

Row Level Security (RLS) คือ feature ของ Postgres เองที่ผูก policy เข้ากับ table เพื่อให้ Postgres คัดกรอง row ที่ role หนึ่ง ๆ อ่านหรือเขียนได้ในทุก query โดยอัตโนมัติ — ไม่ใช่สิ่งที่โค้ดฝั่งแอปต้องมานั่งจำไปเติมเป็น where clause เอง

table ใหม่ใน Postgres จะมี RLS ปิดอยู่ เสมอ ไม่มีการกรองอะไรทั้งนั้น role ไหนก็ตามที่มี grant บน table นั้นเห็นและแก้ทุก row ได้หมด คุณต้องเปิด RLS เองแบบ explicit

alter table public.todos enable row level security;

จุดที่คนเกือบทุกคนพลาดตอนเจอครั้งแรกคือตรงนี้ พอเปิด RLS บน table ที่ ยังไม่มี policy เลยสักอัน table นั้นจะกลายเป็น เข้าไม่ได้เลย สำหรับ role anon และ authenticated — ไม่ใช่ “เปิดกว้างโดย default” แต่คือ deny-by-default ถ้ารัน alter table ... enable row level security แค่บรรทัดเดียวข้างบน แล้ว query todos ผ่าน API จะได้ผลลัพธ์ว่างเปล่า (หรือ permission error) ดูเหมือนมีอะไรพัง แต่จริง ๆ ไม่มีอะไรพังเลย RLS ที่ไม่มี policy หมายความว่าไม่มี role ไหนเห็นหรือแตะ row ได้จนกว่าคุณจะเพิ่ม policy ที่อนุญาตไว้ ทุก table ที่เปิด RLS ต้องมี policy อย่างน้อยหนึ่งอันต่อ operation ที่คุณต้องการให้ทำได้จริง

policy สร้างด้วย create policy ผูกกับ table และหนึ่ง operation หรือมากกว่า (select, insert, update, delete หรือ all)

create policy "Users can view their own todos"
on public.todos
for select
using (auth.uid() = user_id);
create policy "Users can insert their own todos"
on public.todos
for insert
with check (auth.uid() = user_id);

using คุม row ที่มีอยู่แล้วจะมองเห็นได้หรือไม่ สำหรับ select, update, delete — คิดเหมือนตัวกรองที่ใช้กับ row ที่อยู่ใน table อยู่แล้ว ส่วน with check คุม ว่า row ใหม่หรือ row ที่แก้ไขแล้วจะถูกเขียนได้หรือไม่ — ใช้กับ insert และ update โดยตรวจ row ในสภาพ หลังจาก เขียนแล้ว table หนึ่งใบสามารถ (และควร) กำหนดทั้งสองอย่าง using เพื่อตัดสินว่า update มองเห็นและแตะ row ไหนได้ ส่วน with check เพื่อตัดสินว่า row ที่ update แล้วต้องมีหน้าตายังไงถึงจะเขียนได้

auth.uid() คือ function ของ Postgres ที่ Supabase เตรียมไว้ให้ ดึง user ID ที่ authenticated แล้วของ request ปัจจุบันมาจาก JWT ที่พูดถึงในบทก่อนหน้าตรง ๆ นี่คือ expression ที่พบบ่อยที่สุดใน RLS policy ของโลกจริง เพราะ “row นี้เป็นของ user คนปัจจุบันหรือเปล่า” คือกฎการเข้าถึงที่แอปต้องใช้บ่อยที่สุด

flowchart LR
  req["Query arrives as anon/authenticated role"] --> pg[("Postgres evaluates RLS")]
  pg --> using["using: filters existing rows (select/update/delete)"]
  pg --> check["with check: validates new/modified rows (insert/update)"]
  using --> result["Only matching rows returned or written"]
  check --> result
A query is filtered per row by the matching policy
Row Level Security เปิดมาให้โดย default บน table ใหม่ของ Postgres หรือไม่
จะเกิดอะไรขึ้นเมื่อเปิด RLS บน table ที่ยังไม่มี policy เลยสักอัน
ความต่างระหว่าง using กับ with check ใน policy คืออะไร
auth.uid() คืนค่าอะไรภายใน RLS policy ของ Postgres