Role yang sudah dibuat belum bisa berbuat apa-apa terhadap objek database. Di PostgreSQL, setiap operasi — membaca tabel, memanggil function, mengambil nomor dari sequence — harus didukung privilege yang relevan pada objek itu, dan privilege tidak pernah diberikan secara otomatis kecuali beberapa bawaan yang sengaja dipilih desainer sistem. Chapter ini memetakan seluruh taksonomi privilege sebelum kita menyentuh mekanisme pemberiannya: privilege apa yang berlaku untuk tabel, schema, database, sequence, dan function; hak bawaan apa yang diterima role baru tanpa satu pun GRANT; serta bagaimana memeriksa siapa memiliki apa dengan \dp dan catalog. Pemahaman peta ini membuat perintah GRANT dan REVOKE di chapter berikutnya terasa seperti mengisi formulir yang sudah dikenal.

Taksonomi Privilege Per Jenis Objek #

Privilege tidak berlaku seragam untuk semua objek; setiap jenis objek mendefinisikan haknya sendiri. Tabel berikut merangkum yang paling sering dipakai sehari-hari:

ObjekPrivilege yang berlaku
Tabel dan viewSELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
Kolom (tabel)SELECT, INSERT, UPDATE, REFERENCES — versi kolom dari hak tabel
SequenceUSAGE, SELECT, UPDATE
SchemaUSAGE, CREATE
DatabaseCREATE, CONNECT, TEMP
Function/procedureEXECUTE
Type/domainUSAGE

Hak tabel seperti SELECT dan INSERT sudah akrab; REFERENCES diperlukan untuk membuat foreign key yang menunjuk tabel tersebut, TRIGGER untuk memasang trigger, dan TRUNCATE untuk mengosongkan tabel sekaligus. Pada sequence, USAGE mengizinkan nextval() dan currval(), SELECT mengizinkan membaca nilai terakhir, dan UPDATE mengizinkan setval(). Dua level pembungkus — CONNECT pada database serta USAGE pada schema — sering dilupakan pemula padahal menjadi gerbang ganda yang harus dilalui sebelum hak tabel bisa dipakai; schema privileges kita bahas tuntas di chapter khususnya.

Pemilik Objek dan Logika “Hak Tak Terbatas” #

Siapa yang membuat objek, dialah pemiliknya — dan pemilik tidak perlu GRANT apa pun untuk memakai objeknya sendiri. File pembuka menyiapkan bahan latihan lengkap (tabel, sequence, function) yang seluruhnya dibuat oleh postgres, lalu meminta daftar hak dari catalog:

-- Bahan latihan chapter Privileges: tabel, sequence, dan function,
-- semuanya dibuat oleh postgres sehingga berstatus pemilik.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS auditor_tmj;

CREATE ROLE kasir_tmj LOGIN;
CREATE ROLE auditor_tmj LOGIN;

CREATE TABLE penjualan (
    id     integer PRIMARY KEY,
    produk text NOT NULL,
    jumlah integer NOT NULL,
    harga  numeric(10,2) NOT NULL
);

INSERT INTO penjualan (id, produk, jumlah, harga) VALUES
    (1, 'Kopi Gayo',  10, 85000.00),
    (2, 'Teh Melati', 25, 35000.00),
    (3, 'Gula Merah', 40, 15000.00);

CREATE SEQUENCE no_nota START 100;

CREATE FUNCTION hitung_baris() RETURNS bigint
LANGUAGE sql
RETURN (SELECT count(*) FROM penjualan);

-- Pemilik objek menikmati seluruh privilege tanpa satu pun GRANT.
-- Catalog information_schema mencatat semuanya atas nama postgres.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'penjualan'
ORDER BY grantee, privilege_type;

Output yang Dihasilkan:

text
DROP ROLE
DROP ROLE
CREATE ROLE
CREATE ROLE
CREATE TABLE
INSERT 0 3
CREATE SEQUENCE
CREATE FUNCTION
 grantee  | privilege_type 
----------+----------------
 postgres | DELETE
 postgres | INSERT
 postgres | REFERENCES
 postgres | SELECT
 postgres | TRIGGER
 postgres | TRUNCATE
 postgres | UPDATE
(7 rows)

