Di banyak database lain, “user” dan “group” adalah dua objek berbeda dengan sintaks pembuatan masing-masing. PostgreSQL sejak versi 8.1 menyatukan keduanya menjadi satu konsep bernama role: sebagian role berfungsi sebagai akun login aplikasi, sebagian lagi sebagai wadah hak akses bersama, dan sebagian lagi keduanya sekaligus. Pemisahan itu ditentukan bukan oleh jenis objek, melainkan oleh atribut yang melekat pada role — terutama atribut LOGIN. Pada chapter View dan Keamanan Data kita sudah mencicipi pola kerjanya: membuat role kasir_latihan, memberi GRANT SELECT, lalu berpindah kulit dengan SET ROLE. Section ini membongkar model tersebut sampai ke fondasinya: bagaimana role dibuat dan dihapus, bagaimana privilege bekerja, hingga bagaimana Row Level Security menyaring baris per pengguna. Kita mulai dari catalog tempat semua role terdaftar.

Satu Konsep untuk User dan Group #

Sebuah role dengan atribut LOGIN dapat dipakai untuk membuka koneksi — inilah yang dalam bahasa sehari-hari disebut user. Role tanpa LOGIN tidak bisa terhubung sama sekali; peranannya murni sebagai wadah hak akses yang diwarisi anggota-anggotanya — persis seperti group di sistem lain. Karena keduanya objek yang sama, perintah GRANT yang sama bisa dipakai untuk memberi hak ke seorang user maupun ke sebuah grup.

File pembuka membuat tiga role latihan dengan profil berbeda, lalu membaca atributnya dari catalog pg_roles. Role bersifat cluster-wide: satu daftar role dipakai bersama oleh semua database di server, sehingga file ini juga membuang sisa role latihan bila eksekusi sebelumnya berhenti di tengah:

-- Membuat tiga role latihan dengan atribut yang berbeda-beda.
-- Role adalah objek level cluster (berlaku untuk seluruh database di
-- server ini), jadi dibuang dulu bila tersisa dari latihan sebelumnya.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS admin_tmj;
DROP ROLE IF EXISTS grup_keuangan_tmj;

-- Role dengan hak login: inilah yang biasa disebut "user".
CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan';

-- Role tanpa LOGIN: tidak bisa terhubung, cocok sebagai grup.
CREATE ROLE grup_keuangan_tmj NOLOGIN;

-- Role beratribut administratif: boleh membuat database dan role lain.
CREATE ROLE admin_tmj LOGIN CREATEDB CREATEROLE PASSWORD 'sandi_latihan';

-- Catalog pg_roles membaca atribut setiap role sebagai kolom boolean.
-- Baris postgres ikut ditampilkan sebagai pembanding (superuser).
SELECT rolname,
       rolsuper     AS superuser,
       rolcreatedb  AS buat_db,
       rolcreaterole AS buat_role,
       rolcanlogin  AS bisa_login,
       rolreplication AS replikasi,
       rolbypassrls AS lewati_rls
FROM pg_roles
WHERE rolname IN ('postgres', 'kasir_tmj', 'admin_tmj', 'grup_keuangan_tmj')
ORDER BY rolname;

Output yang Dihasilkan:

text
DROP ROLE
DROP ROLE
DROP ROLE
CREATE ROLE
CREATE ROLE
CREATE ROLE
      rolname      | superuser | buat_db | buat_role | bisa_login | replikasi | lewati_rls 
-------------------+-----------+---------+-----------+------------+-----------+------------
 admin_tmj         | f         | t       | t         | t          | f         | f
 grup_keuangan_tmj | f         | f       | f         | f          | f         | f
 kasir_tmj         | f         | f       | f         | t          | f         | f
 postgres          | t         | t       | t         | t          | t         | t
(4 rows)

Mari kita bedah perintah-perintahnya:

  1. DROP ROLE IF EXISTS ... — tiga perintah pembuka memastikan chapter bisa dijalankan ulang. Role hidup di level cluster, bukan per database, jadi kebersihannya tidak bisa dijamin oleh database latihan yang selalu dibuat ulang.
  2. CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan' — role dengan hak masuk; sandi di sini murni penampung latihan, bukan kredensial nyata.
  3. CREATE ROLE grup_keuangan_tmj NOLOGIN — role tanpa hak masuk; tidak akan pernah bisa dipakai untuk koneksi, hanya sebagai grup penerima hak akses.
  4. CREATE ROLE admin_tmj LOGIN CREATEDB CREATEROLE — role login sekaligus dua atribut administratif: boleh membuat database (CREATEDB) dan membuat role lain (CREATEROLE).
  5. Query pg_roles — catalog yang dapat dibaca semua role; setiap atribut menjadi satu kolom boolean, dan baris postgres ditampilkan sebagai pembanding: superuser yang semua atributnya bernilai t.

