Chapter sebelumnya membedah apa itu role; sekarang kita tangani siklus hidupnya. Membuat role terlihat sepele — satu baris CREATE ROLE — tetapi di baliknya ada keputusan yang menentukan keamanan server: apakah role boleh login, boleh membuat database, atau boleh mengatur role lain. Menghapus role justru bagian yang sering menjegal tim operasional: sebuah role yang pernah memiliki tabel atau menerima GRANT akan menolak dihapus, dan PostgreSQL menuntut objek-objek tanggungan itu diselesaikan lebih dulu. Chapter ini melatih keduanya sampai tuntas: pembuatan dengan sandi ter-hash, perubahan lewat ALTER ROLE, sampai pola pembersihan REASSIGN OWNED dan DROP OWNED sebelum DROP ROLE.
CREATE ROLE, CREATE USER, dan Sandi #
Bentuk dasarnya satu baris; kedalaman sesungguhnya ada pada atribut opsional yang menyertainya. File pembuka membuat tiga role dengan tiga gaya berbeda, lalu membuktikan bahwa sandi tidak pernah disimpan sebagai teks polos melainkan sebagai hash SCRAM:
-- Tiga cara membuat role: CREATE ROLE dengan atribut,
-- CREATE USER sebagai padanan LOGIN, dan role polos.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS magasier_tmj;
DROP ROLE IF EXISTS gudang_tmj;
DROP ROLE IF EXISTS kepala_gudang_tmj;
-- Role login dengan sandi (sandi latihan, bukan kredensial nyata).
CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan';
-- CREATE USER padanan penuh CREATE ROLE ... LOGIN.
CREATE USER magasier_tmj PASSWORD 'sandi_latihan';
-- Role polos: tanpa LOGIN, tanpa atribut, tanpa sandi.
CREATE ROLE gudang_tmj;
SELECT rolname, rolcanlogin
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'magasier_tmj', 'gudang_tmj')
ORDER BY rolname;
-- pg_authid menyimpan sandi dalam bentuk hash. Superuser saja yang
-- boleh membacanya; nilainya dipotong di sini agar hanya bagian
-- deterministik (nama skema hash dan jumlah iterasi) yang tampil.
SELECT rolname,
split_part(rolpassword, '$', 1) AS skema_hash,
split_part(split_part(rolpassword, '$', 2), ':', 1) AS iterasi
FROM pg_authid
WHERE rolname IN ('kasir_tmj', 'magasier_tmj')
ORDER BY rolname;
-- Parameter server yang menentukan algoritma hash saat sandi disimpan.
SHOW password_encryption;Output yang Dihasilkan:
DROP ROLE
DROP ROLE
DROP ROLE
DROP ROLE
CREATE ROLE
CREATE ROLE
CREATE ROLE
rolname | rolcanlogin
--------------+-------------
gudang_tmj | f
kasir_tmj | t
magasier_tmj | t
(3 rows)
rolname | skema_hash | iterasi
--------------+---------------+---------
kasir_tmj | SCRAM-SHA-256 | 4096
magasier_tmj | SCRAM-SHA-256 | 4096
(2 rows)
password_encryption
---------------------
scram-sha-256
(1 row)Mari kita bedah baris-baris pentingnya:
CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan'— atributLOGINmenjadikannya akun koneksi, danPASSWORDmenyimpan sandi ter-hash sejak awal.CREATE USER magasier_tmj PASSWORD 'sandi_latihan'—CREATE USERadalah padanan penuhCREATE ROLE ... LOGIN; kolomrolcanloginmembuktikan keduanya sama-sama bernilait. Perintah ini dipertahankan demi kompatibilitas, bukan karena ada dua jenis objek.CREATE ROLE gudang_tmj— role polos tanpa atribut;rolcanloginbernilaifkarenaNOLOGINadalah bawaanCREATE ROLE.- Query
pg_authid— catalog asli tempat sandi disimpan. Tampilannya sengaja dipotong dengansplit_partagar hanya nama skema hash (SCRAM-SHA-256) dan jumlah iterasi (4096) yang terlihat; salt dan kunci hash-nya bersifat acak sehingga berubah setiap kali sandi di-set. SHOW password_encryption— parameter yang menentukan algoritma hash untuk sandi baru;scram-sha-256telah menjadi bawaan sejak PostgreSQL 14.
pg_roles sengaja tidak memuat kolom sandi, dan pg_authid hanya bisa dibaca superuser — itulah sebabnya pembuktian hash di atas harus dijalankan dari role postgres. Kalimat “sandi disimpan dengan aman” dengan demikian bukan janji, melainkan fakta yang bisa diverifikasi dari catalog.
ALTER ROLE: Nama, Atribut, Sandi, dan Setelan Sesi #
Role yang sudah terlanjur dibuat masih bisa diubah total lewat ALTER ROLE: namanya diganti tanpa kehilangan hak, atributnya ditambah atau dicabut, sandinya diganti, bahkan parameter sesi seperti search_path bisa ditetapkan sebagai bawaan khusus role itu. File berikut merangkai semuanya dalam satu alur:
-- ALTER ROLE: mengganti nama, menambah atribut, mengganti sandi,
-- dan menetapkan setelan sesi bawaan per role.
ALTER ROLE gudang_tmj RENAME TO kepala_gudang_tmj;
ALTER ROLE kepala_gudang_tmj LOGIN CREATEDB PASSWORD 'sandi_baru_latihan';
-- Verifikasi atribut baru di catalog.
SELECT rolname, rolcanlogin, rolcreatedb
FROM pg_roles
WHERE rolname = 'kepala_gudang_tmj';
-- Sandi tercatat kembali sebagai hash SCRAM yang baru.
SELECT rolname,
split_part(rolpassword, '$', 1) AS skema_hash
FROM pg_authid
WHERE rolname = 'kepala_gudang_tmj';
-- Setelan sesi bawaan untuk role: berlaku otomatis saat role login.
ALTER ROLE kasir_tmj SET default_transaction_read_only = on;
-- Catalog pg_db_role_setting mencatat setelan tersebut.
SELECT r.rolname, s.setconfig
FROM pg_db_role_setting s
JOIN pg_roles r ON r.oid = s.setrole
WHERE r.rolname = 'kasir_tmj';
-- Menghapus kembali setelan bawaan per parameter.
ALTER ROLE kasir_tmj RESET default_transaction_read_only;
SELECT count(*) AS setelan_tersisa
FROM pg_db_role_setting s
JOIN pg_roles r ON r.oid = s.setrole
WHERE r.rolname = 'kasir_tmj';Output yang Dihasilkan:
ALTER ROLE
ALTER ROLE
rolname | rolcanlogin | rolcreatedb
-------------------+-------------+-------------
kepala_gudang_tmj | t | t
(1 row)
rolname | skema_hash
-------------------+---------------
kepala_gudang_tmj | SCRAM-SHA-256
(1 row)
ALTER ROLE
rolname | setconfig
-----------+------------------------------------
kasir_tmj | {default_transaction_read_only=on}
(1 row)
ALTER ROLE
setelan_tersisa
-----------------
0
(1 row)Tiga perubahan di file itu layak digarisbawahi. Pertama, RENAME TO mengganti identitas role tanpa memutus satu pun grant — PostgreSQL merujuk role lewat OID internal, bukan nama, sehingga seluruh privilege otomatis mengikuti nama baru. Kedua, ALTER ROLE ... LOGIN CREATEDB PASSWORD ... menunjukkan atribut bisa ditambah kapan saja; catalog pg_roles kemudian memastikan rolcreatedb kini bernilai t. Ketiga, ALTER ROLE kasir_tmj SET default_transaction_read_only = on menetapkan setelan sesi bawaan yang diterapkan otomatis setiap kali role tersebut login — berguna misalnya memaksa akun pelaporan selalu membaca tanpa bisa menulis. Catalog pg_db_role_setting mencatat setelan itu, dan ALTER ROLE ... RESET menghapusnya kembali.
Perlu diketahui bahwa setelan bawaan semacam itu berlaku pada saat login, bukan saat SET ROLE: berganti kulit di tengah sesi tidak memuat ulang konfigurasi milik role tujuan. Selain bentuk global di atas, ada juga ALTER ROLE ... IN DATABASE untuk setelan yang hanya berlaku pada satu database tertentu.
DROP ROLE yang Menolak #
Menghapus role idealnya sesederhana membuatnya. Masalahnya, role yang pernah bekerja hampir selalu meninggalkan jejak: tabel yang dimiliki, privilege yang diterima, atau objek lain di database manapun di cluster. File berikut sengaja memindahkan kepemilikan sebuah tabel ke kepala_gudang_tmj lalu langsung mencoba menghapus role itu — dan PostgreSQL menolaknya:
-- DROP ROLE gagal bila role masih memiliki objek.
-- Tabel di bawah dipindahkan kepemilikannya ke kepala_gudang_tmj
-- terlebih dahulu, sehingga role itu punya tanggungan.
CREATE TABLE inventaris (
id integer PRIMARY KEY,
barang text NOT NULL
);
INSERT INTO inventaris (id, barang) VALUES
(1, 'kardus kopi'),
(2, 'karung gula');
ALTER TABLE inventaris OWNER TO kepala_gudang_tmj;
-- Membuat role kini ditolak: masih ada objek miliknya.
DROP ROLE kepala_gudang_tmj;Output yang Dihasilkan:
psql:role-bergantung.sql:16: ERROR: role "kepala_gudang_tmj" cannot be dropped because some objects depend on it
DETAIL: owner of table inventarisPesan errornya jujur dan spesifik: role "kepala_gudang_tmj" cannot be dropped because some objects depend on it, dengan DETAIL yang menyebut owner of table inventaris. PostgreSQL menolak karena menghapus role pemilik akan meninggalkan tabel tanpa pemilik — kondisi yang tidak sehat bagi maintenance seperti pg_dump. Jenis tanggungan lain yang memicu penolakan serupa adalah privilege yang diberikan kepada role tersebut (pernah kita lawan di chapter View dan Keamanan Data
) dan keanggotaan grup yang masih dipegang role lain.
REASSIGN OWNED dan DROP OWNED #
Solusi untuk dua jenis tanggungan itu berbeda. Kepemilikan objek diselesaikan dengan REASSIGN OWNED BY role_lama TO role_baru yang memindahkan seluruh kepemilikan di database berjalan; privilege dan objek milik sendiri dilepas dengan DROP OWNED BY. Urutan lengkapnya tampak seperti ini:
-- Urutan pembersihan yang benar untuk role pemilik objek:
-- REASSIGN OWNED memindahkan kepemilikan, DROP OWNED melepas sisa hak,
-- baru DROP ROLE dijalankan.
REASSIGN OWNED BY kepala_gudang_tmj TO postgres;
DROP OWNED BY kepala_gudang_tmj;
DROP ROLE kepala_gudang_tmj;
-- Tabel tetap hidup dengan data utuh di bawah pemilik barunya.
SELECT id, barang
FROM inventaris
ORDER BY id;
SELECT count(*) AS sisa_kepala_gudang
FROM pg_roles
WHERE rolname = 'kepala_gudang_tmj';Output yang Dihasilkan:
REASSIGN OWNED
DROP OWNED
DROP ROLE
id | barang
----+-------------
1 | kardus kopi
2 | karung gula
(2 rows)
sisa_kepala_gudang
--------------------
0
(1 row)Alur file ini menyelesaikan persis kegagalan sebelumnya: REASSIGN OWNED memindahkan tabel inventaris ke postgres, DROP OWNED melepas sisa hak yang mungkin masih menempel, dan DROP ROLE kini berhasil. Query penutup membuktikan dua hal sekaligus — role sudah lenyap dari pg_roles, sementara tabel inventaris tetap hidup dengan dua baris datanya utuh. Tidak ada data yang ikut terbuang saat kepemilikan dipindahkan.
Konvensi Penamaan Role #
Nama role hidup lama dan dibaca manusia di log, di pg_hba.conf, dan di laporan audit, sehingga konvensi penamaan memudahkan pengelolaan jangka panjang. Praktik yang umum:
- Huruf kecil dengan pemisah jelas —
kasir_tmj,admin_laporan; PostgreSQL melipatgandakan nama tanpa tanda kutip menjadi huruf kecil, dan penulisan campuran memaksa penggunaan kutip ganda selamanya. - Sufiks peruntukan —
_rountuk akun baca-saja,_appuntuk akun aplikasi, atau nama tim seperti_tmjpada latihan kita, agar satu pandanganpg_roleslangsung membedakan kelompok. - Hindari nama role bawaan — awalan
pg_dikhususkan untuk role sistem sepertipg_read_all_data; PostgreSQL menolak pembuatannya lewatCREATE ROLE. - Nama mencerminkan fungsi, bukan orang —
kasir_applebih tahan lama daripadabudi, karena orang berganti jabatan sedangkan fungsi bertahan.
Membersihkan Sisa Role Latihan #
File penutup mengembalikan cluster ke kondisi bersih dengan pola yang sudah terbukti: DROP OWNED BY untuk setiap role, diikuti DROP ROLE:
-- Pembersihan akhir role latihan yang tersisa di chapter ini.
DROP OWNED BY kasir_tmj;
DROP OWNED BY magasier_tmj;
DROP ROLE kasir_tmj;
DROP ROLE magasier_tmj;
SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'magasier_tmj', 'gudang_tmj', 'kepala_gudang_tmj');Output yang Dihasilkan:
DROP OWNED
DROP OWNED
DROP ROLE
DROP ROLE
sisa_role_latihan
-------------------
0
(1 row)Siklus hidup role kini lengkap dikuasai: dibuat dengan atribut dan sandi yang tepat, diubah saat kebutuhan bergeser, dan dihapus tanpa meninggalkan tanggungan. Role yang sudah bersih ini selanjutnya menanti satu hal — hak akses. Database yang tidak memberikan apa pun kepada role hanya menjadikannya pengunjung kosong; sistem privilege-lah yang menentukan objek mana yang boleh dibaca dan ditulis, dan itu bahasan chapter Privileges .
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
bersihkan.sql
Unduh berkas-- Pembersihan akhir role latihan yang tersisa di chapter ini.
DROP OWNED BY kasir_tmj;
DROP OWNED BY magasier_tmj;
DROP ROLE kasir_tmj;
DROP ROLE magasier_tmj;
SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'magasier_tmj', 'gudang_tmj', 'kepala_gudang_tmj');
buat-role.sql
Unduh berkas-- Tiga cara membuat role: CREATE ROLE dengan atribut,
-- CREATE USER sebagai padanan LOGIN, dan role polos.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS magasier_tmj;
DROP ROLE IF EXISTS gudang_tmj;
DROP ROLE IF EXISTS kepala_gudang_tmj;
-- Role login dengan sandi (sandi latihan, bukan kredensial nyata).
CREATE ROLE kasir_tmj LOGIN PASSWORD 'sandi_latihan';
-- CREATE USER padanan penuh CREATE ROLE ... LOGIN.
CREATE USER magasier_tmj PASSWORD 'sandi_latihan';
-- Role polos: tanpa LOGIN, tanpa atribut, tanpa sandi.
CREATE ROLE gudang_tmj;
SELECT rolname, rolcanlogin
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'magasier_tmj', 'gudang_tmj')
ORDER BY rolname;
-- pg_authid menyimpan sandi dalam bentuk hash. Superuser saja yang
-- boleh membacanya; nilainya dipotong di sini agar hanya bagian
-- deterministik (nama skema hash dan jumlah iterasi) yang tampil.
SELECT rolname,
split_part(rolpassword, '$', 1) AS skema_hash,
split_part(split_part(rolpassword, '$', 2), ':', 1) AS iterasi
FROM pg_authid
WHERE rolname IN ('kasir_tmj', 'magasier_tmj')
ORDER BY rolname;
-- Parameter server yang menentukan algoritma hash saat sandi disimpan.
SHOW password_encryption;
reassign-owned.sql
Unduh berkas-- Urutan pembersihan yang benar untuk role pemilik objek:
-- REASSIGN OWNED memindahkan kepemilikan, DROP OWNED melepas sisa hak,
-- baru DROP ROLE dijalankan.
REASSIGN OWNED BY kepala_gudang_tmj TO postgres;
DROP OWNED BY kepala_gudang_tmj;
DROP ROLE kepala_gudang_tmj;
-- Tabel tetap hidup dengan data utuh di bawah pemilik barunya.
SELECT id, barang
FROM inventaris
ORDER BY id;
SELECT count(*) AS sisa_kepala_gudang
FROM pg_roles
WHERE rolname = 'kepala_gudang_tmj';
role-bergantung.sql
Unduh berkas-- DROP ROLE gagal bila role masih memiliki objek.
-- Tabel di bawah dipindahkan kepemilikannya ke kepala_gudang_tmj
-- terlebih dahulu, sehingga role itu punya tanggungan.
CREATE TABLE inventaris (
id integer PRIMARY KEY,
barang text NOT NULL
);
INSERT INTO inventaris (id, barang) VALUES
(1, 'kardus kopi'),
(2, 'karung gula');
ALTER TABLE inventaris OWNER TO kepala_gudang_tmj;
-- Membuat role kini ditolak: masih ada objek miliknya.
DROP ROLE kepala_gudang_tmj;
ubah-role.sql
Unduh berkas-- ALTER ROLE: mengganti nama, menambah atribut, mengganti sandi,
-- dan menetapkan setelan sesi bawaan per role.
ALTER ROLE gudang_tmj RENAME TO kepala_gudang_tmj;
ALTER ROLE kepala_gudang_tmj LOGIN CREATEDB PASSWORD 'sandi_baru_latihan';
-- Verifikasi atribut baru di catalog.
SELECT rolname, rolcanlogin, rolcreatedb
FROM pg_roles
WHERE rolname = 'kepala_gudang_tmj';
-- Sandi tercatat kembali sebagai hash SCRAM yang baru.
SELECT rolname,
split_part(rolpassword, '$', 1) AS skema_hash
FROM pg_authid
WHERE rolname = 'kepala_gudang_tmj';
-- Setelan sesi bawaan untuk role: berlaku otomatis saat role login.
ALTER ROLE kasir_tmj SET default_transaction_read_only = on;
-- Catalog pg_db_role_setting mencatat setelan tersebut.
SELECT r.rolname, s.setconfig
FROM pg_db_role_setting s
JOIN pg_roles r ON r.oid = s.setrole
WHERE r.rolname = 'kasir_tmj';
-- Menghapus kembali setelan bawaan per parameter.
ALTER ROLE kasir_tmj RESET default_transaction_read_only;
SELECT count(*) AS setelan_tersisa
FROM pg_db_role_setting s
JOIN pg_roles r ON r.oid = s.setrole
WHERE r.rolname = 'kasir_tmj';