forest_liana 6.6.0 → 7.0.0.beta.3
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 +44 -29
- data/app/controllers/forest_liana/application_controller.rb +2 -2
- 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 +40 -34
- 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/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 +22 -20
- 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: 3115af32c59b5bf0e32218f3d40cb661809d58316714654520bde1bfee235098
|
4
|
+
data.tar.gz: 850a6dbc96d8ff1e74ffe7970dc98ff2bd721d6b834087353052208722b55810
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea018906c77a41db7a7dc1022cd6485e5f9a293763c1822a64706092f8be74d1046230198af6d0041277cb8f2b852115633b96f622f69a192dc99f911cd31178
|
7
|
+
data.tar.gz: cf12c7f0821b3e9232fe884e3d5c04470f6467d4a571ffc88ce2f6e36c9edac6704dcdd9a0c431dd222435bac1c31b512a5585bc9fb99f141e309e41f9ff8696
|
@@ -27,34 +27,45 @@ module ForestLiana
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_smart_action_load_ctx(fields)
|
30
|
-
fields = fields.
|
31
|
-
ForestLiana::WidgetsHelper.set_field_widget(
|
32
|
-
|
30
|
+
fields = fields.map do |field|
|
31
|
+
ForestLiana::WidgetsHelper.set_field_widget(field)
|
32
|
+
field[:value] = nil unless field[:value]
|
33
|
+
field
|
33
34
|
end
|
34
35
|
{:record => get_record, :fields => fields}
|
35
36
|
end
|
36
37
|
|
37
|
-
def get_smart_action_change_ctx(fields)
|
38
|
-
|
39
|
-
|
38
|
+
def get_smart_action_change_ctx(fields, field_changed)
|
39
|
+
found_field_changed = fields.find{|field| field[:field] == field_changed}
|
40
|
+
fields = fields.map do |field|
|
41
|
+
field = field.permit!.to_h.symbolize_keys
|
40
42
|
ForestLiana::WidgetsHelper.set_field_widget(field)
|
41
|
-
|
43
|
+
field
|
42
44
|
end
|
43
|
-
{:record => get_record, :fields => fields}
|
45
|
+
{:record => get_record, :field_changed => found_field_changed, :fields => fields}
|
44
46
|
end
|
45
47
|
|
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
|
48
|
+
def handle_result(result, action)
|
49
|
+
if result.nil? || !result.is_a?(Array)
|
50
|
+
return render status: 500, json: { error: 'Error in smart action load hook: hook must return an array of fields' }
|
49
51
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
|
53
|
+
# Validate that the fields are well formed.
|
54
|
+
begin
|
55
|
+
# action.hooks[:change] is a hashmap here
|
56
|
+
# to do the validation, only the hook names are require
|
57
|
+
change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
|
58
|
+
ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result, action.name, change_hooks_name)
|
59
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
|
60
|
+
FOREST_LOGGER.warn invalid_field_error.message
|
61
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
|
62
|
+
FOREST_LOGGER.error invalid_hook_error.message
|
63
|
+
return render status: 500, json: { error: invalid_hook_error.message }
|
53
64
|
end
|
54
65
|
|
55
66
|
# Apply result on fields (transform the object back to an array), preserve order.
|
56
|
-
fields =
|
57
|
-
updated_field = result[field[:field]
|
67
|
+
fields = result.map do |field|
|
68
|
+
updated_field = result.find{|f| f[:field] == field[:field]}
|
58
69
|
|
59
70
|
# Reset `value` when not present in `enums` (which means `enums` has changed).
|
60
71
|
if updated_field[:enums].is_a?(Array)
|
@@ -72,7 +83,7 @@ module ForestLiana
|
|
72
83
|
updated_field
|
73
84
|
end
|
74
85
|
|
75
|
-
render serializer: nil, json: { fields: fields}, status: :ok
|
86
|
+
render serializer: nil, json: { fields: fields }, status: :ok
|
76
87
|
end
|
77
88
|
|
78
89
|
def load
|
@@ -81,14 +92,13 @@ module ForestLiana
|
|
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
|
|
@@ -96,17 +106,22 @@ module ForestLiana
|
|
96
106
|
action = get_action(params[:collectionName])
|
97
107
|
|
98
108
|
if !action
|
99
|
-
render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
109
|
+
return render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
110
|
+
elsif params[:fields].nil?
|
111
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params is mandatory'}
|
112
|
+
elsif !params[:fields].is_a?(Array)
|
113
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params must be an array'}
|
114
|
+
end
|
104
115
|
|
105
|
-
|
106
|
-
|
116
|
+
# Get the smart action hook change context
|
117
|
+
context = get_smart_action_change_ctx(params[:fields], params[:changedField])
|
107
118
|
|
108
|
-
|
109
|
-
|
119
|
+
field_changed_hook = context[:field_changed][:hook]
|
120
|
+
|
121
|
+
# Call the user-defined change hook.
|
122
|
+
result = action.hooks[:change][field_changed_hook].(context)
|
123
|
+
|
124
|
+
handle_result(result, action)
|
110
125
|
end
|
111
126
|
end
|
112
127
|
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.
|
@@ -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?
|
@@ -177,7 +183,7 @@ module ForestLiana
|
|
177
183
|
return false unless pool_permissions
|
178
184
|
|
179
185
|
# NOTICE: equivalent to Object.values in js & removes nil values
|
180
|
-
array_permission_infos = @query_request_info.values.
|
186
|
+
array_permission_infos = @query_request_info.values.select{ |x| !x.nil? }
|
181
187
|
|
182
188
|
# NOTICE: Is there any pool_permissions containing the array_permission_infos
|
183
189
|
return pool_permissions.any? {
|
@@ -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
|