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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b0dea02691148f9328f2e8544a53ac5bbaba9c88b5c5d88548cd232f60d7914
|
4
|
+
data.tar.gz: bb591c88ef30c293d9d38df74ee2a035f2c887608573a84bf64445ade3de73c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
-
|
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:
|
256
|
-
read:
|
257
|
-
edit:
|
258
|
-
add:
|
259
|
-
delete:
|
260
|
-
export:
|
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
|