PERM言語とCasbinライブラリに関する前回の記事を書いた後、質問が発生しました。そして、一人だけでなく、コメントで最初に答えたかったのですが、資料の量が通常のコメントを超えていることに気づいたので、別の記事の形でこの答えを提示します。
長い間、質問者の頭の中にある具体的な構成が理解できませんでしたが、最終的には質問を明確にした後、回答を受け取り、それをまとめて引用します。
そして、そのようなDSLで問題はどのように解決されますか?「私が見ることができるオブジェクトのリストを表示しますか?データベースからすべてのレコードを掻き出すのではなく、何らかの方法でこれをSQLクエリに変換する必要があります。
サイトには何かのリストを表示するインターフェースがあります。たとえば、CMS管理領域の記事です。データベースには何万もの記事がありますが、通常、ユーザーは1ダースしかアクセスできません。特定のユーザーに表示される記事をデータベースから取得するにはどうすればよいですか?さて、誰もが見ることができるすべてのルールがある場合-コードから何らかのDSLに取り出されますか?
言い換えると、
select *
fromarticlesのようなクエリを作成する方法 はr.userId = currentUserIdの結合ロールrです。ここでarticle.owner
= currentUserId
OR(r.role in ['admin'、 'supevisor'])-合計の管理者
OR(r.domain = currentDomainId AND r.role in ['domain-admin'、 'domain-supervisor'])-管理者ドメイン
コードにはLINQ式の形式でそのようなルールがあり、この問題を解決できます。そして、そのようなタスクは、「メモリからアンロードされた1つのオブジェクトへのアクセスがあるかどうかを確認する」よりもさらに頻繁に発生します。
この構造を正しく理解し、リバースリバースエンジニアリング中に、この問題を解決するための初期データを引き出すことができたと思います。まず、マルチテナンシー(ドメイン)はタスクを複雑にし、それに応じて理解を複雑にするため、使用を省略します。前回の記事でそれらの使用例を示しました。
, , , 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 .
この記事を書くために使用した完全に機能的で自給自足のサンプルコードをGithubに投稿しました。興味があり、喜んでいる場合は、ダウンロードして再生できます。