Şimdi bu yetkilendirme olayının iki aşaması var. Arada nüans var ve çoğu kişi karıştırıyor bu ikisini. Birbirinin yerine kullanılan şeyler değil.
1- Rol ve yetkiye göre metodlara erişimini korumak. Bu demektir ki A kişisinin kullanıcı ekleme yetkisi yok diyelim. Bu kişinin GET => domain.com/kullanicilar/ekle rotasına eşimini engellemek ve aynı zamanda POST => domain.com/kullanicilar rotasına form olmadan post atmasını engellemek içindir.
2- Rol ve yetkiye göre kayıtları korumak. Bu da şu anlama geliyor. A kişisinin kullanıcı düzenleme yetkisi diyelim. Var ama sadece kendi oluşturduğu kullanıcıları değiştirme yetkisi var. Bu durumda ne yapacağız? Ya hepsini kapatacağız ya da hepsini mi açacağız? Tabii ki hayır. İkisini de kullanacağız ve ikisini de değerlendireceğiz.
İlk seçenek için ben bu zamana kadar spatie/laravel-permission paketini kullandım ve hiçbir zaman sıkıntı çekmedim. Rotaları ve metodları rol ve izinlere göre koruyabilirsiniz.
İkinci seçeneği ise Policy ve Gate ile hallediyoruz. Bunun için de paketler vardır ama kullanmadım. Hatta spatie/permission paketi Gate ile de çalışabiliyor yani bu kısmı da kapsayabiliyorsunuz ama ben kullanmadım bilmiyorum. Ben Policy kullanıyorum. Diyelim User modeli var, bu User modeli ile UserPolicy i ilişkilendiriyorsunuz, policy içinde de kuralları yazıyorsunuz. Örneğin bu User’ı işlem yapan kullanıcı mı oluşturmuş gibi. Eğer policy true dönerse işlemi yapıyor yoksa 403 dönüyor.
Yani ilk aşamada adama kullanıcı düzenleme yetkisi veriyoruz. İkinci aşamada ise düzenle ama sadece seninkileri düzenle diyoruz. Policy ile ilk kısmı da çözebilirsiniz yani bu sizin projenizle işe yaklaşımınızla alakalı bir şey.
Ama ben ayırıyorum tamamen. Özellikle büyük projelerde birlikte çalıştırınca yönetilmesi zorlaşıyor.
Size bol şans. 😃
coder2
alihankoc Bende spatie/laravel-permission kullanıyorum genellikle. Kullanıcının sadece kendi oluşturduğu kullanıcılar/verileri değiştirme yetkisi konusunda model tarafında relation kuruyorum ve kullanıcıyla ilişkili kayıtları gösterip işlem yaparken de bunu kontrol ettiriyorum. (Örneğin Article tablosunda user_id ile ilişkiyi alarak). İnteraktif olarak bu yetkiyi değiştirmek için her model için Read-Write-Delete-Create gibi izinleri kullanıcılara verip çıkaracak yapı kuruyorum.
Açıkçası bugüne kadar çok büyük projelerde çalışmadım. Büyük projelerde bu mantık ilerde sorun çıkarabilir mi ?
sineld
coder2 Çıkmaz, ancak sisteme superadmin tanımlaması yapmayı unutmayın. Bu superadmin Gate::before ile denetlendiğinden diğer kıstaslara takılmadan her user’ın yapabildildiğini yapabilir duruma gelir.
alihankoc
coder2 Sorun proje büyüdüğünde değil iyi kurgulamadığınızda çıkar. Yani mantıklı olan her şey basit de olsa çalışır. Sadece spatie ile kocaman proje de yazabilirsiniz ama ufak bir projeyi de çalışmaz hale getirebilirsiniz. Tecrübeye ve algoritma gücünüze bağlı.
Şöyle bir senaryo hayal edin.
Bir sistem var. Bu sistemde şirketler var, şirketlere bağlı şubeler var, şubelere bağlı departmanlar var, departmanlara bağlı kullanıcılar var. Kullanıcıların da rolleri var.
Diyelim patron rolü var. Bu ne var ne yok görebiliyor. Şube müdürü var. Bu kendi şubesindeki her şeyi görüyor ama başka şubeleri göremiyor. Departman müdürü var bu da kendi şubesindeki kendi departmanını görüyor başka bişeyi göremiyor. Düz kullanıcı da sadece kendi verisini görüyor.
Şimdi bir arkadaş olsun adı Kamil olsun. Kamil’i Z şubesine müdür aynı zamanda da T şubesine ise departman müdürü yapalım. Roller many to many sonuçta birden fazla rol ekleyebiliyoruz, adam hem departman müdürü hem de müdür. Ama ayrı şubelerde. Departman müdürü olduğu yerde kayıt ekleme yapabilirken şube müdürü olduğu yerde yeni kayıt ekleme yetkisi olmasın. Aynı zamanda departman müdürü olduğu yerde de tüm yetkilere sahip olmasına rağmen aynı departmandaki Hasan la ilgili hiçbir kayıdı ne görsün ne düzenlesin ne silsin. Ayrıca kendisiyle hiçbir alakası olmayan V şubesindeki 5 ID’li kayıdı düzenleyebilme yetkisine sahip olsun. Bu kadar detaylı yetkilendirmeyi de her tablo için ayrı ayrı yapabilelim. Yani bu dediklerim sadece müşteriler için olsun. Diğer modüllerde geçerli olmasın.
Kayıtlarda da user_id, department_id, branch_id gibi kolonlar yok tümü many-to-many.
Hadi şimdi bu Kamil sistemdeyken müşterileri görüntülesin.
Müşterileri getirmek için nasıl bir query yazarsın? 😃
Yazamazsın. Query yazarsan için her rol için tek tek yazman gerekecek. Yeni bir rol eklenirse ne olacak? Yeni bir tablo eklenince ne olacak?
Bunları dinamik olarak ve tek bir yerden değiştirebilmen lazım.
Bu yüzden Policy kullanıyoruz işte.
Böyle karışık değil de mesela admin se Customer::all() diyebiliyorsak değilse de Customer::where('user_id', 5)->get()diyebiliyorsak sorun yok. Sorun böyle senaryolarda.
Not: Yukarıdaki Kamil’in senaryosunu birebir karşılayan projede çalıştım. 🙂 sanırım sadece bu yetki muhabbeti için 2-3 ay falan harcamış olabilirim ve yine de öngörmediğimiz senaryolar ortaya çıkıyordu zamanla.
GkyKrkc
yani sonuç itibariyle laravel 8x için route controller ve blade örneğini nasıl yapabileceğimiz hakkında türlü fikirler var öyle mi 🙂)
sineld
GkyKrkc Laravel’de herhangi bir şey yapmanın türlü yolu bulunur, burada best practice nedir onu bulup o kurgu üzerinde gitmekte yarar var.
GkyKrkc
sineld üstad peki bu tür bir işlemi middleware ile mi yapmak daha doğru yoksa Gate ve police ile mi ? middleware controllere ulaşmadan ara katmanda işi çözdüğü için daha hızlı bir sonuç alabiliriz diye düşünüyorum , GATE ve policy ise sanki controllere kadar iniyor veya bunlar tamamiyle farklı amaçlar için mi kullanılıyor kararsızım biraz
alihankoc
GkyKrkc Evet yukarıda da bahsettiğim gibi bazen sadece middleware ile işi çözemezsiniz. Controller katmanına inmeniz gerekir. Bu tamamen sizin senaryonuzla alakalı bir şey. Şu doğru ya da şu değil demek zor. Sizin projeniz hangisini kaldırıyor hangisi gerekiyor ya da gerekmiyor. Buna bakmak lazım.
Mesela bir blog yazısını güncellemek için bu kişi bu blog yazısının sahibi mi değil mi diye middleware de kontrol etmeniz doğru değil. Orda blog yazısını veritabanından çekmemelisiniz. Çünkü bir daha gidip controller da çekeceksiniz. İki kere yapacaksınız aynı sorguyu. Cache ye de alsanız aynı işi yapacaksınız sonuçta. 10 ms de bir zaman dilimi sonuçta.
Bu yüzden her projeye uygun bir cevap değil de sizin projenize uygun bir cevap aramak daha doğru olacaktır.
Bence..
sineld
GkyKrkc Gate sisteme izinleri container bazında enjekte ettiğinden izin sorgulaması yaptığınızda her seferinde yeni bir veritabanı sorgusu çalıştırmıyor; enfes bir özellik. Bu yüzden @coder2 ’nin de belirttiği gibi bu işi çok başarılı yapan spatie/laravel-permission kullanmakta yarar var.
coder2
alihankoc henüz bu büyüklükte bir projede çalışmadım, ondan olsa gerek mevcut kullandığım yapı sorun çıkarmadı şimdiye kadar.
Böyle karmaşık yapılar hoşuma gidiyor aslında. Yazdığınız örnek üzerinden deneme amaçlı bir çalışma yapacağım.
Teşekkürler.
Kevin-Mitnick
alihankoc Allahu Ekber . Bundan bir hacker filmi çıkar . Hacker Kamil diye 🙂
GkyKrkc
sineld spatie/laravel-permission paketini LARAVEL 8X içinde kullanabiliyormuyuz
sineld
GkyKrkc spatie ekibi bu konuda harika çalışıyor. Laravel yeni bir major sürüm çıkarınca iki gün içinde tüm paketlerini son sürümü destekleyecek şekilde güncelliyorlar. Yani evet 🙂
mgsmus
Sadece bir öneri, yol göstermesi açısından; Şu şekilde yardımcı bir morph-pivot kullanabilirsiniz:
user_has_roles_on_entities
+---------+---------+-----------+-----------------+-------------+
| user_id | role_id |   role    |  roleable_type  | roleable_id |
+---------+---------+-----------+-----------------+-------------+