Anatomi Atribut Role #

Tabel berikut merangkum atribut yang paling sering dipakai beserta efeknya:

AtributEfek saat bernilai t
LOGINRole boleh membuka koneksi ke server (menjadi “user” dalam arti harfiah)
SUPERUSERMelewati seluruh pemeriksaan hak akses — atribut paling berbahaya, berikan seperlunya
CREATEDBRole boleh menjalankan CREATE DATABASE
CREATEROLERole boleh membuat, mengubah, dan menghapus role lain
REPLICATIONRole boleh menjalankan streaming replication dan pg_basebackup
BYPASSRLSRole melewati seluruh Row Level Security yang akan kita bahas di dua chapter akhir
INHERITRole otomatis mewarisi hak dari role lain tempat ia menjadi anggota

Atribut-atribut ini bukan konsep teoretis — kolom rolsuper, rolcreatedb, rolcreaterole, rolcanlogin, rolreplication, dan rolbypassrls pada query di atas adalah kolom sungguhan di pg_roles, satu baris per role yang ada di cluster. Role yang dibuat tanpa atribut apa pun (seperti akan kita lihat di chapter berikutnya) otomatis mendapat nilai f untuk semuanya kecuali INHERIT.

Identitas Sesi dan Identitas Aktif #

PostgreSQL mencatat dua identitas berbeda dalam satu koneksi. session_user adalah role yang dipakai saat koneksi dibuka dan tidak bisa berubah selama sesi hidup. current_user adalah role yang sedang bertindak saat ini — bisa berganti lewat SET ROLE, lalu dikembalikan lewat RESET ROLE. Pemeriksaan privilege selalu mengacu pada current_user, dan justru mekanisme inilah yang membuat seluruh demo di section ini bisa dijalankan dari satu koneksi superuser:

sql identitas.sql
-- Membedakan session_user dan current_user.
-- session_user = role yang dipakai saat koneksi dibuka.
-- current_user = role yang sedang aktif, bisa berubah lewat SET ROLE.
SELECT session_user, current_user;

-- SET ROLE mengganti identitas aktif tanpa membuka koneksi baru.
SET ROLE kasir_tmj;

SELECT session_user, current_user;

-- RESET ROLE kembali ke identitas sesi semula.
RESET ROLE;

SELECT session_user, current_user;

Output yang Dihasilkan:

text
 session_user | current_user 
--------------+--------------
 postgres     | postgres
(1 row)

SET
 session_user | current_user 
--------------+--------------
 postgres     | kasir_tmj
(1 row)

RESET
 session_user | current_user 
--------------+--------------
 postgres     | postgres
(1 row)

Urutan tiga query SELECT session_user, current_user memperlihatkan pergantian itu: sebelum SET ROLE keduanya postgres, selama SET ROLE kasir_tmj kolom current_user berubah menjadi kasir_tmj sementara session_user tetap postgres, dan RESET ROLE mengembalikan keduanya seperti semula. Dalam praktik, SET ROLE adalah alat uji hak akses yang ampuh: kita bisa membuktikan “role X memang tidak bisa membaca tabel Y” tanpa harus membuka koneksi baru dan login sebagai X.

Role Sebagai Grup dan Pewarisan Hak #

Kekuatan sesungguhnya dari model unified ini muncul saat role dijadikan anggota role lain melalui GRANT ... TO .... Role penerima keanggotaan otomatis mewarisi seluruh privilege yang diberikan kepada grupnya — tanpa perlu satu pun grant langsung atas nama dirinya. File berikut membangun buktinya: hak SELECT diberikan hanya kepada grup_keuangan_tmj, kemudian dibaca oleh kasir_tmj yang notabene tidak punya grant apa pun:

sql grup-role.sql
-- Role NOLOGIN sebagai grup: hak diberikan ke grup,
-- anggota mewarisinya secara otomatis.
CREATE TABLE nota (
    id  integer PRIMARY KEY,
    isi text NOT NULL
);

INSERT INTO nota (id, isi) VALUES
    (1, 'nota pembelian kopi'),
    (2, 'nota pembelian gula');

