forest_admin_agent 1.12.15 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/forest_admin_agent/builder/agent_factory.rb +1 -14
- data/lib/forest_admin_agent/facades/container.rb +4 -3
- data/lib/forest_admin_agent/services/permissions.rb +37 -14
- data/lib/forest_admin_agent/utils/is_segment_query_allowed_on_connection.rb +28 -0
- data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
- data/lib/forest_admin_agent/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c39283afe40df8a320768b4a276328bae86668d1f7a38f8b2c606f3654e1c76
|
|
4
|
+
data.tar.gz: 508d9bb55d8a4abd92c47198843a46badec11c79426fd1c4842aef180cdd4b61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b25852adba8a2ba5a50da76f72baeac8c95a90d5894bf24f836ecf264bb61d836965e2116e33159669cd76e595b98c23c70d0cec88bd9a7117b9e3be6347fc8d
|
|
7
|
+
data.tar.gz: cb4d1985d20c4897f67d8388c4491475d048b25bc26f7eadb8165745bfed4b9323895c3636ae2d39f5e4a678e889bdd853b5f92a9d3be14f4b532d7fc6150ae3
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'dry-container'
|
|
2
|
-
require 'filecache'
|
|
3
2
|
require 'json'
|
|
4
3
|
|
|
5
4
|
module ForestAdminAgent
|
|
@@ -10,8 +9,6 @@ module ForestAdminAgent
|
|
|
10
9
|
include ForestAdminAgent::Http::Exceptions
|
|
11
10
|
include ForestAdminDatasourceToolkit::Exceptions
|
|
12
11
|
|
|
13
|
-
TTL_SCHEMA = 7200
|
|
14
|
-
|
|
15
12
|
attr_reader :customizer, :container, :has_env_secret
|
|
16
13
|
|
|
17
14
|
def setup(options)
|
|
@@ -137,11 +134,6 @@ module ForestAdminAgent
|
|
|
137
134
|
if should_send || force
|
|
138
135
|
client = ForestAdminAgent::Http::ForestAdminApiRequester.new
|
|
139
136
|
client.post('/forest/apimaps', api_map.to_json)
|
|
140
|
-
schema_file_hash_cache = FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA)
|
|
141
|
-
schema_file_hash_cache.get_or_set 'value' do
|
|
142
|
-
api_map[:meta][:schemaFileHash]
|
|
143
|
-
end
|
|
144
|
-
@container.register(:schema_file_hash, schema_file_hash_cache)
|
|
145
137
|
ForestAdminAgent::Facades::Container.logger.log('Info', 'schema was updated, sending new version')
|
|
146
138
|
else
|
|
147
139
|
@container.resolve(:logger)
|
|
@@ -172,16 +164,11 @@ module ForestAdminAgent
|
|
|
172
164
|
end
|
|
173
165
|
|
|
174
166
|
def build_cache
|
|
175
|
-
@container.register(:cache, FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA))
|
|
176
|
-
return unless @has_env_secret
|
|
177
|
-
|
|
178
|
-
cache = @container.resolve(:cache)
|
|
179
|
-
|
|
180
167
|
@options[:customize_error_message] =
|
|
181
168
|
clean_option_value(@options[:customize_error_message], 'config.customize_error_message =')
|
|
182
169
|
@options[:logger] = clean_option_value(@options[:logger], 'config.logger =')
|
|
183
170
|
|
|
184
|
-
|
|
171
|
+
@container.register(:config, @options.to_h)
|
|
185
172
|
end
|
|
186
173
|
|
|
187
174
|
def build_logger
|
|
@@ -16,13 +16,14 @@ module ForestAdminAgent
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.config_from_cache
|
|
19
|
-
instance.resolve(:
|
|
19
|
+
instance.resolve(:config)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.cache(key)
|
|
23
|
-
|
|
23
|
+
config = config_from_cache
|
|
24
|
+
raise "Key #{key} not found in config" unless config.key?(key)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
config[key]
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
end
|
|
@@ -41,16 +41,13 @@ module ForestAdminAgent
|
|
|
41
41
|
user_data = get_user_data(caller.id)
|
|
42
42
|
collections_data = get_collections_permissions_data(force_fetch: allow_fetch)
|
|
43
43
|
|
|
44
|
-
# First check
|
|
45
44
|
is_allowed = permission_allowed?(collections_data, collection, action, user_data)
|
|
46
45
|
|
|
47
|
-
# Refetch if not allowed
|
|
48
46
|
unless is_allowed
|
|
49
47
|
collections_data = get_collections_permissions_data(force_fetch: true)
|
|
50
48
|
is_allowed = permission_allowed?(collections_data, collection, action, user_data)
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
# still not allowed - throw forbidden message
|
|
54
51
|
raise ForbiddenError, "You don't have permission to #{action} this collection." unless is_allowed
|
|
55
52
|
|
|
56
53
|
is_allowed
|
|
@@ -61,10 +58,8 @@ module ForestAdminAgent
|
|
|
61
58
|
hash_request = "#{attributes[:type]}:#{array_hash(attributes)}"
|
|
62
59
|
is_allowed = get_chart_data(caller.rendering_id).include?(hash_request)
|
|
63
60
|
|
|
64
|
-
# Refetch
|
|
65
61
|
is_allowed ||= get_chart_data(caller.rendering_id, force_fetch: true).include?(hash_request)
|
|
66
62
|
|
|
67
|
-
# still not allowed - throw forbidden message
|
|
68
63
|
unless is_allowed
|
|
69
64
|
ForestAdminAgent::Facades::Container.logger.log(
|
|
70
65
|
'Debug',
|
|
@@ -82,13 +77,24 @@ module ForestAdminAgent
|
|
|
82
77
|
end
|
|
83
78
|
|
|
84
79
|
def can_execute_query_segment?(collection, query, connection_name)
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
user_data = get_user_data(caller.id)
|
|
81
|
+
if %w[admin developer editor].include?(user_data&.dig(:permissionLevel))
|
|
82
|
+
ForestAdminAgent::Facades::Container.logger.log(
|
|
83
|
+
'Debug',
|
|
84
|
+
"User #{caller.id} can retrieve SQL segment on rendering #{caller.rendering_id}"
|
|
85
|
+
)
|
|
86
|
+
return true
|
|
87
|
+
end
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
collection_permissions = get_collection_rendering_permissions(collection, force_fetch: false)
|
|
90
|
+
|
|
91
|
+
is_allowed = segment_permissions_valid?(collection_permissions, query, connection_name)
|
|
92
|
+
|
|
93
|
+
unless is_allowed
|
|
94
|
+
collection_permissions = get_collection_rendering_permissions(collection, force_fetch: true)
|
|
95
|
+
is_allowed = segment_permissions_valid?(collection_permissions, query, connection_name)
|
|
96
|
+
end
|
|
90
97
|
|
|
91
|
-
# still not allowed - throw forbidden message
|
|
92
98
|
unless is_allowed
|
|
93
99
|
ForestAdminAgent::Facades::Container.logger.log(
|
|
94
100
|
'Debug',
|
|
@@ -216,7 +222,6 @@ module ForestAdminAgent
|
|
|
216
222
|
parameters.delete(:collection)
|
|
217
223
|
parameters.delete(:contextVariables)
|
|
218
224
|
parameters.delete(:record_id)
|
|
219
|
-
# rails
|
|
220
225
|
parameters.delete(:route_alias)
|
|
221
226
|
parameters.delete(:controller)
|
|
222
227
|
parameters.delete(:action)
|
|
@@ -242,6 +247,7 @@ module ForestAdminAgent
|
|
|
242
247
|
data[:team] = response[:team]
|
|
243
248
|
data[:segments] = decode_segment_permissions(response[:collections])
|
|
244
249
|
data[:charts] = decode_charts_permissions(response[:stats])
|
|
250
|
+
data[:liveQuerySegments] = decode_live_query_segments_permissions(response[:collections])
|
|
245
251
|
|
|
246
252
|
data
|
|
247
253
|
end
|
|
@@ -269,7 +275,6 @@ module ForestAdminAgent
|
|
|
269
275
|
end
|
|
270
276
|
|
|
271
277
|
def decode_crud_permissions(collection)
|
|
272
|
-
# Validate structure exists
|
|
273
278
|
unless collection.is_a?(Hash) && collection.key?(:collection)
|
|
274
279
|
ForestAdminAgent::Facades::Container.logger.log(
|
|
275
280
|
'Error',
|
|
@@ -297,8 +302,6 @@ module ForestAdminAgent
|
|
|
297
302
|
)
|
|
298
303
|
end
|
|
299
304
|
|
|
300
|
-
# Use dig to safely extract roles, allowing for missing permissions
|
|
301
|
-
# Missing permissions will result in nil values which are handled by permission_allowed?
|
|
302
305
|
{
|
|
303
306
|
browse: collection_data.dig(:browseEnabled, :roles),
|
|
304
307
|
read: collection_data.dig(:readEnabled, :roles),
|
|
@@ -356,6 +359,17 @@ module ForestAdminAgent
|
|
|
356
359
|
segments
|
|
357
360
|
end
|
|
358
361
|
|
|
362
|
+
def decode_live_query_segments_permissions(raw_permissions)
|
|
363
|
+
collections = {}
|
|
364
|
+
raw_permissions.each do |collection_name, value|
|
|
365
|
+
collections[collection_name] = {
|
|
366
|
+
liveQuerySegments: value[:liveQuerySegments] || []
|
|
367
|
+
}
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
collections
|
|
371
|
+
end
|
|
372
|
+
|
|
359
373
|
def fetch(url)
|
|
360
374
|
response = forest_api.get(url)
|
|
361
375
|
|
|
@@ -463,6 +477,15 @@ module ForestAdminAgent
|
|
|
463
477
|
|
|
464
478
|
collection_actions
|
|
465
479
|
end
|
|
480
|
+
|
|
481
|
+
def get_collection_rendering_permissions(collection, force_fetch: false)
|
|
482
|
+
rendering_data = get_rendering_data(caller.rendering_id, force_fetch: force_fetch)
|
|
483
|
+
rendering_data[:liveQuerySegments][collection.name.to_sym]
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def segment_permissions_valid?(collection_permissions, query, connection_name)
|
|
487
|
+
IsSegmentQueryAllowedOnConnection.allowed?(collection_permissions, query, connection_name)
|
|
488
|
+
end
|
|
466
489
|
end
|
|
467
490
|
end
|
|
468
491
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ForestAdminAgent
|
|
2
|
+
module Utils
|
|
3
|
+
module IsSegmentQueryAllowedOnConnection
|
|
4
|
+
# Check if a segment query is allowed on a specific connection
|
|
5
|
+
# This handles both single queries and UNION queries for multi-segment operations
|
|
6
|
+
def self.allowed?(collection_permissions, segment_query, connection_name)
|
|
7
|
+
return false if connection_name.nil? || connection_name.empty?
|
|
8
|
+
|
|
9
|
+
# Get all queries for the specified connection
|
|
10
|
+
queries = collection_permissions[:liveQuerySegments]
|
|
11
|
+
.select { |segment| segment[:connectionName] == connection_name }
|
|
12
|
+
.map { |segment| segment[:query] }
|
|
13
|
+
|
|
14
|
+
# Handle UNION queries made by the FRONT to display available actions
|
|
15
|
+
# Smart Actions restricted to segment when a Smart Action is available on multiple SQL segments
|
|
16
|
+
union_queries = segment_query.split('/*MULTI-SEGMENTS-QUERIES-UNION*/ UNION ')
|
|
17
|
+
|
|
18
|
+
if union_queries.length > 1
|
|
19
|
+
authorized_queries = queries.to_set { |query| query.gsub(/;\s*\z/i, '').strip }
|
|
20
|
+
|
|
21
|
+
return union_queries.all? { |union_query| authorized_queries.include?(union_query.strip) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
queries.any?(segment_query)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: forest_admin_agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthieu
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-10-
|
|
12
|
+
date: 2025-10-31 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activesupport
|
|
@@ -349,6 +349,7 @@ files:
|
|
|
349
349
|
- lib/forest_admin_agent/utils/csv_generator_stream.rb
|
|
350
350
|
- lib/forest_admin_agent/utils/error_messages.rb
|
|
351
351
|
- lib/forest_admin_agent/utils/id.rb
|
|
352
|
+
- lib/forest_admin_agent/utils/is_segment_query_allowed_on_connection.rb
|
|
352
353
|
- lib/forest_admin_agent/utils/query_string_parser.rb
|
|
353
354
|
- lib/forest_admin_agent/utils/query_validator.rb
|
|
354
355
|
- lib/forest_admin_agent/utils/schema/action_fields.rb
|