📌 v2 Güncellemeleri
- Kök Sebep Bulundu: Tenant Redis prefix mismatch (tenant1001_ vs prefix yok)
- Tekrar Etme Nedeni: Config cache yapıldığında sorun yeniden oluşuyor
- Kalıcı Çözüm Önerileri: 3 farklı yaklaşım detaylandırıldı
Sorun Özeti
Muzibu (Tenant 1001) admin paneline ve Horizon'a giriş yapılamıyordu. Database'de ROOT yetkisi olmasına rağmen sürekli 403 Forbidden hatası alınıyordu. Sorun iki kez yaşandı: İlk düzeltmeden sonra tekrar etti.
📝 Basit Anlatım (Herkes İçin)
Ne Oldu?
Site yöneticileri (nurullah@nurullah.net ve info@turkbilisim.com.tr) admin paneline girmeye çalıştıklarında "Erişim Reddedildi" hatası alıyorlardı. Oysa database'de (veritabanı) tam yetkileri vardı. Sorun düzeltildi ama birkaç saat sonra tekrar etti.
Neden Oldu?
Laravel'in yetki sistemi (Spatie Permission), kullanıcı yetkilerini hızlı erişim için geçici hafızada (cache) tutuyor. Bu geçici hafıza bozulmuştu ve kullanıcıların yetkilerini göremiyordu.
Daha Derin Sebep: Sistemde her müşteri (tenant) için ayrı geçici hafıza bölümü var. Muzibu için "tenant1001_" başlığı var. Ama yetki sistemi yetkiyi kaydederken bu başlığı koymadı, okurken aradı. Sanki "Anahtar Muzibu Kasası'nda" diye etiket koyulmadan kasaya konulan para, sonra "Muzibu Kasası" diye aranınca bulunamadı.
Neden Tekrar Etti?
Sistem ayarları yeniden yüklendiğinde (config cache) veya yetkilerde değişiklik yapıldığında, aynı etiket sorunu tekrar oluşuyor. Geçici çözüm işe yarıyor ama kalıcı değil.
Nasıl Çözüldü?
- Bozuk geçici hafıza tamamen temizlendi (Redis FLUSHDB)
- Veritabanından yetkileri tekrar okutup doğru etiketle kaydedildi (syncRoles)
- Kullanıcıların eski oturumu kapatıldı (session silindi)
- Yeni oturum açtıklarında artık yeni, doğru yetkiler yüklendi
✅ Geçici Çözüm: Logout yapıp yeniden login olduktan sonra her şey düzeldi. Admin panel ve Horizon'a giriş yapılabildi.
⚠️ Uyarı: Bu geçici bir çözüm. Sistem ayarları yenilendiğinde veya yetki değişikliğinde sorun tekrar edebilir. Kalıcı çözüm için sistem yapılandırması değiştirilmeli (detaylar aşağıda).
🔍 Kök Sebep Analizi (v2'de Bulundu)
1. Tenant Redis Prefix Sistemi
Bu ayar, her tenant için Redis key'lerinin başına tenant{id}_
prefix'i ekler. Örnek: Muzibu (Tenant 1001) için tenant1001_
2. Spatie Permission Cache Yapılandırması
Spatie Permission, cache key olarak sabit spatie.permission.cache kullanıyor.
Store olarak default (Redis) seçilmiş.
3. Sorunun Oluşum Süreci
php artisan config:cache çalıştırılıyor.
Bu sırada tenant context henüz initialize olmadı.
muzibu.com adresine giriyor.
Tenant middleware devreye giriyor, Tenant 1001 initialize oluyor.
Redis prefix artık tenant1001_
$user->hasRole('root') çalıştırıyor.
Spatie Permission cache'ten okumaya çalışıyor:
spatie.permission.cache var,
ama tenant1001_spatie.permission.cache YOK!
$user->roles = [] → 403 Forbidden
4. Redis Key Mismatch (Görsel)
spatie.permission.cache,
diğeri tenant1001_spatie.permission.cache.
Redis'te sadece prefix'siz olan var, prefix'li olan yok. O yüzden bulamıyor.
🔧 Teknik Detaylar (Geliştiriciler İçin)
✓ Database Doğru'ydu
Dosya: tenant_muzibu_1528d0 database (tenant context)
✗ Spatie Permission Cache Bozuktu
Database'de rol atanmış olmasına rağmen Laravel runtime'da role relationship boş dönüyordu. Sebep: Spatie Permission cache'i Redis'te tenant prefix mismatch ile bozuk/okunamaz durumdaydı.
Session'da Eski User Object
Login olunduğunda user object session'a kaydedilmişti. Bu object'te roles = [] (boş array) vardı.
Her sayfa yüklendiğinde bu eski session'dan okunuyordu, dolayısıyla cache temizleme bile yeterli olmadı.
⚡ Uygulanan Geçici Çözüm
Tinker ile Role Sync
Etki: Spatie Permission cache'ini temizler ve role relationship'i fresh yükler
Redis FLUSHDB
Etki: Tüm Spatie cache + Laravel session + diğer cache'ler tamamen silindi (prefix'li ve prefix'siz hepsi)
Cache Rebuild
Etki: Config cache rebuild + OPcache (PHP bytecode) reset
Logout + Login (Kullanıcı)
Kullanıcı logout yapıp yeniden login oldu. Bu sayede:
- Eski session silindi
- Yeni session oluştu
- Fresh user object + fresh roles yüklendi
🔄 Neden Sorun Tekrar Ediyor?
Yukarıdaki çözüm geçici bir çözümdür. Aşağıdaki durumlardan herhangi biri gerçekleştiğinde sorun tekrar edecektir:
php artisan config:cache komutu çalıştırıldığında,
Spatie Permission cache'i yeniden oluşturulur ve yine tenant prefix olmadan yazılır.
php artisan optimize veya
config:cache çalıştırılır. Yine aynı problem.
⚠️ Sonuç: Bu geçici çözüm, acil durumda işe yarar ama kalıcı değildir. Sistem yapılandırması değiştirilmediği sürece sorun periyodik olarak tekrar edecektir.
🛠️ Kalıcı Çözüm Önerileri
Çözüm 1: Permission Cache'i Array Driver Yap
Spatie Permission cache'ini Redis yerine array driver kullanacak şekilde değiştir.
Bu sayede cache sadece request süresince bellekte kalır, Redis'e yazılmaz. Tenant prefix sorunu ortadan kalkar.
- Tenant prefix sorunu yok
- Her request fresh data
- Kolay uygulama (1 satır değişiklik)
- Side effect yok
- Her request'te DB query (performans)
- Yüksek trafikte yük artışı
- Redis cache avantajı kaybedilir
Çözüm 2: Permission Cache'i Devre Dışı Bırak
Spatie Permission cache'ini tamamen kapatır. Her permission check'te direkt database'den okur.
Çözüm 3: Tenant-Aware Custom Cache Key
Spatie Permission cache key'ini tenant ID ile dinamik oluştur. Her tenant kendi cache key'ini kullanır. Bu çözüm Redis cache avantajını korur ama implement etmesi daha karmaşık.
- Redis cache avantajı korunur
- Tenant isolation tam
- Performans optimal
- Spatie API değişikliği gerekebilir
- Test ve debug zor
- Package update'te sorun çıkabilir
- Kompleks implementation
PermissionRegistrar sınıfını extend etmek ve
getCacheKey() metodunu override etmek gerekir. Advanced seviye PHP bilgisi gerektirir.
🎯 Hangi Çözümü Seçmeli?
Çözüm 1 (Array Driver) en uygun seçenek.
- Müzik sitesi: Yüksek trafik yok
- Kullanıcı sayısı: ~100 aktif, admin ~2-3 kişi
- Permission check: Sadece admin panelde (kullanıcı tarafında yok)
- Uygulama: 1 satır değişiklik, hemen production'a alınabilir
- Risk: Çok düşük, side effect yok
Çözüm 3 (Custom Cache Key) daha uygun olur. Ama Muzibu için şu aşamada gerekli değil.
💡 Neden Geçici Çözüm İşe Yaradı?
❌ Önce (Bozuk)
✅ Sonra (Geçici Düzeldi)
核心 (Core):
syncRoles() fonksiyonu
sadece database'e yazmadı, aynı zamanda Spatie Permission'ın internal cache'ini de temizledi.
Ardından Redis flush ve logout/login ile session'daki eski user object'i yenilendi.
Bu üçlü kombinasyon sorunu geçici olarak çözdü.
Ama kök sebep (tenant prefix mismatch) hala var, bu yüzden tekrar edebilir.
🛡️ Gelecek İçin Önlemler
Kalıcı Çözümü Uygula: Yukarıdaki "Çözüm 1: Array Driver" önerisini uygula. Bu sorunu kökten çözer.
Role değişikliklerinde cache temizle:
Role atama/çıkarma işlemlerinden sonra php artisan permission:cache-reset çalıştır
(eğer Redis cache kullanmaya devam edersen).
Admin role değişikliği izni: Kritik roller (root, admin) değiştirildiğinde kullanıcıyı otomatik logout et.
Monitoring: AdminAccessMiddleware'de role check fail olduğunda log at (debug için).
Deployment checklist:
Production deploy sonrası config:cache çalıştırıyorsan,
ardından permission:cache-reset de çalıştır (eğer Redis cache kullanıyorsan).