forest_liana 7.8.0 → 8.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/forest_liana/actions_controller.rb +5 -3
  3. data/app/controllers/forest_liana/application_controller.rb +15 -0
  4. data/app/controllers/forest_liana/resources_controller.rb +31 -57
  5. data/app/controllers/forest_liana/smart_actions_controller.rb +44 -58
  6. data/app/controllers/forest_liana/stats_controller.rb +14 -58
  7. data/app/services/forest_liana/ability/exceptions/access_denied.rb +16 -0
  8. data/app/services/forest_liana/ability/exceptions/action_condition_error.rb +16 -0
  9. data/app/services/forest_liana/ability/exceptions/require_approval.rb +18 -0
  10. data/app/services/forest_liana/ability/exceptions/trigger_forbidden.rb +16 -0
  11. data/app/services/forest_liana/ability/fetch.rb +23 -0
  12. data/app/services/forest_liana/ability/permission/request_permission.rb +19 -0
  13. data/app/services/forest_liana/ability/permission/smart_action_checker.rb +71 -0
  14. data/app/services/forest_liana/ability/permission.rb +148 -0
  15. data/app/services/forest_liana/ability.rb +24 -0
  16. data/app/services/forest_liana/filters_parser.rb +7 -7
  17. data/app/services/forest_liana/leaderboard_stat_getter.rb +7 -7
  18. data/app/services/forest_liana/line_stat_getter.rb +8 -8
  19. data/app/services/forest_liana/pie_stat_getter.rb +17 -17
  20. data/app/services/forest_liana/stat_getter.rb +1 -2
  21. data/app/services/forest_liana/value_stat_getter.rb +7 -7
  22. data/lib/forest_liana/bootstrapper.rb +1 -1
  23. data/lib/forest_liana/version.rb +1 -1
  24. data/spec/dummy/lib/forest_liana/collections/island.rb +1 -1
  25. data/spec/requests/actions_controller_spec.rb +3 -4
  26. data/spec/requests/count_spec.rb +5 -9
  27. data/spec/requests/resources_spec.rb +55 -11
  28. data/spec/requests/stats_spec.rb +103 -42
  29. data/spec/services/forest_liana/ability/ability_spec.rb +48 -0
  30. data/spec/services/forest_liana/ability/permission/smart_action_checker_spec.rb +357 -0
  31. data/spec/services/forest_liana/ability/permission_spec.rb +332 -0
  32. data/spec/services/forest_liana/filters_parser_spec.rb +0 -12
  33. data/spec/services/forest_liana/line_stat_getter_spec.rb +9 -9
  34. data/spec/services/forest_liana/pie_stat_getter_spec.rb +7 -7
  35. data/spec/services/forest_liana/value_stat_getter_spec.rb +11 -11
  36. data/spec/spec_helper.rb +1 -0
  37. metadata +33 -17
  38. data/app/services/forest_liana/permissions_checker.rb +0 -223
  39. data/app/services/forest_liana/permissions_formatter.rb +0 -52
  40. data/app/services/forest_liana/permissions_getter.rb +0 -59
  41. data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +0 -713
  42. data/spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb +0 -845
  43. data/spec/services/forest_liana/permissions_checker_live_queries_spec.rb +0 -175
  44. data/spec/services/forest_liana/permissions_formatter_spec.rb +0 -222
  45. data/spec/services/forest_liana/permissions_getter_spec.rb +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82f94463ae1662d3d4eeac62d1b68b5c3a6895adfce97c87e99eb7312d8b0966
4
- data.tar.gz: fa9f1794cde3218d3c7aa27231549493203a49876957c170fb26ee7c1b4baf73
3
+ metadata.gz: 8c3bc9feb8975c2d4bb725c5badda68b4d880fcc3a2553cc2d1d6164f5e56366
4
+ data.tar.gz: b1f6e70a0723450674b53944f933036fea21b4a9b6cceab806cf32cd7654896a
5
5
  SHA512:
