🔐 Root ve Normal Kullanıcı Yetkilendirme Sistemi

Muzibu.com Multi-Tenant SaaS Platformu - Kapsamlı Yetkilendirme Analizi

ROOT ADMIN EDITOR

📋 Sistem Özeti

3
Rol Seviyesi
root, admin, editor
4
İzin Tipi
view, create, update, delete
2
Veritabanı
Central + Tenant DB

1️⃣ Rol Yapısı ve Hiyerarşi

📝 Basit Anlatım (Herkes İçin)

Sistem, kullanıcıları üç ana gruba ayırır: root (süper yönetici), admin (yönetici) ve editor (editör). Her grubun farklı yetkileri vardır.

ROOT Tam Yetkili Yönetici

Her şeyi yapabilir. Sistemin her yerine erişebilir, tüm ayarları değiştirebilir. Hem merkezi sistemde (central) hem de her müşterinin (tenant) kendi alanında tam yetkilidir.

✅ Tüm modüllere erişim
✅ Debugbar görüntüleme
✅ Kritik sistem ayarları
✅ Central ve tenant DB'ye tam erişim
ADMIN Yönetici

Kendi alanında tam yetkili. Bir müşterinin (tenant) kendi sisteminde her şeyi yönetebilir, ancak sadece kendisine atanan modülleri kullanabilir. Central sistemde bazı kısıtlamaları vardır.

