Wednesday

18-06-2025 Vol 19

Setting Up Row-Level Security in Supabase User and Admin

Mengamankan Data Anda: Panduan Lengkap Implementasi Row-Level Security (RLS) di Supabase untuk Pengguna dan Admin

Keamanan data adalah hal yang paling utama dalam setiap aplikasi. Supabase, sebagai alternatif open-source untuk Firebase, menyediakan fitur ampuh yang disebut Row-Level Security (RLS). RLS memungkinkan Anda mengontrol akses ke baris data berdasarkan atribut pengguna. Artikel ini akan memandu Anda melalui proses penerapan RLS di Supabase untuk membedakan akses antara pengguna biasa dan administrator, memastikan bahwa data sensitif tetap terlindungi dan hanya dapat diakses oleh pihak yang berwenang.

Mengapa Row-Level Security Penting?

Sebelum kita masuk ke implementasi, mari pahami mengapa RLS sangat penting:

  1. Kontrol Akses Granular: RLS memungkinkan Anda mendefinisikan kebijakan yang menentukan pengguna mana yang dapat melihat, menyisipkan, memperbarui, atau menghapus baris data tertentu.
  2. Pencegahan Kebocoran Data: Dengan membatasi akses data berdasarkan peran atau atribut pengguna, RLS membantu mencegah kebocoran data dan akses yang tidak sah.
  3. Kepatuhan Regulasi: RLS membantu Anda memenuhi persyaratan kepatuhan terhadap regulasi privasi data seperti GDPR dan HIPAA.
  4. Keamanan yang Ditingkatkan: RLS menambahkan lapisan keamanan tambahan ke aplikasi Anda, melindungi data Anda bahkan jika terjadi kerentanan di lapisan aplikasi.

Prasyarat

Sebelum memulai, pastikan Anda memiliki hal berikut:

  1. Akun Supabase: Anda memerlukan akun Supabase. Jika Anda belum memilikinya, daftarlah di Supabase.
  2. Proyek Supabase: Buat proyek Supabase baru atau gunakan proyek yang sudah ada.
  3. Pengetahuan Dasar SQL: Pemahaman dasar tentang SQL diperlukan untuk mendefinisikan kebijakan RLS.
  4. Pengetahuan Autentikasi Supabase: Pahami bagaimana autentikasi pengguna bekerja di Supabase.

Langkah 1: Membuat Tabel Contoh

Mari buat tabel contoh bernama products untuk mendemonstrasikan RLS. Tabel ini akan menyimpan informasi tentang produk, dan kita akan mengontrol akses ke produk berdasarkan pemiliknya (user_id).


CREATE TABLE products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name TEXT NOT NULL,
    description TEXT,
    price DECIMAL NOT NULL,
    user_id UUID NOT NULL REFERENCES auth.users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
);
  

Penjelasan kolom:

  • id: ID unik produk (UUID).
  • name: Nama produk.
  • description: Deskripsi produk.
  • price: Harga produk.
  • user_id: ID pengguna yang memiliki produk (foreign key ke tabel auth.users).
  • created_at: Tanggal dan waktu pembuatan produk.

Sekarang, mari masukkan beberapa data contoh:


INSERT INTO products (name, description, price, user_id)
VALUES
    ('Laptop', 'Laptop high-performance', 1200.00, 'user_id_1'),
    ('Smartphone', 'Smartphone flagship', 800.00, 'user_id_1'),
    ('Tablet', 'Tablet Android', 300.00, 'user_id_2');
  

Catatan: Ganti 'user_id_1' dan 'user_id_2' dengan ID pengguna yang sebenarnya dari tabel auth.users Anda. Anda dapat menemukan ID pengguna di dashboard Supabase pada bagian Authentication -> Users.

Langkah 2: Mengaktifkan Row-Level Security

Secara default, RLS dinonaktifkan pada tabel baru. Kita perlu mengaktifkannya secara eksplisit. Buka SQL Editor di dashboard Supabase dan jalankan perintah berikut:


