
宣言的な認可ライブラリ。ポリシールール、条件、キャッシュを記述するためのDSLを提供。Rubyアプリケーション向けのスケーラブルでDRYな権限管理を実現します。
DeclarativePolicy: 宣言的な認可ライブラリこのライブラリは、認可ポリシーを記述するためのDSLを提供します。
ロジックと権限を分離するために使用でき、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 Predicate [Subject]
User を Actor に、Subject を Resource に名称変更することについては、この issue で議論されています。
例:
user :is_aliveuser :can_drive caruser :can_sell carこれは、文の目的語に Policy (どの文が真であるかに関する一連のルール) を関連付けることで実現します。文は、それを prevent (禁止) するルールがなく、少なくとも1つのルールが enable (許可) する場合に成立すると見なされます。
例えば、車両とユーザーを含むデータモデルがあり、ユーザーが車両を運転できるかどうかを知りたいとします。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' を設定できます。値が大きいほど「計算コストが高い」ことを示します
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
注意すべき点: これは1つの大きなルールとして書くこともできましたが ((owns | has_access_to) & old_enough_to_drive & ~intoxicated & has_driving_license)、宣言型ポリシーを大規模システムでスケーラブルにする機能の一部が見て取れます: ルールを小さな要素に分解し、より大きなルールに合成できます。新しい条件やルールはいつでも追加できます。
あまり明白ではありませんが、多くのパフォーマンス最適化が透過的に処理されます:
つまり、このライブラリは宣言型を目指しています。重要なルールを宣言すると、ライブラリがそれらの評価方法を調整します。
キャッシングはポリシーの特に価値のある機能です。例えば、車両の販売に関する新しいルールを追加する場合:
rule { owns }.enable :sell_vehicle
所有権の事実は、ポリシーへの異なる呼び出し間で共有でき、データベース呼び出しやその他の高コストな I/O 操作を節約できます。
次の方法でポリシーの判定を確認できます:
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 タグが作成され、git コミットとタグがプッシュされ、.gem ファイルが rubygems.org にプッシュされます。
ポリシーとカスタムロールの詳細については、以下のページを参照してください:
バグ報告とマージリクエストは、GitLab の https://gitlab.com/gitlab-org/ruby/gems/declarative-policy で受け付けています。このプロジェクトは、安全で歓迎的なコラボレーションの場となることを意図しており、コントリビューターは GitLab の行動規範 に従うことが期待されます。
declarative_policy は随時リリースされます。リリースの定期性はなく、変更があった場合にリリースします。変更の規模は問いません。
新しいバージョンをリリースする手順:
Gemfile を更新して declarative_policy Ruby gem を新しいバージョンにバンプすることをお勧めします。この gem は MIT ライセンス の条件に基づいてオープンソースとして利用可能です。
DeclarativePolicy プロジェクトのコードベース、issue トラッカー、チャットルーム、メーリングリストで交流するすべての人は、行動規範 に従うことが期待されます。