forest_liana 6.6.1 → 7.0.0.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/forest_liana/actions_controller.rb +58 -41
- data/app/controllers/forest_liana/application_controller.rb +2 -11
- data/app/controllers/forest_liana/associations_controller.rb +1 -1
- data/app/controllers/forest_liana/base_controller.rb +1 -1
- data/app/controllers/forest_liana/resources_controller.rb +7 -6
- data/app/serializers/forest_liana/intercom_attribute_serializer.rb +1 -1
- data/app/serializers/forest_liana/intercom_conversation_serializer.rb +1 -1
- data/app/serializers/forest_liana/mixpanel_event_serializer.rb +1 -1
- data/app/serializers/forest_liana/serializer_factory.rb +1 -1
- data/app/serializers/forest_liana/stat_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_bank_account_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_card_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_invoice_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_payment_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_subscription_serializer.rb +1 -1
- data/app/services/forest_liana/apimap_sorter.rb +1 -0
- data/app/services/forest_liana/permissions_checker.rb +39 -33
- data/app/services/forest_liana/permissions_formatter.rb +1 -1
- data/app/services/forest_liana/permissions_getter.rb +1 -4
- data/app/services/forest_liana/smart_action_field_validator.rb +49 -0
- data/config/initializers/errors.rb +17 -0
- data/config/routes.rb +0 -1
- data/lib/forest_liana/bootstrapper.rb +2 -2
- data/lib/forest_liana/schema_file_updater.rb +8 -0
- data/lib/forest_liana/version.rb +1 -1
- data/spec/lib/forest_liana/schema_file_updater_spec.rb +94 -0
- data/spec/requests/actions_controller_spec.rb +69 -34
- data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +45 -69
- data/spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb +39 -63
- data/spec/services/forest_liana/permissions_checker_live_queries_spec.rb +3 -3
- data/spec/services/forest_liana/permissions_formatter_spec.rb +11 -11
- data/spec/services/forest_liana/smart_action_field_validator_spec.rb +70 -0
- metadata +119 -117
- data/app/helpers/forest_liana/is_same_data_structure_helper.rb +0 -44
- data/spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bf8bedca443e88f415a14995d430f5ba021cfe846d8471588680c5f35e619eb
|
4
|
+
data.tar.gz: f68ffb4494911b3b674965b3370988c4505be91673f7d67e194482b0975226e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97aeec5fc463e97cc3b8475c0f09228ea131a322d458104f2cf28306d390b904fd433a78e2a53b7baf34aaf05c6f66d7783e15a36b1f464790ab78bc5bd60ea6
|
7
|
+
data.tar.gz: 9339a84947baea79f91ebb48716760eee7f8e2acee6fc473b881904001c7f3f6bd6f1a1de6603df63eeba15967fcf6d982a60a7f42c049f007b958d60ff9732e
|
@@ -1,8 +1,13 @@
|
|
1
1
|
module ForestLiana
|
2
2
|
class ActionsController < ForestLiana::BaseController
|
3
3
|
|
4
|
-
def
|
5
|
-
|
4
|
+
def get_smart_action_hook_request
|
5
|
+
begin
|
6
|
+
params[:data][:attributes]
|
7
|
+
rescue => error
|
8
|
+
FOREST_LOGGER.error "Smart Action hook request error: #{error}"
|
9
|
+
{}
|
10
|
+
end
|
6
11
|
end
|
7
12
|
|
8
13
|
def get_collection(collection_name)
|
@@ -18,43 +23,47 @@ module ForestLiana
|
|
18
23
|
nil
|
19
24
|
end
|
20
25
|
end
|
21
|
-
|
22
|
-
def get_record
|
23
|
-
model = ForestLiana::SchemaUtils.find_model_from_collection_name(params[:collectionName])
|
24
|
-
redord_getter = ForestLiana::ResourceGetter.new(model, {:id => params[:recordIds][0]})
|
25
|
-
redord_getter.perform
|
26
|
-
redord_getter.record
|
27
|
-
end
|
28
26
|
|
29
27
|
def get_smart_action_load_ctx(fields)
|
30
|
-
fields = fields.
|
31
|
-
ForestLiana::WidgetsHelper.set_field_widget(
|
32
|
-
|
28
|
+
fields = fields.map do |field|
|
29
|
+
ForestLiana::WidgetsHelper.set_field_widget(field)
|
30
|
+
field[:value] = nil unless field[:value]
|
31
|
+
field
|
33
32
|
end
|
34
|
-
{:
|
33
|
+
{:fields => fields, :params => params}
|
35
34
|
end
|
36
35
|
|
37
|
-
def get_smart_action_change_ctx(fields)
|
38
|
-
|
39
|
-
|
36
|
+
def get_smart_action_change_ctx(fields, field_changed)
|
37
|
+
found_field_changed = fields.find{|field| field[:field] == field_changed}
|
38
|
+
fields = fields.map do |field|
|
39
|
+
field = field.permit!.to_h.symbolize_keys
|
40
40
|
ForestLiana::WidgetsHelper.set_field_widget(field)
|
41
|
-
|
41
|
+
field
|
42
42
|
end
|
43
|
-
{:
|
43
|
+
{:field_changed => found_field_changed, :fields => fields, :params => params}
|
44
44
|
end
|
45
45
|
|
46
|
-
def handle_result(result,
|
47
|
-
if result.nil? || !result.is_a?(
|
48
|
-
return render status: 500, json: { error: 'Error in smart action load hook: hook must return an
|
46
|
+
def handle_result(result, action)
|
47
|
+
if result.nil? || !result.is_a?(Array)
|
48
|
+
return render status: 500, json: { error: 'Error in smart action load hook: hook must return an array of fields' }
|
49
49
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
|
51
|
+
# Validate that the fields are well formed.
|
52
|
+
begin
|
53
|
+
# action.hooks[:change] is a hashmap here
|
54
|
+
# to do the validation, only the hook names are require
|
55
|
+
change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
|
56
|
+
ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result, action.name, change_hooks_name)
|
57
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
|
58
|
+
FOREST_LOGGER.warn invalid_field_error.message
|
59
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
|
60
|
+
FOREST_LOGGER.error invalid_hook_error.message
|
61
|
+
return render status: 500, json: { error: invalid_hook_error.message }
|
53
62
|
end
|
54
63
|
|
55
64
|
# Apply result on fields (transform the object back to an array), preserve order.
|
56
|
-
fields =
|
57
|
-
updated_field = result[field[:field]
|
65
|
+
fields = result.map do |field|
|
66
|
+
updated_field = result.find{|f| f[:field] == field[:field]}
|
58
67
|
|
59
68
|
# Reset `value` when not present in `enums` (which means `enums` has changed).
|
60
69
|
if updated_field[:enums].is_a?(Array)
|
@@ -72,41 +81,49 @@ module ForestLiana
|
|
72
81
|
updated_field
|
73
82
|
end
|
74
83
|
|
75
|
-
render serializer: nil, json: { fields: fields}, status: :ok
|
84
|
+
render serializer: nil, json: { fields: fields }, status: :ok
|
76
85
|
end
|
77
86
|
|
78
87
|
def load
|
79
|
-
|
88
|
+
load_request = get_smart_action_hook_request
|
89
|
+
|
90
|
+
action = get_action(load_request[:collection_name])
|
80
91
|
|
81
92
|
if !action
|
82
93
|
render status: 500, json: {error: 'Error in smart action load hook: cannot retrieve action from collection'}
|
83
94
|
else
|
84
|
-
#
|
95
|
+
# Get the smart action hook load context
|
85
96
|
context = get_smart_action_load_ctx(action.fields)
|
86
|
-
formatted_fields = context[:fields].clone # clone for following test on is_same_data_structure
|
87
97
|
|
88
98
|
# Call the user-defined load hook.
|
89
99
|
result = action.hooks[:load].(context)
|
90
100
|
|
91
|
-
handle_result(result,
|
101
|
+
handle_result(result, action)
|
92
102
|
end
|
93
103
|
end
|
94
104
|
|
95
105
|
def change
|
96
|
-
|
106
|
+
change_request = get_smart_action_hook_request
|
107
|
+
|
108
|
+
action = get_action(change_request[:collection_name])
|
97
109
|
|
98
110
|
if !action
|
99
|
-
render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
111
|
+
return render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
112
|
+
elsif change_request[:fields].nil?
|
113
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params is mandatory'}
|
114
|
+
elsif !change_request[:fields].is_a?(Array)
|
115
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params must be an array'}
|
116
|
+
end
|
104
117
|
|
105
|
-
|
106
|
-
|
118
|
+
# Get the smart action hook change context
|
119
|
+
context = get_smart_action_change_ctx(change_request[:fields], change_request[:changed_field])
|
107
120
|
|
108
|
-
|
109
|
-
|
121
|
+
field_changed_hook = context[:field_changed][:hook]
|
122
|
+
|
123
|
+
# Call the user-defined change hook.
|
124
|
+
result = action.hooks[:change][field_changed_hook].(context)
|
125
|
+
|
126
|
+
handle_result(result, action)
|
110
127
|
end
|
111
128
|
end
|
112
129
|
end
|
@@ -34,14 +34,14 @@ module ForestLiana
|
|
34
34
|
|
35
35
|
def serialize_model(record, options = {})
|
36
36
|
options[:is_collection] = false
|
37
|
-
json = JSONAPI::Serializer.serialize(record, options)
|
37
|
+
json = ForestAdmin::JSONAPI::Serializer.serialize(record, options)
|
38
38
|
|
39
39
|
force_utf8_encoding(json)
|
40
40
|
end
|
41
41
|
|
42
42
|
def serialize_models(records, options = {}, fields_searched = [])
|
43
43
|
options[:is_collection] = true
|
44
|
-
json = JSONAPI::Serializer.serialize(records, options)
|
44
|
+
json = ForestAdmin::JSONAPI::Serializer.serialize(records, options)
|
45
45
|
|
46
46
|
if options[:params] && options[:params][:search]
|
47
47
|
# NOTICE: Add the Smart Fields with a 'String' type.
|
@@ -86,15 +86,6 @@ module ForestLiana
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def get_smart_action_context
|
90
|
-
begin
|
91
|
-
params[:data][:attributes].values[0].to_hash.symbolize_keys
|
92
|
-
rescue => error
|
93
|
-
FOREST_LOGGER.error "Smart Action context retrieval error: #{error}"
|
94
|
-
{}
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
89
|
def internal_server_error
|
99
90
|
head :internal_server_error
|
100
91
|
end
|
@@ -41,7 +41,7 @@ module ForestLiana
|
|
41
41
|
updater.perform
|
42
42
|
|
43
43
|
if updater.errors
|
44
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
44
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
45
45
|
updater.errors), status: 422
|
46
46
|
else
|
47
47
|
head :no_content
|
@@ -24,7 +24,7 @@ module ForestLiana
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
rescue ForestLiana::Errors::ExpectedError => exception
|
27
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
27
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
28
28
|
status: exception.error_code,
|
29
29
|
detail: exception.message
|
30
30
|
}])
|
@@ -41,7 +41,7 @@ module ForestLiana
|
|
41
41
|
status: :unprocessable_entity, serializer: nil
|
42
42
|
rescue ForestLiana::Errors::ExpectedError => error
|
43
43
|
error.display_error
|
44
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
44
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
45
45
|
status: error.error_code,
|
46
46
|
detail: error.message
|
47
47
|
}])
|
@@ -73,7 +73,7 @@ module ForestLiana
|
|
73
73
|
status: :unprocessable_entity, serializer: nil
|
74
74
|
rescue ForestLiana::Errors::ExpectedError => error
|
75
75
|
error.display_error
|
76
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
76
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
77
77
|
status: error.error_code,
|
78
78
|
detail: error.message
|
79
79
|
}])
|
@@ -108,12 +108,12 @@ module ForestLiana
|
|
108
108
|
creator.perform
|
109
109
|
|
110
110
|
if creator.errors
|
111
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
111
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
112
112
|
creator.errors), status: 400
|
113
113
|
elsif creator.record.valid?
|
114
114
|
render serializer: nil, json: render_record_jsonapi(creator.record)
|
115
115
|
else
|
116
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
116
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
117
117
|
creator.record.errors), status: 400
|
118
118
|
end
|
119
119
|
rescue => error
|
@@ -131,12 +131,12 @@ module ForestLiana
|
|
131
131
|
updater.perform
|
132
132
|
|
133
133
|
if updater.errors
|
134
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
134
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
135
135
|
updater.errors), status: 400
|
136
136
|
elsif updater.record.valid?
|
137
137
|
render serializer: nil, json: render_record_jsonapi(updater.record)
|
138
138
|
else
|
139
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
139
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
140
140
|
updater.record.errors), status: 400
|
141
141
|
end
|
142
142
|
rescue => error
|
@@ -249,6 +249,7 @@ module ForestLiana
|
|
249
249
|
{
|
250
250
|
user_id: user['id'],
|
251
251
|
filters: collection_list_request[:filters],
|
252
|
+
segmentQuery: collection_list_request[:segmentQuery],
|
252
253
|
}
|
253
254
|
end
|
254
255
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ForestLiana
|
2
2
|
class PermissionsChecker
|
3
3
|
@@permissions_cached = Hash.new
|
4
|
-
@@
|
4
|
+
@@renderings_cached = Hash.new
|
5
5
|
@@roles_acl_activated = false
|
6
|
-
|
6
|
+
|
7
7
|
@@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
|
8
8
|
|
9
|
-
def initialize(resource, permission_name, rendering_id, user_id: nil, smart_action_request_info: nil, collection_list_parameters:
|
9
|
+
def initialize(resource, permission_name, rendering_id, user_id: nil, smart_action_request_info: nil, collection_list_parameters: Hash.new, query_request_info: nil)
|
10
10
|
|
11
11
|
@collection_name = resource.present? ? ForestLiana.name_for(resource) : nil
|
12
12
|
@permission_name = permission_name
|
@@ -39,13 +39,16 @@ module ForestLiana
|
|
39
39
|
permissions['data'] = ForestLiana::PermissionsFormatter.convert_to_new_format(permissions['data'], @rendering_id)
|
40
40
|
@@permissions_cached[@rendering_id] = permissions
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
|
+
# NOTICE: Add stats permissions to the RenderingPermissions
|
44
|
+
permissions['data']['renderings'][@rendering_id]['stats'] = permissions['stats']
|
45
|
+
add_rendering_permissions_to_cache(permissions)
|
43
46
|
end
|
44
47
|
|
45
|
-
def
|
48
|
+
def add_rendering_permissions_to_cache(permissions)
|
46
49
|
permissions['data']['renderings'].keys.each { |rendering_id|
|
47
|
-
@@
|
48
|
-
@@
|
50
|
+
@@renderings_cached[rendering_id] = permissions['data']['renderings'][rendering_id]
|
51
|
+
@@renderings_cached[rendering_id]['last_fetch'] = Time.now
|
49
52
|
} if permissions['data']['renderings']
|
50
53
|
end
|
51
54
|
|
@@ -59,20 +62,17 @@ module ForestLiana
|
|
59
62
|
return stat_with_parameters_allowed?
|
60
63
|
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
65
|
if permissions && permissions[@collection_name] &&
|
65
66
|
permissions[@collection_name]['collection']
|
66
67
|
if @permission_name === 'actions'
|
67
68
|
return smart_action_allowed?(permissions[@collection_name]['actions'])
|
68
69
|
else
|
69
70
|
if @permission_name === 'browseEnabled'
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
return false unless are_scopes_valid?(scope_permissions)
|
71
|
+
refresh_rendering_cache if rendering_cache_expired?
|
72
|
+
|
73
|
+
# NOTICE: In this case we need to check that that query is allowed
|
74
|
+
if @collection_list_parameters[:segmentQuery].present?
|
75
|
+
return false unless segment_query_allowed?
|
76
76
|
end
|
77
77
|
end
|
78
78
|
return is_user_allowed(permissions[@collection_name]['collection'][@permission_name])
|
@@ -82,23 +82,27 @@ module ForestLiana
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def
|
86
|
-
@@
|
87
|
-
@@
|
88
|
-
@@
|
85
|
+
def get_segments_in_permissions
|
86
|
+
@@renderings_cached[@rendering_id] &&
|
87
|
+
@@renderings_cached[@rendering_id][@collection_name] &&
|
88
|
+
@@renderings_cached[@rendering_id][@collection_name]['segments']
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
return true unless @@
|
91
|
+
def rendering_cache_expired?
|
92
|
+
return true unless @@renderings_cached[@rendering_id] && @@renderings_cached[@rendering_id]['last_fetch']
|
93
93
|
|
94
|
-
elapsed_seconds = date_difference_in_seconds(Time.now, @@
|
94
|
+
elapsed_seconds = date_difference_in_seconds(Time.now, @@renderings_cached[@rendering_id]['last_fetch'])
|
95
95
|
elapsed_seconds >= @@expiration_in_seconds
|
96
96
|
end
|
97
97
|
|
98
|
-
# This will happen only on rolesACLActivated (as
|
99
|
-
def
|
98
|
+
# This will happen only on rolesACLActivated (as segments cache will always be up to date on disabled)
|
99
|
+
def refresh_rendering_cache
|
100
100
|
permissions = ForestLiana::PermissionsGetter::get_permissions_for_rendering(@rendering_id, rendering_specific_only: true)
|
101
|
-
|
101
|
+
|
102
|
+
# NOTICE: Add stats permissions to the RenderingPermissions
|
103
|
+
permissions['data']['renderings'][@rendering_id]['stats'] = permissions['stats']
|
104
|
+
|
105
|
+
add_rendering_permissions_to_cache(permissions)
|
102
106
|
end
|
103
107
|
|
104
108
|
# When acl disabled permissions are stored and retrieved by rendering
|
@@ -112,12 +116,12 @@ module ForestLiana
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def get_live_query_permissions_content
|
115
|
-
permissions =
|
119
|
+
permissions = @@renderings_cached[@rendering_id]
|
116
120
|
permissions && permissions['stats'] && permissions['stats']['queries']
|
117
121
|
end
|
118
122
|
|
119
123
|
def get_stat_with_parameters_content(statPermissionType)
|
120
|
-
permissions =
|
124
|
+
permissions = @@renderings_cached[@rendering_id]
|
121
125
|
permissions && permissions['stats'] && permissions['stats'][statPermissionType]
|
122
126
|
end
|
123
127
|
|
@@ -154,11 +158,13 @@ module ForestLiana
|
|
154
158
|
is_user_allowed(smart_action_permissions['triggerEnabled'])
|
155
159
|
end
|
156
160
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
161
|
+
def segment_query_allowed?
|
162
|
+
segments_queries_permissions = get_segments_in_permissions
|
163
|
+
|
164
|
+
return false unless segments_queries_permissions
|
165
|
+
|
166
|
+
# NOTICE: @query_request_info matching an existing segment query
|
167
|
+
return segments_queries_permissions.include? @collection_list_parameters[:segmentQuery]
|
162
168
|
end
|
163
169
|
|
164
170
|
def live_query_allowed?
|
@@ -201,7 +207,7 @@ module ForestLiana
|
|
201
207
|
# Used only for testing purpose
|
202
208
|
def self.empty_cache
|
203
209
|
@@permissions_cached = Hash.new
|
204
|
-
@@
|
210
|
+
@@renderings_cached = Hash.new
|
205
211
|
@@roles_acl_activated = false
|
206
212
|
@@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
|
207
213
|
end
|