Syntax for Models
Une configuration de modèle (CONF) doit avoir au moins quatre sections :
[request_definition]
,[policy_definition]
,[policy_effect]
, et[matchers]
.Si un modèle utilise le contrôle d'accès basé sur les rôles (RBAC), il doit également inclure la section
[role_definition]
.Une configuration de modèle (CONF) peut contenir des commentaires. Les commentaires commencent par le symbole
#
, et tout ce qui suit le symbole#
sera commenté.
Définition de la demande
La section [request_definition]
définit les arguments de la fonction e.Enforce(...)
.
[request_definition]
r = sub, obj, act
Dans cet exemple, sub
, obj
, et act
représentent le triple d'accès classique : le sujet (entité accédant), l'objet (ressource accédée), et l'action (méthode d'accès). Cependant, vous pouvez personnaliser votre propre format de demande. Par exemple, vous pouvez utiliser sub, act
si vous n'avez pas besoin de spécifier une ressource particulière, ou sub, sub2, obj, act
si vous avez deux entités accédant.
Définition de la politique
La [policy_definition]
est la définition d'une politique. Elle définit le sens de la politique. Par exemple, nous avons le modèle suivant :
[policy_definition]
p = sub, obj, act
p2 = sub, act
Et nous avons la politique suivante (si dans un fichier de politique) :
p, alice, data1, read
p2, bob, write-all-objects
Chaque ligne d'une politique est appelée une règle de politique. Chaque règle de politique commence par un type de politique
, tel que p
ou p2
. Il est utilisé pour faire correspondre la définition de la politique s'il y a plusieurs définitions. La politique ci-dessus montre la liaison suivante. La liaison peut être utilisée dans le comparateur.
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
Les éléments d'une règle de politique sont toujours considérés comme des chaînes de caractères
. Si vous avez des questions à ce sujet, veuillez vous référer à la discussion à : https://github.com/casbin/casbin/issues/113
Effet de la politique
[policy_effect]
est la définition de l'effet de la politique. Il détermine si la demande d'accès doit être approuvée si plusieurs règles de politique correspondent à la demande. Par exemple, une règle autorise et l'autre refuse.
[policy_effect]
e = some(where (p.eft == allow))
L'effet de la politique ci-dessus signifie que s'il y a une règle de politique correspondante de allow
, l'effet final est allow
(également connu sous le nom de allow-override). p.eft
est l'effet d'une politique, et il peut être soit allow
soit deny
. Il est facultatif, et la valeur par défaut est allow
. Comme nous ne l'avons pas spécifié ci-dessus, il utilise la valeur par défaut.
Un autre exemple de l'effet de la politique est :
[policy_effect]
e = !some(where (p.eft == deny))
Cela signifie que s'il n'y a pas de règles de politique correspondantes de deny
, l'effet final est allow
(également connu sous le nom de deny-override). some
signifie qu'il existe une règle de politique correspondante. any
signifie que toutes les règles de politique correspondantes (non utilisées ici). L'effet de la politique peut même être lié à des expressions logiques :
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
Cela signifie qu'il doit y avoir au moins une règle de politique correspondante de allow
, et il ne peut y avoir aucune règle de politique correspondante de deny
. Par conséquent, de cette manière, les autorisations allow et deny sont prises en charge, et le deny prévaut.
Bien que nous ayons conçu la syntaxe de l'effet de la politique comme ci-dessus, les implémentations actuelles n'utilisent que des effets de politique codés en dur. C'est parce que nous avons constaté qu'il n'y a pas beaucoup de besoin pour ce niveau de flexibilité. Donc pour l'instant, vous devez utiliser l'un des effets de politique intégrés au lieu de personnaliser le vôtre.
Les effets de politique intégrés pris en charge sont :
Effet de la politique | Signification | Exemple |
---|---|---|
some(where (p.eft == allow)) | allow-override | ACL, RBAC, etc. |
!some(where (p.eft == deny)) | deny-override | Deny-override |
some(where (p.eft == allow)) && !some(where (p.eft == deny)) | allow-and-deny | Allow-and-deny |
priority(p.eft) || deny | priority | Priority |
subjectPriority(p.eft) | priorité basée sur le rôle | Subject-Priority |
Matchers
[matchers]
est la définition des comparateurs de politique. Les comparateurs sont des expressions qui définissent comment les règles de politique sont évaluées par rapport à la demande.
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Le comparateur ci-dessus est le plus simple et signifie que le sujet, l'objet et l'action dans une demande doivent correspondre à ceux d'une règle de politique.
Des opérateurs arithmétiques comme +, -, *, /
et des opérateurs logiques comme &&, ||, !
peuvent être utilisés dans les comparateurs.
Ordre des expressions dans les comparateurs
L'ordre des expressions peut grandement affecter les performances. Jetez un coup d'œil à l'exemple suivant pour plus de détails :
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
}
Le temps d'application peut être très long, jusqu'à 6 secondes.
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
Cependant, si nous ajustons l'ordre des expressions dans les comparateurs et mettons des expressions plus longues comme des fonctions derrière, le temps d'exécution sera très court.
Changer l'ordre des expressions dans les comparateurs dans l'exemple ci-dessus pour :
[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
Types de sections multiples
Si vous avez besoin de plusieurs définitions de politiques ou de plusieurs comparateurs, vous pouvez utiliser p2
ou m2
comme exemples. En fait, les quatre sections mentionnées ci-dessus peuvent utiliser plusieurs types, et la syntaxe est r
suivi d'un nombre, comme r2
ou e2
. Par défaut, ces quatre sections doivent correspondre une à une. Par exemple, votre section r2
n'utilisera que le comparateur m2
pour correspondre aux politiques p2
.
Vous pouvez passer un EnforceContext
comme premier paramètre de la méthode enforce
pour spécifier les types. Le EnforceContext
est défini comme suit :
- Go
- Node.js
- Java
EnforceContext{"r2","p2","e2","m2"}
type EnforceContext struct {
RType string
PType string
EType string
MType string
}
const enforceContext = new EnforceContext('r2', 'p2', 'e2', 'm2');
class EnforceContext {
constructor(rType, pType, eType, mType) {
this.pType = pType;
this.eType = eType;
this.mType = mType;
this.rType = rType;
}
}
EnforceContext enforceContext = new EnforceContext("2");
public class EnforceContext {
private String pType;
private String eType;
private String mType;
private String rType;
public EnforceContext(String suffix) {
this.pType = "p" + suffix;
this.eType = "e" + suffix;
this.mType = "m" + suffix;
this.rType = "r" + suffix;
}
}
Voici un exemple d'utilisation. Veuillez vous référer au modèle et à la politique. La demande est la suivante :
- Go
- Node.js
- Java
// 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
// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
const enforceContext = new 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, {Age: 70}, "/data1", "read") //false
e.Enforce(enforceContext, {Age: 30}, "/data1", "read") //true
// Pass in a suffix as a parameter to NewEnforceContext, such as 2 or 3, and it will create r2, p2, etc.
EnforceContext enforceContext = new EnforceContext("2");
// You can also specify a certain type individually
enforceContext.seteType("e");
// Don't pass in EnforceContext; the default is r, p, e, m
e.enforce("alice", "data2", "read"); // true
// Pass in EnforceContext
// TestEvalRule is located in https://github.com/casbin/jcasbin/blob/master/src/test/java/org/casbin/jcasbin/main/AbacAPIUnitTest.java#L56
e.enforce(enforceContext, new AbacAPIUnitTest.TestEvalRule("alice", 70), "/data1", "read"); // false
e.enforce(enforceContext, new AbacAPIUnitTest.TestEvalRule("alice", 30), "/data1", "read"); // true
Grammaire spéciale
Vous pouvez également utiliser l'opérateur "in", qui est le seul opérateur avec un nom de texte. Cet opérateur vérifie le tableau du côté droit pour voir s'il contient une valeur égale à la valeur du côté gauche. L'égalité est déterminée en utilisant l'opérateur ==, et cette bibliothèque ne vérifie pas les types entre les valeurs. Tant que deux valeurs peuvent être converties en interface{} et peuvent encore être vérifiées pour l'égalité avec ==, elles agiront comme prévu. Notez que vous pouvez utiliser un paramètre pour le tableau, mais il doit être un []interface{}.
Voir aussi rbac_model_matcher_using_in_op, keyget2_model, et keyget_model.
Exemple :
[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"}})
Évaluateur d'expression
L'évaluation des comparateurs dans Casbin est mise en œuvre par des évaluateurs d'expression dans chaque langue. Casbin intègre leurs pouvoirs pour fournir le langage PERM unifié. En plus de la syntaxe du modèle fournie ici, ces évaluateurs d'expressions peuvent offrir des fonctionnalités supplémentaires qui pourraient ne pas être prises en charge par une autre langue ou mise en œuvre. Soyez prudent lorsque vous utilisez cette fonctionnalité.
Les évaluateurs d'expression utilisés par chaque implémentation de Casbin sont les suivants :
Implémentation | Langage | Évaluateur d'Expression |
---|---|---|
Casbin | Golang | https://github.com/Knetic/govaluate |
jCasbin | Java | https://github.com/killme2008/aviator |
Node-Casbin | Node.js | https://github.com/donmccurdy/expression-eval |
PHP-Casbin | PHP | https://github.com/symfony/expression-language |
PyCasbin | Python | https://github.com/danthedeckie/simpleeval |
Casbin.NET | C# | https://github.com/davideicardi/DynamicExpresso |
Casbin4D | Delphi | https://github.com/casbin4d/Casbin4D/tree/master/SourceCode/Common/Third%20Party/TExpressionParser |
casbin-rs | Rust | https://github.com/jonathandturner/rhai |
casbin-cpp | C++ | https://github.com/ArashPartow/exprtk |
Si vous rencontrez un problème de performance avec Casbin, il est probablement causé par la faible efficacité de l'évaluateur d'expression. Vous pouvez adresser le problème à Casbin ou à l'évaluateur d'expression directement pour obtenir des conseils sur l'accélération des performances. Pour plus de détails, veuillez vous référer à la section Benchmarks.