$table->primary(['roleable_id', 'user_id', 'role_id'], 'roleable_primary');
app/Contracts/Roleable/RoleableEntityInterface.php:
interface RoleableInterface
{
    public function roles(): MorphToMany;
}
app/Contracts/Roleable/RoleableEntity.php:
trait RoleableEntity
{
    public function roles(): MorphToMany
    {
        return $this->morphToMany(Role::class, 'roleable');
    }
}
app/Company.php:
class Company extends Model implements RoleableInterface
{
    use RoleableEntity;
}
app/Department.php
class Department extends Model implements RoleableInterface
{
    use RoleableEntity;
}
app/User.php:
public function companies(): MorphToMany
{
    return $this->morphedByMany(Company::class, 'roleable', 'user_has_roles_on_entities')
        ->with([
            'roles' => function ($q) {
                $q->where('user_id', $this->id);
            }
        ])->whereHas('roles', function ($q) {
            $q->where('user_id', $this->id);
        })->withPivot([
            'role'
        ]);
}

public function departments(): MorphToMany
{
    return $this->morphedByMany(Department::class, 'roleable', 'user_has_roles_on_entities')
        ->with([
            'roles' => function ($q) {
                $q->where('user_id', $this->id);
            }
        ])->whereHas('roles', function ($q) {
            $q->where('user_id', $this->id);
        })->withPivot([
            'role'
        ]);
}

