forest_admin_agent 1.8.8 → 1.8.9

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: c2aae6ff9fe9cca589c65b4fc6c66d5636a4fcce6221381c7cf1db88a5bc6c0f
4
- data.tar.gz: 1181798a55291fb7178b86e398e3bf9571b2f299bd3678d02ea561d2b746b80c
3
+ metadata.gz: 7b0dea02691148f9328f2e8544a53ac5bbaba9c88b5c5d88548cd232f60d7914
4
+ data.tar.gz: bb591c88ef30c293d9d38df74ee2a035f2c887608573a84bf64445ade3de73c1
5
5
  SHA512:
6
- metadata.gz: 7feddf88c198be073a0a2abda768ed59eefee58cbc6e388798bb19915f6219ba73950d1fa77baeb8e56f9c7b89c6951c7d85906801c7cdb7c1e4c0dde1a80e2e
7
- data.tar.gz: caa912666955fe5f18c056584da01fd7ba824f6d19fbe4144574269305c8343e09b79ca8e8bd90ca78cc0974051798722d3376149656ac0efcb964229605d308
6
+ metadata.gz: 9d8fc038e89c10096471e3978ad9dc51f3433d3f0c82bb766548bd9ad44aa99fca2d393647f5814501f1768b32e4146c952e3cb5ca49ef63315ee74ff161c90b
7
+ data.tar.gz: 661d0efdaedc34899c77b9e703e36bf1eb884b79931ae8ecdb53d4bc10dc6f94c48f4cc3910ce0d78da93e485ce4027d85a1f2836bc7ac6dd7ace434bc886457
@@ -41,12 +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
- is_allowed = collections_data.key?(collection.name.to_sym) && collections_data[collection.name.to_sym][action].include?(user_data[:roleId])
44
+ # First check
45
+ is_allowed = permission_allowed?(collections_data, collection, action, user_data)
45
46
 
46
- # Refetch
47
+ # Refetch if not allowed
47
48
  unless is_allowed
48
49
  collections_data = get_collections_permissions_data(force_fetch: true)
49
- is_allowed = collections_data[collection.name.to_sym][action].include?(user_data[:roleId])
50
+ is_allowed = permission_allowed?(collections_data, collection, action, user_data)
50
51
  end
51
52
 
52
53
  # still not allowed - throw forbidden message
@@ -111,10 +112,13 @@ module ForestAdminAgent
111
112
  user_data = get_user_data(caller.id)
112
113
  collections_data = get_collections_permissions_data(force_fetch: allow_fetch)
113
114
  action = find_action_from_endpoint(collection.name, request[:headers]['REQUEST_PATH'], request[:headers]['REQUEST_METHOD'])
115
+
116
+ collection_actions = validate_smart_action_permissions(collections_data, collection, action)
117
+
114
118
  smart_action_approval = SmartActionChecker.new(
115
119
  request[:params],
116
120
  collection,
117
- collections_data[collection.name.to_sym][:actions][action['name'].to_sym],
121
+ collection_actions[action['name'].to_sym],
118
122
  caller,
119
123
  user_data[:roleId],
120
124
  filter
@@ -125,6 +129,8 @@ module ForestAdminAgent
125
129
  'Debug',
126
130
  "User #{user_data[:roleId]} is #{"not" unless is_allowed} allowed to perform #{action["name"]}"
127
131
  )
132
+
133
+ is_allowed
128
134
  end
129
135
 
130
136
  def get_scope(collection)
@@ -170,6 +176,18 @@ module ForestAdminAgent
170
176
 
171
177
  private
172
178
 
179
+ def permission_allowed?(collections_data, collection, action, user_data)
180
+ return false unless user_data_valid?(user_data)
181
+
182
+ collection_key = collection.name.to_sym
183
+ return false unless collection_exists?(collections_data, collection_key, collection.name, user_data)
184
+
185
+ role_ids = get_role_ids_for_action(collections_data, collection_key, action, collection.name, user_data)
186
+ return false unless role_ids
187
+
188
+ check_user_permission(role_ids, user_data, action, collection.name)
189
+ end
190
+
173
191
  def get_collections_permissions_data(force_fetch: false)
174
192
  self.class.invalidate_cache('forest.collections') if force_fetch == true
175
193
 
@@ -251,13 +269,37 @@ module ForestAdminAgent
251
269
  end
252
270
 
253
271
  def decode_crud_permissions(collection)
272
+ # Validate structure exists
273
+ unless collection.is_a?(Hash) && collection.key?(:collection)
274
+ ForestAdminAgent::Facades::Container.logger.log(
275
+ 'Error',
276
+ 'Invalid permissions data structure: missing :collection key. ' \
277
+ "Available keys: #{collection.is_a?(Hash) ? collection.keys.join(", ") : "N/A (not a hash)"}. " \
278
+ 'This indicates an API contract violation or data corruption.'
279
+ )
280
+ raise ForestException, 'Invalid permission data structure received from Forest Admin API'
281
+ end
282
+
283
+ collection_data = collection[:collection]
284
+
285
+ unless collection_data.is_a?(Hash)
286
+ ForestAdminAgent::Facades::Container.logger.log(
287
+ 'Error',
288
+ "Invalid permissions data: :collection is not a hash (got #{collection_data.class}). " \
289
+ 'This indicates an API contract violation or data corruption.'
290
+ )
291
+ raise ForestException, 'Invalid permission data structure: :collection must be a hash'
292
+ end
293
+
294
+ # Use dig to safely extract roles, allowing for missing permissions
295
+ # Missing permissions will result in nil values which are handled by permission_allowed?
254
296
  {
255
- browse: collection[:collection][:browseEnabled][:roles],
256
- read: collection[:collection][:readEnabled][:roles],
257
- edit: collection[:collection][:editEnabled][:roles],
258
- add: collection[:collection][:addEnabled][:roles],
259
- delete: collection[:collection][:deleteEnabled][:roles],
260
- export: collection[:collection][:exportEnabled][:roles]
297
+ browse: collection_data.dig(:browseEnabled, :roles),
298
+ read: collection_data.dig(:readEnabled, :roles),
299
+ edit: collection_data.dig(:editEnabled, :roles),
300
+ add: collection_data.dig(:addEnabled, :roles),
301
+ delete: collection_data.dig(:deleteEnabled, :roles),
302
+ export: collection_data.dig(:exportEnabled, :roles)
261
303
  }