6
- metadata.gz: 9f30218cb8f5df0e921c754fa1d0b08f79194da46ebe7dac74bffb5343fdf8dd0797ef2542bd41d4bd240aff04821ac0bdd6bdf307375ef3c9a8d978ef07d6f9
7
- data.tar.gz: 7192187737dfb2a67f724bb5c444072f707b3ed02f8c69dc216f4504e2e249ffebe683dff9719f405dca9960f4a44b196e1bb1ccbe021cd62bec4cc36b687621
6
+ metadata.gz: 42a2ae2c6800ea8cf501ee3896554852cd677003d2aea5353fbc2d74d5dee34ee55d02079f8a9a31949af19d7a6ecc8c97e8be3a15a9844aa2ae11ff2fb75e73
7
+ data.tar.gz: 2a97e185ccde89910027740ea833a8e3373f4cb2466a16c2023554534be4ddc3c2419d40015be0edbecb6283a304d4ef4b80472fcea98b99649a122b28fb9c2e
@@ -2,12 +2,14 @@ module ForestLiana
2
2
  class ActionsController < ApplicationController
3
3
 
4
4
  def get_smart_action_hook_request
5
- begin
5
+ if params[:data] && params[:data][:attributes] && params[:data][:attributes][:collection_name]
6
6
  params[:data][:attributes]
7
- rescue => error
7
+ else
8
+ error = 'parameters data attributes missing'
8
9
  FOREST_REPORTER.report error
9
10
  FOREST_LOGGER.error "Smart Action hook request error: #{error}"
10
- {}
11
+
12
+ raise ForestLiana::Errors::HTTP422Error.new("Error in smart action load hook: cannot retrieve action from collection")
11
13
  end
12
14
  end
13
15
 
@@ -3,6 +3,9 @@ require 'csv'
3
3
 
4
4
  module ForestLiana
5
5
  class ApplicationController < ForestLiana::BaseController
6
+ rescue_from ForestLiana::Ability::Exceptions::AccessDenied, with: :render_error
7
+ rescue_from ForestLiana::Errors::HTTP422Error, with: :render_error
8
+
6
9
  def self.papertrail?
7
10
  Object.const_get('PaperTrail::Version').is_a?(Class) rescue false
8
11
  end
@@ -96,6 +99,18 @@ module ForestLiana
96
99
 
97
100
  private
98
101
 
102
+ def render_error(exception)
103
+ errors = {
104
+ status: exception.error_code,
105
+ detail: exception.message,
106
+ }
107
+
108
+ errors['name'] = exception.name if exception.try(:name)
109
+ errors['data'] = exception.data if exception.try(:data)
110
+
111
+ render json: { errors: [errors] }, status: exception.status
112
+ end
113
+
99
114
  def force_utf8_encoding(json)
100
115
  if json['data'].class == Array
101
116
  # NOTICE: Collection of records case
@@ -1,5 +1,6 @@
1
1
  module ForestLiana
2
2
  class ResourcesController < ForestLiana::ApplicationController
3
+ include ForestLiana::Ability
3
4
  begin
4
5
  prepend ResourcesExtensions
5
6
  rescue NameError
@@ -14,24 +15,11 @@ module ForestLiana
14
15
  end
15
16
 
16
17
  def index
18
+ action = request.format == 'csv' ? 'export' : 'browse'
19
+ forest_authorize!(action, forest_user, @resource)
17
20
  begin
18
- if request.format == 'csv'
19
- checker = ForestLiana::PermissionsChecker.new(@resource, 'exportEnabled', @rendering_id, user: forest_user)
20
- return head :forbidden unless checker.is_authorized?
21
- else
22
- checker = ForestLiana::PermissionsChecker.new(
23
- @resource,
24
- 'browseEnabled',
25
- @rendering_id,
26
- user: forest_user,
27
- collection_list_parameters: get_collection_list_permission_info(forest_user, request)
28
- )
29
- return head :forbidden unless checker.is_authorized?
30
- end
31
-
32
21
  getter = ForestLiana::ResourcesGetter.new(@resource, params, forest_user)
33
22
  getter.perform
34
-
35
23
  respond_to do |format|
36
24
  format.json { render_jsonapi(getter) }
37
25
  format.csv { render_csv(getter, @resource) }
@@ -55,16 +43,8 @@ module ForestLiana
55
43
 
