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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d06c27d8cb7afd9c9e531c215dd12f253af4e0dc92f67496d45a789a925c8024
4
- data.tar.gz: 93fa83fec26dcce76b175cdb1cafdbc2bf55a4d94bb91835a9ed7cb830e67da8
3
+ metadata.gz: 8c39283afe40df8a320768b4a276328bae86668d1f7a38f8b2c606f3654e1c76
4
+ data.tar.gz: 508d9bb55d8a4abd92c47198843a46badec11c79426fd1c4842aef180cdd4b61
5
5
  SHA512:
6
- metadata.gz: 48ef7caf5cec3cb36abc575bf1f49b1c12c45c21380e0a579a5637feda6ed9d894f1a088019666526cc9ec688fc0018544917327aab82060370ba97278911dcc
7
- data.tar.gz: 8b962f14fdc92733b8d4ed312d167d99169ad472c93ec885117a3569c691b271c3c0e64d37b221f42ecf62fc487a0af557d3ee0470e43d18d9768cd57ce091f9
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
- cache.set('config', @options.to_h)
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(:cache).get('config')
19
+ instance.resolve(:config)
20
20
  end
21
21
 
22
22
  def self.cache(key)
23
- raise "Key #{key} not found in container" unless config_from_cache.key?(key)
23
+ config = config_from_cache
24
+ raise "Key #{key} not found in config" unless config.key?(key)
24
25
 
25
- config_from_cache[key]
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
- hash_request = array_hash({ query: query, connectionName: connection_name })
86
- is_allowed = get_segments(collection).include?(hash_request)
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
- # Refetch
89
- is_allowed ||= get_segments(collection, force_fetch: true).include?(hash_request)
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
@@ -6,7 +6,7 @@ module ForestAdminAgent
6
6
  module Schema
7
7
  class SchemaEmitter
8
8
  LIANA_NAME = "agent-ruby"
9
- LIANA_VERSION = "1.12.15"
9
+ LIANA_VERSION = "1.13.0"
10
10
 
11
11
  def self.generate(datasource)
12
12
  datasource.collections
@@ -1,3 +1,3 @@
1
1
  module ForestAdminAgent
2
- VERSION = "1.12.15"
2
+ VERSION = "1.13.0"
3
3
  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.12.15
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-29 00:00:00.000000000 Z
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