
Декларативная библиотека авторизации с DSL для написания правил политик, условий и кэширования. Обеспечивает масштабируемое, DRY управление правами доступа для приложений на Ruby.
DeclarativePolicy: Декларативная библиотека авторизацииЭта библиотека предоставляет DSL для написания политик авторизации.
Она может использоваться для отделения логики от прав доступа и применяется в production в масштабе GitLab.com.
Оригинальным автором библиотеки является Jeanine Adkisson, а авторские права принадлежат GitLab.
Добавьте эту строку в ваш Gemfile:
gem 'declarative_policy'
Затем выполните:
$ bundle install
Или установите самостоятельно:
$ gem install declarative_policy
require 'declarative_policy'
class User
attr_reader :name
def initialize(name:)
@name = name
end
end
class Vehicle
def initialize(owner:, trusted: [])
@owner = owner
@trusted = trusted
end
def owner?(user)
@owner.name == user.name
end
def trusted?(user)
@owner.name == user.name || @trusted.detect { |t| t.name == user.name }
end
end
class VehiclePolicy < DeclarativePolicy::Base
condition(:owns) { @subject.owner?(@user) }
condition(:trusted) { @subject.trusted?(@user) }
rule { owns }.enable :sell_vehicle
rule { trusted }.enable :drive_vehicle
end
jack = User.new(name: 'jack')
jill = User.new(name: 'jill')
jacks_vehicle = Vehicle.new(owner: jack, trusted: [jill])
jills_vehicle = Vehicle.new(owner: jill, trusted: [jack])
puts "Jack can drive Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:drive_vehicle)}"
puts "Jack can drive Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:drive_vehicle)}"
puts "Jack can sell Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:sell_vehicle)}"
puts "Jack can sell Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:sell_vehicle)}"
$ ruby example.rb
Jack can drive Jack's vehicle? -> true
Jack can drive Jill's vehicle? -> true
Jack can sell Jack's vehicle? -> true
Jack can sell Jill's vehicle? -> false
Основная абстракция этой библиотеки — Policy (Политика). Политики объединяют:
conditions) о состоянии мираrules)Эта библиотека существует для определения истинностного значения утверждений вида:
Пользователь Предикат [Субъект]
Переименование User в Actor и Subject в Resource обсуждается в этом issue.
Например:
user :is_aliveuser :can_drive caruser :can_sell carЭто достигается путём связывания Policy (набора правил о том, какие утверждения истинны) с объектами предложений. Утверждение считается истинным, если никакое правило его prevent (не запрещает), и хотя бы одно правило enables (разрешает).
Например, представим, что у нас есть модель данных с транспортными средствами и пользователями, и мы хотим узнать, может ли пользователь управлять транспортным средством. Нам понадобится VehiclePolicy:
class VehiclePolicy < DeclarativePolicy::Base
# релевантные факты
condition(:owns) { @subject.owner == @user }
condition(:has_access_to) { @subject.owner.trusts?(@user) }
condition(:old_enough_to_drive) { @user.age >= laws.minimum_age }
condition(:has_driving_license) { @user.driving_license&.valid? }
# дорогие правила могут иметь 'score'. Чем выше score, тем 'дороже' вычисление
condition(:owns, score: 0) { @subject.owner == @user }
condition(:has_access_to, score: 3) { @subject.owner.trusts?(@user) }
condition(:intoxicated, score: 5) { @user.blood_alcohol > laws.max_blood_alcohol }
# выводы, которые мы можем сделать:
rule { owns }.enable :drive_vehicle
rule { has_access_to }.enable :drive_vehicle
rule { ~old_enough_to_drive }.prevent :drive_vehicle
rule { intoxicated }.prevent :drive_vehicle
rule { ~has_driving_license }.prevent :drive_vehicle
# мы можем использовать методы для абстрагирования общей логики
def laws
@subject.registration.country.driving_laws
end
end
Несколько замечаний: мы могли бы записать это как одно большое правило ((owns | has_access_to) & old_enough_to_drive & ~intoxicated & has_driving_license), но здесь видны некоторые особенности, которые делают декларативные политики масштабируемыми для больших систем: правила можно разбить на мелкие элементы и компоновать в более крупные правила. Новые условия и правила можно добавлять в любое время.
Что менее очевидно, так это то, что многие оптимизации производительности обрабатываются для нас автоматически:
Короче говоря, эта библиотека стремится быть декларативной: мы объявляем важные правила, а библиотека организует их оценку.
Кэширование — особенно ценная функция политик. Если мы добавим новые правила о продаже транспортного средства, например:
rule { owns }.enable :sell_vehicle
То факт владения может быть разделён между разными вызовами политики, что сокращает обращения к базе данных и другие дорогие операции ввода-вывода.
Мы можем проверить решение политики с помощью:
cache = Session.current_session
policy = DeclarativePolicy.policy_for(user, car, cache: cache)
policy.can?(:drive_vehicle)
Для получения более подробной информации об использовании см. документацию.
После клонирования репозитория выполните bundle install для установки зависимостей. Затем выполните rake spec для запуска тестов. Вы также можете запустить bin/console для интерактивного режима, который позволит вам поэкспериментировать.
Чтобы установить этот gem на локальную машину, выполните bundle exec rake install. Чтобы выпустить новую версию, обновите номер версии в version.rb, а затем выполните bundle exec rake release, что создаст git-тег для версии, отправит коммиты и теги, а также отправит файл .gem на rubygems.org.
Более подробная информация о политиках и пользовательских ролях доступна на следующих страницах:
Отчёты об ошибках и запросы на слияние приветствуются на GitLab по адресу https://gitlab.com/gitlab-org/ruby/gems/declarative-policy. Этот проект задуман как безопасное и гостеприимное пространство для сотрудничества, и ожидается, что участники будут соблюдать кодекс поведения GitLab.
Мы выпускаем declarative_policy по мере необходимости. Регулярности в выпусках нет, мы выпускаем новую версию при каждом изменении — независимо от размера изменения.
Чтобы выпустить новую версию:
declarative_policy до новой также.Gem доступен как open source на условиях лицензии MIT.
Все, кто взаимодействует в кодовой базе, трекерах issues, чатах и списках рассылки проекта DeclarativePolicy, обязаны следовать кодексу поведения.