Flexible Autorisierung mit Casbin und PERM. Ein praktisches Beispiel

Nach dem Schreiben des vorherigen Artikels über die PERM-Sprache und die Casbin-Bibliothek stellten sich Fragen . Und nicht nur eine Person, und ich wollte zuerst in einem Kommentar antworten, sondern mir wurde klar, dass das Volumen des Materials über den üblichen Kommentar hinausgeht. Deshalb werde ich diese Antwort in Form eines separaten Artikels präsentieren.







Lange Zeit konnte ich die spezifische Konstruktion, die sich im Kopf der Fragesteller befand, nicht verstehen, aber am Ende erhielt ich nach Klärung der Fragen Antworten, deren Zusammenstellung ich im Zitat geben werde.







Und wie wird mit solchen DSLs das Problem gelöst? Zeigen Sie die Liste der Objekte, die ich sehen kann? Es ist notwendig, dies irgendwie in eine SQL-Abfrage zu übersetzen, um nicht alle Datensätze aus der Datenbank zu entfernen.



Es gibt eine Oberfläche auf der Site, die eine Liste von etwas zeigt. Angenommen, Artikel im CMS-Administrationsbereich. Es gibt Zehntausende von Artikeln aus der Datenbank, aber normalerweise hat der Benutzer nur Zugriff auf ein Dutzend. Wie erhalte ich Artikel aus der Datenbank, die für einen bestimmten Benutzer sichtbar sind? Nun, wenn wir alle Regeln haben, die jeder sehen kann - aus dem Code in eine Art DSL herausgenommen?

Mit anderen Worten - wie schreibe ich eine Abfrage wie



select * aus Artikeln und

Join-Rollen r auf r.userId = currentUserId,

wobei article.owner = currentUserId

OR (r.role in ['admin', 'supevisor']) - admin des gesamten

OR (r.domain = currentDomainId AND r.role in ['domain-admin', 'domain-Supervisor']) - admin des Domäne



Ich habe solche Regeln im Code in Form von LINQ-Ausdrücken, und ich kann dieses Problem lösen. Und eine solche Aufgabe tritt noch häufiger auf als "Überprüfen, ob Zugriff auf ein aus dem Speicher entladenes Objekt besteht".

Ich hoffe, ich habe diese Konstruktion richtig verstanden und konnte während des Reverse Reverse Engineering die ersten Daten zur Lösung dieses Problems abrufen. Zunächst werden wir auf die Verwendung von Mandantenfähigkeit (Domains) verzichten, da diese die Aufgabe und dementsprechend das Verständnis erschweren. Ich habe im letzten Artikel ein Beispiel für ihre Verwendung gegeben.







, , , Casbin.









CMS, . user



.       , admin



supervisor



. supervisor



, admin



supervisor



, , .







, :



CMS:

DB-Tabellenschema







Users:

Inhalt der Benutzertabelle







:

Roles:

Inhalt der Rollentabelle







, Piter , Bob — . Alice , , .







Articles:

Inhalt der Artikeltabelle







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





Beispielergebnis für 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'))
      
      





Beispielergebnis für 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'))
      
      





Beispielergebnis für Benutzer







, 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 .







Ich habe einen voll funktionsfähigen und autarken Beispielcode veröffentlicht, mit dem ich diesen Artikel auf meinem Github geschrieben habe . Sie können ihn herunterladen und spielen, wenn Sie interessiert und bereit sind.







Nützliche Links






All Articles