public function assignRoleToEntity(RoleableInterface $roleable, Role $role): bool
{
    $roleable->roles()->attach($role->id, [
        'user_id' => $this->id,
        'role' => $role->name
    ]);

    return true;
}

public function hasRoleOnEntity(RoleableInterface $roleable, Role $role): bool
{
    return $roleable->roles()
        ->where('user_id', $this->id)
        ->where('role_id', $role->id)
        ->exists();
}
Örneğin;
$adminRole = Role::find(1);
$moderatorRole = Role::find(2);

$company1 = Company::find(5);
$company2 = Company::find(8);
$department = Department::find(16);

$user = User::find(1);

// Kullanıcıyı id'si 5 olan firmada admin yap
$user->assignRoleToEntity($company1, $adminRole);

// Aynı kullanıcıyı id'si 8 olan firmada moderator yap
$user->assignRoleToEntity($company2, $moderatorRole);

// Aynı kullanıcıyı id'si 16 olan departmanda admin yap
$user->assignRoleToEntity($department, $adminRole);

$user->hasRoleOnEntity($company1, $adminRole); // true
$user->hasRoleOnEntity($company2, $moderatorRole); // true
$user->hasRoleOnEntity($department, $adminRole); // true

// Kullanıcının admin olduğu firmalar:
$user->companies()
    ->where('role_id', $adminRole->id)
    ->get();

// Departman adminleri
$department->roles()
    ->where('role_id', $adminRole->id)
    ->first()
    ->users;

// vs vs
user_has_roles_on_entities
+---------+---------+-----------+-----------------+-------------+
| user_id | role_id |   role    |  roleable_type  | roleable_id |
+---------+---------+-----------+-----------------+-------------+
|       1 |       1 | admin     | \App\Company    |           5 |
|       1 |       2 | moderator | \App\Company    |           8 |
|       1 |       1 | admin     | \App\Department |          16 |
+---------+---------+-----------+-----------------+-------------+
sineld
Kevin-Mitnick 😂 🤣
mizraklar
Merhaba arkadaşlar.
Konu biraz eski ama ben yeni denk deldim.
Yetki ve roller konusunda bu kadar dertlenmek ve gereksiz kod kalabalığı olmadan ben kendime özel bir yapı kurdum.
Menu Tablosu
Rol isim Tablosu
Menu Rol Terms Tablosu (bu tabloda ayrıca kullanıcıya özel yetki için role_id ve user_id alanı var bunlar menu_id eşleştirmesini kontrol ediyor. Ayrıca edits ve clear alanı var bunu özellikle listeler için kullanıyorum. Rolde edit ve clear işaretlenmiş ise silme ve düzenleme butonu görünüyor.
Middleware ile sayfalar arası gezgini ve post işlemlerini kontrol ediyorum. Hepsini yapılandırmak 1 dakika sürmüyor bir daha bu kısımlara işimiz yok. Gerisini panelden rollere göre açtığımız modül için clickleyip kaydetmek.