Query information_schema.role_table_grants menampilkan tujuh privilege tabel atas nama postgres padahal tidak ada satu baris GRANT pun di file ini. Itulah makna kepemilikan: hak penuh tercatat seolah-olah diberikan, tanpa bisa dicabut dengan REVOKE biasa. Kepemilikan berpindah lewat ALTER TABLE ... OWNER TO — perintah yang kita pakai di chapter Membuat dan Menghapus Role saat menyiapkan role dengan tanggungan — dan satu-satunya peran lain yang setara adalah superuser yang melewati semua pemeriksaan.

Hak Bawaan yang Diterima Tanpa GRANT #

Desain PostgreSQL memilih beberapa hak diberikan sejak awal kepada pseudo-role PUBLIC — kumpulan semua role yang ada. File berikut memeriksa semuanya dari sudut pandang kasir_tmj yang belum menerima GRANT apa pun:

sql hak-bawaan.sql
-- Hak bawaan yang role baru terima tanpa GRANT apa pun,
-- diperiksa dari tiga level objek: database, schema, dan objek di dalamnya.
-- Level database: CONNECT dan TEMP diberikan ke PUBLIC secara bawaan.
SELECT has_database_privilege('kasir_tmj', current_database(), 'CONNECT') AS bisa_koneksi,
       has_database_privilege('kasir_tmj', current_database(), 'TEMP')    AS bisa_tabel_sementara,
       has_database_privilege('kasir_tmj', current_database(), 'CREATE')  AS bisa_buat_skema;

-- Level schema: sejak PostgreSQL 15 PUBLIC hanya punya USAGE pada schema
-- public; hak CREATE sudah dicabut. Catalog menunjukkan ACL aslinya.
SELECT nspname, nspacl
FROM pg_namespace
WHERE nspname = 'public';

SELECT has_schema_privilege('kasir_tmj', 'public', 'USAGE')  AS bisa_akses_schema,
       has_schema_privilege('kasir_tmj', 'public', 'CREATE') AS bisa_buat_tabel;

-- Level objek: EXECUTE pada function diberikan ke PUBLIC secara bawaan,
-- sedangkan sequence tidak mendapat hak bawaan apa pun.
SELECT has_function_privilege('kasir_tmj', 'hitung_baris()', 'EXECUTE') AS bisa_panggil_fungsi;

SELECT has_sequence_privilege('kasir_tmj', 'no_nota', 'USAGE')  AS seq_usage,
       has_sequence_privilege('kasir_tmj', 'no_nota', 'SELECT') AS seq_select,
       has_sequence_privilege('kasir_tmj', 'no_nota', 'UPDATE') AS seq_update;

Output yang Dihasilkan:

text
 bisa_koneksi | bisa_tabel_sementara | bisa_buat_skema 
--------------+----------------------+-----------------
 t            | t                    | f
(1 row)

 nspname |                            nspacl                             
---------+---------------------------------------------------------------
 public  | {pg_database_owner=UC/pg_database_owner,=U/pg_database_owner}
(1 row)

 bisa_akses_schema | bisa_buat_tabel 
-------------------+-----------------
 t                 | f
(1 row)

 bisa_panggil_fungsi 
---------------------
 t
(1 row)

 seq_usage | seq_select | seq_update 
-----------+------------+------------
 f         | f          | f
(1 row)

Tiga kelompok fakta terbaca dari hasilnya. Pertama, pada level database kasir_tmj boleh CONNECT dan membuat tabel sementara (TEMP) — keduanya hak bawaan PUBLIC — tetapi tidak boleh CREATE skema di database milik postgres. Kedua, pada schema public role itu hanya memegang USAGE; baris nspacl memperlihatkan mengapa: pg_database_owner memegang UC (USAGE + CREATE) sedangkan PUBLIC hanya U. Ketiga, EXECUTE pada function juga hak bawaan PUBLIC, sementara sequence no_nota tidak memberikan apa-apa — f pada tiga kolom terakhir.

Memeriksa Privilege dengan \dp dan Catalog #

Dua alat pemeriksa saling melengkapi. Meta-command \dp di psql memberi ringkasan visual satu objek dalam satu tampilan — termasuk versi kolomnya nanti — sedangkan catalog information_schema dan fungsi keluarga has_*_privilege bisa ditanyai dengan query biasa dan difilter sesuka hati. File berikut memberi privilege pertama kepada auditor_tmj, lalu memeriksa hasilnya dengan kedua alat:

