Skip to content

Commit 1e6333c

Browse files
fix(permission): forward action approval exception for return expected response (#685)
1 parent c3c6935 commit 1e6333c

File tree

8 files changed

+178
-13
lines changed

8 files changed

+178
-13
lines changed

app/controllers/forest_liana/application_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ class ApplicationController < ForestLiana::BaseController
66
rescue_from ForestLiana::Ability::Exceptions::AccessDenied, with: :render_error
77
rescue_from ForestLiana::Errors::HTTP403Error, with: :render_error
88
rescue_from ForestLiana::Errors::HTTP422Error, with: :render_error
9-
rescue_from ForestLiana::Errors::ExpectedError, with: :render_error
9+
rescue_from ForestLiana::Ability::Exceptions::ActionConditionError, with: :render_error
10+
rescue_from ForestLiana::Ability::Exceptions::UnknownCollection, with: :render_error
1011

1112
def self.papertrail?
1213
Object.const_get('PaperTrail::Version').is_a?(Class) rescue false
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module ForestLiana
2+
module Ability
3+
module Exceptions
4+
class UnknownCollection < ForestLiana::Errors::ExpectedError
5+
def initialize(collection_name)
6+
super(
7+
409,
8+
:conflict,
9+
"The collection #{collection_name} doesn't exist",
10+
'collection not found'
11+
)
12+
end
13+
end
14+
end
15+
end
16+
end

app/services/forest_liana/ability/permission.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ def is_crud_authorized?(action, user, collection)
2525
end
2626

2727
is_allowed
28+
rescue ForestLiana::Errors::ExpectedError => exception
29+
raise exception
2830
rescue
29-
raise ForestLiana::Errors::ExpectedError.new(409, :conflict, "The collection #{collection_name} doesn't exist", 'collection not found')
31+
raise ForestLiana::Ability::Exceptions::UnknownCollection.new(collection_name)
3032
end
3133
end
3234

@@ -35,13 +37,16 @@ def is_smart_action_authorized?(user, collection, parameters, endpoint, http_met
3537

3638
user_data = get_user_data(user['id'])
3739
collections_data = get_collections_permissions_data
40+
collection_name = ForestLiana.name_for(collection)
3841
begin
39-
action = find_action_from_endpoint(ForestLiana.name_for(collection), endpoint, http_method).name
42+
action = find_action_from_endpoint(collection_name, endpoint, http_method).name
4043

41-
smart_action_approval = SmartActionChecker.new(parameters, collection, collections_data[ForestLiana.name_for(collection)][:actions][action], user_data)
44+
smart_action_approval = SmartActionChecker.new(parameters, collection, collections_data[collection_name][:actions][action], user_data)
4245
smart_action_approval.can_execute?
46+
rescue ForestLiana::Errors::ExpectedError => exception
47+
raise exception
4348
rescue
44-
raise ForestLiana::Errors::ExpectedError.new(409, :conflict, "The collection #{collection} doesn't exist", 'collection not found')
49+
raise ForestLiana::Ability::Exceptions::UnknownCollection.new(collection_name)
4550
end
4651
end
4752

@@ -66,7 +71,7 @@ def is_chart_authorized?(user, parameters)
6671

6772
private
6873

69-
def get_user_data(user_id)
74+
def get_user_data(user_id, force_fetch = true)
7075
cache = Rails.cache.fetch('forest.users', expires_in: TTL) do
7176
users = {}
7277
get_permissions('/liana/v4/permissions/users').each do |user|
@@ -76,7 +81,12 @@ def get_user_data(user_id)
7681
users
7782
end
7883

79-
cache[user_id.to_s]
84+
if !cache.key?(user_id.to_s) && force_fetch
85+
Rails.cache.delete('forest.users')
86+
get_user_data(user_id, false)
87+
else
88+
cache[user_id.to_s]
89+
end
8090
end
8191

8292
def get_collections_permissions_data(force_fetch = false)

spec/dummy/app/controllers/forest/islands_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ class Forest::IslandsController < ForestLiana::SmartActionsController
22
def test
33
render json: { success: 'You are OK.' }
44
end
5+
6+
def unknown_action
7+
render json: { success: 'unknown action' }
8+
end
59
end

spec/dummy/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Rails.application.routes.draw do
22
namespace :forest do
33
post '/actions/test' => 'islands#test'
4+
post '/actions/unknown_action' => 'islands#unknown_action'
45
get '/Owner/count' , to: 'owners#count'
56
get '/Owner/:id/relationships/trees/count' , to: 'owner_trees#count'
67
end

spec/dummy/lib/forest_liana/collections/island.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class Forest::Island
2626
enums: %w[a b c],
2727
}
2828

29+
action 'test'
30+
2931
action 'my_action',
3032
fields: [foo],
3133
hooks: {

spec/requests/actions_controller_spec.rb

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,6 @@
261261
end
262262

263263
describe 'calling the action' do
264-
before(:each) do
265-
allow_any_instance_of(ForestLiana::Ability).to receive(:forest_authorize!) { true }
266-
end
267-
268264
let(:all_records) { false }
269265
let(:params) {
270266
{
@@ -283,13 +279,119 @@
283279

284280
describe 'without scopes' do
285281
it 'should respond 200 and perform the action' do
282+
allow_any_instance_of(ForestLiana::Ability).to receive(:forest_authorize!) { true }
286283
post '/forest/actions/test', params: JSON.dump(params), headers: headers
287284
expect(response.status).to eq(200)
288285
expect(JSON.parse(response.body)).to eq({'success' => 'You are OK.'})
289286
end
287+
288+
let(:params) {
289+
{
290+
data: {
291+
attributes: {
292+
collection_name: 'Island',
293+
ids: ['1'],
294+
all_records: all_records,
295+
smart_action_id: 'Island-Test'
296+
},
297+
type: 'custom-action-requests'
298+
},
299+
timezone: 'Europe/Paris'
300+
}
301+
}
302+
303+
describe 'with invalid conditions' do
304+
it 'should respond a 409' do
305+
Rails.cache.write('forest.has_permission', true)
306+
Rails.cache.write('forest.users', {'38' => { 'id' => 38, 'roleId' => 1, 'rendering_id' => '13' }})
307+
Rails.cache.write(
308+
'forest.collections',
309+
{
310+
'Island' => {
311+
:actions =>
312+
{
313+
'test' => { 'triggerEnabled' => [1],
314+
'triggerConditions' => [],
315+
'approvalRequired' => [1],
316+
'approvalRequiredConditions' =>
317+
[
318+
{ 'filter' =>
319+
{ 'field' => 'id',
320+
'value' => 2,
321+
'source' => 'data',
322+
'operator' => 'foo-greater-than'
323+
},
324+
'roleId' => 1
325+
}
326+
],
327+
}
328+
}
329+
}
330+
}
331+
)
332+
333+
post '/forest/actions/test', params: JSON.dump(params), headers: headers
334+
335+
expect(response.status).to eq(409)
336+
expect(JSON.parse(response.body)).to eq(
337+
{
338+
"errors" => [
339+
{
340+
"status" => 409,
341+
"detail" => "The conditions to trigger this action cannot be verified. Please contact an administrator.",
342+
"name" => "InvalidActionConditionError"
343+
}
344+
]
345+
}
346+
)
347+
end
348+
end
349+
350+
describe 'with unknown action' do
351+
it 'should respond a 409' do
352+
Rails.cache.write('forest.has_permission', true)
353+
Rails.cache.write('forest.users', {'38' => { 'id' => 38, 'roleId' => 1, 'rendering_id' => '13' }})
354+
Rails.cache.write(
355+
'forest.collections',
356+
{
357+
'Island' => {
358+
:actions =>
359+
{
360+
'test' => { 'triggerEnabled' => [1],
361+
'triggerConditions' => [],
362+
'approvalRequired' => [1],
363+
'approvalRequiredConditions' =>
364+
[
365+
{ 'filter' =>
366+
{ 'field' => 'id',
367+
'value' => 2,
368+
'source' => 'data',
369+
'operator' => 'foo-greater-than'
370+
},
371+
'roleId' => 1
372+
}
373+
],
374+
}
375+
}
376+
}
377+
}
378+
)
379+
380+
post '/forest/actions/unknown_action', params: JSON.dump(params), headers: headers
381+
382+
expect(response.status).to eq(409)
383+
expect(JSON.parse(response.body)).to eq(
384+
{"errors"=> [{"detail" => "The collection Island doesn't exist", "name" => "collection not found", "status" => 409}]}
385+
)
386+
end
387+
end
290388
end
291389

292390
describe 'with scopes' do
391+
before(:each) do
392+
allow_any_instance_of(ForestLiana::Ability).to receive(:forest_authorize!) { true }
393+
end
394+
293395
describe 'when record is in scope' do
294396
let(:scope_filters) {
295397
{

spec/services/forest_liana/ability/permission_spec.rb

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ module Ability
8181

8282

8383
it 'should throw an exception when the collection doesn\'t exist' do
84-
expect {dummy_class.is_crud_authorized?('browse', user, String)}.to raise_error(ForestLiana::Errors::ExpectedError, 'The collection String doesn\'t exist')
84+
allow_any_instance_of(ForestLiana::Ability::Fetch)
85+
.to receive(:get_permissions)
86+
.and_return({ "collections" => {} })
87+
expect {dummy_class.is_crud_authorized?('browse', user, String)}
88+
.to raise_error(ForestLiana::Ability::Exceptions::UnknownCollection, 'The collection String doesn\'t exist')
8589
end
8690

8791
it 'should re-fetch the permission once when user permission is not allowed' do
@@ -211,6 +215,30 @@ module Ability
211215
expect(dummy_class.is_crud_authorized?('browse', user, Island)).to equal true
212216
end
213217

218+
it 'should re-fetch the users list once when user doesn\'t exist' do
219+
Rails.cache.write('forest.users', {'2' => { 'id' => 2, 'roleId' => 1, 'rendering_id' => '1' }})
220+
# Rails.cache.write('forest.users', {'1' => user})
221+
Rails.cache.write(
222+
'forest.collections',
223+
'Island' => {
224+
'browse' => [1],
225+
'read' => [1],
226+
'edit' => [1],
227+
'add' => [1],
228+
'delete' => [1],
229+
'export' => [1],
230+
}
231+
)
232+
233+
allow_any_instance_of(ForestLiana::Ability::Fetch)
234+
.to receive(:get_permissions)
235+
.and_return(
236+
[user]
237+
)
238+
239+
expect(dummy_class.is_crud_authorized?('browse', user, Island)).to equal true
240+
end
241+
214242
it 'should return false when user permission is not allowed' do
215243
Rails.cache.delete('forest.users')
216244

@@ -372,7 +400,8 @@ module Ability
372400
end
373401

374402
it 'should throw an exception when the collection doesn\'t exist' do
375-
expect {dummy_class.is_smart_action_authorized?(user, String, parameters, '/forest/actions/my_action', 'POST')}.to raise_error(ForestLiana::Errors::ExpectedError, 'The collection String doesn\'t exist')
403+
expect {dummy_class.is_smart_action_authorized?(user, String, parameters, '/forest/actions/my_action', 'POST')}
404+
.to raise_error(ForestLiana::Ability::Exceptions::UnknownCollection, 'The collection String doesn\'t exist')
376405
end
377406
end
378407

0 commit comments

Comments
 (0)