Pertanyaan pertama
Tolong, bisakah Anda menjelaskan kepada saya bagaimana ACL paling sederhana dapat diterapkan di MVC.
Berikut adalah pendekatan pertama menggunakan Acl di Controller ...
<?php
class MyController extends Controller {
public function myMethod() {
//It is just abstract code
$acl = new Acl();
$acl->setController('MyController');
$acl->setMethod('myMethod');
$acl->getRole();
if (!$acl->allowed()) die("You're not allowed to do it!");
...
}
}
?>
Ini adalah pendekatan yang sangat buruk, dan minusnya adalah kita harus menambahkan potongan kode Acl ke dalam setiap metode pengontrol, tetapi kita tidak memerlukan dependensi tambahan!
Pendekatan selanjutnya adalah membuat semua metode pengontrol private
dan menambahkan kode ACL ke dalam __call
metode pengontrol .
<?php
class MyController extends Controller {
private function myMethod() {
...
}
public function __call($name, $params) {
//It is just abstract code
$acl = new Acl();
$acl->setController(__CLASS__);
$acl->setMethod($name);
$acl->getRole();
if (!$acl->allowed()) die("You're not allowed to do it!");
...
}
}
?>
Ini lebih baik dari kode sebelumnya, tetapi minus utama adalah ...
- Semua metode pengontrol harus bersifat pribadi
- Kita harus menambahkan kode ACL ke dalam metode __call masing-masing pengontrol.
Pendekatan selanjutnya adalah menempatkan kode Acl ke dalam Pengontrol induk, tetapi kita masih perlu merahasiakan semua metode pengontrol anak.
Apa solusinya? Dan apa praktik terbaiknya? Di mana saya harus memanggil fungsi Acl untuk memutuskan mengizinkan atau melarang metode yang akan dijalankan.
Pertanyaan kedua
Pertanyaan kedua adalah tentang mendapatkan peran menggunakan Acl. Bayangkan kita memiliki tamu, pengguna, dan teman pengguna. Pengguna memiliki akses terbatas untuk melihat profilnya yang hanya dapat dilihat oleh teman. Semua tamu tidak dapat melihat profil pengguna ini. Jadi, inilah logikanya ..
- kita harus memastikan bahwa metode yang dipanggil adalah profil
- kami harus mendeteksi pemilik profil ini
- kita harus mendeteksi apakah pemirsa adalah pemilik profil ini atau bukan
- kita harus membaca aturan pembatasan tentang profil ini
- kita harus memutuskan untuk mengeksekusi atau tidak menjalankan metode profil
Pertanyaan utamanya adalah tentang mendeteksi pemilik profil. Kita dapat mendeteksi siapa pemilik profil yang hanya menjalankan metode model $ model-> getOwner (), tetapi Acl tidak memiliki akses ke model. Bagaimana kita bisa menerapkan ini?
Saya berharap pikiran saya jernih. Maaf untuk bahasa Inggris saya.
Terima kasih.
sumber
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(jika tidak, tampilkan "Anda tidak memiliki akses ke profil pengguna ini" atau sesuatu seperti itu? Saya tidak mengerti.Jawaban:
Bagian / jawaban pertama (implementasi ACL)
Menurut pendapat saya, cara terbaik untuk mendekati ini adalah dengan menggunakan pola dekorator . Pada dasarnya, ini berarti Anda mengambil objek Anda, dan meletakkannya di dalam objek lain, yang akan bertindak seperti cangkang pelindung. Ini TIDAK mengharuskan Anda untuk memperluas kelas asli. Berikut ini contohnya:
Dan inilah cara Anda menggunakan struktur semacam ini:
Seperti yang mungkin Anda perhatikan, solusi ini memiliki beberapa keunggulan:
Controller
Namun , ada satu masalah besar dengan metode ini juga - Anda tidak dapat memeriksa secara asli apakah objek yang diamankan mengimplementasikan dan antarmuka (yang juga berlaku untuk mencari metode yang ada) atau merupakan bagian dari beberapa rantai warisan.
Bagian / jawaban kedua (RBAC untuk objek)
Dalam hal ini, perbedaan utama yang harus Anda kenali adalah bahwa Objek Domain Anda (misalnya
Profile
:) itu sendiri berisi detail tentang pemiliknya. Artinya, bagi Anda untuk memeriksa, jika (dan pada level mana) pengguna memiliki akses ke sana, Anda akan diminta untuk mengubah baris ini:Pada dasarnya Anda memiliki dua opsi:
Berikan ACL objek yang dimaksud. Tetapi Anda harus berhati-hati untuk tidak melanggar Law of Demeter :
Minta semua detail yang relevan dan berikan ACL hanya dengan apa yang dibutuhkannya, yang juga akan membuatnya sedikit lebih ramah untuk pengujian unit:
Beberapa video yang mungkin membantu Anda membuat penerapan sendiri:
Catatan sampingan
Anda tampaknya memiliki pemahaman yang cukup umum (dan sepenuhnya salah) tentang apa Model di MVC itu. Model bukanlah kelas . Jika Anda memiliki nama kelas
FooBarModel
atau sesuatu yang mewarisiAbstractModel
maka Anda salah melakukannya.Dalam MVC yang tepat, Model adalah lapisan, yang berisi banyak kelas. Sebagian besar kelas dapat dipisahkan dalam dua kelompok, berdasarkan tanggung jawab:
- Logika Bisnis Domain
( baca lebih lanjut : di sini dan di sini ):
Contoh dari grup kelas ini menangani penghitungan nilai, memeriksa kondisi yang berbeda, menerapkan aturan penjualan, dan melakukan semua yang Anda sebut "logika bisnis". Mereka tidak memiliki petunjuk bagaimana data disimpan, di mana disimpan atau bahkan jika penyimpanan itu ada di tempat pertama.
Objek Bisnis Domain tidak bergantung pada database. Saat Anda membuat faktur, tidak masalah dari mana data berasal. Ini bisa dari SQL atau dari REST API jarak jauh, atau bahkan tangkapan layar dari dokumen MSWord. Logika bisnis tidak berubah.
- Akses dan Penyimpanan Data
Contoh yang dibuat dari grup kelas ini terkadang disebut Objek Akses Data. Biasanya struktur yang mengimplementasikan pola Data Mapper (jangan bingung dengan ORM dengan nama yang sama .. tidak ada hubungan). Di sinilah pernyataan SQL Anda (atau mungkin DomDocument Anda, karena Anda menyimpannya dalam XML).
Di samping dua bagian utama, ada satu lagi kelompok instance / kelas, yang harus disebutkan:
- Layanan
Di sinilah komponen Anda dan pihak ke-3 berperan. Misalnya, Anda dapat menganggap "otentikasi" sebagai layanan, yang dapat disediakan oleh Anda sendiri, atau beberapa kode eksternal. Juga "pengirim email" akan menjadi layanan, yang mungkin menggabungkan beberapa objek domain dengan PHPMailer atau SwiftMailer, atau komponen pengirim email Anda sendiri.
Sumber layanan lain adalah abstraksi pada domain dan lapisan akses data. Mereka dibuat untuk menyederhanakan kode yang digunakan oleh pengontrol. Misalnya: membuat akun pengguna baru mungkin perlu bekerja dengan beberapa objek domain dan pembuat peta . Tapi, dengan menggunakan layanan, itu hanya membutuhkan satu atau dua baris di pengontrol.
Apa yang harus Anda ingat saat membuat layanan, adalah bahwa seluruh lapisan seharusnya tipis . Tidak ada logika bisnis dalam layanan. Mereka hanya ada di sana untuk mengatur objek domain, komponen, dan pembuat peta.
Salah satu kesamaan dari mereka semua adalah bahwa layanan tidak memengaruhi lapisan View dengan cara apa pun secara langsung, dan bersifat otonom sedemikian rupa, sehingga mereka dapat (dan sering berhenti) digunakan di luar struktur MVC itu sendiri. Selain itu, struktur mandiri seperti itu membuat migrasi ke kerangka kerja / arsitektur yang berbeda jauh lebih mudah, karena kopling yang sangat rendah antara layanan dan aplikasi lainnya.
sumber
Request
instance (atau analoginya). Pengontrol hanya mengekstrak data dariRequest
instance dan meneruskan sebagian besar ke layanan yang sesuai (beberapa di antaranya juga akan ditampilkan). Layanan melakukan operasi yang Anda perintahkan. Kemudian, saat tampilan menghasilkan respons, itu meminta data dari layanan, dan berdasarkan informasi itu, menghasilkan respons. Respons tersebut dapat berupa HTML yang dibuat dari beberapa template atau hanya header lokasi HTTP. Tergantung pada status yang diatur oleh pengontrol.ACL dan Pengontrol
Pertama-tama: Ini adalah hal / lapisan yang paling sering berbeda. Saat Anda mengkritik kode pengontrol yang patut dicontoh, kode ini menggabungkan keduanya - jelas sekali terlalu ketat.
tereško telah menjelaskan cara bagaimana Anda dapat lebih memisahkan ini dengan pola dekorator.
Saya akan mundur selangkah terlebih dahulu untuk mencari masalah asli yang Anda hadapi dan membahasnya sebentar lagi.
Di satu sisi Anda ingin memiliki pengontrol yang hanya melakukan pekerjaan yang diperintahkan (perintah atau tindakan, sebut saja perintah).
Di sisi lain, Anda ingin dapat menempatkan ACL di aplikasi Anda. Bidang kerja ACL ini harus - jika saya memahami pertanyaan Anda dengan benar - untuk mengontrol akses ke perintah tertentu dari aplikasi Anda.
Kontrol akses semacam ini oleh karena itu membutuhkan sesuatu yang lain yang menyatukan keduanya. Berdasarkan konteks di mana sebuah perintah dieksekusi, ACL masuk dan keputusan perlu dilakukan apakah perintah tertentu dapat dijalankan oleh subjek tertentu (misalnya pengguna).
Mari kita rangkum sampai titik ini apa yang kita miliki:
Komponen ACL sangat penting di sini: Ia perlu mengetahui setidaknya sesuatu tentang perintah (untuk mengidentifikasi perintah dengan tepat) dan harus dapat mengidentifikasi pengguna. Pengguna biasanya mudah diidentifikasi dengan ID unik. Namun seringkali dalam aplikasi web ada pengguna yang tidak teridentifikasi sama sekali, sering disebut tamu, anonim, semua orang, dll. Untuk contoh ini kami berasumsi bahwa ACL dapat menggunakan objek pengguna dan merangkum detail ini. Objek pengguna terikat ke objek permintaan aplikasi dan ACL dapat mengkonsumsinya.
Bagaimana dengan mengidentifikasi perintah? Interpretasi Anda tentang pola MVC menunjukkan bahwa sebuah perintah adalah gabungan dari nama kelas dan nama metode. Jika kita melihat lebih dekat bahkan ada argumen (parameter) untuk sebuah perintah. Jadi valid untuk menanyakan apa sebenarnya yang mengidentifikasi sebuah perintah? Nama kelas, nama metode, jumlah atau nama argumen, bahkan data di dalam salah satu argumen atau campuran dari semua ini?
Bergantung pada tingkat detail yang Anda perlukan untuk mengidentifikasi perintah di ACL Anda, ini bisa sangat bervariasi. Sebagai contoh, mari kita buat sederhana dan tentukan bahwa perintah diidentifikasi oleh nama kelas dan nama metode.
Jadi konteks bagaimana ketiga bagian ini (ACL, Command dan User) menjadi milik satu sama lain sekarang lebih jelas.
Bisa dibilang, dengan komponen ACL imajiner kita sudah bisa melakukan hal berikut:
Lihat saja apa yang terjadi di sini: Dengan membuat perintah dan pengguna dapat diidentifikasi, ACL dapat melakukan tugasnya. Pekerjaan ACL tidak terkait dengan pekerjaan objek pengguna dan perintah konkret.
Hanya ada satu bagian yang hilang, ini tidak bisa hidup di udara. Dan ternyata tidak. Jadi, Anda perlu menemukan tempat di mana kontrol akses perlu dijalankan. Mari kita lihat apa yang terjadi di aplikasi web standar:
Untuk menemukan tempat itu, kita tahu itu harus sebelum perintah konkret dijalankan, jadi kita bisa mengurangi daftar itu dan hanya perlu melihat ke tempat (potensial) berikut:
Di beberapa titik dalam aplikasi Anda, Anda tahu bahwa pengguna tertentu telah meminta untuk melakukan perintah konkret. Anda sudah melakukan semacam ACL di sini: Jika pengguna meminta perintah yang tidak ada, Anda tidak mengizinkan perintah tersebut untuk dijalankan. Jadi, di mana pun yang terjadi dalam aplikasi Anda mungkin merupakan tempat yang baik untuk menambahkan pemeriksaan ACL "nyata":
Perintah telah ditemukan dan kita dapat membuat identifikasi sehingga ACL dapat mengatasinya. Jika perintah tidak diizinkan untuk pengguna, perintah tersebut tidak akan dijalankan (tindakan). Mungkin
CommandNotAllowedResponse
bukanCommandNotFoundResponse
untuk kasus permintaan tidak dapat diselesaikan ke perintah konkret.Tempat di mana pemetaan HTTPRequest beton dipetakan ke sebuah perintah sering disebut Routing . Sebagai Perutean sudah memiliki tugas untuk menemukan perintah, mengapa tidak memperluasnya untuk memeriksa apakah perintah tersebut benar-benar diizinkan per ACL? Misalnya dengan memperluas
Router
ke router menyadari ACL:RouterACL
. Jika router Anda belum mengetahuiUser
, makaRouter
itu bukan tempat yang tepat, karena agar ACL bekerja tidak hanya perintah tetapi juga pengguna harus diidentifikasi. Jadi tempat ini dapat bervariasi, tetapi saya yakin Anda dapat dengan mudah menemukan tempat yang perlu diperluas, karena ini adalah tempat yang memenuhi persyaratan pengguna dan perintah:Pengguna tersedia sejak awal, Perintah dulu dengan
Request(Command)
.Jadi, alih-alih menempatkan pemeriksaan ACL Anda di dalam implementasi konkret setiap perintah, Anda menempatkannya sebelumnya. Anda tidak memerlukan pola yang berat, sihir atau apapun, ACL melakukan tugasnya, pengguna melakukan tugasnya dan terutama perintah yang melakukan tugasnya: Hanya perintah, tidak ada yang lain. Perintah tersebut tidak memiliki kepentingan untuk mengetahui apakah peran tersebut berlaku atau tidak, apakah itu dijaga atau tidak.
Jadi pisahkan saja hal-hal yang bukan milik satu sama lain. Gunakan sedikit penulisan ulang Prinsip Tanggung Jawab Tunggal (SRP) : Seharusnya hanya ada satu alasan untuk mengubah perintah - karena perintah telah berubah. Bukan karena Anda sekarang memperkenalkan ACL dalam aplikasi Anda. Bukan karena Anda mengalihkan objek Pengguna. Bukan karena Anda bermigrasi dari antarmuka HTTP / HTML ke SOAP atau antarmuka baris perintah.
ACL dalam kasus Anda mengontrol akses ke perintah, bukan perintah itu sendiri.
sumber
Salah satu kemungkinannya adalah menggabungkan semua pengontrol Anda di kelas lain yang memperluas Kontroler dan membuatnya mendelegasikan semua panggilan fungsi ke instance yang dibungkus setelah memeriksa otorisasi.
Anda juga dapat melakukannya lebih upstream, di petugas operator (jika aplikasi Anda memang memilikinya) dan mencari izin berdasarkan URL, bukan metode kontrol.
edit : Apakah Anda perlu mengakses database, server LDAP, dll. adalah ortogonal untuk pertanyaan tersebut. Maksud saya adalah bahwa Anda dapat menerapkan otorisasi berdasarkan URL, bukan metode pengontrol. Ini lebih kuat karena Anda biasanya tidak akan mengubah URL Anda (jenis area URL antarmuka publik), tetapi Anda mungkin juga mengubah implementasi pengontrol Anda.
Biasanya, Anda memiliki satu atau beberapa file konfigurasi tempat Anda memetakan pola URL tertentu ke metode otentikasi dan arahan otorisasi tertentu. Petugas operator, sebelum mengirimkan permintaan ke pengontrol, menentukan apakah pengguna berwenang dan membatalkan pengiriman jika tidak.
sumber