
정책 규칙, 조건 및 캐싱을 작성하기 위한 DSL을 제공하는 선언적 권한 부여 라이브러리입니다. Ruby 애플리케이션을 위한 확장 가능하고 DRY한 권한 관리를 가능하게 합니다.
DeclarativePolicy: 선언적 권한 부여(Authorization) 라이브러리이 라이브러리는 권한 부여 정책(Authorization Policies) 작성을 위한 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입니다. 정책(Policies)은 다음을 결합합니다:
conditions이라고 함)rules이라고 함)이 라이브러리는 다음과 같은 형태의 문장의 진리값을 결정하기 위해 존재합니다:
사용자 술어 [주체]
사용자를 행위자(Actor)로, 주체를 리소스(Resource)로 변경하는 것은 이 이슈에서 논의되었습니다.
예를 들어:
user :is_aliveuser :can_drive caruser :can_sell car이는 Policy(어떤 문장이 참인지에 대한 규칙 집합)를 문장의 객체와 연결함으로써 수행됩니다. 어떤 규칙도 prevent하지 않고, 적어도 하나의 규칙이 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
주목할 점: 이 모든 것을 하나의 큰 규칙((owns | has_access_to) & old_enough_to_drive & ~intoxicated & has_driving_license)으로 작성할 수도 있었지만, 여기서는 선언적 정책(declarative policies)이 대규모 시스템에서 확장 가능하게 만드는 기능 중 일부를 볼 수 있습니다: 규칙을 작은 요소로 나누고 더 큰 규칙으로 구성할 수 있습니다. 새로운 조건과 규칙은 언제든지 추가할 수 있습니다.
더 잘 드러나지 않는 점은 많은 성능 최적화가 투명하게 처리된다는 것입니다:
간단히 말해, 이 라이브러리는 선언적(declarative)을 목표로 합니다: 중요한 규칙을 선언하면 라이브러리가 평가 방법을 정리합니다.
캐싱은 특히 유용한 정책 기능입니다. 예를 들어 차량 판매에 대한 새 규칙을 추가하면:
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에 푸시됩니다.
정책 및 사용자 지정 역할(custom roles)에 대한 자세한 내용은 다음 페이지에서 확인할 수 있습니다:
버그 보고 및 병합 요청은 GitLab의 https://gitlab.com/gitlab-org/ruby/gems/declarative-policy 에서 환영합니다. 이 프로젝트는 안전하고 환영하는 협업 공간이 되도록 의도되었으며, 기여자는 GitLab 행동 강령을 준수해야 합니다.
declarative_policy는 필요에 따라 릴리스됩니다. 정해진 주기는 없으며, 변경 사항이 있을 때마다 릴리스합니다. 변경 사항의 크기와 관계없이 말이죠.
새 버전을 릴리스하려면:
Gemfile을 업데이트하여 declarative_policy Ruby gem을 새 버전으로 업데이트하는 것이 좋습니다.이 gem은 MIT 라이선스 조건에 따라 오픈 소스로 제공됩니다.
DeclarativePolicy 프로젝트의 코드베이스, 이슈 트래커, 채팅방 및 메일링 리스트에서 상호 작용하는 모든 사람은 행동 강령을 따를 것으로 예상됩니다.