56
44
  def count
57
45
  find_resource
46
+ forest_authorize!('browse', forest_user, @resource)
58
47
  begin
59
- checker = ForestLiana::PermissionsChecker.new(
60
- @resource,
61
- 'browseEnabled',
62
- @rendering_id,
63
- user: forest_user,
64
- collection_list_parameters: get_collection_list_permission_info(forest_user, request)
65
- )
66
- return head :forbidden unless checker.is_authorized?
67
-
68
48
  getter = ForestLiana::ResourcesGetter.new(@resource, params, forest_user)
69
49
  getter.count
70
50
 
@@ -88,10 +68,8 @@ module ForestLiana
88
68
  end
89
69
 
90
70
  def show
71
+ forest_authorize!('read', forest_user, @resource)
91
72
  begin
92
- checker = ForestLiana::PermissionsChecker.new(@resource, 'readEnabled', @rendering_id, user: forest_user)
93
- return head :forbidden unless checker.is_authorized?
94
-
95
73
  getter = ForestLiana::ResourceGetter.new(@resource, params, forest_user)
96
74
  getter.perform
97
75
 
@@ -106,10 +84,8 @@ module ForestLiana
106
84
  end
107
85
 
108
86
  def create
87
+ forest_authorize!('add', forest_user, @resource)
109
88
  begin
110
- checker = ForestLiana::PermissionsChecker.new(@resource, 'addEnabled', @rendering_id, user: forest_user)
111
- return head :forbidden unless checker.is_authorized?
112
-
113
89
  creator = ForestLiana::ResourceCreator.new(@resource, params)
114
90
  creator.perform
115
91
 
@@ -130,10 +106,8 @@ module ForestLiana
130
106
  end
131
107
 
132
108
  def update
109
+ forest_authorize!('edit', forest_user, @resource)
133
110
  begin
134
- checker = ForestLiana::PermissionsChecker.new(@resource, 'editEnabled', @rendering_id, user: forest_user)
135
- return head :forbidden unless checker.is_authorized?
136
-
137
111
  updater = ForestLiana::ResourceUpdater.new(@resource, params, forest_user)
138
112
  updater.perform
139
113
 
@@ -154,37 +128,37 @@ module ForestLiana
154
128
  end
155
129
 
156
130
  def destroy
157
- checker = ForestLiana::PermissionsChecker.new(@resource, 'deleteEnabled', @rendering_id, user: forest_user)
158
- return head :forbidden unless checker.is_authorized?
131
+ forest_authorize!('delete', forest_user, @resource)
132
+ begin
133
+ collection_name = ForestLiana.name_for(@resource)
134
+ scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(@resource, forest_user, collection_name, params[:timezone])
159
135
 
160
- collection_name = ForestLiana.name_for(@resource)
161
- scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(@resource, forest_user, collection_name, params[:timezone])
162
-
163
- unless scoped_records.exists?(params[:id])
164
- return render serializer: nil, json: { status: 404 }, status: :not_found
165
- end
136
+ unless scoped_records.exists?(params[:id])
137
+ return render serializer: nil, json: { status: 404 }, status: :not_found
138
+ end
166
139
 
167
- scoped_records.destroy(params[:id])
140
+ scoped_records.destroy(params[:id])
168
141
 
169
- head :no_content
170
- rescue => error
171
- FOREST_REPORTER.report error
172
- FOREST_LOGGER.error "Record Destroy error: #{error}\n#{format_stacktrace(error)}"
173
- internal_server_error
142
+ head :no_content
143
+ rescue => error
144
+ FOREST_REPORTER.report error
145
+ FOREST_LOGGER.error "Record Destroy error: #{error}\n#{format_stacktrace(error)}"
146
+ internal_server_error
147
+ end
174
148
  end
175
149
 
176
150
  def destroy_bulk