262
304
  end
263
305
 
@@ -315,6 +357,106 @@ module ForestAdminAgent
315
357
  rescue StandardError => e
316
358
  forest_api.handle_response_error(e)
317
359
  end
360
+
361
+ def user_data_valid?(user_data)
362
+ return true if user_data&.key?(:roleId)
363
+
364
+ ForestAdminAgent::Facades::Container.logger.log(
365
+ 'Error',
366
+ "Invalid user data: user_data is #{user_data.nil? ? "nil" : "missing :roleId key"}. " \
367
+ 'This indicates a session or authentication issue.'
368
+ )
369
+ false
370
+ end
371
+
372
+ def collection_exists?(collections_data, collection_key, collection_name, user_data)
373
+ return true if collections_data.key?(collection_key)
374
+
375
+ available = collections_data.keys.join(', ')
376
+ ForestAdminAgent::Facades::Container.logger.log(
377
+ 'Warn',
378
+ "Collection '#{collection_name}' not found in permissions " \
379
+ "(user_id: #{user_data[:id]}, role_id: #{user_data[:roleId]}). " \
380
+ "Available: #{available.empty? ? "none" : available}. " \
381
+ 'This may indicate a configuration mismatch or timing issue during permission refresh.'
382
+ )
383
+ false
384
+ end
385
+
386
+ def get_role_ids_for_action(collections_data, collection_key, action, collection_name, user_data)
387
+ collection_permissions = collections_data[collection_key]
388
+ role_ids = collection_permissions[action]
389
+
390
+ if role_ids.nil?
391
+ available = collection_permissions.compact.keys.join(', ')
392
+ ForestAdminAgent::Facades::Container.logger.log(
393
+ 'Warn',
394
+ "Action '#{action}' not found for collection '#{collection_name}' " \
395
+ "(user_id: #{user_data[:id]}, role_id: #{user_data[:roleId]}). " \
396
+ "Available actions: #{available.empty? ? "none" : available}. " \
397
+ 'This may indicate a permission schema change or misconfiguration.'
398
+ )
399
+ return nil
400
+ end
401
+
402
+ unless role_ids.is_a?(Array)
403
+ ForestAdminAgent::Facades::Container.logger.log(
404
+ 'Error',
405
+ "Invalid permission data: roles for action '#{action}' in collection '#{collection_name}' " \
406
+ "is not an array (got #{role_ids.class}). This indicates data corruption."
407
+ )
408
+ return nil
409
+ end
410
+
411
+ role_ids
412
+ end
413
+
414
+ def check_user_permission(role_ids, user_data, action, collection_name)
415
+ has_permission = role_ids.include?(user_data[:roleId])
416
+
417
+ unless has_permission
418
+ ForestAdminAgent::Facades::Container.logger.log(
419
+ 'Debug',
420
+ "Permission denied: User #{user_data[:id]} (role #{user_data[:roleId]}) " \
421
+ "lacks permission to #{action} collection '#{collection_name}'. " \
422
+ "Required roles: #{role_ids.join(", ")}"
423
+ )
424
+ end
425
+
426
+ has_permission
427
+ end
428
+
429
+ def validate_smart_action_permissions(collections_data, collection, action)
430
+ collection_key = collection.name.to_sym
431
+
432
+ unless collections_data.key?(collection_key)
433
+ ForestAdminAgent::Facades::Container.logger.log(
434
+ 'Warn',
435
+ "Smart action check: Collection '#{collection.name}' not found in permissions"
436
+ )
437
+ raise ForbiddenError, "Collection '#{collection.name}' not found in permissions"
438
+ end
439
+
440
+ collection_actions = collections_data[collection_key][:actions]
441
+ if collection_actions.nil?
442
+ ForestAdminAgent::Facades::Container.logger.log(
443
+ 'Warn',
444
+ "Smart action check: No actions configured for collection '#{collection.name}'"
445
+ )
446
+ raise ForbiddenError, "No actions configured for collection '#{collection.name}'"
447
+ end
448
+
449
+ action_key = action['name'].to_sym
450
+ unless collection_actions.key?(action_key)
451
+ ForestAdminAgent::Facades::Container.logger.log(
452
+ 'Warn',
453
+ "Smart action '#{action["name"]}' not found in permissions for collection '#{collection.name}'"
454
+ )
455
+ raise ForbiddenError, "Smart action '#{action["name"]}' is not configured"
456
+ end
457
+
458
+ collection_actions
459
+ end
318
460
  end
319
461
  end
320
462
  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.8.8"
9
+ LIANA_VERSION = "1.8.9"
10
10
 
11
11
  def self.generate(datasource)
12
12
  datasource.collections
@@ -1,3 +1,3 @@
1
1
  module ForestAdminAgent
2
- VERSION = "1.8.8"
2
+ VERSION = "1.8.9"
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.8.8
4
+ version: 1.8.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu