Skip to content

Commit

Permalink
Finish authorization logic
Browse files Browse the repository at this point in the history
  • Loading branch information
bonyiii committed May 4, 2018
1 parent 78d54be commit 12c1cf2
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 37 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ end


- [X] Strong Paramters
- [ ] Authorization
- [X] Authorization
- [ ] Swagger compatible output

## License
Expand Down
10 changes: 5 additions & 5 deletions lib/documentary/params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Params
before_action :describe_params, if: :describe_params?

def self.params(action = nil, **_args, &block)
return @store.authorized_params unless action
return @store unless action

unless public_method_defined?(action)
raise(Documentary::PublicMethodMissing, "'#{self}' has no public instance method '#{action}' defined!")
Expand All @@ -18,8 +18,8 @@ def self.params(action = nil, **_args, &block)
end
end

def params_of(method)
self.class.params[method.to_sym]
def authorized_params(action)
self.class.params[action.to_sym]&.authorized_params(self)
end

private
Expand All @@ -30,8 +30,8 @@ def describe_params?

def describe_params
respond_to do |format|
format.json { render json: params_of(action_name) }
format.xml { render xml: params_of(action_name) }
format.json { render json: authorized_params(action_name) }
format.xml { render xml: authorized_paramsf(action_name) }
end
end
end
Expand Down
15 changes: 7 additions & 8 deletions lib/documentary/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,24 @@ def to_strong
recursive_each(self)
end

def authorized_params
dup.tap { |store| make_sure_top_level_keys_included(store.delete_unauthorized) }
def authorized_params(controller)
dup.tap { |store| (store.delete_unauthorized(controller)) }
end

protected

def delete_unauthorized
def delete_unauthorized(controller)
delete_if do |k, v|
next unless v.instance_of?(Store)
(v[:if] && !v[:if].call) || v.delete_unauthorized.empty?
(v[:if] && !evaluate_if(v[:if], controller)) || v.delete_unauthorized(controller).empty?
end
end

private

def make_sure_top_level_keys_included(allowed_params)
each_key do |key|
allowed_params[key] = Store.new unless allowed_params[key]
end
def evaluate_if(symbol_or_proc, controller)
return symbol_or_proc.call(controller) if symbol_or_proc.respond_to?(:call)
return controller.send(symbol_or_proc) if symbol_or_proc.is_a?(Symbol)
end

def recursive_each(hash)
Expand Down
109 changes: 86 additions & 23 deletions spec/documentary/params_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
require 'spec_helper'

RSpec.describe Documentary::Params do
subject { TestController }
let(:controller_class) { TestController }
subject { controller_class }

describe '#params mock' do
it 'should have benn called' do
Expand Down Expand Up @@ -107,9 +108,11 @@
end

context 'authorization' do
subject { controller_class.new }

it 'should raise error for invalid option' do
expect do
subject.params :show do
controller_class.params :show do
optional(:type, not_defined: ->(controller) { controller.to_s })
end
end.to raise_error ArgumentError
Expand All @@ -119,13 +122,13 @@
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
controller_class.params :show do
optional(:type, if: ->(controller) { false })
end
end

it { expect(subject.params[:show]).not_to include(:type) }
it { expect(subject.params[:edit]).not_to be }
it { expect(subject.authorized_params(:show)).not_to include(:type) }
it { expect(subject.authorized_params(:edit)).not_to be }
end

context 'when two method defined, one with a single
Expand All @@ -134,54 +137,114 @@
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
controller_class.params :show do
optional(:type, if: ->(controller) { false })
end

# Set up edit method here
expect(Documentary::ParamBuilder).to receive(:build) do
{ type: 'Foo', vintage: 'Bar' }
store = Documentary::Store.new
store[:type] = 'Foo'
store[:vintage] = 'Bar'
store
end
expect(subject).to receive(:public_method_defined?).and_return(true)
subject.params(:edit)
expect(controller_class).to receive(:public_method_defined?).and_return(true)
controller_class.params(:edit)
end

after { TestController.instance_eval { @store.delete(:edit) } }
after { controller_class.instance_eval { @store.delete(:edit) } }

it { expect(subject.params[:show]).not_to include(:type) }
it { expect(subject.params[:edit]).to include(:type) }
it { expect(subject.authorized_params(:show)).not_to include(:type) }
it { expect(subject.authorized_params(:edit)).to include(:type) }
end

context 'without nested params' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
controller_class.params :show do
optional(:type, if: ->(controller) { false })
required :name
end
end

it { expect(subject.params[:show]).not_to include(:type) }
it { expect(subject.authorized_params(:show)).not_to include(:type) }
end

context 'with nested params' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
controller_class.params :show do
optional(:type)
required(:vintage, type: Array) do
required(:year, type: Integer, desc: 'Year of the vintage', if: -> { false })
required(:year, type: Integer, desc: 'Year of the vintage', if: ->(controller) { false })
optional(:month, type: Integer)
end
end
end

it { expect(subject.params[:show][:vintage]).not_to include(:year) }
it { expect(subject.params[:show][:vintage]).to include(:month) }
it { expect(subject.params[:show]).to include(:type) }
it { expect(subject.params[:edit]).not_to be }
it { expect(subject.authorized_params(:show)[:vintage]).not_to include(:year) }
it { expect(subject.authorized_params(:show)[:vintage]).to include(:month) }
it { expect(subject.authorized_params(:show)).to include(:type) }
it { expect(subject.authorized_params(:edit)).not_to be }
end

context 'with a lambda which uses the controller' do
let(:year_desc) { 'Year of the vintage' }

before do
controller_class.params :show do
optional(:type, if: ->(controller) { controller.allowed? })
required :name
end
end

context 'when params is allowed' do
before { expect(subject).to receive(:allowed?).and_return(true) }
it { expect(subject.authorized_params(:show)).to include(:type) }
end

context 'when params is not allowed' do
before { expect(subject).to receive(:allowed?).and_return(false) }
it { expect(subject.authorized_params(:show)).not_to include(:type) }
end
end

context 'with a proc which does not use the controller' do
let(:year_desc) { 'Year of the vintage' }

before do
controller_class.params :show do
optional(:type, if: proc { true })
required :name
end
end

context 'when params is allowed' do
it { expect(subject.authorized_params(:show)).to include(:type) }
end
end

context 'with a symbol which uses the controller' do
let(:year_desc) { 'Year of the vintage' }

before do
controller_class.params :show do
optional(:type, if: :allowed?)
required :name
end
end

context 'when params is allowed' do
before { expect(subject).to receive(:allowed?).and_return(true) }
it { expect(subject.authorized_params(:show)).to include(:type) }
end

context 'when params is not allowed' do
before { expect(subject).to receive(:allowed?).and_return(false) }
it { expect(subject.authorized_params(:show)).not_to include(:type) }
end
end
end
end
Expand Down

0 comments on commit 12c1cf2

Please sign in to comment.