177
- checker = ForestLiana::PermissionsChecker.new(@resource, 'deleteEnabled', @rendering_id, user: forest_user)
178
- return head :forbidden unless checker.is_authorized?
179
-
180
- ids = ForestLiana::ResourcesGetter.get_ids_from_request(params, forest_user)
181
- @resource.destroy(ids) if ids&.any?
151
+ forest_authorize!('delete', forest_user, @resource)
152
+ begin
153
+ ids = ForestLiana::ResourcesGetter.get_ids_from_request(params, forest_user)
154
+ @resource.destroy(ids) if ids&.any?
182
155
 
183
- head :no_content
184
- rescue => error
185
- FOREST_REPORTER.report error
186
- FOREST_LOGGER.error "Records Destroy error: #{error}\n#{format_stacktrace(error)}"
187
- internal_server_error
156
+ head :no_content
157
+ rescue => error
158
+ FOREST_REPORTER.report error
159
+ FOREST_LOGGER.error "Records Destroy error: #{error}\n#{format_stacktrace(error)}"
160
+ internal_server_error
161
+ end
188
162
  end
189
163
 
190
164
  private
@@ -1,5 +1,9 @@
1
1
  module ForestLiana
2
2
  class SmartActionsController < ForestLiana::ApplicationController
3
+ rescue_from ForestLiana::Ability::Exceptions::TriggerForbidden, with: :render_error
4
+ rescue_from ForestLiana::Ability::Exceptions::RequireApproval, with: :render_error
5
+ rescue_from ForestLiana::Ability::Exceptions::ActionConditionError, with: :render_error
6
+ include ForestLiana::Ability
3
7
  if Rails::VERSION::MAJOR < 4
4
8
  before_filter :smart_action_pre_perform_checks
5
9
  else
@@ -8,9 +12,17 @@ module ForestLiana
8
12
 
9
13
  private
10
14
 
15
+ def smart_action_pre_perform_checks
16
+ get_smart_action_request
17
+ find_resource
18
+ check_permission_for_smart_route
19
+ ensure_record_ids_in_scope
20
+ end
21
+
11
22
  def get_smart_action_request
12
23
  begin
13
24
  params[:data][:attributes]
25
+ @parameters = ForestLiana::Ability::Permission::RequestPermission::decodeSignedApprovalRequest(params.permit!)
14
26
  rescue => error
15
27
  FOREST_REPORTER.report error
16
28
  FOREST_LOGGER.error "Smart Action execution error: #{error}"
@@ -18,29 +30,52 @@ module ForestLiana
18
30
  end
19
31
  end
20
32
 
21
- def smart_action_pre_perform_checks
22
- check_permission_for_smart_route
23
- ensure_record_ids_in_scope
33
+ def find_resource
34
+ begin
35
+ @resource = SchemaUtils.find_model_from_collection_name(@parameters[:data][:attributes][:collection_name])
36
+ if @resource.nil? || !SchemaUtils.model_included?(@resource) ||
37
+ !@resource.ancestors.include?(ActiveRecord::Base)
38
+ render serializer: nil, json: { status: 404 }, status: :not_found
39
+ end
40
+ @resource
41
+ rescue => error
42
+ FOREST_REPORTER.report error
43
+ FOREST_LOGGER.error "Find Collection error: #{error}\n#{format_stacktrace(error)}"
44
+ render serializer: nil, json: { status: 404 }, status: :not_found
45
+ end
46
+ end
47
+
48
+ def check_permission_for_smart_route
49
+ smart_action_request = @parameters[:data][:attributes]
50
+ if !smart_action_request.nil? && smart_action_request.has_key?(:smart_action_id)
51
+ forest_authorize!(
52
+ 'action',
53
+ forest_user,
54
+ @resource,
55
+ {parameters: params, endpoint: request.fullpath.split('?').first, http_method: request.request_method}
56
+ )
57
+ else
58
+ FOREST_LOGGER.error 'Smart action execution error: Unable to retrieve the smart action id.'
59
+ render serializer: nil, json: { status: 400 }, status: :bad_request
60
+ end
24
61
  end
25
62
 
26
63
  def ensure_record_ids_in_scope
27
64
  begin
28
- attributes = get_smart_action_request
65
+ attributes = @parameters[:data][:attributes]
29
66
 
30
67
  # if performing a `selectAll` let the `get_ids_from_request` handle the scopes
31
68
  return if attributes[:all_records]
32
69
 