sql cek-hak.sql
-- Memberi privilege pertama sebagai pembuka jalan chapter GRANT dan REVOKE,
-- lalu memeriksa hasilnya dengan dua alat: meta-command \dp dan catalog.
GRANT SELECT ON penjualan TO auditor_tmj;

GRANT USAGE, SELECT ON SEQUENCE no_nota TO auditor_tmj;

-- \dp meringkas seluruh privilege pada satu objek dalam satu tampilan.
\dp penjualan

-- Catalog information_schema dapat ditanyai dengan query biasa
-- dan hanya menampilkan grant yang benar-benar diberikan.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'penjualan'
ORDER BY grantee, privilege_type;

-- Verifikasi sequence lewat fungsi pemeriksa privilege.
SELECT has_sequence_privilege('auditor_tmj', 'no_nota', 'USAGE')  AS seq_usage,
       has_sequence_privilege('auditor_tmj', 'no_nota', 'SELECT') AS seq_select;

Output yang Dihasilkan:

text
GRANT
GRANT
                                   Access privileges
 Schema |   Name    | Type  |     Access privileges      | Column privileges | Policies 
--------+-----------+-------+----------------------------+-------------------+----------
 public | penjualan | table | postgres=arwdDxtm/postgres+|                   | 
        |           |       | auditor_tmj=r/postgres     |                   | 
(1 row)

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

 seq_usage | seq_select 
-----------+------------
 t         | t
(1 row)

Mari kita baca kedua hasilnya. Kolom Access privileges dari \dp menampilkan dua entri: postgres=arwdDxtm/postgres untuk pemilik dan auditor_tmj=r/postgres untuk grantee. Huruf-huruf itu kode ringkas privilege — r SELECT, w UPDATE, a INSERT, d DELETE, D TRUNCATE, x REFERENCES, t TRIGGER, m MAINTAIN (fitur baru PostgreSQL 17 untuk VACUUM dan maintenance) — dan nama setelah garis miring adalah grantor. Sementara itu, query information_schema.role_table_grants mengembalikan bentuk panjang yang lebih mudah diproses programatik: delapan baris, tujuh milik pemilik dan satu SELECT milik auditor_tmj. Pemeriksaan sequence menutup file dengan konfirmasi bahwa USAGE dan SELECT kini dimiliki auditor.

Fungsi keluarga has_*_privilege pada file sebelumnya sebenarnya alat ketiga yang paling praktis untuk skrip pengecekan: has_table_privilege, has_schema_privilege, has_database_privilege, has_function_privilege, dan has_sequence_privilege menerima nama role dan objek lalu menjawab t/f — pasangan sempurna untuk pengujian otomatis konfigurasi hak akses.

Membersihkan Role Latihan #

Pola penutup yang sama berlaku untuk setiap chapter di section ini — DROP OWNED BY melepas seluruh privilege yang diterima role di database berjalan, lalu DROP ROLE menghapusnya dari cluster:

sql bersihkan.sql
-- Membersihkan role latihan chapter ini.
DROP OWNED BY kasir_tmj;
DROP OWNED BY auditor_tmj;

DROP ROLE kasir_tmj;
DROP ROLE auditor_tmj;

SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'auditor_tmj');

Output yang Dihasilkan:

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

Peta privilege kini tergambar lengkap: jenis objek dengan haknya masing-masing, posisi istimewa pemilik, hak bawaan PUBLIC, dan cara memeriksanya. Yang belum kita bahas adalah kata kerjanya — bagaimana hak itu dibagikan dengan GRANT, dibatasi sampai tingkat kolom, diwariskan dengan GRANT OPTION, lalu dicabut kembali dengan REVOKE beserta efek kaskadenya. Semuanya menunggu di chapter GRANT dan REVOKE .

Referensi #

Berkas Sumber

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

bersihkan.sql

Unduh berkas
sql
-- Membersihkan role latihan chapter ini.
DROP OWNED BY kasir_tmj;
DROP OWNED BY auditor_tmj;

DROP ROLE kasir_tmj;
DROP ROLE auditor_tmj;