-- Memasukkan kasir_tmj sebagai anggota grup_keuangan_tmj.
GRANT grup_keuangan_tmj TO kasir_tmj;

-- Catalog pg_auth_members mencatat keanggotaan beserta opsi-opsinya.
SELECT anggota.rolname AS nama_anggota,
       grup.rolname   AS nama_grup,
       m.inherit_option AS mewarisi,
       m.set_option     AS bisa_set_role,
       m.admin_option   AS bisa_grant_lagi
FROM pg_auth_members m
JOIN pg_roles anggota ON anggota.oid = m.member
JOIN pg_roles grup    ON grup.oid = m.roleid
WHERE grup.rolname = 'grup_keuangan_tmj'
ORDER BY nama_anggota;

-- Hak SELECT diberikan ke GRUP, bukan ke kasir langsung.
GRANT SELECT ON nota TO grup_keuangan_tmj;

-- Sebagai kasir: SELECT berhasil karena hak grup diwarisi anggotanya.
SET ROLE kasir_tmj;

SELECT id, isi FROM nota ORDER BY id;

RESET ROLE;

-- Bukti dari catalog: grant tertulis atas nama grup,
-- sementara kasir mendapatkannya lewat pewarisan keanggotaan.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'nota'
ORDER BY grantee, privilege_type;

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 2
GRANT ROLE
 nama_anggota |     nama_grup     | mewarisi | bisa_set_role | bisa_grant_lagi 
--------------+-------------------+----------+---------------+-----------------
 kasir_tmj    | grup_keuangan_tmj | t        | t             | f
(1 row)

GRANT
SET
 id |         isi         
----+---------------------
  1 | nota pembelian kopi
  2 | nota pembelian gula
(2 rows)

RESET
      grantee      | privilege_type 
-------------------+----------------
 grup_keuangan_tmj | SELECT
 postgres          | DELETE
 postgres          | INSERT
 postgres          | REFERENCES
 postgres          | SELECT
 postgres          | TRIGGER
 postgres          | TRUNCATE
 postgres          | UPDATE
(8 rows)

Ada dua pembuktian yang saling menguatkan di sini. Query information_schema.role_table_grants menunjukkan grant SELECT hanya tercatat atas nama grup_keuangan_tmj — nama kasir_tmj tidak muncul sebagai grantee. Namun SELECT yang dijalankan setelah SET ROLE kasir_tmj berhasil menampilkan dua baris nota, karena keanggotaan membuat kasir mewarisi hak grupnya. Catalog pg_auth_members melengkapi ceritanya: baris keanggotaan menyimpan opsi inherit_option (anggota mewarisi hak), set_option (anggota boleh SET ROLE menjadi grup), dan admin_option (anggota boleh memberikan keanggotaan kepada role lain).

Membersihkan Role Latihan #

Role tidak ikut lenyap ketika database latihan dijatuhkan, karena registrasinya berada di level cluster. Kebiasaan penutup yang sama dengan chapter View dan Keamanan Data berlaku di sini: DROP OWNED BY melepas seluruh hak yang diberikan kepada role di database berjalan, baru DROP ROLE menghapus role-nya:

sql bersihkan.sql
-- Membersihkan seluruh role latihan chapter ini.
-- DROP OWNED BY melepas hak yang diberikan kepada role di database ini;
-- tanpa langkah itu, DROP ROLE gagal karena masih ada privilege bergantung.
DROP OWNED BY kasir_tmj;
DROP OWNED BY admin_tmj;
DROP OWNED BY grup_keuangan_tmj;

DROP ROLE kasir_tmj;
DROP ROLE admin_tmj;
DROP ROLE grup_keuangan_tmj;

-- Verifikasi: tidak ada role latihan yang tersisa di catalog.
SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'admin_tmj', 'grup_keuangan_tmj');

Output yang Dihasilkan:

text
DROP OWNED
DROP OWNED
DROP OWNED
DROP ROLE
DROP ROLE
DROP ROLE
 sisa_role_latihan 
-------------------
                 0
(1 row)

Query verifikasi di akhir file menghitung role latihan yang tersisa dan mendapat jawaban 0 — cluster kembali bersih dan file-file chapter ini bisa dijalankan ulang berkali-kali. Pola “buang dulu di pembuka, bersihkan tuntas di penutup” ini akan dipakai di seluruh chapter section ini karena semua demo kita memainkan role sungguhan.

Model perannya sudah jelas; yang belum kita kuasai adalah siklus hidupnya — membuat role dengan sandi, mengganti atribut, mengubah nama, dan menghapus role yang barangkalian masih memiliki objek. Itulah pekerjaan chapter berikutnya, Membuat dan Menghapus Role .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

