Zum Hauptinhalt springen

Syntax for Models

  • Eine Modellkonfiguration (CONF) sollte mindestens vier Abschnitte haben: [request_definition], [policy_definition], [policy_effect] und [matchers].

  • Wenn ein Modell Role-Based Access Control (RBAC) verwendet, sollte es auch den Abschnitt [role_definition] enthalten.

  • Eine Modellkonfiguration (CONF) kann Kommentare enthalten. Kommentare beginnen mit dem # Symbol, und alles nach dem # Symbol wird auskommentiert.

Anforderungsdefinition

Der Abschnitt [request_definition] definiert die Argumente in der Funktion e.Enforce(...).

[request_definition]
r = sub, obj, act

In diesem Beispiel repräsentieren sub, obj und act das klassische Zugriffstriplett: das Subjekt (zugreifende Entität), das Objekt (zugegriffene Ressource) und die Aktion (Zugriffsmethode). Sie können jedoch Ihr eigenes Anforderungsformat anpassen. Zum Beispiel können Sie sub, act verwenden, wenn Sie keine bestimmte Ressource angeben müssen, oder sub, sub2, obj, act, wenn Sie zwei zugreifende Entitäten haben.

Richtliniendefinition

Die [policy_definition] ist die Definition für eine Richtlinie. Es definiert die Bedeutung der Richtlinie. Zum Beispiel haben wir das folgende Modell:

[policy_definition]
p = sub, obj, act
p2 = sub, act

Und wir haben die folgende Richtlinie (wenn in einer Richtliniendatei):

p, alice, data1, read
p2, bob, write-all-objects

Jede Zeile in einer Richtlinie wird als Richtlinienregel bezeichnet. Jede Richtlinienregel beginnt mit einem Richtlinientyp, wie p oder p2. Es wird verwendet, um die Richtliniendefinition abzugleichen, wenn es mehrere Definitionen gibt. Die obige Richtlinie zeigt die folgende Bindung. Die Bindung kann im Matcher verwendet werden.

