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:

โ Users:

:
โ Roles:

, Piter , Bob โ . Alice , , .
โ Articles:

, (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'))

(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'))

(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'))

, 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
) โ โ
. , . , , โ .
" "
[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.