33
- resource = find_resource(attributes[:collection_name])
34
-
35
70
  # user is using the composite_primary_keys gem
36
- if resource.primary_key.kind_of?(Array)
71
+ if @resource.primary_key.kind_of?(Array)
37
72
  # TODO: handle primary keys
38
73
  return
39
74
  end
40
75
 
41
- filter = JSON.generate({ 'field' => resource.primary_key, 'operator' => 'in', 'value' => attributes[:ids] })
76
+ filter = JSON.generate({ 'field' => @resource.primary_key, 'operator' => 'in', 'value' => attributes[:ids] })
42
77
 
43
- resources_getter = ForestLiana::ResourcesGetter.new(resource, { :filters => filter, :timezone => attributes[:timezone] }, forest_user)
78
+ resources_getter = ForestLiana::ResourcesGetter.new(@resource, { :filters => filter, :timezone => attributes[:timezone] }, forest_user)
44
79
 
45
80
  # resources getter will return records inside the scope. if the length differs then ids are out of scope
46
81
  return if resources_getter.count == attributes[:ids].length
@@ -53,54 +88,5 @@ module ForestLiana
53
88
  render serializer: nil, json: { error: 'Smart Action: failed to evaluate permissions' }, status: :internal_server_error
54
89
  end
55
90
  end
56
-
57
- def check_permission_for_smart_route
58
- begin
59
-
60
- smart_action_request = get_smart_action_request
61
- if !smart_action_request.nil? && smart_action_request.has_key?(:smart_action_id)
62
- checker = ForestLiana::PermissionsChecker.new(
63
- find_resource(smart_action_request[:collection_name]),
64
- 'actions',
65
- @rendering_id,
66
- user: forest_user,
67
- smart_action_request_info: get_smart_action_request_info
68
- )
69
- return head :forbidden unless checker.is_authorized?
70
- else
71
- FOREST_LOGGER.error 'Smart action execution error: Unable to retrieve the smart action id.'
72
- render serializer: nil, json: { status: 400 }, status: :bad_request
73
- end
74
- rescue => error
75
- FOREST_REPORTER.report error
76
- FOREST_LOGGER.error "Smart Action execution error: #{error}"
77
- render serializer: nil, json: { status: 400 }, status: :bad_request
78
- end
79
- end
80
-
81
- def find_resource(collection_name)
82
- begin
83
- resource = SchemaUtils.find_model_from_collection_name(collection_name)
84
-
85
- if resource.nil? || !SchemaUtils.model_included?(resource) ||
86
- !resource.ancestors.include?(ActiveRecord::Base)
87
- render serializer: nil, json: { status: 404 }, status: :not_found
88
- end
89
- resource
90
- rescue => error
91
- FOREST_REPORTER.report error
92
- FOREST_LOGGER.error "Find Collection error: #{error}\n#{format_stacktrace(error)}"
93
- render serializer: nil, json: { status: 404 }, status: :not_found
94
- end
95
- end
96
-
97
- # smart action permissions are retrieved from the action's endpoint and http_method
98
- def get_smart_action_request_info
99
- {
100
- # trim query params to get the endpoint
101
- endpoint: request.fullpath.split('?').first,
102
- http_method: request.request_method
103
- }
104
- end
105
91
  end
106
92
  end
@@ -1,22 +1,6 @@
1
1
  module ForestLiana
2
2
  class StatsController < ForestLiana::ApplicationController
3
- if Rails::VERSION::MAJOR < 4
4
- before_filter only: [:get] do
5
- find_resource()
6
- check_permission('statWithParameters')
7
- end
8
- before_filter only: [:get_with_live_query] do
9
- check_permission('liveQueries')
10
- end
11
- else
12
- before_action only: [:get] do
13
- find_resource()
14
- check_permission('statWithParameters')
15
- end
16
- before_action only: [:get_with_live_query] do
17
- check_permission('liveQueries')
18
- end
19
- end
3
+ include ForestLiana::Ability
20
4
 
21
5
  CHART_TYPE_VALUE = 'Value'
22
6
  CHART_TYPE_PIE = 'Pie'
