Перейти к основному контенту

Syntax for Models

  • Конфигурация модели (CONF) должна содержать как минимум четыре раздела: [request_definition], [policy_definition], [policy_effect] и [matchers].

  • Если модель использует управление доступом на основе ролей (RBAC), она также должна включать раздел [role_definition].

  • Конфигурация модели (CONF) может содержать комментарии. Комментарии начинаются с символа #, и все, что следует после символа #, будет закомментировано.

Определение запроса

Раздел [request_definition] определяет аргументы в функции e.Enforce(...).

[request_definition]
r = sub, obj, act

В этом примере sub, obj и act представляют классическую тройку доступа: субъект (сущность, осуществляющая доступ), объект (ресурс, к которому осуществляется доступ) и действие (метод доступа). Однако вы можете настроить свой собственный формат запроса. Например, вы можете использовать sub, act, если вам не нужно указывать конкретный ресурс, или sub, sub2, obj, act, если у вас есть две сущности, осуществляющие доступ.

Определение политики

[policy_definition] - это определение политики. Оно определяет смысл политики. Например, у нас есть следующая модель:

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

И у нас есть следующая политика (если в файле политики):

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

Каждая строка в политике называется правилом политики. Каждое правило политики начинается с типа политики, такого как p или p2. Он используется для сопоставления определения политики, если есть несколько определений. Вышеуказанная политика показывает следующую привязку. Привязка может быть использована в сопоставителе.

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

Элементы в правиле политики всегда рассматриваются как строки. Если у вас есть вопросы об этом, пожалуйста, обратитесь к обсуждению по адресу: https://github.com/casbin/casbin/issues/113

Эффект политики

[policy_effect] - это определение эффекта политики. Он определяет, следует ли одобрить запрос на доступ, если несколько правил политики соответствуют запросу. Например, одно правило разрешает, а другое запрещает.

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

Вышеуказанный эффект политики означает, что если есть какое-либо совпадающее правило политики allow, то окончательный эффект - allow (также известный как переопределение разрешения). p.eft - это эффект для политики, и он может быть либо allow, либо deny. Это необязательно, и значение по умолчанию - allow. Поскольку мы не указали его выше, используется значение по умолчанию.

Еще один пример влияния политики:

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

Это означает, что если нет соответствующих правил политики «запретить», то окончательный эффект - «разрешить» (также известный как переопределение запрета). some означает, что существует одно соответствующее правило политики. any означает, что все соответствующие правила политики (здесь не используются). Эффект политики может даже быть связан с логическими выражениями:

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

Это означает, что должно быть хотя бы одно соответствующее правило политики «разрешить», и не может быть ни одного соответствующего правила политики «запретить». Таким образом, поддерживаются как разрешения, так и запреты, причем запрет имеет приоритет.

заметка

Хотя мы разработали синтаксис эффекта политики, как указано выше, текущие реализации используют только жестко закодированные эффекты политики. Это потому, что мы обнаружили, что нет большой необходимости в такой гибкости. Поэтому на данный момент вы должны использовать один из встроенных эффектов политики, а не настраивать свой собственный.

Поддерживаемые встроенные эффекты политики:

Эффект политикиЗначениеПример
some(where (p.eft == allow))allow-overrideACL, RBAC и т.д.
!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)приоритет на основе ролиSubject-Priority

Сопоставители

[matchers] - это определение для сопоставителей политики. Сопоставители - это выражения, которые определяют, как правила политики оцениваются по отношению к запросу.

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

Вышеуказанный сопоставитель является самым простым и означает, что субъект, объект и действие в запросе должны соответствовать тем, которые в правиле политики.

В сопоставителях можно использовать арифметические операторы, такие как +, -, *, /, и логические операторы, такие как &&, ||, !.

Порядок выражений в сопоставителях

Порядок выражений может существенно влиять на производительность. Посмотрите на следующий пример для более подробной информации:

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
}

Время принятия решения может быть очень долгим, до 6 секунд.

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

Однако, если мы скорректируем порядок выражений в сопоставителях и поместим более трудоемкие выражения, такие как функции, позади, время выполнения будет очень коротким.

Изменение порядка выражений в сопоставителях в приведенном выше примере на:

[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

Несколько типов разделов

Если вам нужны несколько определений политики или несколько сопоставителей, вы можете использовать p2 или m2 в качестве примеров. На самом деле, все четыре упомянутых выше раздела могут использовать несколько типов, и синтаксис - это r, за которым следует число, например, r2 или e2. По умолчанию эти четыре раздела должны соответствовать один-к-одному. Например, ваш раздел r2 будет использовать только сопоставитель m2 для сопоставления политик p2.

Вы можете передать EnforceContext в качестве первого параметра метода enforce для указания типов. EnforceContext определен следующим образом:

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

Вот пример использования. Пожалуйста, обратитесь к model и policy. Запрос выглядит следующим образом:

// 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

Специальная грамматика

Вы также можете использовать оператор "in", который является единственным оператором с текстовым названием. Этот оператор проверяет массив с правой стороны, чтобы увидеть, содержит ли он значение, равное значению с левой стороны. Равенство определяется с помощью оператора ==, и эта библиотека не проверяет типы между значениями. Пока два значения могут быть приведены к interface{} и все еще могут быть проверены на равенство с ==, они будут действовать как ожидается. Обратите внимание, что вы можете использовать параметр для массива, но он должен быть []interface{}.

Также обратитесь к rbac_model_matcher_using_in_op, keyget2_model и keyget_model.

Пример:

[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"}})

Оценщик выражений

Оценка сопоставителя в Casbin реализована с помощью оценщиков выражений на каждом языке. Casbin объединяет свои силы для предоставления единого языка PERM. В дополнение к предоставленному здесь синтаксису модели, эти оценщики выражений могут предлагать дополнительные функции, которые могут не поддерживаться другим языком или реализацией. Пожалуйста, будьте осторожны при использовании этой функциональности.

Оценщики выражений, используемые каждой реализацией Casbin, следующие:

РеализацияЯзыкОценщик выражений
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-rsRusthttps://github.com/jonathandturner/rhai
casbin-cppC++https://github.com/ArashPartow/exprtk
заметка

Если вы столкнулись с проблемой производительности Casbin, скорее всего, причина в низкой эффективности вычислительного выражения. Вы можете обратиться с этой проблемой к Casbin или напрямую к вычислительному выражению за советом по ускорению производительности. Для получения дополнительной информации, пожалуйста, обратитесь к разделу Benchmarks.