Использование opa для abac для проверки утверждений пользователей в определенных политиках

#istio #envoyproxy #abac #opa #open-policy-agent

Вопрос:

Поэтому я пытаюсь внедрить довольно простую систему ABAC для своего приложения и столкнулся с агентом открытой политики во время своих расследований. Кажется, это хорошо подходит для моих нужд, но я просто не могу заставить его работать в моем случае использования, когда у меня есть объект пользователя, считываемый из утверждений jwt, который выглядит примерно так: { email: "1@1.com", role: "admin", location: "us" } . Я хочу проверить, имеет ли этот пользователь права доступа к определенному пути (который предоставляется в качестве входных данных, так же, как и пользователь). Так, например, я хочу предоставить права доступа /admin/us if user.role == admin and user.location == us . Я создал пример на игровой площадке rego, и он работает нормально, пока у пользователя есть точно такие же претензии, как написано в политике, но не работает, если у пользователя есть какие-либо дополнительные претензии:

 package play

default allow = false

allow {
    some p
    policy := data.policies[p]
    policy.request_path == input.request_path
    
    # check if all input.user[x] matches to a policy
    policy.user == input.user # works only if objects have the same keys and values
}
 

Я думал, что мог бы использовать пересечение, чтобы получить соответствующие ключи и сравнить это с определением политики, как это:

 # check if all input.user[x] matches to a policy
count(intersection(input.user[data.user])) == count(policy.user)
 

но это не работает, скорее всего, потому, что синтаксис неправильный.

Я также попытался использовать понимание, чтобы отфильтровать ключи от пользователя, а затем сравнить это с полным равенством со всеми политиками, но это тоже не сработало.

Не мог бы кто-нибудь, пожалуйста, подтолкнуть меня в правильном направлении или предоставить некоторые учебные материалы для opa/rego (официальных документов немного не хватает).

Вот полный пример на игровой площадке rego: https://play.openpolicyagent.org/p/ijtOjxXRKk

Ответ №1:

Я нашел способ сделать это с помощью object.union() :

 patchedpolicy := object.union(input.user, policy.user)
patchedpolicy == input.user
 

https://play.openpolicyagent.org/p/7LKqnCz7Wr

По сути, он копирует все недостающие ключи от пользователя в политику, а затем сравнивает их с вводом.

Однако это не сработает, если у пользователя вообще нет ключа, определенного в политике, что-то вроде:

 # this will evaluate to true even though it is not desired
user = { "email": "1@1.com", location: "us" }
policy = { "path": "/admin", user: { role: "admin" }
 

Это можно исправить с помощью дополнительного правила для проверки ключей input.user в соответствии с исправленной политикой:

 some k
input.user[k] == patchedpolicy[k]
 

https://play.openpolicyagent.org/p/muHCb2TBv3

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

Ответ №2:

То, с чем вы столкнулись здесь, — это отсутствие у Рего «для всех», или «универсальной количественной оценки». Смотрите документы по этой теме здесь.

Как вы уже отмечали, существует еще довольно много способов сделать это. Одним из них было бы использовать отрицание во вспомогательном правиле (т. Е. Какой-то ключ/значение в policy.user не входит input.user ). Другим вариантом было бы использовать понимание и сравнить количество всех совпадений с количеством требуемых атрибутов:

 package play

default allow = false

allow {
    some p
    policy := data.policies[p]
    policy.request_path == input.request_path
    
    required := count(policy.user)
    matches  := count([v |  v := policy.user[k]; v == input.user[k]])
    
    required == matches
}
 

Будущие версии OPA, вероятно, облегчат это.