<

Laravel'de Yetkilendirme Katmanı: Policy ve Gate ile Dağılmayan Yetki Kontrolü

Laravel projelerinde yetki kontrolü genellikle masum başlar: controller'ın başına bir if ($user->role === 'admin') yazılır. Üç ay sonra aynı koşul on beş yerde, her biri biraz farklı hâlde durur ve kimse hangisinin doğru olduğundan emin değildir. Devraldığımız projelerde en sık gördüğümüz teknik borçlardan biri bu.

Çözüm yeni bir paket değil, framework'ün zaten sunduğu iki parçayı disiplinle kullanmak: Gate ve Policy.

Gate ne zaman, Policy ne zaman

Gate, bir model ile ilgisi olmayan yetenek kontrolleri için uygundur: "raporları dışa aktarabilir mi", "ayar sayfasını görebilir mi" gibi. Gate::define() ile tanımlanır, Gate::allows() ile sorgulanır. Policy ise tek bir model etrafında toplanan kuralları barındırır: bir siparişi kim görebilir, kim güncelleyebilir, kim silebilir.

Pratik kuralımız şu: ortada bir Eloquent modeli varsa Policy, yoksa Gate. Model bazlı kuralları Gate içine yığmak, AuthServiceProvider dosyasını kısa sürede okunmaz hâle getiriyor.

Policy'yi doğru kurmak

php artisan make:policy OrderPolicy --model=Order komutu viewAny, view, create, update ve delete metotlarını hazır getirir. Laravel, model ile policy eşleşmesini isim kuralıyla otomatik yapar; elle kayıt yalnızca isimlendirme dışına çıktığınızda gerekir.

Metot içinde kural, kullanıcı ile modelin ilişkisine bakmalı, rol adına değil. Örneğin return $user->id === $order->user_id || $user->can('orders.manage'); gibi. Bu sayede "sipariş sahibi" ve "yönetici" aynı yerde, okunur biçimde tanımlanır.

Policy kullanımını tamamlayan üç küçük ayrıntı var:

  • Süper yönetici istisnasını her metoda yazmak yerine Gate::before() içinde tek yerde verin.
  • Misafir kullanıcılar için metot imzasında ?User $user kullanın; aksi hâlde giriş yapmamış istek policy'ye hiç ulaşmadan reddedilir.
  • Policy metotları yalnızca true/false döndürsün; yan etki, sorgu veya log yazmasın.

Kontrolü nerede çağırmalı

Controller içinde $this->authorize('update', $order) en açık yoldur ve başarısız olduğunda otomatik 403 üretir. Resource controller kullanıyorsanız authorizeResource() ile tüm standart metotları tek satırda bağlayabilirsiniz. Route seviyesinde ise ->can('update', 'order') middleware'i işe yarar.

Blade tarafında @can yönergesi yalnızca butonu gizlemek içindir. Bir butonu gizlemek yetki kontrolü değildir; asıl kontrol her zaman sunucu tarafındaki istek yolunda olmalı. Arayüzde görünmeyen ama doğrudan URL ile erişilebilen bir uç nokta, saha incelemelerinde en çok bulduğumuz açık.

Çok kiracılı yapılarda dikkat edilecekler

Birden fazla firmanın aynı panelde çalıştığı sistemlerde policy tek başına yetmez. Route model binding ile gelen kaydın o firmaya ait olduğunu da doğrulamanız gerekir. Biz bunu genellikle global scope ile sorguyu kiracıya kilitleyerek, policy içinde de $user->tenant_id === $model->tenant_id kontrolünü ikinci savunma hattı olarak tutarak çözüyoruz. Tek bir katmana güvenmek, bir gün unutulan bir scope yüzünden başka firmanın verisini göstermeye dönüşebiliyor.

Yetki kurallarını test etmek

Policy'nin en büyük avantajı test edilebilir olması. Bir OrderPolicyTest içinde kullanıcı, sipariş ve beklenen sonuçtan oluşan küçük bir tablo, kuralların tamamını saniyeler içinde doğrular. Her rol için en az bir "izinli" ve bir "yasak" senaryo yazın. Yeni bir rol eklendiğinde bu testler hangi ekranların etkilendiğini size söyler.

Controller testlerinde de actingAs($user)->put(...)->assertForbidden() kalıbıyla en az bir yetkisiz deneme bulunsun. Mutlu yol testleri geçerken yetki açıkları fark edilmeden kalabilir.

İlk hata, policy'yi yazıp çağırmayı unutmaktır; kural vardır ama hiçbir yol onu kullanmaz. İkincisi, kontrolü yalnızca liste ekranında yapıp detay, güncelleme ya da API uç noktasında atlamaktır. Üçüncüsü ise Gate::before() içinde gereğinden geniş bir istisna tanımlamaktır; bir kez return true döndüren geniş bir koşul, bütün kuralları sessizce devre dışı bırakır. Yeni bir özellik tamamlandığında "bu uç noktada authorize çağrısı var mı" sorusunu kod incelemesi listesinin sabit bir maddesi yapmanızı öneririz.

Mevcut Laravel projenizde yetki kontrolleri dağılmış durumdaysa ya da yeni bir panel için sağlam bir yetki modeli kurmak istiyorsanız iletişim sayfamızdan bize ulaşın. Yetkilendirmeyi sonradan yamamak, baştan doğru kurmaktan her zaman daha pahalı çıkıyor.

📅 Yayınlanma:  ·  Yakup Zengin