ALTER TABLE products ENABLE ROW LEVEL SECURITY;
  

Setelah menjalankan perintah ini, RLS diaktifkan untuk tabel products. Namun, saat ini tidak ada kebijakan yang diterapkan, jadi semua orang masih dapat mengakses semua data. Mari kita definisikan kebijakan.

Langkah 3: Membuat Kebijakan RLS untuk Pengguna

Kita akan membuat kebijakan yang memungkinkan pengguna hanya melihat, menyisipkan, memperbarui, dan menghapus produk yang mereka miliki. Ini berarti pengguna hanya dapat berinteraksi dengan baris di mana user_id sesuai dengan ID pengguna mereka saat ini.

  1. Kebijakan SELECT (Melihat): Izinkan pengguna untuk melihat produk mereka sendiri.
  2. 
    CREATE POLICY "Users can view their own products"
    ON products
    FOR SELECT
    USING (auth.uid() = user_id);
          

    Kebijakan ini memungkinkan pengguna memilih (melihat) baris dari tabel products hanya jika ID pengguna mereka (auth.uid()) sama dengan nilai di kolom user_id.

  3. Kebijakan INSERT (Menyisipkan): Izinkan pengguna untuk menyisipkan produk baru dengan ID pengguna mereka sendiri.
  4. 
    CREATE POLICY "Users can insert their own products"
    ON products
    FOR INSERT
    WITH CHECK (auth.uid() = user_id);
          

    Kebijakan ini memungkinkan pengguna menyisipkan baris baru ke dalam tabel products, tetapi hanya jika ID pengguna mereka (auth.uid()) sama dengan nilai di kolom user_id. WITH CHECK memastikan bahwa nilai user_id di baris baru selalu sesuai dengan ID pengguna yang melakukan penyisipan.

  5. Kebijakan UPDATE (Memperbarui): Izinkan pengguna untuk memperbarui produk mereka sendiri.
  6. 
    CREATE POLICY "Users can update their own products"
    ON products
    FOR UPDATE
    USING (auth.uid() = user_id)
    WITH CHECK (auth.uid() = user_id);
          

    Kebijakan ini memungkinkan pengguna memperbarui baris yang ada dalam tabel products, tetapi hanya jika ID pengguna mereka (auth.uid()) sama dengan nilai di kolom user_id. USING menentukan baris mana yang dapat diperbarui, dan WITH CHECK memastikan bahwa nilai user_id tidak dapat diubah ke nilai yang berbeda.

  7. Kebijakan DELETE (Menghapus): Izinkan pengguna untuk menghapus produk mereka sendiri.
  8. 
    CREATE POLICY "Users can delete their own products"
    ON products
    FOR DELETE
    USING (auth.uid() = user_id);
          

    Kebijakan ini memungkinkan pengguna menghapus baris dari tabel products, tetapi hanya jika ID pengguna mereka (auth.uid()) sama dengan nilai di kolom user_id.

Langkah 4: Membuat Kebijakan RLS untuk Admin

Kita akan membuat kebijakan yang memungkinkan administrator untuk mengakses semua data dalam tabel products. Untuk melakukan ini, kita perlu mekanisme untuk membedakan antara pengguna biasa dan administrator. Salah satu caranya adalah dengan menggunakan peran (role) di Supabase Auth.

Menambahkan Peran Admin

Pertama, kita perlu menambahkan kolom ke tabel auth.users untuk menyimpan informasi peran pengguna. Supabase menyediakan fungsi supabase.add_claims() untuk melakukan ini. Namun, secara langsung memodifikasi tabel auth.users tidak disarankan. Sebaiknya, buat tabel profil terpisah yang memiliki referensi ke tabel auth.users. Ini memberikan fleksibilitas lebih dan menjaga struktur tabel autentikasi tetap bersih.


CREATE TABLE profiles (
    id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
    email TEXT UNIQUE,
    full_name TEXT,
    avatar_url TEXT,
    role TEXT DEFAULT 'user'
);

