Anggap saya memiliki daftar item:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Sekarang, saya ingin memperkenalkan konsep "izin" untuk setiap item (harap dicatat, saya tidak berbicara tentang izin akses database di sini, tetapi izin logika bisnis untuk item tersebut). Setiap item memiliki izin default dan juga izin per pengguna yang dapat mengesampingkan izin default.
Saya mencoba memikirkan beberapa cara untuk mengimplementasikan ini dan menemukan solusi berikut, tetapi saya tidak yakin yang mana yang terbaik dan mengapa:
1) Solusi Boolean
Gunakan kolom boolean untuk setiap izin:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Keuntungan : Setiap izin diberi nama.
Kekurangan : Ada puluhan izin yang meningkatkan jumlah kolom secara signifikan dan Anda harus mendefinisikannya dua kali (satu kali di setiap tabel).
2) Solusi Integer
Gunakan integer dan perlakukan sebagai bitfield (yaitu bit 0 untuk can_change_description
, bit 1 untuk can_change_price
, dan sebagainya, dan gunakan operasi bitwise untuk mengatur atau membaca izin).
CREATE DOMAIN permissions AS integer;
Keuntungan : sangat cepat.
Kekurangan : Anda harus melacak bit mana yang menunjukkan izin di database dan antarmuka front-end.
3) Solusi Bitfield
Sama seperti 2), tetapi gunakan bit(n)
. Kemungkinan besar kelebihan dan kekurangan yang sama, mungkin sedikit lebih lambat.
4) Solusi Enum
Gunakan tipe enum untuk izin:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
lalu buat tabel tambahan untuk izin default:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
dan ubah tabel definisi per pengguna ke:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Keuntungan : Mudah menyebutkan izin individu (Anda tidak harus menangani posisi bit).
Kekurangan : Bahkan ketika hanya mengambil izin default, itu memerlukan mengakses dua tabel tambahan: pertama, tabel izin default, dan kedua, katalog sistem menyimpan nilai enum.
Terutama karena izin default harus diambil untuk setiap tampilan halaman dari item itu , dampak kinerja dari alternatif terakhir mungkin signifikan.
5) Solusi Enum Array
Sama seperti 4), tetapi gunakan array untuk menampung semua izin (default):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Keuntungan : Mudah menyebutkan izin individu (Anda tidak harus menangani posisi bit).
Kekurangan : Mematahkan bentuk normal pertama dan agak jelek. Mengambil banyak byte dalam satu baris jika jumlah izinnya besar (sekitar 50).
Bisakah Anda memikirkan alternatif lain?
Pendekatan mana yang harus diambil dan mengapa?
Harap dicatat: ini adalah versi modifikasi dari pertanyaan yang diposting sebelumnya di Stackoverflow .
sumber
bigint
bidang (masing-masing bagus untuk 64 bit) atau sedikit string. Saya menulis beberapa jawaban terkait pada SO yang mungkin bisa membantu.Jawaban:
Saya tahu bahwa Anda tidak bertanya tentang keamanan basis data per se , tetapi Anda dapat melakukan apa yang Anda inginkan menggunakan keamanan basis data. Anda bahkan dapat menggunakan ini di aplikasi web. Jika Anda tidak ingin menggunakan keamanan basis data, maka skema tersebut masih berlaku.
Anda ingin keamanan tingkat kolom, keamanan tingkat baris, dan mungkin manajemen peran hierarkis. Keamanan berbasis peran jauh lebih mudah dikelola daripada keamanan berbasis pengguna.
Contoh kode ini adalah untuk PostgreSQL 9.4, yang segera keluar. Anda dapat melakukannya dengan 9.3, tetapi ada lebih banyak tenaga kerja manual yang dibutuhkan.
Anda ingin semuanya dapat diindeks jika Anda khawatir dengan kinerja †, yang seharusnya Anda lakukan. Ini berarti bidang bit-mask dan array mungkin bukan ide yang bagus.
Dalam contoh ini, kami menyimpan tabel data utama dalam
data
skema, dan tampilan yang sesuai dipublic
.Letakkan pemicu pada data. Sesuatu untuk sisipan dan pembaruan yang memaksa kolom pemilik adalah current_user. Mungkin hanya mengizinkan pemilik untuk menghapus catatannya sendiri (pemicu lain).
Buat
WITH CHECK OPTION
tampilan, yang sebenarnya akan digunakan pengguna. Berusaha sangat keras untuk membuatnya bisa diupdate, jika tidak Anda akan memerlukan pemicu / aturan, yang lebih banyak bekerja.Selanjutnya, buat tabel daftar kontrol-akses:
Ubah tampilan Anda ke akun untuk ACL:
Buat tabel hak istimewa baris default:
Letakkan pemicu pada insert pada data.thing sehingga menyalin hak baris default ke security.thing_acl.
grantor
danadmin_option
kolom ke tabel acl untuk melacak siapa yang memberikan hak istimewa, dan apakah penerima dapat mengelola hak istimewa pada baris itu.† Dalam hal ini pg_has_role mungkin tidak dapat diindeks. Anda harus mendapatkan daftar semua peran superior ke current_user dan membandingkannya dengan nilai pemilik / penerima.
sumber
Sudahkah Anda mempertimbangkan untuk menggunakan ekstensi PostgreSQL Daftar Kontrol Akses ?
Ini berisi ACE tipe data PostgreSQL asli dan serangkaian fungsi yang memungkinkan Anda untuk memeriksa apakah pengguna memiliki izin untuk mengakses data. Ini berfungsi baik dengan sistem peran PostgreSQL atau dengan angka abstrak (atau UUID) yang mewakili pengguna aplikasi / ID peran Anda.
Dalam kasus Anda, Anda cukup menambahkan kolom ACL ke tabel data Anda dan menggunakan salah satu
acl_check_access
fungsi untuk memeriksa pengguna terhadap ACL.Menggunakan ACL adalah cara yang sangat fleksibel untuk berurusan dengan izin logika bisnis. Selain itu, ini sangat cepat - biaya overhead rata-rata hanya 25% dari waktu yang diperlukan untuk membaca catatan. Satu-satunya batasan adalah bahwa ia mendukung maksimum 16 izin khusus per jenis objek.
sumber
Saya bisa memikirkan kemungkinan lain untuk menyandikan ini, yang relasional
Jika Anda tidak membutuhkan
permission_per_item
tabel, Anda dapat melewati dan menghubungkanPermissions
danItems
langsung keitem_per_user_permissions
tabel.diagram legenda
sumber