@@ -24,7 +8,14 @@ module ForestLiana
24
8
  CHART_TYPE_LEADERBOARD = 'Leaderboard'
25
9
  CHART_TYPE_OBJECTIVE = 'Objective'
26
10
 
11
+ if Rails::VERSION::MAJOR < 4
12
+ before_filter :find_resource, only: :get
13
+ else
14
+ before_action :find_resource, only: :get
15
+ end
16
+
27
17
  def get
18
+ forest_authorize!('chart', forest_user, nil, {parameters: params})
28
19
  case params[:type]
29
20
  when CHART_TYPE_VALUE
30
21
  stat = ValueStatGetter.new(@resource, params, forest_user)
@@ -47,6 +38,7 @@ module ForestLiana
47
38
  end
48
39
 
49
40
  def get_with_live_query
41
+ forest_authorize!('chart', forest_user, nil, {parameters: params})
50
42
  begin
51
43
  stat = QueryStatGetter.new(params)
52
44
  stat.perform
@@ -67,54 +59,18 @@ module ForestLiana
67
59
  end
68
60
  end
69
61
 
62
+ def params
63
+ super.permit!
64
+ end
65
+
70
66
  private
71
67
 
72
68
  def find_resource
73
- @resource = SchemaUtils.find_model_from_collection_name(
74
- params[:collection])
69
+ @resource = SchemaUtils.find_model_from_collection_name(params[:collection])
75
70
 
76
71
  if @resource.nil? || !@resource.ancestors.include?(ActiveRecord::Base)
77
72
  render json: {status: 404}, status: :not_found, serializer: nil
78
73
  end
79
74
  end
80
-
81
- def get_live_query_request_info
82
- params['query']
83
- end
84
-
85
- def get_stat_parameter_request_info
86
- parameters = Rails::VERSION::MAJOR < 5 ? params.dup : params.permit(params.keys).to_h;
87
-
88
- # Notice: Removes useless properties
89
- parameters.delete('timezone');
90
- parameters.delete('controller');
91
- parameters.delete('action');
92
-
93
- # NOTICE: Remove the field information from group_by_field => collection:id
94
- if parameters['group_by_field']
95
- parameters['group_by_field'] = parameters['group_by_field'].split(':').first
96
- end
97
-
98
- return parameters;
99
- end
100
-
101
- def check_permission(permission_name)
102
- begin
103
- query_request = permission_name == 'liveQueries' ? get_live_query_request_info : get_stat_parameter_request_info;
104
- checker = ForestLiana::PermissionsChecker.new(
105
- nil,
106
- permission_name,
107
- @rendering_id,
108
- user: forest_user,
109
- query_request_info: query_request
110
- )
111
-
112
- return head :forbidden unless checker.is_authorized?
113
- rescue => error
114
- FOREST_REPORTER.report error
115
- FOREST_LOGGER.error "Stats execution error: #{error}"
116
- render serializer: nil, json: { status: 400 }, status: :bad_request
117
- end
118
- end
119
75
  end
120
76
  end