-- Trigger untuk membuat profil baru secara otomatis saat user baru mendaftar
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, email)
  VALUES (NEW.id, NEW.email);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW
  EXECUTE PROCEDURE public.handle_new_user();
  

Penjelasan:

  • profiles: Tabel yang menyimpan informasi profil pengguna, termasuk peran (role).
  • id: Foreign key ke tabel auth.users.
  • email: Email pengguna (diambil dari auth.users).
  • full_name, avatar_url: Kolom tambahan untuk informasi profil.
  • role: Peran pengguna (default adalah ‘user’).
  • Trigger handle_new_user: Secara otomatis membuat entry baru di tabel profiles ketika ada user baru yang mendaftar. Ini membantu kita untuk memiliki data profil yang konsisten.

Sekarang, kita dapat memperbarui peran pengguna menjadi ‘admin’ jika diperlukan. Anda dapat melakukan ini melalui SQL Editor atau melalui kode aplikasi Anda.


UPDATE profiles SET role = 'admin' WHERE id = 'user_id_admin'; -- Ganti 'user_id_admin' dengan ID pengguna admin yang sebenarnya
  

Membuat Kebijakan RLS untuk Admin

Sekarang kita memiliki peran admin, kita dapat membuat kebijakan RLS yang memberikan akses penuh kepada admin.

  1. Kebijakan SELECT (Melihat) untuk Admin: Izinkan admin untuk melihat semua produk.
  2. 
    CREATE POLICY "Admins can view all products"
    ON products
    FOR SELECT
    USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
          

    Kebijakan ini menggunakan subquery EXISTS untuk memeriksa apakah pengguna saat ini memiliki peran ‘admin’ di tabel profiles. Jika pengguna adalah admin, mereka diizinkan untuk memilih (melihat) semua baris dari tabel products.

  3. Kebijakan INSERT (Menyisipkan) untuk Admin: Izinkan admin untuk menyisipkan produk baru.
  4. 
    CREATE POLICY "Admins can insert new products"
    ON products
    FOR INSERT
    WITH CHECK (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
          

    Mirip dengan kebijakan SELECT, kebijakan ini menggunakan subquery EXISTS untuk memeriksa peran admin. Jika pengguna adalah admin, mereka dapat menyisipkan baris baru ke dalam tabel products.

  5. Kebijakan UPDATE (Memperbarui) untuk Admin: Izinkan admin untuk memperbarui produk apa pun.
  6. 
    CREATE POLICY "Admins can update any product"
    ON products
    FOR UPDATE
    USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'))
    WITH CHECK (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
          

    Kebijakan ini memungkinkan admin untuk memperbarui baris apa pun. USING dan WITH CHECK keduanya menggunakan subquery EXISTS untuk memastikan bahwa hanya admin yang dapat melakukan pembaruan.

  7. Kebijakan DELETE (Menghapus) untuk Admin: Izinkan admin untuk menghapus produk apa pun.
  8. 
    CREATE POLICY "Admins can delete any product"
    ON products
    FOR DELETE
    USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
          

    Kebijakan ini memungkinkan admin untuk menghapus baris apa pun dari tabel products.

Langkah 5: Menguji Kebijakan RLS

Setelah Anda mendefinisikan kebijakan RLS, penting untuk mengujinya secara menyeluruh untuk memastikan bahwa mereka berfungsi seperti yang diharapkan. Berikut adalah beberapa cara untuk menguji kebijakan RLS Anda:

  1. Menggunakan SQL Editor: Anda dapat menggunakan SQL Editor di dashboard Supabase untuk menjalankan kueri sebagai pengguna yang berbeda. Untuk melakukan ini, Anda perlu mengatur variabel request.jwt.claim.sub ke ID pengguna yang ingin Anda simulasikan.
    
    -- Atur variabel request.jwt.claim.sub ke ID pengguna
    set request.jwt.claim.sub = 'user_id_1';
    
    -- Jalankan kueri SELECT
    SELECT * FROM products;
          

    Ganti 'user_id_1' dengan ID pengguna yang sebenarnya. Setelah menjalankan kueri, Anda akan melihat hanya produk yang dimiliki oleh pengguna tersebut.

    
    -- Atur variabel request.jwt.claim.sub ke ID admin
    set request.jwt.claim.sub = 'user_id_admin';
    
    -- Jalankan kueri SELECT
    SELECT * FROM products;
          

    Ganti 'user_id_admin' dengan ID pengguna admin yang sebenarnya. Setelah menjalankan kueri, Anda akan melihat semua produk.

  2. Menggunakan Aplikasi Anda: Integrasikan autentikasi Supabase ke dalam aplikasi Anda dan uji kebijakan RLS dari sisi klien. Masuk sebagai pengguna yang berbeda dan coba melakukan operasi yang berbeda (SELECT, INSERT, UPDATE, DELETE) pada tabel products. Pastikan bahwa pengguna hanya dapat melakukan operasi pada data yang mereka miliki dan bahwa admin dapat melakukan operasi pada semua data.

Praktik Terbaik untuk Row-Level Security

Berikut adalah beberapa praktik terbaik untuk menerapkan RLS di Supabase:

  • Minimal Privilege: Berikan hanya hak akses yang diperlukan kepada pengguna. Hindari memberikan akses yang tidak perlu ke data.
  • Audit dan Pantau: Secara teratur audit kebijakan RLS Anda dan pantau aktivitas pengguna untuk memastikan keamanan data.
  • Dokumentasikan Kebijakan: Dokumentasikan semua kebijakan RLS Anda dengan jelas dan ringkas. Ini akan membantu Anda memahami dan memelihara kebijakan Anda di masa mendatang.
  • Uji Secara Menyeluruh: Uji kebijakan RLS Anda secara menyeluruh sebelum menerapkan ke produksi. Pastikan bahwa mereka berfungsi seperti yang diharapkan dan tidak ada celah keamanan.
  • Gunakan Fungsi auth.uid() dengan Hati-hati: Fungsi auth.uid() bergantung pada klaim JWT yang dikirim oleh klien. Pastikan bahwa klien Anda aman dan tidak dapat dimanipulasi untuk mengirim klaim palsu.
  • Pertimbangkan Performa: Kebijakan RLS yang kompleks dapat memengaruhi performa database Anda. Optimalkan kebijakan Anda untuk meminimalkan dampak performa.

Contoh Kasus Penggunaan Lainnya

Berikut adalah beberapa contoh kasus penggunaan lain untuk RLS:

  • Aplikasi SaaS Multi-Tenant: Memastikan bahwa setiap penyewa (tenant) hanya dapat mengakses data mereka sendiri.
  • Aplikasi Kesehatan: Memastikan bahwa pasien hanya dapat mengakses catatan kesehatan mereka sendiri dan bahwa dokter memiliki akses ke catatan pasien yang mereka rawat.
  • Aplikasi Keuangan: Memastikan bahwa pengguna hanya dapat mengakses akun keuangan mereka sendiri dan bahwa staf keuangan memiliki akses ke semua akun.

Kesimpulan

Row-Level Security adalah fitur yang ampuh di Supabase yang memungkinkan Anda mengontrol akses ke data Anda dengan granularitas yang tinggi. Dengan menerapkan kebijakan RLS yang tepat, Anda dapat memastikan bahwa data sensitif Anda tetap terlindungi dan hanya dapat diakses oleh pihak yang berwenang. Dalam panduan ini, kita telah membahas cara menerapkan RLS untuk membedakan akses antara pengguna biasa dan administrator. Ingatlah untuk selalu menguji kebijakan RLS Anda secara menyeluruh dan mengikuti praktik terbaik untuk memastikan keamanan data Anda.

Dengan kombinasi autentikasi Supabase dan Row-Level Security, Anda dapat membangun aplikasi yang aman dan andal dengan mudah.

“`

omcoding

Leave a Reply

Your email address will not be published. Required fields are marked *