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:
$acl->commandAllowedForUser($command, $user);
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:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
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:
User -> Browser -> Request (HTTP)
-> Request (Command)
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
bukan CommandNotFoundResponse
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 mengetahui User
, maka Router
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:
User -> Browser -> Request (HTTP)
-> Request (Command)
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.
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.