SELECT count(*) AS sisa_role_latihan
FROM pg_roles
WHERE rolname IN ('kasir_tmj', 'auditor_tmj');

cek-hak.sql

Unduh berkas
sql
-- Memberi privilege pertama sebagai pembuka jalan chapter GRANT dan REVOKE,
-- lalu memeriksa hasilnya dengan dua alat: meta-command \dp dan catalog.
GRANT SELECT ON penjualan TO auditor_tmj;

GRANT USAGE, SELECT ON SEQUENCE no_nota TO auditor_tmj;

-- \dp meringkas seluruh privilege pada satu objek dalam satu tampilan.
\dp penjualan

-- Catalog information_schema dapat ditanyai dengan query biasa
-- dan hanya menampilkan grant yang benar-benar diberikan.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'penjualan'
ORDER BY grantee, privilege_type;

-- Verifikasi sequence lewat fungsi pemeriksa privilege.
SELECT has_sequence_privilege('auditor_tmj', 'no_nota', 'USAGE')  AS seq_usage,
       has_sequence_privilege('auditor_tmj', 'no_nota', 'SELECT') AS seq_select;

hak-bawaan.sql

Unduh berkas
sql
-- Hak bawaan yang role baru terima tanpa GRANT apa pun,
-- diperiksa dari tiga level objek: database, schema, dan objek di dalamnya.
-- Level database: CONNECT dan TEMP diberikan ke PUBLIC secara bawaan.
SELECT has_database_privilege('kasir_tmj', current_database(), 'CONNECT') AS bisa_koneksi,
       has_database_privilege('kasir_tmj', current_database(), 'TEMP')    AS bisa_tabel_sementara,
       has_database_privilege('kasir_tmj', current_database(), 'CREATE')  AS bisa_buat_skema;

-- Level schema: sejak PostgreSQL 15 PUBLIC hanya punya USAGE pada schema
-- public; hak CREATE sudah dicabut. Catalog menunjukkan ACL aslinya.
SELECT nspname, nspacl
FROM pg_namespace
WHERE nspname = 'public';

SELECT has_schema_privilege('kasir_tmj', 'public', 'USAGE')  AS bisa_akses_schema,
       has_schema_privilege('kasir_tmj', 'public', 'CREATE') AS bisa_buat_tabel;

-- Level objek: EXECUTE pada function diberikan ke PUBLIC secara bawaan,
-- sedangkan sequence tidak mendapat hak bawaan apa pun.
SELECT has_function_privilege('kasir_tmj', 'hitung_baris()', 'EXECUTE') AS bisa_panggil_fungsi;

SELECT has_sequence_privilege('kasir_tmj', 'no_nota', 'USAGE')  AS seq_usage,
       has_sequence_privilege('kasir_tmj', 'no_nota', 'SELECT') AS seq_select,
       has_sequence_privilege('kasir_tmj', 'no_nota', 'UPDATE') AS seq_update;

siapkan-privilege.sql

Unduh berkas
sql
-- Bahan latihan chapter Privileges: tabel, sequence, dan function,
-- semuanya dibuat oleh postgres sehingga berstatus pemilik.
DROP ROLE IF EXISTS kasir_tmj;
DROP ROLE IF EXISTS auditor_tmj;

CREATE ROLE kasir_tmj LOGIN;
CREATE ROLE auditor_tmj LOGIN;

CREATE TABLE penjualan (
    id     integer PRIMARY KEY,
    produk text NOT NULL,
    jumlah integer NOT NULL,
    harga  numeric(10,2) NOT NULL
);

INSERT INTO penjualan (id, produk, jumlah, harga) VALUES
    (1, 'Kopi Gayo',  10, 85000.00),
    (2, 'Teh Melati', 25, 35000.00),
    (3, 'Gula Merah', 40, 15000.00);

CREATE SEQUENCE no_nota START 100;

CREATE FUNCTION hitung_baris() RETURNS bigint
LANGUAGE sql
RETURN (SELECT count(*) FROM penjualan);

-- Pemilik objek menikmati seluruh privilege tanpa satu pun GRANT.
-- Catalog information_schema mencatat semuanya atas nama postgres.
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'penjualan'
ORDER BY grantee, privilege_type;