forest_liana 9.22.6 → 9.22.7

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: bf311206c7fdea9b79789aba4523930ddccdf1904826d04c36e5e72995eb8640
4
- data.tar.gz: 8f1aff957bd3471d3766ec99e5d1ac0e7c808fc96b7193432efde480c85cbfbe
3
+ metadata.gz: 1899aa8f55f2188189973046573741eb4492ad3e47c8bac900125d7468cc1845
4
+ data.tar.gz: 8fe15b7c2eaca28fa7c74ad66af35eecc7299dcea6f0671b8e07fa36f07529cd
5
5
  SHA512:
6
- metadata.gz: edd1873169d8762a7c8687de26c98dd062aca95b3a2c522f6c9ac8c1167ef9d9c26810c148dd6113053414b5cc7e5ae63b3d722ba6a12938f141f1d9a8e2fa5e
7
- data.tar.gz: f80f9add4df869e43af24d2944e379d8deb2b27207b66d7fa1ec1e8bc91c8be8dcd4c570ce01ae06423823756cf65574d0c49c57de020626123c0b79726bd718
6
+ metadata.gz: d5c3b883ebdc5412f0a982b5cd12d23e361163471e5cbe39bc6e7ca956672f8a34ef06eb41300279ce0ac9ca79aab26f28377539e9ada69cf39f8716b17323d3
7
+ data.tar.gz: 320775ad84781e8b15a27b5cffa365975afa2da9e4d59317bf6bdf2cd9978951eac6b0671e989b6201203df37bc1450e349cd171a4eaf799d6f5a94a28ed56bc
@@ -205,7 +205,7 @@ module ForestLiana
205
205
  if readable.call(entry[:owners][collection_key])
206
206
  acc[collection_key] = entry[:field_names].join(',')
207
207
  else
208
- denied << denial_entry(collection_key, entry[:owners][collection_key], readable_collection_names) if named
208
+ denied << denial_entry(collection_key, entry[:owners][collection_key], readable_collection_names) if named && !FieldPath.unresolved?(entry[:owners][collection_key])
209
209
  end
210
210
  else
211
211
  kept = entry[:field_names].select do |field_name|
@@ -216,7 +216,7 @@ module ForestLiana
216
216
  # differs from root_name — prefix the message so it doesn't read as if 'field_name'
217
217
  # were a bare field of the root.
218
218
  display_path = collection_key == root_name ? field_name : "#{collection_key}:#{field_name}"
219
- denied << denial_entry(field_name, entry[:owners][field_name], readable_collection_names, display_path) if named
219
+ denied << denial_entry(field_name, entry[:owners][field_name], readable_collection_names, display_path) if named && !FieldPath.unresolved?(entry[:owners][field_name])
220
220
  false
221
221
  end
222
222
  end
@@ -1,7 +1,7 @@
1
1
  module ForestLiana
2
2
  class FieldPath
3
3
  # An empty list (a polymorphic relation with no declared target) is read as denied by
4
- # readable_leaves?, not as nothing to check.
4
+ # readable_leaves?, not as nothing to check: a query refuses it, a projection drops it.
5
5
  #
6
6
  # A prefix naming no relation raises rather than falling back to the root collection, which
7
7
  # is pinned readable upstream — a fallback would turn "does not resolve" into "allowed".
@@ -30,6 +30,10 @@ module ForestLiana
30
30
  collection_names.any? && collection_names.all? { |name| readable_collection_names.include?(name) }
31
31
  end
32
32
 
33
+ def self.unresolved?(collection_names)
34
+ collection_names.empty?
35
+ end
36
+
33
37
  def self.leaf_label(collection_names)
34
38
  return 'an unresolved polymorphic relation' if collection_names.empty?
35
39
 
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.22.6"
2
+ VERSION = "9.22.7"
3
3
  end
