Row Level Security Fundamentals
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Row Level Security (RLS) คือ feature ของ Postgres เองที่ผูก policy เข้ากับ table เพื่อให้ Postgres คัดกรอง row ที่ role หนึ่ง ๆ อ่านหรือเขียนได้ในทุก query โดยอัตโนมัติ — ไม่ใช่สิ่งที่โค้ดฝั่งแอปต้องมานั่งจำไปเติมเป็น where clause เอง
RLS ปิดอยู่จนกว่าคุณจะเปิดเอง
หัวข้อที่มีชื่อว่า “RLS ปิดอยู่จนกว่าคุณจะเปิดเอง”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: using vs with check
หัวข้อที่มีชื่อว่า “กายวิภาคของ policy: using vs with check”policy สร้างด้วย create policy ผูกกับ table และหนึ่ง operation หรือมากกว่า (select, insert, update, delete หรือ all)
create policy "Users can view their own todos"on public.todosfor selectusing (auth.uid() = user_id);
create policy "Users can insert their own todos"on public.todosfor insertwith 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(): ตัวประกอบหลักที่ใช้บ่อยที่สุด
หัวข้อที่มีชื่อว่า “auth.uid(): ตัวประกอบหลักที่ใช้บ่อยที่สุด”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