Otorisasi fleksibel menggunakan Casbin dan PERM. Contoh praktis

Setelah menulis artikel sebelumnya tentang bahasa PERM dan perpustakaan Casbin , pertanyaan muncul . Dan bukan hanya satu orang, dan saya ingin menjawab pertama dalam komentar, tetapi saya menyadari bahwa volume materi melebihi komentar biasanya, jadi saya akan menyajikan jawaban ini dalam bentuk artikel terpisah.







Untuk waktu yang lama saya tidak dapat memahami konstruksi spesifik yang ada di kepala penanya, tetapi pada akhirnya, setelah mengklarifikasi pertanyaan, saya menerima jawaban, kompilasi yang akan saya berikan dalam kutipan.







Dan bagaimana dengan DSL seperti itu masalah diselesaikan โ€œtunjukkan daftar objek yang dapat saya lihat? Hal ini diperlukan untuk menerjemahkan ini ke dalam kueri SQL, bukan untuk mengambil semua catatan dari database.



Ada antarmuka di situs yang menampilkan daftar sesuatu. Katakanlah - artikel di area admin CMS. Ada puluhan ribu artikel dari database, tetapi biasanya pengguna hanya memiliki akses ke selusin. Bagaimana cara mendapatkan artikel dari database yang dapat dilihat oleh pengguna tertentu? Nah, jika kita memiliki semua aturan yang siapa pun dapat melihat - diambil dari kode menjadi semacam DSL?

Dengan kata lain - cara menulis kueri seperti



pilih * dari artikel a

join peran r di r.userId = currentUserId di

mana article.owner = currentUserId

OR (r.role di ['admin', 'supevisor']) - admin dari total

OR (r.domain = currentDomainId DAN r.role di ['domain-admin', 'domain-supervisor']) - admin dari domain



Saya memiliki aturan seperti itu dalam kode, dalam bentuk ekspresi LINQ, dan saya bisa memecahkan masalah ini. Dan tugas semacam itu muncul lebih sering daripada "memeriksa apakah ada akses ke satu objek yang diturunkan dari memori"

Saya harap saya memahami konstruksi ini dengan benar dan selama rekayasa balik terbalik, saya dapat mengeluarkan data awal untuk memecahkan masalah ini. Untuk memulainya, kami akan membuang penggunaan multi-tenancy (domain), karena mereka mempersulit tugas dan, karenanya, memahami. Saya memberikan contoh penggunaannya di artikel terakhir.







, , , Casbin.









CMS, . user



.       , admin



supervisor



. supervisor



, admin



supervisor



, , .







, :



CMS:

Skema tabel DB







โ€” Users:

Isi tabel Pengguna







:

โ€” Roles:

Konten tabel peran







, Piter , Bob โ€” . Alice , , .







โ€” Articles:

Isi tabel Artikel







, (Piter, id=3) :







select * from articles a
left join roles r on r.userId = 3
where a.owner = 3
OR (r.role in ('admin', 'supevisor'))
      
      





Contoh hasil untuk administrator







(Bob, id=2) :







select * from articles a
left join roles r on r.userId = 2
where a.owner = 2
OR (r.role in ('admin', 'supevisor'))
      
      





Contoh hasil untuk supervisor







(Alice, id=1) :







select * from articles a
left join roles r on r.userId = 1
where a.owner = 1
OR (r.role in ('admin', 'supevisor'))
      
      





Hasil sampel untuk pengguna







, Casbin.







Casbin



PERM โ€” , .

.. , () . ( Id=1 ).







, , โ€” RBAC.

RBAC , . RBAC user



, author



user



(.. ), , admin



.

, , . user



supervisor



admin



, โ€” , . , user



, . admin



supervisor



, .

RBAC, , -, , .

: RBAC vs. ABAC







, (user



, supervisor



,admin



) โ€” โ€”



. , . , , โ€” .







" "



RBAC (rbac_model.conf



), :







[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
      
      





, Roles. . *.csv



, . cvs rbac_policy.csv



:







p, user, article, read
p, user, article, modify
p, user, article, create
p, user, article, delete

g, supervisor, user
g, admin, supervisor

g, 1, user
g, 2, supervisor
g, 3, admin
      
      





, user



, , . supervisor



user.



admin



supervisor



.

alice(1) user



, bob(2) supervisor



, piter(3) โ€” admin



.

, , .







, . cross-cutting concern CQRS+MediatR







public IList<Article> GetArticlesForAdminPanel(int currentUserId)
{
    var e = new Enforcer("CasbinConfig/rbac_model.conf", "CasbinConfig/rbac_policy.csv");

    var obj = "article";
    var act = "read";

    // ,       
    if (e.Enforce(currentUserId.ToString(), obj, act))
    {
        //   
        var currentUserRoles = e.GetRolesForUser(currentUserId.ToString());
        //,      
        var isAdmin = currentUserRoles.Any(x => x == "admin" || x == "supervisor");

        // ,   ,   ,   
        if (!isAdmin) return _context.Articles.Where(x => x.OwnerId == currentUserId).ToList();
        else return _context.Articles.ToList();
    }
    else
    {
        //  ,  
        throw new Exception("403.       ");   
    }
}
      
      





! , .







" "



. , , - . , , user



, supervisor



admin



.







, rbac_with_abac_model.conf



:







[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "supervisor")) && g(r.sub, p.sub) && r.act == p.act
      
      





, [matchers]



, r.obj == p.obj



(r.sub == r.obj.OwnerId.ToString() || g(r.sub, "supervisor"))



. r.sub (id ) r.obj.OwnerId (id ) r.sub "supervisor". admin



supervisor



admin



.







, . :







public void UpdateArticle(int currentUserId, Article newArticle)
{
    var e = new Enforcer("CasbinConfig/rbac_with_abac_model.conf", "CasbinConfig/rbac_policy.csv");

    var act = "modify";

    //,       
    if (e.Enforce(currentUserId.ToString(), newArticle, act))
    {
        //,   
        _context.Articles.Update(newArticle);
        _context.SaveChanges();
    }
    else
    {
        //  ,  
        throw new Exception("403.  ");
    }
}
      
      





, e.Enforce



, Article



.







โ€” .









- , user



, supervisor



โ€” , admin



.

- PERM, delete_model.conf



:







[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "admin")) && g(r.sub, p.sub) && r.act == p.act
      
      





, , admin



. supervisor



, .







, :







public void DeleteArticle(int currentUserId, Article deleteArticle)
{
    var e = new Enforcer("CasbinConfig/delete_model.conf", "CasbinConfig/rbac_policy.csv");

    var act = "delete";

    //,       
    if (e.Enforce(currentUserId.ToString(), deleteArticle, act))
    {
        // 
        _context.Articles.Remove(deleteArticle);
        _context.SaveChanges();
    }
    else
    {
        //  ,  
        throw new Exception("403.  ");
    }
}
      
      







, , Casbin PERM .







, , . , , .







Casbin DynamicExpresso.Core C# , Casbin .







, Casbin , , API. UI .







Saya memposting kode contoh yang berfungsi penuh dan mandiri yang saya gunakan untuk menulis artikel ini di Github saya , Anda dapat mengunduh dan bermain jika Anda tertarik dan mau.







link yang berguna






All Articles