Syntax for Models
Bir model yapılandırması (CONF), en az dört bölüm içermelidir:
[request_definition]
,[policy_definition]
,[policy_effect]
, ve[matchers]
.Eğer bir model Rol Tabanlı Erişim Kontrolü (RBAC) kullanıyorsa,
[role_definition]
bölümünü de içermelidir.Bir model yapılandırması (CONF) yorumlar içerebilir. Yorumlar
#
sembolü ile başlar ve#
sembolünden sonraki her şey yorum olarak kabul edilir.
İstek tanımı
[request_definition]
bölümü, e.Enforce(...)
fonksiyonundaki argümanları tanımlar.
[request_definition]
r = sub, obj, act
Bu örnekte, sub
, obj
, ve act
klasik erişim üçlüsünü temsil eder: konu (erişim sağlayan varlık), nesne (erişilen kaynak) ve eylem (erişim yöntemi). Ancak, kendi istek formatınızı özelleştirebilirsiniz. Örneğin, belirli bir kaynağı belirtmenize gerek yoksa sub, act
kullanabilirsiniz veya iki erişim varlığınız varsa sub, sub2, obj, act
kullanabilirsiniz.
Politika Tanımı
[policy_definition]
, bir politika için tanımı ifade eder. Politikanın anlamını belirler. Örneğin, aşağıdaki modelimiz var:
[policy_definition]
p = sub, obj, act
p2 = sub, act
Ve aşağıdaki politikamız var (eğer bir politika dosyasında ise):
p, alice, data1, read
p2, bob, write-all-objects
Bir politika içindeki her satır bir politika kuralı olarak adlandırılır. Her politika kuralı, p
veya p2
gibi bir politika tipi
ile başlar. Eğer birden fazla tanım varsa, politika tanımıyla eşleştirmek için kullanılır. Yukarıdaki politikada aşağıdaki bağlama gösterilmektedir. Bağlama eşleştiricide kullanılabilir.
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
Bir politika kuralındaki öğeler her zaman dizeler
olarak kabul edilir. Bu konuda herhangi bir sorunuz varsa, lütfen şu tartışmaya başvurun: https://github.com/casbin/casbin/issues/113
Politika Etkisi
[policy_effect]
, politika etkisi için tanımdır. Birden fazla politika kuralı isteğe uyarlarsa, erişim isteğinin onaylanıp onaylanmayacağını belirler. Örneğin, bir kural izin verir ve diğeri reddeder.
[policy_effect]
e = some(where (p.eft == allow))
Yukarıdaki politika etkisi, eğer izin ver
şeklinde eşleşen herhangi bir politika kuralı varsa, son etkinin izin ver
olduğu anlamına gelir (ayrıca izin-geçersiz kılma olarak da bilinir). p.eft
, bir politika için etkidir ve izin ver
ya da reddet
olabilir. İsteğe bağlıdır ve varsayılan değer allow
şeklindedir. Yukarıda belirtmediğimiz için, varsayılan değeri kullanır.
Politika etkisi için başka bir örnek:
[policy_effect]
e = !some(where (p.eft == deny))
Bu, eğer deny
şeklinde eşleşen bir politika kuralı yoksa, son etkinin allow
(deny-override olarak da bilinir) olduğu anlamına gelir. some
demek, eşleşen bir politika kuralının var olduğu anlamına gelir. any
demek, tüm eşleşen politikaların (burada kullanılmaz) olduğu anlamına gelir. Politika etkisi, mantıksal ifadelerle bile bağlantılı olabilir:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
Bu, en az bir tane allow
şeklinde eşleşen politika kuralı olması gerektiği ve hiçbir deny
şeklinde eşleşen politika kuralı olmaması gerektiği anlamına gelir. Bu şekilde, hem izin verme hem de reddetme yetkilendirmeleri desteklenir ve reddetme geçersiz kılar.
Politika etkisinin sözdizimini yukarıdaki gibi tasarlamış olsak da, mevcut uygulamalar sadece sabit kodlanmış politika etkilerini kullanmaktadır. Bunun nedeni, o seviyede esnekliğe pek ihtiyaç olmadığını bulmamızdır. Bu nedenle şimdilik, kendi özel politikanızı özelleştirmek yerine yerleşik bir politika etkisinden birini kullanmalısınız.
Desteklenen yerleşik politika etkileri şunlardır:
Politika Etkisi | Anlamı | Örnek |
---|---|---|
some(where (p.eft == allow)) | izin-geçersiz kılma | ACL, RBAC, vb. |
!some(where (p.eft == deny)) | reddet-geçersiz kıl | Reddet-geçersiz kıl |
bazı(nerede (p.eft == izin)) && !bazı(nerede (p.eft == reddet)) | izin-ve-reddet | İzin-ve-reddet |
öncelik(p.eft) || reddet | öncelik | Öncelik |
konuÖnceliği(p.eft) | rol bazlı öncelik | Subject-Priority |
Eşleştiriciler
[matchers]
politikalar için eşleştiricilerin tanımıdır. Eşleştiriciler, politikaların kurallarının talebe göre nasıl değerlendirileceğini belirleyen ifadelerdir.
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Yukarıdaki eşleştirici en basitidir ve bir talebin konusu, nesnesi ve eyleminin bir politika kuralındakiyle eşleşmesi gerektiği anlamına gelir.
+, -, *, /
gibi aritmetik operatörler ve &&, ||, !
gibi mantıksal operatörler eşleştiricilerde kullanılabilir.
Eşleştiricilerde ifadelerin sırası
İfadelerin sırası performansı büyük ölçüde etkileyebilir. Daha fazla detay için aşağıdaki örneğe göz atın:
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
}
Uygulama süresi çok uzun olabilir, 6 saniyeye kadar.
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
Ancak, eşleştiricilerdeki ifadelerin sırasını ayarlayarak ve fonksiyonlar gibi zaman alıcı ifadeleri arkaya koyarak, yürütme süresi çok kısa olacaktır.
Yukarıdaki örnekte eşleştiricilerdeki ifadelerin sırasını şu şekilde değiştirmek:
[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
Çoklu Bölüm Tipleri
Eğer birden fazla politikayı tanımlamanız veya birden fazla eşleştiriciye ihtiyacınız varsa, p2
veya m2
örneklerini kullanabilirsiniz. Aslında, yukarıda bahsedilen dört bölümün hepsi çoklu tipler kullanabilir ve sözdizimi r
ve ardından bir sayıdır, örneğin r2
veya e2
. Varsayılan olarak, bu dört bölüm bire bir eşleşmelidir. Örneğin, r2
bölümünüz sadece m2
eşleştiriciyi kullanarak p2
politikalarını eşleştirecektir.
enforce
metodunun ilk parametresi olarak bir EnforceContext
geçirebilirsiniz. EnforceContext
şu şekilde tanımlanmıştır:
- 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;
}
}
İşte bir örnek kullanım. Lütfen model ve policy dosyalarına başvurun. İstek şu şekildedir:
- 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
Özel Gramer
Ayrıca "in" operatörünü de kullanabilirsiniz, bu tek metin adına sahip olan operatördür. Bu operatör, sağ taraftaki dizinin, sol taraftaki değere eşit bir değer içerip içermediğini kontrol eder. Eşitlik == operatörü kullanılarak belirlenir ve bu kütüphane değerler arasındaki türleri kontrol etmez. İki değer interface{}'e dönüştürülebilir ve hala == ile eşitlik kontrolü yapılabilirse, beklendiği gibi çalışacaklardır. Dizinin parametre olarak kullanılabileceğini unutmayın, ancak bu bir []interface{} olmalıdır.
Ayrıca rbac_model_matcher_using_in_op, keyget2_model ve keyget_model dosyalarına da başvurun.
Örnek:
[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"}})
İfade Değerlendirici
Casbin'de eşleştirici değerlendirme, her dilin ifade değerlendiricileri tarafından uygulanmaktadır. Casbin, güçlerini birleştirerek birleşik PERM dilini sağlar. Burada sağlanan model sözdizimine ek olarak, bu ifade değerlendiriciler başka bir dil veya uygulama tarafından desteklenmeyebilecek ek işlevler sunabilir. Lütfen bu işlevselliği kullanırken dikkatli olun.
Her Casbin uygulaması tarafından kullanılan ifade değerlendiriciler şunlardır:
Uygulama | Dil | İfade Değerlendirici |
---|---|---|
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 |
Eğer Casbin ile bir performans sorunu yaşıyorsanız, muhtemelen ifade değerlendiricinin düşük verimliliği yüzündendir. Sorunu Casbin'e veya ifade değerlendiriciye doğrudan danışarak performansı hızlandırma konusunda tavsiye alabilirsiniz. Daha fazla ayrıntı için lütfen Kıyaslamalar bölümüne bakın.