@@ -0,0 +1,16 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Exceptions
4
+ class AccessDenied < ForestLiana::Errors::ExpectedError
5
+ def initialize
6
+ super(
7
+ 403,
8
+ :forbidden,
9
+ 'You don\'t have permission to access this resource',
10
+ 'AccessDenied'
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Exceptions
4
+ class ActionConditionError < ForestLiana::Errors::ExpectedError
5
+ def initialize
6
+ super(
7
+ 409,
8
+ :conflict,
9
+ 'The conditions to trigger this action cannot be verified. Please contact an administrator.',
10
+ 'InvalidActionConditionError'
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Exceptions
4
+ class RequireApproval < ForestLiana::Errors::ExpectedError
5
+ attr_reader :data
6
+ def initialize(data)
7
+ @data = data
8
+ super(
9
+ 403,
10
+ :forbidden,
11
+ 'This action requires to be approved.',
12
+ 'CustomActionRequiresApprovalError',
13
+ )
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Exceptions
4
+ class TriggerForbidden < ForestLiana::Errors::ExpectedError
5
+ def initialize
6
+ super(
7
+ 403,
8
+ :forbidden,
9
+ 'You don\'t have the permission to trigger this action',
10
+ 'CustomActionTriggerForbiddenError'
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Fetch
4
+ def get_permissions(route)
5
+ begin
6
+ response = ForestLiana::ForestApiRequester.get(route)
7
+
8
+ if response.is_a?(Net::HTTPOK)
9
+ JSON.parse(response.body)
10
+ else
11
+ raise "Forest API returned an #{ForestLiana::Errors::HTTPErrorHelper.format(response)}"
12
+ end
13
+ rescue => exception
14
+ FOREST_REPORTER.report exception
15
+ FOREST_LOGGER.error 'Cannot retrieve the permissions from the Forest server.'
16
+ FOREST_LOGGER.error 'Which was caused by:'
17
+ ForestLiana::Errors::ExceptionHelper.recursively_print(exception, margin: ' ', is_error: true)
18
+ nil
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'jwt'
2
+
3
+ module ForestLiana
4
+ module Ability
5
+ module Permission
6
+ class RequestPermission
7
+ def self.decodeSignedApprovalRequest(params)
8
+ if (params[:data][:attributes][:signed_approval_request])
9
+ decode_parameters = JWT.decode(params[:data][:attributes][:signed_approval_request], ForestLiana.env_secret, true, { algorithm: 'HS256' }).try(:first)
10
+
11
+ ActionController::Parameters.new(decode_parameters)
12
+ else
13
+ params
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,71 @@
1
+ module ForestLiana
2
+ module Ability
3
+ module Permission
4
+ class SmartActionChecker
5
+
6
+ def initialize(parameters, collection, smart_action, user)
7
+ @parameters = parameters
8
+ @collection = collection
9
+ @smart_action = smart_action
10
+ @user = user
11
+ end
12
+
13
+ def can_execute?
14
+ if @parameters[:data][:attributes][:signed_approval_request].present? && @smart_action['userApprovalEnabled'].include?(@user['roleId'])
15
+ can_approve?
16
+ else
17
+ can_trigger?
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def can_approve?
24
+ @parameters = RequestPermission.decodeSignedApprovalRequest(@parameters)
25
+ if ((@smart_action['userApprovalConditions'].empty? || match_conditions('userApprovalConditions')) &&
26
+ (@parameters[:data][:attributes][:requester_id] != @user['id'] || @smart_action['selfApprovalEnabled'].include?(@user['roleId']))
27
+ )
28
+ return true
29
+ end
30
+
31
+ raise ForestLiana::Ability::Exceptions::TriggerForbidden.new
32
+ end
33
+
34
+ def can_trigger?
35
+ if @smart_action['triggerEnabled'].include?(@user['roleId']) && @smart_action['approvalRequired'].exclude?(@user['roleId'])
36
+ return true if @smart_action['triggerConditions'].empty? || match_conditions('triggerConditions')
37
+ elsif @smart_action['approvalRequired'].include?(@user['roleId'])
38
+ if @smart_action['approvalRequiredConditions'].empty? || match_conditions('approvalRequiredConditions')
39
+ raise ForestLiana::Ability::Exceptions::RequireApproval.new(@smart_action['userApprovalEnabled'])
40
+ end
41
+ end
42
+
43
+ raise ForestLiana::Ability::Exceptions::TriggerForbidden.new
44
+ end
45
+
46
+ def match_conditions(condition_name)
47
+ begin
48
+ attributes = @parameters[:data][:attributes]
49
+ records = FiltersParser.new(
50
+ @smart_action[condition_name][0]['filter'].to_json,
51
+ @collection,
52
+ @parameters[:timezone],
53
+ @parameters
54
+ ).apply_filters
55
+
56
+ if attributes[:all_records]
57
+ records = records.where.not(id: attributes[:all_records_ids_excluded])
58
+ else
59
+ # check if the ids are present into the request of activeRecord
60
+ records = records.where(id: attributes[:ids])
61
+ end
62
+
63
+ records.select(@collection.table_name + '.id').count == attributes[:ids].count
64
+ rescue
65
+ raise ForestLiana::Ability::Exceptions::ActionConditionError.new
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end