✅ Tenant'a atanan modüllere tam erişim
✅ Kullanıcı yönetimi (kendi tenant'ı)
✅ İçerik düzenleme (kendi tenant'ı)
❌ Central'da bazı modüller kısıtlı
EDITOR Editör

Sınırlı yetkiler. Sadece kendisine özel olarak verilen modülleri ve belirli işlemleri (görüntüleme, ekleme, düzenleme, silme) yapabilir. Her modül için ayrı ayrı izin verilir.

✅ Belirli modüllere özel izinler
✅ İzin tipi bazlı erişim (view/create/update/delete)
❌ Sistem ayarlarına erişim yok
❌ Kullanıcı yönetimi yok

💡 Neden Önemli?

Bu sistem sayesinde her kullanıcı sadece yapması gereken işleri görebilir. Bir editörün yanlışlıkla önemli ayarları bozması veya başka kullanıcıların verilerine erişmesi engellenir. Güvenlik ve düzen sağlanır.

🔧 Teknik Detaylar (Geliştiriciler İçin)

Rol Modeli

📁 Modules/UserManagement/App/Models/Role.php

class Role extends SpatieRole {
    const BASE_ROLES = ['root', 'admin', 'editor'];
    const ROLE_TYPES = [
        'root' => 'Tam Yetkili Yönetici',
        'admin' => 'Yönetici',
        'editor' => 'Editör'
    ];

    protected $fillable = ['name', 'guard_name', 'is_protected', 'role_type', 'description'];

    public function isRoot(): bool { return $this->role_type === 'root'; }
    public function isAdmin(): bool { return $this->role_type === 'admin'; }
    public function isEditor(): bool { return $this->role_type === 'editor'; }
    public function isDeletable(): bool {
        return !$this->isBaseRole() && !$this->is_protected && $this->users()->count() === 0;
    }
}

User Model Entegrasyonu

📁 app/Models/User.php

use Spatie\Permission\Traits\HasRoles;
use Modules\UserManagement\App\Traits\HasModulePermissions;

class User extends Authenticatable {
    use HasRoles, HasModulePermissions;

    protected $guard_name = 'web';

    // Spatie Permission override (global scope bypass)
    public function roles() {
        return $this->morphToMany(Role::class, 'model', 'model_has_roles')
            ->where('roles.guard_name', 'web')
            ->withoutGlobalScopes(); // 🔴 KRİTİK: Multi-tenant için
    }

    // Cache-aware rol kontrolü
    public function hasCachedRole($role): bool {
        return cache()->remember("user_role_{$this->id}_{$role}", 300, fn() => $this->hasRole($role));
    }

    // Rol helper metodları
    public function isRoot(): bool { return $this->hasRole('root'); }
    public function isAdmin(): bool { return $this->hasRole('admin'); }
    public function isEditor(): bool { return $this->hasRole('editor'); }
}

Veritabanı Yapısı

📊 roles tablosu (Central + Tenant)
┌───────────────┬──────────────┬─────────────┐
│ Kolon         │ Tip          │ Açıklama    │
├───────────────┼──────────────┼─────────────┤
│ id            │ bigint       │ PK          │
│ name          │ varchar(255) │ root/admin  │
│ guard_name    │ varchar(255) │ web         │
│ role_type     │ varchar(50)  │ rol tipi    │
│ is_protected  │ boolean      │ silinemez   │
│ description   │ text         │ açıklama    │
└───────────────┴──────────────┴─────────────┘

📊 model_has_roles tablosu (Spatie)
┌───────────────┬──────────────┬─────────────┐
│ role_id       │ bigint       │ FK roles    │
│ model_type    │ varchar(255) │ App\Models\User │
│ model_id      │ bigint       │ user_id     │
└───────────────┴──────────────┴─────────────┘

Seeder

📁 database/seeders/RolePermissionSeeder.php

// Root kullanıcıları (her tenant + central)
$rootEmails = ['nurullah@nurullah.net', 'info@turkbilisim.com.tr'];

// Central DB
if (isCentral()) {
    createOrUpdateRole('root', 'Tam yetkili sistem yöneticisi', 'root', true);
    createOrUpdateRole('admin', 'Tenant yöneticisi', 'admin', true);
    createOrUpdateRole('editor', 'Modül bazlı yetkilendirilebilir editör', 'editor', true);
}

// Tenant DB
if (isTenant()) {
    // Aynı roller tenant DB'ye de eklenir
    Role::create(['name' => 'root', 'role_type' => 'root', 'is_protected' => true]);
}

2️⃣ Modül Bazlı Yetkilendirme Sistemi

📝 Basit Anlatım (Herkes İçin)

Sistem, modüler bir yapıya sahip. Yani her özellik (blog, müzik, ödeme sistemi gibi) birer modüldür. Her modül için 4 farklı işlem izni vardır:

👁️ VIEW (Görüntüleme)

Modülün içeriğini görebilir, listeleyebilir

➕ CREATE (Oluşturma)

Yeni kayıt ekleyebilir (yeni şarkı, blog yazısı)

✏️ UPDATE (Güncelleme)

Mevcut kayıtları düzenleyebilir

🗑️ DELETE (Silme)

Kayıtları silebilir

📌 Örnek Senaryo

Ali adında bir editör var. Ali'ye Blog modülü için şu izinler verilmiş:

  • ✅ VIEW: Blog yazılarını görebilir
  • ✅ CREATE: Yeni blog yazısı ekleyebilir
  • ✅ UPDATE: Yazıları düzenleyebilir
  • ❌ DELETE: Yazıları SİLEMEZ

Aynı zamanda Ali'ye Müzik modülü için sadece VIEW izni verilmiş. Yani şarkıları görebilir ama ekleyemez, düzenleyemez veya silemez.

💡 Neden Önemli?

Bu sistem sayesinde her editörün yetkisi tam olarak kontrol edilir. Bir kişinin yanlışlıkla önemli verileri silmesi veya yetkisi olmayan alanlara erişmesi engellenir. Her kişi sadece yapması gereken işleri yapar.

🔧 Teknik Detaylar (Geliştiriciler İçin)

HasModulePermissions Trait

📁 Modules/UserManagement/App/Traits/HasModulePermissions.php

trait HasModulePermissions {

    // Ana yetki kontrolü
    public function hasModulePermission(string $moduleName, string $permissionType): bool {
        // 1. Root: Her zaman true
        if ($this->hasRole('root')) return true;

        // 2. Admin: Tenant'a atanmış modüller için true
        if ($this->hasRole('admin')) {
            if (tenant()) {
                $module = ModuleAccessService::getModuleByName($moduleName);
                return ModuleAccessService::isModuleAssignedToTenant($module->module_id, tenant()->id);
            }
            // Central'da kısıtlı modüller kontrol edilir
            return !in_array($moduleName, config('module-permissions.admin_restricted_modules'));
        }

        // 3. Editor: UserModulePermission tablosundan kontrol
        $cacheKey = "user_{$this->id}_module_{$moduleName}_permission_{$permissionType}";
        return Cache::remember($cacheKey, 300, function() {
            return $this->userModulePermissions()
                ->where('module_name', $moduleName)
                ->where('permission_type', $permissionType)
                ->where('is_active', true)
                ->exists();
        });
    }

    // Modülün tüm izinlerini getir
    public function getModulePermissions(string $moduleName): array {
        if ($this->isRoot() || $this->isAdmin()) {
            return ['view', 'create', 'update', 'delete'];
        }

        return $this->userModulePermissions()
            ->where('module_name', $moduleName)
            ->where('is_active', true)
            ->pluck('permission_type')
            ->toArray();
    }

    // İzin ekleme
    public function giveModulePermissionTo(string $moduleName, $permissionTypes): void {
        foreach ((array)$permissionTypes as $type) {
            UserModulePermission::updateOrCreate(
                ['user_id' => $this->id, 'module_name' => $moduleName, 'permission_type' => $type],
                ['is_active' => true]
            );
        }
        $this->clearModulePermissionCache($moduleName);
    }
}

UserModulePermission Model

📁 Modules/UserManagement/App/Models/UserModulePermission.php

class UserModulePermission extends Model {
    protected $fillable = ['user_id', 'module_name', 'permission_type', 'is_active'];
    protected $casts = ['is_active' => 'boolean'];

    public function user(): BelongsTo {
        return $this->belongsTo(User::class);
    }

    public static function getPermissionTypes(): array {
        return ModulePermission::getPermissionTypes();
    }
}

📊 user_module_permissions tablosu
┌──────────────────┬──────────────┬────────────────────────────┐
│ Kolon            │ Tip          │ Açıklama                   │
├──────────────────┼──────────────┼────────────────────────────┤
│ id               │ bigint       │ PK                         │
│ user_id          │ bigint       │ FK users                   │
│ module_name      │ varchar(50)  │ 'blog', 'muzibu', vb.      │
│ permission_type  │ varchar(20)  │ 'view', 'create', vb.      │
│ is_active        │ boolean      │ aktif/pasif                │
│ created_at       │ timestamp    │                            │
│ updated_at       │ timestamp    │                            │
├──────────────────┴──────────────┴────────────────────────────┤
│ UNIQUE: (user_id, module_name, permission_type)              │
│ INDEX: module_name, permission_type, is_active               │
└───────────────────────────────────────────────────────────────┘

Helper Fonksiyonları

📁 app/Helpers/ModulePermissionHelper.php

// Blade'de kullanım: @if(can_module('blog', 'create'))
function can_module(string $moduleName, string $permissionType = 'view'): bool {
    $user = Auth::user();
    if (!$user) return false;

    if ($user->hasRole('root') || $user->hasRole('admin')) {
        return true;
    }

    $cacheKey = "user_{$user->id}_module_{$moduleName}_permission_{$permissionType}";
    return Cache::remember($cacheKey, 86400, fn() =>
        $user->hasModulePermission($moduleName, $permissionType)
    );
}

// Modül aktif mi kontrolü
function is_module_active(string $moduleName, string $permissionType = 'view'): bool {
    $cacheKey = "module_{$moduleName}_permission_{$permissionType}_active";
    return Cache::remember($cacheKey, 86400, function() use ($moduleName, $permissionType) {
        return Module::where('name', $moduleName)
            ->where('is_active', true)
            ->whereHas('permissions', fn($q) =>
                $q->where('permission_type', $permissionType)->where('is_active', true)
            )
            ->exists();
    });
}

3️⃣ Middleware Koruma Katmanları

📝 Basit Anlatım (Herkes İçin)

Middleware, bir sayfa açılmadan önce çalışan güvenlik kontrol noktalarıdır. Tıpkı bir binanın güvenlik görevlisi gibi, her ziyaretçiyi kontrol eder ve yetkisi yoksa içeri almaz.

🚫 RootAccessMiddleware (Sadece Root)

Bazı sayfalar sadece root kullanıcıların erişebileceği kadar kritiktir. Bu middleware, root olmayan hiç kimsenin o sayfalara girmesine izin vermez.

Örnek: Sistem ayarları, tenant oluşturma, global yapılandırma sayfaları
🔐 AdminAccessMiddleware (Admin Kontrolü)

Admin paneline giriş yapmak isteyen herkesi kontrol eder. Root ve admin'lere izin verir, editörler için özel kontroller yapar. Ayrıca hangi modüle erişmeye çalıştığını tespit eder.

✅ Root: Her yere erişebilir
✅ Admin: Tenant'a atanan modüllere erişebilir
✅ Editor: İzin verilen modüllere erişebilir
❌ Yetkisiz kullanıcı: 403 Forbidden
🔍 RootOnlyDebugbar (Geliştirici Araçları)

Debugbar (geliştirici debug araçları), sadece root kullanıcıların görebileceği hassas bilgiler içerir. Bu middleware, debugbar'ı sadece root kullanıcılara gösterir.

Performans: Session cache kullanarak her istekte DB sorgusu yapmaz

💡 Neden Önemli?

Middleware sistemi sayesinde her sayfa açılmadan önce yetki kontrolü yapılır. Yanlışlıkla veya kasıtlı olarak yetkisiz sayfalara erişim denemeleri engellenir. Sistem güvenliği otomatik olarak sağlanır, her sayfada ayrı kontrol yazmaya gerek kalmaz.

🔧 Teknik Detaylar (Geliştiriciler İçin)

RootAccessMiddleware

📁 app/Http/Middleware/RootAccessMiddleware.php

class RootAccessMiddleware {
    public function handle(Request $request, Closure $next) {
        // Root yetkisi kontrolü
        if (!auth()->user() || !auth()->user()->isRoot()) {
            abort(403, 'ERİŞİM ENGELENDİ.');
        }

        // Tenant kontrolü - sadece central'da erişime izin ver
        if (TenantHelpers::isTenant()) {
            abort(403, 'ERİŞİM ENGELENDİ.');
        }

        return $next($request);
    }
}

📋 Kullanım:
Route::middleware(['auth', 'root'])->group(function() {
    Route::get('/admin/system-settings', ...);
    Route::get('/admin/create-tenant', ...);
});

AdminAccessMiddleware

📁 app/Http/Middleware/AdminAccessMiddleware.php

class AdminAccessMiddleware {
    public function handle(Request $request, Closure $next) {
        if (!auth()->check()) {
            abort(403, 'Bu alana erişim yetkiniz bulunmamaktadır.');
        }

        $user = auth()->user();

        // URL'den modül adını çıkar: /admin/blog/posts → 'blog'
        preg_match('/^admin\/([^\/]+)/', $request->path(), $matches);
        $moduleName = $matches[1] ?? null;

        // Path-to-module mapping
        $pathModuleMap = ['orders' => 'cart', 'checkout' => 'cart'];
        if ($moduleName && isset($pathModuleMap[$moduleName])) {
            $moduleName = $pathModuleMap[$moduleName];
        }

        // ROOT: Her yere erişebilir
        if ($user->isRoot()) {
            return $next($request);
        }

        // ADMIN: Tenant'a atanan modüllere erişebilir
        if ($user->isAdmin()) {
            $systemRoutes = ['dashboard', 'cache', 'debug', 'ai', 'language'];
            if (in_array($moduleName, $systemRoutes)) {
                return $next($request);
            }

            // Central'da kısıtlı modül kontrolü
            if (TenantHelpers::isCentral() &&
                in_array($moduleName, config('module-permissions.admin_restricted_modules'))) {
                abort(403, 'Bu modüle erişim yetkiniz bulunmamaktadır.');
            }

            // Tenant'ta modül atama kontrolü (10 dakika cache)
            if (TenantHelpers::isTenant() && $moduleName) {
                $cacheKey = "module_tenant_access:{$moduleName}:" . tenant()->id;
                $hasAccess = Cache::remember($cacheKey, 600, function() use ($moduleName) {
                    $module = ModuleAccessService::getModuleByName($moduleName);
                    return $module && ModuleAccessService::isModuleAssignedToTenant($module->module_id, tenant()->id);
                });

                if (!$hasAccess) {
                    abort(403, 'Bu modül bu tenant\'a atanmamış.');
                }
            }

            return $next($request);
        }

        // EDITOR: Modül bazlı izin kontrolü
        if ($user->isEditor()) {
            if ($moduleName && !ModuleAccessService::canAccess($moduleName, 'view')) {
                abort(403, 'Bu modüle erişim yetkiniz bulunmamaktadır.');
            }
            return $next($request);
        }

        abort(403, 'Bu alana erişim yetkiniz bulunmamaktadır.');
    }
}

RootOnlyDebugbar

📁 app/Http/Middleware/RootOnlyDebugbar.php

class RootOnlyDebugbar {
    public function handle(Request $request, Closure $next): Response {
        if (!class_exists(\Barryvdh\Debugbar\Facade::class)) {
            return $next($request);
        }

        $showDebugbar = false;

        if (auth()->check()) {
            $userId = auth()->id();
            $sessionKey = "is_root_user_{$userId}";

            if (session()->has($sessionKey)) {
                // Session'dan al (0 query)
                $showDebugbar = session($sessionKey);
            } else {
                // İlk kez: Cache'den al, session'a kaydet
                $isRoot = Cache::remember("user_is_root_{$userId}", 300, fn() =>
                    auth()->user()?->hasRole('root') ?? false
                );
                session([$sessionKey => $isRoot]);
                $showDebugbar = $isRoot;
            }
        }

        if (!$showDebugbar) {
            \Barryvdh\Debugbar\Facade::disable();
        }

        return $next($request);
    }
}

4️⃣ Multi-Tenant Yetkilendirme Mimarisi

📝 Basit Anlatım (Herkes İçin)

Sistem, multi-tenant (çok kiracılı) bir yapıya sahip. Yani her müşteri (tenant) kendi bağımsız veritabanına sahip. Bir müşterinin yöneticisi, sadece kendi müşterisinin verilerine erişebilir, başka müşterilerin verilerini göremez.

🏢 Central (Merkezi) Veritabanı

Tüm tenant'ları, modülleri ve global ayarları tutan merkezi veritabanı. Sadece root kullanıcılar buraya tam erişebilir.

📊 Tenant listesi
📦 Modül tanımları
🔗 Modül-tenant atamaları
🏪 Tenant Veritabanı

Her müşterinin kendi veritabanı. Kullanıcılar, içerikler, siparişler gibi tenant'a özel tüm veriler burada saklanır.

👥 Kullanıcılar
📝 İçerikler (blog, müzik vb.)
🔐 Roller ve izinler

📌 Örnek Senaryo

3 farklı müşteri var: Tuufi.com (central), Ixtif.com ve Muzibu.com

ROOT Nurullah: Tüm tenant'lara ve central DB'ye erişebilir
ADMIN Ahmet (Muzibu yöneticisi): Sadece Muzibu DB'ye erişebilir, Ixtif'i göremez
EDITOR Ayşe (Muzibu editör): Muzibu'da sadece kendisine verilen modüllere erişebilir

🎯 Modül Atama Sistemi

Her tenant'a hangi modüllerin aktif olacağı central DB'den kontrol edilir. Örneğin Muzibu'ya "Müzik" modülü atanmış ama Ixtif'e atanmamış.

Böylece her müşteri sadece ihtiyacı olan özellikleri kullanır, gereksiz karmaşa olmaz.

💡 Neden Önemli?

Multi-tenant yapı sayesinde her müşteri tamamen izole edilmiş ortamda çalışır. Bir müşterinin yöneticisi yanlışlıkla başka müşterinin verilerini göremez veya değiştiremez. Veri güvenliği ve gizlilik maksimum seviyede sağlanır.

🔧 Teknik Detaylar (Geliştiriciler İçin)

Tenant Kontrol Mekanizması

📁 HasModulePermissions Trait - Tenant-Aware Permission Check

public function hasModulePermission(string $moduleName, string $permissionType): bool {
    // Root: Her zaman bypass
    if ($this->hasRole('root')) return true;

    // Admin: Tenant context'e göre kontrol
    if ($this->hasRole('admin')) {
        // Tenant initialized mı?
        if (app(\Stancl\Tenancy\Tenancy::class)->initialized) {
            // Modül bu tenant'a atanmış mı? (Central DB sorgusu)
            $moduleService = app(\App\Services\ModuleAccessService::class);
            $module = $moduleService->getModuleByName($moduleName);

            if (!$module) {
                \Log::warning("Module not found: {$moduleName}");
                return false;
            }

            return $moduleService->isModuleAssignedToTenant($module->module_id, tenant()->id);
        }

        // Central'da ise admin_restricted_modules kontrolü
        if (in_array($moduleName, config('module-permissions.admin_restricted_modules', []))) {
            \Log::warning("Admin tried to access restricted module: {$moduleName}");
            return false;
        }

        return true;
    }

    // Editor: UserModulePermission kontrolü (Tenant DB)
    return $this->userModulePermissions()
        ->where('module_name', $moduleName)
        ->where('permission_type', $permissionType)
        ->where('is_active', true)
        ->exists();
}

ModuleAccessService - Tenant Assignment

📁 app/Services/ModuleAccessService.php

public function isModuleAssignedToTenant(string $moduleId, string $tenantId): bool {
    // Central tenant (ID: 1) tüm modüllere erişebilir
    if ($tenantId === '1' || $tenantId === 1) {
        return true;
    }

    // Cache kontrolü (10 dakika)
    $cached = $this->cache->getTenantAssignmentCache($moduleId, $tenantId);
    if ($cached !== null) return $cached;

    // Database'den kontrol et (Central DB'den!)
    $isAssigned = TenantHelpers::central(function () use ($moduleId, $tenantId) {
        // module_tenants tablosunda aktif atama var mı?
        $assigned = DB::table('module_tenants')
            ->where('module_id', $moduleId)
            ->where('tenant_id', $tenantId)
            ->where('is_active', true)
            ->exists();

        if ($assigned) return true;

        // Veya tenant central tenant mı?
        return DB::table('tenants')
            ->where('id', $tenantId)
            ->where('central', true)
            ->where('is_active', true)
            ->exists();
    });

    // Cache'e kaydet
    $this->cache->setTenantAssignmentCache($moduleId, $tenantId, $isAssigned);

    return $isAssigned;
}

// Tenant'ın modüllerini getir
public function getTenantModules(int $tenantId): \Illuminate\Support\Collection {
    return TenantHelpers::central(function () use ($tenantId) {
        // Central tenant tüm modüllere erişir
        if ($tenantId === 1) {
            return Module::where('is_active', true)->get();
        }

        // Diğer tenant'lar için module_tenants tablosundan
        return Module::select('modules.*')
            ->join('module_tenants', 'modules.module_id', '=', 'module_tenants.module_id')
            ->where('module_tenants.tenant_id', $tenantId)
            ->where('module_tenants.is_active', true)
            ->where('modules.is_active', true)
            ->get();
    });
}

Database Schema

📊 Central DB - module_tenants tablosu
┌─────────────┬──────────┬──────────────────────────────┐
│ Kolon       │ Tip      │ Açıklama                     │
├─────────────┼──────────┼──────────────────────────────┤
│ id          │ bigint   │ PK                           │
│ module_id   │ string   │ FK modules (UUID)            │
│ tenant_id   │ bigint   │ FK tenants                   │
│ is_active   │ boolean  │ aktif/pasif                  │
│ created_at  │timestamp │                              │
│ updated_at  │timestamp │                              │
└─────────────┴──────────┴──────────────────────────────┘

📊 Tenant DB - user_module_permissions tablosu
┌──────────────────┬──────────────┬────────────────┐
│ user_id          │ module_name  │ permission_type│
├──────────────────┼──────────────┼────────────────┤
│ 42               │ blog         │ view           │
│ 42               │ blog         │ create         │
│ 42               │ blog         │ update         │
│ 58               │ muzibu       │ view           │
└──────────────────┴──────────────┴────────────────┘
⚠️ Bu tablo her tenant'ın kendi DB'sinde!

TenantHelpers Kullanımı

📁 app/Helpers/TenantHelpers.php

// Central DB'de sorgu çalıştırma
TenantHelpers::central(function () {
    return Module::all(); // Central DB'den modülleri al
});

// Tenant kontrolü
if (TenantHelpers::isTenant()) {
    // Tenant context'indeyiz
}

if (TenantHelpers::isCentral()) {
    // Central domain'deyiz
}

5️⃣ Cache Sistemi ve Performans Optimizasyonu

📝 Basit Anlatım (Herkes İçin)

Sistem, yetki kontrollerinde cache (önbellek) kullanır. Yani bir kullanıcının yetkisi bir kez kontrol edildiğinde, sonuç hatırlanır ve tekrar veritabanına sorulmaz. Bu sayede sistem çok daha hızlı çalışır.

🚀 Nasıl Çalışır?

1️⃣ Kullanıcı bir sayfaya girmek ister (örn: Blog modülü)
2️⃣ Sistem önce cache'e bakar: "Bu kullanıcının blog yetkisi daha önce sorulmuş mu?"
3️⃣ Eğer cache'de varsa direkt oradan alır (çok hızlı ⚡)
4️⃣ Eğer cache'de yoksa veritabanından kontrol eder ve sonucu cache'e kaydeder
5️⃣ 5-24 saat boyunca cache'deki sonuç kullanılır, tekrar sorulmaz
⏱️ 5 Dakika

Modül izin kontrolü cache süresi

⏱️ 10 Dakika

Tenant-modül atama cache

⏱️ 24 Saat

Helper fonksiyon cache

💡 Neden Önemli?

Cache kullanmadan her sayfa yüklenişinde veritabanına 10-20 kez "bu kullanıcının yetkisi var mı?" diye sorulurdu. Cache sayesinde bu sorgu sayısı 0'a iner. Sayfa yükleme süreleri 10 kat hızlanır, veritabanı yükü azalır.

🔧 Teknik Detaylar (Geliştiriciler İçin)

Cache Key Stratejisi

📁 Cache Key Yapısı

// Modül izin cache (5 dakika)
"user_{$userId}_module_{$moduleName}_permission_{$permissionType}"
Örnek: "user_42_module_blog_permission_create"

// Modülün tüm izinleri (60 dakika)
"user_{$userId}_module_{$moduleName}_permissions"
Örnek: "user_42_module_blog_permissions"

// Rol cache (5 dakika)
"user_role_{$userId}_{$roleName}"
"user_is_root_{$userId}"

// Tenant-modül atama (10 dakika)
"module_tenant_access:{$moduleName}:{$tenantId}"

// Modül aktif mi (24 saat)
"module_{$moduleName}_permission_{$permissionType}_active"

// Modül by name (15 dakika)
"module_by_name:{$moduleName}"

Cache Implementation

📁 HasModulePermissions Trait

public function hasModulePermission(string $moduleName, string $permissionType): bool {
    if ($this->hasRole('root')) return true;
    if ($this->hasRole('admin')) { /* ... */ }

    // Editor için cache'li kontrol
    $cacheKey = "user_{$this->id}_module_{$moduleName}_permission_{$permissionType}";

    return Cache::remember($cacheKey, now()->addMinutes(5), function () use ($moduleName, $permissionType) {
        // Spatie Permission kontrolü
        $permissionName = "{$moduleName}.{$permissionType}";
        if (!Permission::where('name', $permissionName)->exists()) {
            \Log::error("Permission does not exist: {$permissionName}");
            return false;
        }

        if ($this->hasPermissionTo($permissionName)) {
            return true;
        }

        // Direct DB kontrolü (fallback)
        $hasPermissionInDB = \DB::table('model_has_permissions')
            ->join('permissions', 'model_has_permissions.permission_id', '=', 'permissions.id')
            ->where('model_id', $this->id)
            ->where('model_type', get_class($this))
            ->where('permissions.name', $permissionName)
            ->exists();

        if ($hasPermissionInDB) return true;

        // UserModulePermission kontrolü
        return $this->userModulePermissions()
            ->where('module_name', $moduleName)
            ->where('permission_type', $permissionType)
            ->where('is_active', true)
            ->exists();
    });
}

ModuleAccessCache Service

📁 app/Services/ModuleAccessCache.php

class ModuleAccessCache {
    const CACHE_TTL = 600; // 10 dakika

    public function getAccessCache(int $userId, string $moduleName, string $permissionType): ?bool {
        $key = $this->buildAccessKey($userId, $moduleName, $permissionType);
        return Cache::get($key);
    }

    public function setAccessCache(int $userId, string $moduleName, string $permissionType, bool $hasAccess): void {
        $key = $this->buildAccessKey($userId, $moduleName, $permissionType);
        Cache::put($key, $hasAccess, self::CACHE_TTL);
    }

    public function getTenantAssignmentCache(string $moduleId, string $tenantId): ?bool {
        $key = "module_tenant_assignment:{$moduleId}:{$tenantId}";
        return Cache::get($key);
    }

    public function setTenantAssignmentCache(string $moduleId, string $tenantId, bool $isAssigned): void {
        $key = "module_tenant_assignment:{$moduleId}:{$tenantId}";
        Cache::put($key, $isAssigned, self::CACHE_TTL);
    }

    public function clearUserCache(string $userId): void {
        // Wildcard cache clear (Redis pattern delete)
        $pattern = "laravel_cache:user_{$userId}_*";
        $keys = Redis::keys($pattern);
        if ($keys) Redis::del($keys);
    }

    private function buildAccessKey(int $userId, string $moduleName, string $permissionType): string {
        return "user_module_access:{$userId}:{$moduleName}:{$permissionType}";
    }
}

Cache Temizleme Stratejisi

📁 Cache Invalidation Points

// Rol değiştiğinde (User model)
public function flushRoleCache(): void {
    app(\Spatie\Permission\PermissionRegistrar::class)->forgetCachedPermissions();
    $this->unsetRelation('roles');
    $this->unsetRelation('permissions');
    \Cache::forget("user_{$this->id}_roles");
    \Cache::forget("user_{$this->id}_permissions");
}

// Modül izni eklendiğinde/kaldırıldığında
private function clearModulePermissionCache(string $moduleName): void {
    $permissionTypes = UserModulePermission::getPermissionTypes();
    foreach ($permissionTypes as $type => $name) {
        Cache::forget("user_{$this->id}_module_{$moduleName}_permission_{$type}");
    }
    Cache::forget("user_{$this->id}_module_{$moduleName}_permissions");
}

// Tüm modül cache'i temizleme
ModuleAccessService::clearModuleAccessCache($userId); // Belirli kullanıcı
ModuleAccessService::clearModuleAccessCache();        // Tüm cache

Session Cache (Debugbar)

📁 RootOnlyDebugbar Middleware - Zero Query Optimization

if (auth()->check()) {
    $userId = auth()->id();
    $sessionKey = "is_root_user_{$userId}";

    if (session()->has($sessionKey)) {
        // Session'dan al (0 DB query, 0 Redis query)
        $showDebugbar = session($sessionKey);
    } else {
        // İlk kez: Redis cache + session
        $isRoot = Cache::remember("user_is_root_{$userId}", 300, fn() =>
            auth()->user()?->hasRole('root') ?? false
        );
        session([$sessionKey => $isRoot]); // Session'a kaydet
        $showDebugbar = $isRoot;
    }
}

// Sonraki isteklerde: Session'dan okur, hiç sorgu yapmaz! ⚡

6️⃣ Kod Kullanım Örnekleri

Blade Template'de Yetki Kontrolü

{{-- Root kontrolü --}}
@if(auth()->user()->isRoot())
    <button class="btn btn-danger">Sistemi Sıfırla</button>
@endif

{{-- Modül izin kontrolü --}}
@if(can_module('blog', 'create'))
    <a href="{{ route('admin.blog.create') }}">Yeni Yazı Ekle</a>
@endif

{{-- Çoklu izin kontrolü --}}
@if(auth()->user()->hasModulePermissions('muzibu', ['update', 'delete']))
    <button class="btn btn-warning">Düzenle</button>
    <button class="btn btn-danger">Sil</button>
@endif

{{-- Rol bazlı UI --}}
@role('admin')
    <div class="admin-panel">Yönetici Paneli</div>
@endrole

@role('editor')
    <div class="editor-panel">Editör Paneli</div>
@endrole

Controller'da Yetki Kontrolü

use App\Services\ModuleAccessService;

class BlogController extends Controller {

    public function __construct(protected ModuleAccessService $moduleAccess) {
        // Middleware ile genel kontrol
        $this->middleware(['auth', 'admin']);
    }

    public function create() {
        // İzin kontrolü
        if (!$this->moduleAccess->canAccess('blog', 'create')) {
            abort(403, 'Blog oluşturma yetkiniz yok.');
        }

        return view('blog.create');
    }

    public function destroy(Blog $blog) {
        // Policy kullanımı
        $this->authorize('delete', $blog);

        // veya manuel kontrol
        if (!auth()->user()->hasModulePermission('blog', 'delete')) {
            abort(403);
        }

        $blog->delete();
        return redirect()->back();
    }
}

Policy Kullanımı

📁 app/Policies/WidgetPolicy.php

class WidgetPolicy {

    // Root-only işlem
    public function update(User $user, Widget $widget): bool {
        return $user->hasRole('root');
    }

    // Modül bazlı izin
    public function manageContent(User $user, Widget $widget): bool {
        return $user->hasModulePermission('widgetmanagement', 'update');
    }

    // Karmaşık kontrol
    public function delete(User $user, Widget $widget): bool {
        if ($user->isRoot()) return true;

        if ($user->isAdmin()) {
            // Admin sadece kendi tenant'ının widget'larını silebilir
            return $widget->tenant_id === tenant()->id;
        }

        return $user->hasModulePermission('widgetmanagement', 'delete');
    }
}

Route Middleware Kullanımı

📁 routes/web.php

// Root-only routes
Route::middleware(['auth', 'root'])->group(function() {
    Route::get('/admin/system-settings', [SystemController::class, 'index']);
    Route::post('/admin/create-tenant', [TenantController::class, 'store']);
});

// Admin routes (tenant-aware)
Route::middleware(['auth', 'admin'])->prefix('admin')->group(function() {
    Route::resource('blog', BlogController::class);
    Route::resource('pages', PageController::class);
});

// Spatie middleware kullanımı
Route::middleware(['role:root'])->group(function() {
    Route::get('/debug/queries', ...);
});

Route::middleware(['role:admin|editor'])->group(function() {
    Route::get('/admin/dashboard', ...);
});

Route::middleware(['permission:blog.create'])->group(function() {
    Route::post('/admin/blog', ...);
});