diff --git a/.reek.yml b/.reek.yml index 8d9a345e..c9345720 100644 --- a/.reek.yml +++ b/.reek.yml @@ -48,6 +48,7 @@ detectors: - Service::JsonApi::ErrorDataStructureParser#plain_errors? - Service::JsonApi::UriQueryErrorSerializer#compose_nested_errors - Macro::Contract::BaseSchemaObject#build_schema + - Macro::Policy::Pundit::Condition#result! LongParameterList: max_params: 6 diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc index 5ba4c45a..7a2eddaf 100644 --- a/config/credentials/development.yml.enc +++ b/config/credentials/development.yml.enc @@ -1 +1 @@ -qYaVjn7AHyHSmLShL6FUSc0gkDN/TnlTBcyxWC/aVqSw9Yomczems0sliw1sbV28P9OxJiYKf/lAc/tkdZXlsFPRmf3Ax1RXdVdpCX9aoegsgqaTfZM/ho/hXcgfV+tWuyg0ay/GVrYO/EeehSMCENtnBOLU/f8jLyWN8or/7hhQdDYQ6xKV+8eQRy/vTvNtfeHHoadvwpNFBBQV5yVznOZXv5PVoJBzmUtvGkHZkyjKoXXFPY0XvwHOI1BZHo37ogeQylXVhdFABdfQoCWQvJNUgB8kkPsBPx87--Fvpot/k3S0Abti7S--qv435P5TTlbWwzBcxrsnjQ== \ No newline at end of file +hk3xOYEGE+oGJgblVqmInjtqi4GfMW8AZaanzxUgBF5CIrgjEx5lff3+wxuAQTkJBarwUsdISKRJ9hxuxNVKy86F1Y5FR21GRIBmF/a6Mx3HGLDLd8qopPxiiECm/cLc2nNFBG56W0oDM1Oeoa/c/TWKvbpCZvvy0jcsT+pwqDewF36vhJAjF5+DsSIOoKhM6qJvUxVuzXMpp8Bi+fGwfPlrPw51TqtFCA8yOebymCvCt+sR+PYSAURNvoafKWHPHbOMWKJtFiR95CWX5O7LeEDrwSSHar/gUmx0V50CDw0/OMuUutX1dTHXb86P9kgQGfJNg5o=--6PcDleD9XIhgGIO2--ctaBkvJ7VlLBYwRbrENu0A== \ No newline at end of file diff --git a/lib/macro/pundit.rb b/lib/macro/pundit.rb new file mode 100644 index 00000000..58e61849 --- /dev/null +++ b/lib/macro/pundit.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Macro # :reek:MissingSafeMethod { exclude: [ result! ] } + module Policy + def self.Pundit(policy_class, action, user: nil, model: nil, name: :default) + Trailblazer::Macro::Policy.step(Pundit.build(policy_class, action, user: user, model: model), name: name) + end + + module Pundit + def self.build(*args, &block) + Condition.new(*args, &block) + end + + class Condition + def initialize(policy_class, action, user: nil, model: nil) + @policy_class, @user, @model, @action = policy_class, user, model, action + end + + def call((options), *) + policy = build_policy(options) + result!(policy.send(@action), policy) + end + + private + + def build_policy(options) + @policy_class.new(@user, @model) if @user && @model + @policy_class.new(options[:current_user], options[:model]) unless @user && @model + end + + def result!(success, policy) + data = { policy: policy } + data[:message] = 'Breach' unless success + + Trailblazer::Operation::Result.new(success, data) + end + end + end + end +end diff --git a/spec/lib/macro/pundit_spec.rb b/spec/lib/macro/pundit_spec.rb new file mode 100644 index 00000000..4812e4f0 --- /dev/null +++ b/spec/lib/macro/pundit_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +RSpec.describe Macro do + describe '.Pundit' do + class Auth + def only_user? + @user == Module && @model.nil? + end + end + + # rubocop:disable RSpec/LeakyConstantDeclaration + OperationPolicyPundit = Class.new(Trailblazer::Operation) do + step Macro::Policy::Pundit(Auth, :only_user?) + step :process + + def process(options, **) + options[:process] = true + end + end + # rubocop:enable RSpec/LeakyConstantDeclaration + + context 'when successful' do + let(:result) { OperationPolicyPundit.call(params: {}, current_user: Module) } + + it 'process to be truthy' do + expect(result[:process]).to be true + end + end + + context 'when breach' do + let(:result) { OperationPolicyPundit.call(params: {}, current_user: nil) } + + it 'process to be falsey' do + expect(result[:process]).to be nil + end + end + end +end