@@ -0,0 +1,64 @@
1
+ require 'rails_helper'
2
+
3
+ # devise_invitable declares `belongs_to :invited_by, polymorphic: true` with no `as: :invited_by`
4
+ # inverse anywhere, so no target can be resolved for it (#824).
5
+ describe 'A polymorphic relation with no declared target', type: :request do
6
+ token = JWT.encode({ id: 1, email: 'michael.kelso@that70.show', first_name: 'Michael',
7
+ last_name: 'Kelso', team: 'Operations', rendering_id: 16,
8
+ exp: Time.now.to_i + 2.weeks.to_i, permission_level: 'admin' },
9
+ ForestLiana.auth_secret, 'HS256')
10
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json',
11
+ 'Authorization' => "Bearer #{token}" }
12
+
13
+ let!(:user) { User.create!(name: 'Michel') }
14
+ let!(:address) { Address.create!(line1: '1 Palm Street', city: 'Papeete', zipcode: '98713', addressable: user) }
15
+
16
+ before do
17
+ Rails.cache.clear
18
+ Rails.cache.write('forest.users', { '1' => { 'id' => 1, 'roleId' => 1, 'rendering_id' => '1' } })
19
+ Rails.cache.write('forest.has_permission', true)
20
+ enabled = { 'roles' => [1] }
21
+ allow_any_instance_of(ForestLiana::Ability::Fetch).to receive(:get_permissions)
22
+ .with('/liana/v4/permissions/environment').and_return(
23
+ 'collections' => %w[Address User].to_h do |name|
24
+ [name, { 'collection' => %w[browseEnabled readEnabled editEnabled addEnabled deleteEnabled exportEnabled]
25
+ .to_h { |key| [key, enabled] }, 'actions' => {} }]
26
+ end
27
+ )
28
+ allow(ForestLiana::SchemaUtils).to receive(:polymorphic_models).and_call_original
29
+ allow(ForestLiana::SchemaUtils).to receive(:polymorphic_models)
30
+ .with(Address.reflect_on_association(:addressable)).and_return([])
31
+
32
+ allow(ForestLiana::IpWhitelist).to receive(:retrieve) { true }
33
+ allow(ForestLiana::IpWhitelist).to receive(:is_ip_whitelist_retrieved) { true }
34
+ allow(ForestLiana::IpWhitelist).to receive(:is_ip_valid) { true }
35
+ allow_any_instance_of(ForestLiana::Ability).to receive(:forest_authorize!) { true }
36
+ allow(ForestLiana::ScopeManager).to receive(:fetch_scopes)
37
+ .and_return('scopes' => {}, 'team' => { 'id' => '1', 'name' => 'Operations' })
38
+ end
39
+
40
+ after do
41
+ Address.destroy_all
42
+ User.destroy_all
43
+ end
44
+
45
+ it 'serves the get-one without the relation instead of refusing it' do
46
+ get "/forest/Address/#{address.id}",
47
+ params: { fields: { 'Address' => 'id,line1,addressable', 'addressable' => 'id' }, timezone: 'Europe/Paris' },
48
+ headers: headers
49
+
50
+ expect(response.status).to eq(200)
51
+ body = JSON.parse(response.body)
52
+ expect(body['data']['attributes']['line1']).to eq('1 Palm Street')
53
+ expect(body['data'].fetch('relationships', {})).not_to have_key('addressable')
54
+ end
55
+
56
+ it 'still refuses a sort on the relation' do
57
+ get '/forest/Address',
58
+ params: { fields: { 'Address' => 'id,line1' }, sort: 'addressable.id', page: { 'number' => '1', 'size' => '10' },
59
+ timezone: 'Europe/Paris' },
60
+ headers: headers
61
+
62
+ expect(response.status).to eq(403)
63
+ end
64
+ end
@@ -510,16 +510,17 @@ module ForestLiana
510
510
  %w[addresses addresses= address_ids address_ids=].each { |m| Island.undef_method(m) rescue nil }
511
511
  end
512
512
 
513
- it 'treats a relation with no declared target as denied' do
513
+ it 'drops a relation with no declared target even when the caller named it' do
514
514
  write_permissions('Tree' => true)
515
515
  Tree.class_eval { belongs_to :subject, polymorphic: true, optional: true }
516
516
 
517
- expect do
518
- dummy_class.redact_fields(user, Tree, { 'subject' => 'id' }, named_collections: ['subject'])
519
- end.to raise_error(
520
- ForestLiana::Ability::Exceptions::UnauthorizedFieldsError,
521
- "You are not allowed to read 'subject' from an unresolved polymorphic relation."
522
- )
517
+ expect(dummy_class.redact_fields(user, Tree, { 'subject' => 'id' }, named_collections: ['subject']))
518
+ .to eq({})
519
+ expect(
520
+ dummy_class.redact_fields(
521
+ user, Tree, { 'Tree' => 'id,subject', 'subject' => 'id' }, named_collections: %w[Tree subject]
522
+ )
523
+ ).to eq('Tree' => 'id')
523
524
  ensure
524
525
  Tree._reflections.delete('subject')
525
526
  Tree._reflections.delete(:subject)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.22.6
4
+ version: 9.22.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-01 00:00:00.000000000 Z
11
+ date: 2026-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -490,6 +490,7 @@ files:
490
490
  - spec/requests/stats_spec.rb
491
491
  - spec/requests/sti_record_conversion_spec.rb
492
492
  - spec/requests/test.ru
493
+ - spec/requests/unresolved_polymorphic_relation_spec.rb
493
494
  - spec/requests/workflow_executions_spec.rb
494
495
  - spec/routing/routes_spec.rb
495
496
  - spec/serializers/forest_liana/missing_attribute_valve_spec.rb
@@ -847,6 +848,7 @@ test_files:
847
848
  - spec/requests/stats_spec.rb
848
849
  - spec/requests/sti_record_conversion_spec.rb
849
850
  - spec/requests/test.ru
851
+ - spec/requests/unresolved_polymorphic_relation_spec.rb
850
852
  - spec/requests/workflow_executions_spec.rb
851
853
  - spec/routing/routes_spec.rb
852
854
  - spec/serializers/forest_liana/missing_attribute_valve_spec.rb