(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
tip

Die Elemente in einer Richtlinienregel werden immer als Zeichenketten betrachtet. Wenn Sie dazu Fragen haben, verweisen Sie bitte auf die Diskussion unter: https://github.com/casbin/casbin/issues/113

Richtlinienwirkung

[policy_effect] ist die Definition für die Richtlinienwirkung. Es bestimmt, ob die Zugriffsanforderung genehmigt werden sollte, wenn mehrere Richtlinienregeln der Anforderung entsprechen. Zum Beispiel erlaubt eine Regel und die andere verbietet.

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

Die obige Richtlinienwirkung bedeutet, dass, wenn es irgendeine übereinstimmende Richtlinienregel von allow gibt, die endgültige Wirkung allow ist (auch bekannt als allow-override). p.eft ist die Wirkung für eine Richtlinie, und es kann entweder allow oder deny sein. Es ist optional, und der Standardwert ist allow. Da wir es oben nicht angegeben haben, verwendet es den Standardwert.

Ein weiteres Beispiel für den Effekt der Richtlinie ist:

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

Das bedeutet, dass, wenn es keine passenden Richtlinienregeln für deny gibt, der endgültige Effekt allow ist (auch bekannt als deny-override). some bedeutet, dass es eine passende Richtlinienregel gibt. any bedeutet, dass alle passenden Richtlinienregeln (hier nicht verwendet). Der Effekt der Richtlinie kann sogar mit logischen Ausdrücken verbunden werden:

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

Das bedeutet, dass es mindestens eine passende Richtlinienregel für allow geben muss und es darf keine passende Richtlinienregel für deny geben. Daher werden auf diese Weise sowohl allow- als auch deny-Autorisierungen unterstützt und deny hat Vorrang.

notiz

Obwohl wir die Syntax des Richtlinieneffekts wie oben entworfen haben, verwenden die aktuellen Implementierungen nur fest codierte Richtlinieneffekte. Das liegt daran, dass wir festgestellt haben, dass es nicht viel Bedarf für diese Flexibilität gibt. Daher müssen Sie derzeit einen der eingebauten Richtlinieneffekte verwenden, anstatt Ihren eigenen anzupassen.

Die unterstützten eingebauten Richtlinieneffekte sind:

Richtlinien EffektBedeutungBeispiel
some(where (p.eft == allow))allow-overrideACL, RBAC, etc.
!some(where (p.eft == deny))deny-overrideDeny-override
some(where (p.eft == allow)) && !some(where (p.eft == deny))allow-and-denyAllow-and-deny
priority(p.eft) || denypriorityPriority
subjectPriority(p.eft)Priorität basierend auf RolleSubject-Priority

Matchers

[matchers] ist die Definition für Policy-Matcher. Die Matcher sind Ausdrücke, die definieren, wie die Policy-Regeln gegen die Anfrage ausgewertet werden.

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

Der oben genannte Matcher ist der einfachste und bedeutet, dass das Subjekt, das Objekt und die Aktion in einer Anfrage mit denen in einer Policy-Regel übereinstimmen sollten.

In Matchern können arithmetische Operatoren wie +, -, *, / und logische Operatoren wie &&, ||, ! verwendet werden.

Reihenfolge der Ausdrücke in Matchern

Die Reihenfolge der Ausdrücke kann die Leistung erheblich beeinflussen. Schauen Sie sich das folgende Beispiel für weitere Details an:

const rbac_models = `
[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
`

func TestManyRoles(t *testing.T) {

m, _ := model.NewModelFromString(rbac_models)
e, _ := NewEnforcer(m, false)

roles := []string{"admin", "manager", "developer", "tester"}

// 2500 projects
for nbPrj := 1; nbPrj < 2500; nbPrj++ {
// 4 objects and 1 role per object (so 4 roles)
for _, role := range roles {
roleDB := fmt.Sprintf("%s_project:%d", role, nbPrj)
objectDB := fmt.Sprintf("/projects/%d", nbPrj)
e.AddPolicy(roleDB, objectDB, "GET")
}
jasmineRole := fmt.Sprintf("%s_project:%d", roles[1], nbPrj)
e.AddGroupingPolicy("jasmine", jasmineRole)
}

e.AddGroupingPolicy("abu", "manager_project:1")
e.AddGroupingPolicy("abu", "manager_project:2499")

// With same number of policies
// User 'abu' has only two roles
// User 'jasmine' has many roles (1 role per policy, here 2500 roles)

request := func(subject, object, action string) {
t0 := time.Now()
resp, _ := e.Enforce(subject, object, action)
tElapse := time.Since(t0)
t.Logf("RESPONSE %-10s %s\t %s : %5v IN: %+v", subject, object, action, resp, tElapse)
if tElapse > time.Millisecond*100 {
t.Errorf("More than 100 milliseconds for %s %s %s : %+v", subject, object, action, tElapse)
}
}

request("abu", "/projects/1", "GET") // really fast because only 2 roles in all policies and at the beginning of the casbin_rule table
request("abu", "/projects/2499", "GET") // fast because only 2 roles in all policies
request("jasmine", "/projects/1", "GET") // really fast at the beginning of the casbin_rule table

request("jasmine", "/projects/2499", "GET") // slow and fails the only 1st time <<<< pb here
request("jasmine", "/projects/2499", "GET") // fast maybe due to internal cache mechanism

// same issue with non-existing roles
// request("jasmine", "/projects/999999", "GET") // slow fails the only 1st time <<<< pb here
// request("jasmine", "/projects/999999", "GET") // fast maybe due to internal cache mechanism
}

Die Durchsetzungszeit kann sehr lang sein, bis zu 6 Sekunden.

go test -run ^TestManyRoles$ github.com/casbin/casbin/v2 -v

=== RUN TestManyRoles
rbac_api_test.go:598: RESPONSE abu /projects/1 GET : true IN: 438.379µs
rbac_api_test.go:598: RESPONSE abu /projects/2499 GET : true IN: 39.005173ms
rbac_api_test.go:598: RESPONSE jasmine /projects/1 GET : true IN: 1.774319ms
rbac_api_test.go:598: RESPONSE jasmine /projects/2499 GET : true IN: 6.164071648s
rbac_api_test.go:600: More than 100 milliseconds for jasmine /projects/2499 GET : 6.164071648s
rbac_api_test.go:598: RESPONSE jasmine /projects/2499 GET : true IN: 12.164122ms
--- FAIL: TestManyRoles (6.24s)
FAIL
FAIL github.com/casbin/casbin/v2 6.244s
FAIL

Wenn wir jedoch die Reihenfolge der Ausdrücke in Matchern anpassen und zeitaufwändigere Ausdrücke wie Funktionen nach hinten stellen, wird die Ausführungszeit sehr kurz sein.

Ändern der Reihenfolge der Ausdrücke in Matchern im obigen Beispiel zu:

[matchers]
m = r.obj == p.obj && g(r.sub, p.sub) && r.act == p.act
go test -run ^TestManyRoles$ github.com/casbin/casbin/v2 -v
=== RUN TestManyRoles
rbac_api_test.go:599: RESPONSE abu /projects/1 GET : true IN: 786.635µs
rbac_api_test.go:599: RESPONSE abu /projects/2499 GET : true IN: 4.933064ms
rbac_api_test.go:599: RESPONSE jasmine /projects/1 GET : true IN: 2.908534ms
rbac_api_test.go:599: RESPONSE jasmine /projects/2499 GET : true IN: 7.292963ms
rbac_api_test.go:599: RESPONSE jasmine /projects/2499 GET : true IN: 6.168307ms
--- PASS: TestManyRoles (0.05s)
PASS
ok github.com/casbin/casbin/v2 0.053s

Mehrere Abschnittstypen

Wenn Sie mehrere Policy-Definitionen oder mehrere Matcher benötigen, können Sie p2 oder m2 als Beispiele verwenden. Tatsächlich können alle vier oben genannten Abschnitte mehrere Typen verwenden, und die Syntax ist r gefolgt von einer Zahl, wie r2 oder e2. Standardmäßig sollten diese vier Abschnitte eins zu eins entsprechen. Zum Beispiel wird Ihr Abschnitt r2 nur den Matcher m2 verwenden, um p2-Richtlinien abzugleichen.

Sie können einen EnforceContext als ersten Parameter der enforce-Methode übergeben, um die Typen anzugeben. Der EnforceContext ist wie folgt definiert:

EnforceContext{"r2","p2","e2","m2"}
type EnforceContext struct {
RType string
PType string
EType string
MType string
}

Hier ist ein Beispiel für die Verwendung. Bitte beziehen Sie sich auf das Modell und die Richtlinie. Die Anfrage ist wie folgt:

// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
enforceContext := NewEnforceContext("2")
// You can also specify a certain type individually
enforceContext.EType = "e"
// Don't pass in EnforceContext; the default is r, p, e, m
e.Enforce("alice", "data2", "read") // true
// Pass in EnforceContext
e.Enforce(enforceContext, struct{ Age int }{Age: 70}, "/data1", "read") //false
e.Enforce(enforceContext, struct{ Age int }{Age: 30}, "/data1", "read") //true

Spezielle Grammatik

Sie könnten auch den Operator "in" verwenden, der der einzige Operator mit einem Textnamen ist. Dieser Operator überprüft das Array auf der rechten Seite, um zu sehen, ob es einen Wert enthält, der dem Wert auf der linken Seite entspricht. Die Gleichheit wird durch Verwendung des == Operators bestimmt, und diese Bibliothek überprüft nicht die Typen zwischen den Werten. Solange zwei Werte in interface{} umgewandelt werden können und immer noch auf Gleichheit mit == überprüft werden können, werden sie wie erwartet funktionieren. Beachten Sie, dass Sie einen Parameter für das Array verwenden können, aber es muss ein []interface{} sein.

Siehe auch rbac_model_matcher_using_in_op, keyget2_model und keyget_model.

Beispiel:

[request_definition]
r = sub, obj
...
[matchers]
m = r.sub.Name in (r.obj.Admins)
e.Enforce(Sub{Name: "alice"}, Obj{Name: "a book", Admins: []interface{}{"alice", "bob"}})

Ausdrucksbewerter

Die Matcher-Bewertung in Casbin wird durch Ausdrucksbewerter in jeder Sprache implementiert. Casbin integriert ihre Kräfte, um die einheitliche PERM-Sprache bereitzustellen. Zusätzlich zur hier bereitgestellten Modellsyntax können diese Ausdrucksauswerter zusätzliche Funktionen bieten, die möglicherweise nicht von einer anderen Sprache oder Implementierung unterstützt werden. Bitte seien Sie vorsichtig bei der Verwendung dieser Funktion.

Die von jeder Casbin-Implementierung verwendeten Ausdrucksauswerter sind wie folgt:

ImplementierungSpracheAusdrucksauswerter
CasbinGolanghttps://github.com/Knetic/govaluate
jCasbinJavahttps://github.com/killme2008/aviator
Node-CasbinNode.jshttps://github.com/donmccurdy/expression-eval
PHP-CasbinPHPhttps://github.com/symfony/expression-language
PyCasbinPythonhttps://github.com/danthedeckie/simpleeval
Casbin.NETC#https://github.com/davideicardi/DynamicExpresso
Casbin4DDelphihttps://github.com/casbin4d/Casbin4D/tree/master/SourceCode/Common/Third%20Party/TExpressionParser
casbin-rsRosthttps://github.com/jonathandturner/rhai
casbin-cppC++https://github.com/ArashPartow/exprtk
notiz

Wenn Sie auf ein Leistungsproblem mit Casbin stoßen, wird dies wahrscheinlich durch die geringe Effizienz des Ausdrucksauswerters verursacht. Sie können das Problem an Casbin oder den Ausdrucksauswerter direkt zur Beratung zur Beschleunigung der Leistung richten. Für weitere Details verweisen Sie bitte auf den Abschnitt Benchmarks.