bersihkan.sql

Unduh berkas
sql
-- Membersihkan seluruh role latihan chapter ini.
-- DROP OWNED BY melepas hak yang diberikan kepada role di database ini;
-- tanpa langkah itu, DROP ROLE gagal karena masih ada privilege bergantung.
DROP OWNED BY kasir_tmj;
DROP OWNED BY admin_tmj;
DROP OWNED BY grup_keuangan_tmj;

DROP ROLE kasir_tmj;
DROP ROLE admin_tmj;
DROP ROLE grup_keuangan_tmj;

-- Verifikasi: tidak ada role latihan yang tersisa di catalog.
SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'admin_tmj', 'grup_keuangan_tmj');

grup-role.sql

Unduh berkas
sql
-- Role NOLOGIN sebagai grup: hak diberikan ke grup,
-- anggota mewarisinya secara otomatis.
CREATE TABLE nota (
    id  integer PRIMARY KEY,
    isi text NOT NULL
);

INSERT INTO nota (id, isi) VALUES
    (1, 'nota pembelian kopi'),
    (2, 'nota pembelian gula');

-- Memasukkan kasir_tmj sebagai anggota grup_keuangan_tmj.
GRANT grup_keuangan_tmj TO kasir_tmj;

-- Catalog pg_auth_members mencatat keanggotaan beserta opsi-opsinya.
SELECT anggota.rolname AS nama_anggota,
       grup.rolname   AS nama_grup,
       m.inherit_option AS mewarisi,
       m.set_option     AS bisa_set_role,
       m.admin_option   AS bisa_grant_lagi
FROM pg_auth_members m
JOIN pg_roles anggota ON anggota.oid = m.member
JOIN pg_roles grup    ON grup.oid = m.roleid
WHERE grup.rolname = 'grup_keuangan_tmj'
ORDER BY nama_anggota;

-- Hak SELECT diberikan ke GRUP, bukan ke kasir langsung.
GRANT SELECT ON nota TO grup_keuangan_tmj;

-- Sebagai kasir: SELECT berhasil karena hak grup diwarisi anggotanya.
SET ROLE kasir_tmj;

SELECT id, isi FROM nota ORDER BY id;

RESET ROLE;

-- Bukti dari catalog: grant tertulis atas nama grup,
-- sementara kasir mendapatkannya lewat pewarisan keanggotaan.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'nota'
ORDER BY grantee, privilege_type;

identitas.sql

Unduh berkas
sql
-- Membedakan session_user dan current_user.
-- session_user = role yang dipakai saat koneksi dibuka.
-- current_user = role yang sedang aktif, bisa berubah lewat SET ROLE.
SELECT session_user, current_user;

-- SET ROLE mengganti identitas aktif tanpa membuka koneksi baru.
SET ROLE kasir_tmj;

SELECT session_user, current_user;

-- RESET ROLE kembali ke identitas sesi semula.
RESET ROLE;

SELECT session_user, current_user;

perkenalan-role.sql

Unduh berkas
sql
-- Membuat tiga role latihan dengan atribut yang berbeda-beda.
-- Role adalah objek level cluster (berlaku untuk seluruh database di
-- server ini), jadi dibuang dulu bila tersisa dari latihan sebelumnya.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS admin_tmj;
DROP ROLE IF EXISTS grup_keuangan_tmj;

-- Role dengan hak login: inilah yang biasa disebut "user".
CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan';

-- Role tanpa LOGIN: tidak bisa terhubung, cocok sebagai grup.
CREATE ROLE grup_keuangan_tmj NOLOGIN;

-- Role beratribut administratif: boleh membuat database dan role lain.
CREATE ROLE admin_tmj LOGIN CREATEDB CREATEROLE PASSWORD 'sandi_latihan';

-- Catalog pg_roles membaca atribut setiap role sebagai kolom boolean.
-- Baris postgres ikut ditampilkan sebagai pembanding (superuser).
SELECT rolname,
       rolsuper     AS superuser,
       rolcreatedb  AS buat_db,
       rolcreaterole AS buat_role,
       rolcanlogin  AS bisa_login,
       rolreplication AS replikasi,
       rolbypassrls AS lewati_rls
FROM pg_roles
WHERE rolname IN ('postgres', 'kasir_tmj', 'admin_tmj', 'grup_keuangan_tmj')
ORDER BY rolname;