foreman_rh_cloud 11.4.3 → 11.4.4

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: 541ff74a97d4bb59af8e835360a62fa7a3b346d6bed9c9d656c40349d4868bb9
4
- data.tar.gz: ed004b4c72d0da64e92b6a4cf983fd4b77d9fb054fdbd8f192dc1555618c989d
3
+ metadata.gz: a69803d7adc37a51cb851a218a60b2150a56503eccece90e30b688f1ec8a1ae6
4
+ data.tar.gz: 1d79759ed6f0095579f485ead899d8708f151920f9c33e979797672ab78b1585
5
5
  SHA512:
6
- metadata.gz: e7fe51a950deae4b0093048db11ee7eef4695c4d713c3d0861ea46da5ba83ba2a21dbfde9ab7f1bc9727e4e26e366a1ebdcb2d9abc66a3e893373884a3646db6
7
- data.tar.gz: 5f94192b51c29e2a11ee8c04913e937a8cfb731363dc783ebeb78c9dcf813aba3ccb72efc9e699475604901ec2ee8381e24ddbaacbe24e1f8558de5843b745c3
6
+ metadata.gz: a6b24e9d002cebdbfd00e74d08fbb04cfe8b413648d9e0ec3b60c1eb2222b652dbe14e5379cd5fbf9927b707144d7b4d61204ee490397cae187189012a153d67
7
+ data.tar.gz: d63ecaa47ba66a4078b15367092840a44f219f1e99a5cb619bbc7fe7fe3f40569ac8068de89b98c08e2b1f7f4e0ff98502ce8b5a473b635367d80a1f077959e4
data/README.md CHANGED
@@ -15,7 +15,7 @@ for how to install Foreman plugins
15
15
 
16
16
  #### Inventory upload
17
17
 
18
- In UI: Configure -> Inventory Upload -> Generate and upload report
18
+ In UI: Insights -> Inventory Upload -> select the organization -> Generate and upload report
19
19
 
20
20
  From command-line:
21
21
 
@@ -38,7 +38,7 @@ From command-line:
38
38
 
39
39
  #### Fetch hosts remediation data
40
40
 
41
- In UI: Configure -> Insights -> Sync now
41
+ In UI: Insights -> Recommendations -> Sync recommendations (under the vertical ellipsis)
42
42
 
43
43
  From command-line:
44
44
 
@@ -46,7 +46,7 @@ From command-line:
46
46
 
47
47
  #### Synchronize inventory status
48
48
 
49
- In UI: Configure -> Inventory Upload -> Sync all inventory status
49
+ In UI: Insights -> Inventory Upload -> Sync all inventory status
50
50
 
51
51
  From command-line:
52
52
 
@@ -18,35 +18,39 @@ module InsightsCloud::Api
18
18
  certs = candlepin_id_cert @organization
19
19
  begin
20
20
  @cloud_response = ::ForemanRhCloud::CloudRequestForwarder.new.forward_request(request, controller_name, @branch_id, certs)
21
- rescue RestClient::Exception => e
22
- logger.info("Forwarding request failed with exception: #{e}")
23
- return render json: { error: e }, status: :bad_gateway
24
- rescue RestClient::Timeout => e
25
- logger.info("Forwarding request failed with timeout: #{e}")
26
- return render json: { error: e }, status: :gateway_timeout
27
- end
28
-
29
- return render json: { message: @cloud_response.to_s }, status: :gateway_timeout if @cloud_response.is_a?(RestClient::Exceptions::OpenTimeout)
21
+ rescue RestClient::Exceptions::Timeout => e
22
+ response_obj = e.response.presence || e.exception
23
+ return render json: { message: response_obj.to_s, error: response_obj.to_s }, status: :gateway_timeout
24
+ rescue RestClient::Unauthorized => e
25
+ logger.info("Forwarding request auth error: #{e}")
26
+ message = 'Authentication to the Insights Service failed.'
27
+ return render json: { message: message, error: message }, status: :unauthorized
28
+ rescue RestClient::NotModified => e
29
+ logger.info("Forwarding request not modified: #{e}")
30
+ message = 'Cloud request not modified'
31
+ return render json: { message: message, error: message }, status: :not_modified
32
+ rescue RestClient::ExceptionWithResponse => e
33
+ response_obj = e.response.presence || e.exception
34
+ code = response_obj.try(:code) || response_obj.try(:http_code) || 500
35
+ message = 'Cloud request failed'
30
36
 
31
- if @cloud_response.code == 401
32
37
  return render json: {
33
- :message => 'Authentication to the Insights Service failed.',
38
+ :message => message,
39
+ :error => response_obj.to_s,
34
40
  :headers => {},
35
- }, status: :bad_gateway
36
- end
37
-
38
- if @cloud_response.code >= 300
39
- return render json: {
40
- :message => 'Cloud request failed',
41
- :headers => {},
42
- :response => @cloud_response,
43
- }, status: @cloud_response.code
41
+ :response => response_obj,
42
+ }, status: code
43
+ rescue StandardError => e
44
+ # Catch any other exceptions here, such as Errno::ECONNREFUSED
45
+ logger.info("Cloud request failed with exception: #{e}")
46
+ return render json: { error: e }, status: :bad_gateway
44
47
  end
45
48
 
46
49
  # Append redhat-specific headers
47
50
  @cloud_response.headers.each do |key, value|
48
51
  assign_header(response, @cloud_response, key, false) if key.to_s.start_with?('x_rh_')
49
52
  end
53
+
50
54
  # Append general headers
51
55
  assign_header(response, @cloud_response, :x_resource_count, true)
52
56
  headers[Rack::ETAG] = @cloud_response.headers[:etag]
@@ -56,8 +60,8 @@ module InsightsCloud::Api
56
60
  # content type
57
61
  send_data @cloud_response, disposition: @cloud_response.headers[:content_disposition], type: @cloud_response.headers[:content_type]
58
62
  elsif @cloud_response.headers[:content_type] =~ /zip/
59
- # if there is no Content-Disposition, but the content type is binary according the content type,
60
- # forward the request as binry too
63
+ # If there is no Content-Disposition, but the content type is binary according to Content-Type, send the raw data
64
+ # with proper content type
61
65
  send_data @cloud_response, type: @cloud_response.headers[:content_type]
62
66
  else
63
67
  render json: @cloud_response, status: @cloud_response.code
@@ -17,8 +17,14 @@ module ForemanRhCloud
17
17
  logger.debug("Response headers for request url #{final_params[:url]} are: #{response.headers}")
18
18
 
19
19
  response
20
- rescue RestClient::Exception => ex
21
- logger.debug("Failed response with code #{ex.http_code} headers for request url #{final_params[:url]} are: #{ex.http_headers} and body: #{ex.http_body}")
20
+ rescue RestClient::Exceptions::Timeout => ex
21
+ logger.debug("Timeout exception raised for request url #{final_params[:url]}: #{ex}")
22
+ raise ex
23
+ rescue RestClient::ExceptionWithResponse => ex
24
+ logger.debug("Response headers for request url #{final_params[:url]} with status code #{ex.http_code} are: #{ex.http_headers} and body: #{ex.http_body}")
25
+ raise ex
26
+ rescue StandardError => ex
27
+ logger.debug("Exception raised for request url #{final_params[:url]}: #{ex}")
22
28
  raise ex
23
29
  end
24
30
  end
@@ -17,8 +17,6 @@ module ForemanRhCloud
17
17
  logger.debug("Sending request to: #{request_opts[:url]}")
18
18
 
19
19
  execute_cloud_request(request_opts)
20
- rescue RestClient::ExceptionWithResponse => error_response
21
- error_response.response.presence || error_response.exception
22
20
  end
23
21
 
24
22
  def prepare_request_opts(original_request, forward_payload, forward_params, certs)
@@ -57,17 +57,39 @@ module ForemanInventoryUpload
57
57
  end
58
58
 
59
59
  def obfuscate_hostname?(host)
60
+ # Returns true if hostname obfuscation should be applied for a given host, based on hierarchy:
61
+ # 1. Global setting for hostname obfuscation.
62
+ return true if Setting[:obfuscate_inventory_hostnames]
63
+
60
64
  insights_client_setting = fact_value(host, 'insights_client::obfuscate_hostname_enabled')
61
65
  insights_client_setting = ActiveModel::Type::Boolean.new.cast(insights_client_setting)
62
- return insights_client_setting unless insights_client_setting.nil?
63
66
 
64
- Setting[:obfuscate_inventory_hostnames]
67
+ # 2. host fact reported by insights_client
68
+ # 3. if neither of the above, don't obfuscate.
69
+ insights_client_setting.nil? ? false : insights_client_setting
65
70
  end
66
71
 
67
72
  def fqdn(host)
68
- return host.fqdn unless obfuscate_hostname?(host)
69
-
70
- fact_value(host, 'insights_client::hostname') || obfuscate_fqdn(host.fqdn)
73
+ if obfuscate_hostname?(host)
74
+ # If obfuscation is enabled, attempt to retrieve an already obfuscated hostname
75
+ # from the 'insights_client::obfuscated_hostname' fact.
76
+ # Example format of `parsed_insights_array`:
77
+ # [{"original"=>"host.example.com", "obfuscated"=>"0dd449d0a027.example.com"},
78
+ # {"original"=>"satellite.example.com", "obfuscated"=>"host2.example.com"}]
79
+ begin
80
+ parsed_insights_array = JSON.parse(fact_value(host, 'insights_client::obfuscated_hostname') || '[]')
81
+ rescue JSON::ParserError
82
+ parsed_insights_array = []
83
+ end
84
+ # Obfuscate using the following hierarchy:
85
+ # 1. the obfuscated_hostname fact sent by insights_client
86
+ parsed_insights_item = parsed_insights_array.find { |item| item['original'] == host.fqdn }
87
+ # 2. our own helper method
88
+ parsed_insights_item&.[]('obfuscated') || obfuscate_fqdn(host.fqdn)
89
+ else
90
+ # If hostname obfuscation is not enabled for this host, return the host's original FQDN.
91
+ host.fqdn
92
+ end
71
93
  end
72
94
 
73
95
  def obfuscate_fqdn(fqdn)
@@ -75,35 +97,65 @@ module ForemanInventoryUpload
75
97
  end
76
98
 
77
99
  def obfuscate_ips?(host)
78
- insights_client_setting = fact_value(host, 'insights_client::obfuscate_ip_enabled')
79
- insights_client_setting = ActiveModel::Type::Boolean.new.cast(insights_client_setting)
80
- return insights_client_setting unless insights_client_setting.nil?
100
+ # Returns true if IP obfuscation should be applied for a given host, based on hierarchy:
101
+ # 1. Global setting for IP obfuscation.
102
+ return true if Setting[:obfuscate_inventory_ips]
81
103
 
82
- Setting[:obfuscate_inventory_ips]
104
+ insights_client_ipv4_setting = fact_value(host, 'insights_client::obfuscate_ipv4_enabled')
105
+ insights_client_ipv6_setting = fact_value(host, 'insights_client::obfuscate_ipv6_enabled')
106
+
107
+ cast_ipv4_setting = ActiveModel::Type::Boolean.new.cast(insights_client_ipv4_setting)
108
+ cast_ipv6_setting = ActiveModel::Type::Boolean.new.cast(insights_client_ipv6_setting)
109
+
110
+ # 2. The host's IPv4 or IPv6 obfuscation fact value is true
111
+ # 3. If neither of the above, don't obfuscate.
112
+ cast_ipv4_setting || cast_ipv6_setting || false
83
113
  end
84
114
 
85
115
  def host_ips(host)
116
+ # Determines and returns the IP addresses associated with a host, applying obfuscation if enabled.
117
+
118
+ # If IP obfuscation is enabled for the host return a representation of obfuscated IP addresses.
86
119
  return obfuscated_ips(host) if obfuscate_ips?(host)
87
120
 
88
- # return a pass through proxy hash in case no obfuscation needed
121
+ # If IP obfuscation is NOT needed, return a special kind of Hash.
122
+ # where when you try to access a key in it
123
+ # if the key doesn't exist, it simply returns the key itself.
124
+ # This is useful because it means if you try to get an IP from this hash,
125
+ # you'll just get the original IP back. It allows the calling code to
126
+ # use the same interface whether obfuscation is applied or not.
89
127
  Hash.new { |h, k| k }
90
128
  end
91
129
 
92
130
  def obfuscated_ips(host)
93
- insights_client_ips = JSON.parse(fact_value(host, 'insights_client::ips') || '[]')
131
+ # Example format of `parsed_insights_array`:
132
+ # [{"original": "192.168.1.10", "obfuscated": "10.230.230.1"},
133
+ # {"original": "192.168.1.11", "obfuscated": "10.230.230.2"}]
134
+ begin
135
+ parsed_insights_array = JSON.parse(fact_value(host, 'insights_client::obfuscated_ipv4') || '[]')
136
+ rescue JSON::ParserError
137
+ parsed_insights_array = []
138
+ end
94
139
 
140
+ # Create a new Hash to store the mapping from original IP addresses to their obfuscated versions.
141
+ # where the 'original' IP is the key and the 'obfuscated' IP is the value.
95
142
  obfuscated_ips = Hash[
96
- insights_client_ips.map { |ip_record| [ip_record['original'], ip_record['obfuscated']] }
143
+ parsed_insights_array.map { |ip_record| [ip_record['original'], ip_record['obfuscated']] }
97
144
  ]
98
145
 
146
+ # Sets a default proc for the obfuscated_ips hash.
147
+ # When a key is accessed that does not exist in the hash, this proc is called.
148
+ # It assigns the result of obfuscate_ip(key, hash) to the missing key in the hash.
149
+ # This ensures that any missing IP address key will be obfuscated and stored automatically.
99
150
  obfuscated_ips.default_proc = proc do |hash, key|
100
151
  hash[key] = obfuscate_ip(key, hash)
101
152
  end
102
-
103
153
  obfuscated_ips
104
154
  end
105
155
 
106
156
  def obfuscate_ip(ip, ips_dict)
157
+ # Produce a new, unique obfuscated IP that is
158
+ # numerically one greater than the highest existing obfuscated IP
107
159
  max_obfuscated = ips_dict.values.map { |v| IPAddr.new(v).to_i }.max || IPAddr.new('10.230.230.0').to_i
108
160
 
109
161
  IPAddr.new(max_obfuscated + 1, Socket::AF_INET).to_s
@@ -24,9 +24,11 @@ module ForemanInventoryUpload
24
24
  'dmi::system::product_name',
25
25
  'dmi::chassis::asset_tag',
26
26
  'insights_client::obfuscate_hostname_enabled',
27
- 'insights_client::obfuscate_ip_enabled',
28
- 'insights_client::hostname',
29
- 'insights_client::ips',
27
+ 'insights_client::obfuscate_ipv4_enabled',
28
+ 'insights_client::obfuscate_ipv6_enabled',
29
+ 'insights_client::obfuscated_ipv4',
30
+ 'insights_client::obfuscated_ipv6',
31
+ 'insights_client::obfuscated_hostname',
30
32
  'insights_id',
31
33
  'conversions::activity',
32
34
  'conversions::packages::0::nevra',
@@ -149,7 +149,6 @@ module ForemanInventoryUpload
149
149
  ) { |v| os_release_value(*v) }
150
150
  @stream.simple_field('os_kernel_version', fact_value(host, 'uname::release'))
151
151
  @stream.simple_field('arch', host.architecture&.name)
152
- @stream.simple_field('katello_agent_running', false)
153
152
  @stream.simple_field(
154
153
  'infrastructure_type',
155
154
  ActiveModel::Type::Boolean.new.cast(fact_value(host, 'virt::is_guest')) ? 'virtual' : 'physical'
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '11.4.3'.freeze
2
+ VERSION = '11.4.4'.freeze
3
3
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "11.4.3",
3
+ "version": "11.4.4",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -98,13 +98,25 @@ module InsightsCloud::Api
98
98
  assert_equal net_http_resp[:content_type], @response.headers['Content-Type']
99
99
  end
100
100
 
101
+ test "should handle 304 cloud" do
102
+ net_http_resp = Net::HTTPResponse.new(1.0, 304, "Not Modified")
103
+ res = RestClient::Response.create(@body, net_http_resp, @http_req)
104
+
105
+ ::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotModified.new(res))
106
+
107
+ get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
108
+ assert_equal 304, @response.status
109
+ assert_equal 'Cloud request not modified', JSON.parse(@response.body)['message']
110
+ end
111
+
101
112
  test "should handle failed authentication to cloud" do
102
113
  net_http_resp = Net::HTTPResponse.new(1.0, 401, "Unauthorized")
103
114
  res = RestClient::Response.create(@body, net_http_resp, @http_req)
104
- ::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:forward_request).returns(res)
115
+
116
+ ::ForemanRhCloud::CloudRequestForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::Unauthorized.new(res))
105
117
 
106
118
  get :forward_request, params: { "path" => "platform/module-update-router/v1/channel" }
107
- assert_equal 502, @response.status
119
+ assert_equal 401, @response.status
108
120
  assert_equal 'Authentication to the Insights Service failed.', JSON.parse(@response.body)['message']
109
121
  end
110
122
 
@@ -1,4 +1,5 @@
1
1
  require 'test_plugin_helper'
2
+ require 'digest'
2
3
 
3
4
  class FactHelpersTest < ActiveSupport::TestCase
4
5
  class FactsHelpersTestStub
@@ -29,7 +30,7 @@ class FactHelpersTest < ActiveSupport::TestCase
29
30
 
30
31
  test 'obfuscates ips with insights-client data' do
31
32
  host = mock('host')
32
- @instance.expects(:fact_value).with(host, 'insights_client::ips').returns(
33
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns(
33
34
  '[{"obfuscated": "10.230.230.1", "original": "224.0.0.1"}, {"obfuscated": "10.230.230.255", "original": "224.0.0.251"}]'
34
35
  )
35
36
 
@@ -41,11 +42,275 @@ class FactHelpersTest < ActiveSupport::TestCase
41
42
 
42
43
  test 'obfuscates ips without insights-client data' do
43
44
  host = mock('host')
44
- @instance.expects(:fact_value).with(host, 'insights_client::ips').returns(nil)
45
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns(nil)
45
46
 
46
47
  actual = @instance.obfuscated_ips(host)
47
48
 
48
49
  assert_equal '10.230.230.1', actual['224.0.0.1']
49
50
  assert_equal '10.230.230.2', actual['224.0.0.2']
50
51
  end
52
+
53
+ describe 'obfuscate_hostname?' do
54
+ test 'returns true when global setting is enabled' do
55
+ Setting.expects(:[]).with(:obfuscate_inventory_hostnames).returns(true)
56
+ host = mock('host')
57
+
58
+ result = @instance.obfuscate_hostname?(host)
59
+
60
+ assert result
61
+ end
62
+
63
+ test 'returns false when global setting is disabled and no host-specific setting' do
64
+ Setting.expects(:[]).with(:obfuscate_inventory_hostnames).returns(false)
65
+ host = mock('host')
66
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_hostname_enabled').returns(nil)
67
+
68
+ result = @instance.obfuscate_hostname?(host)
69
+
70
+ refute result
71
+ end
72
+
73
+ test 'returns true when host-specific setting is enabled' do
74
+ Setting.expects(:[]).with(:obfuscate_inventory_hostnames).returns(false)
75
+ host = mock('host')
76
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_hostname_enabled').returns('true')
77
+
78
+ result = @instance.obfuscate_hostname?(host)
79
+
80
+ assert result
81
+ end
82
+
83
+ test 'returns false when host-specific setting is disabled' do
84
+ Setting.expects(:[]).with(:obfuscate_inventory_hostnames).returns(false)
85
+ host = mock('host')
86
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_hostname_enabled').returns('false')
87
+
88
+ result = @instance.obfuscate_hostname?(host)
89
+
90
+ refute result
91
+ end
92
+ end
93
+
94
+ describe 'fqdn' do
95
+ test 'returns original fqdn when obfuscation is disabled' do
96
+ host = mock('host')
97
+ host.expects(:fqdn).returns('test.example.com')
98
+ @instance.expects(:obfuscate_hostname?).with(host).returns(false)
99
+
100
+ result = @instance.fqdn(host)
101
+
102
+ assert_equal 'test.example.com', result
103
+ end
104
+
105
+ test 'returns obfuscated hostname from insights_client fact when available' do
106
+ host = mock('host')
107
+ host.expects(:fqdn).returns('test.example.com').once
108
+ @instance.expects(:obfuscate_hostname?).with(host).returns(true)
109
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_hostname').returns(
110
+ '[{"original": "test.example.com", "obfuscated": "abc123.example.com"}]'
111
+ )
112
+
113
+ result = @instance.fqdn(host)
114
+
115
+ assert_equal 'abc123.example.com', result
116
+ end
117
+
118
+ test 'returns dynamically obfuscated hostname when insights_client fact is not available' do
119
+ host = mock('host')
120
+ host.stubs(:fqdn).returns('test.example.com')
121
+ @instance.expects(:obfuscate_hostname?).with(host).returns(true)
122
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_hostname').returns(nil)
123
+
124
+ result = @instance.fqdn(host)
125
+
126
+ expected = "#{Digest::SHA1.hexdigest('test.example.com')}.example.com"
127
+ assert_equal expected, result
128
+ end
129
+
130
+ test 'returns dynamically obfuscated hostname when insights_client fact does not contain matching host' do
131
+ host = mock('host')
132
+ host.expects(:fqdn).returns('test.example.com').twice
133
+ @instance.expects(:obfuscate_hostname?).with(host).returns(true)
134
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_hostname').returns(
135
+ '[{"original": "other.example.com", "obfuscated": "abc123.example.com"}]'
136
+ )
137
+ @instance.expects(:obfuscate_fqdn).with('test.example.com').returns('dynamically_obfuscated.example.com')
138
+
139
+ result = @instance.fqdn(host)
140
+
141
+ assert_equal 'dynamically_obfuscated.example.com', result
142
+ end
143
+
144
+ test 'handles invalid JSON in insights_client fact gracefully' do
145
+ host = mock('host')
146
+ host.stubs(:fqdn).returns('test.example.com')
147
+ @instance.expects(:obfuscate_hostname?).with(host).returns(true)
148
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_hostname').returns('invalid json')
149
+
150
+ result = @instance.fqdn(host)
151
+
152
+ expected = "#{Digest::SHA1.hexdigest('test.example.com')}.example.com"
153
+ assert_equal expected, result
154
+ end
155
+
156
+ test 'handles empty insights_client fact' do
157
+ host = mock('host')
158
+ host.stubs(:fqdn).returns('test.example.com')
159
+ @instance.expects(:obfuscate_hostname?).with(host).returns(true)
160
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_hostname').returns('[]')
161
+
162
+ result = @instance.fqdn(host)
163
+
164
+ expected = "#{Digest::SHA1.hexdigest('test.example.com')}.example.com"
165
+ assert_equal expected, result
166
+ end
167
+ end
168
+
169
+ describe 'obfuscate_ips?' do
170
+ test 'returns true when global setting is enabled' do
171
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(true)
172
+ host = mock('host')
173
+
174
+ result = @instance.obfuscate_ips?(host)
175
+
176
+ assert result
177
+ end
178
+
179
+ test 'returns false when global setting is disabled and no host-specific settings' do
180
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(false)
181
+ host = mock('host')
182
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv4_enabled').returns(nil)
183
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv6_enabled').returns(nil)
184
+
185
+ result = @instance.obfuscate_ips?(host)
186
+
187
+ refute result
188
+ end
189
+
190
+ test 'returns true when host-specific IPv4 setting is enabled' do
191
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(false)
192
+ host = mock('host')
193
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv4_enabled').returns('true')
194
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv6_enabled').returns(nil)
195
+
196
+ result = @instance.obfuscate_ips?(host)
197
+
198
+ assert result
199
+ end
200
+
201
+ test 'returns true when host-specific IPv6 setting is enabled' do
202
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(false)
203
+ host = mock('host')
204
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv4_enabled').returns(nil)
205
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv6_enabled').returns('true')
206
+
207
+ result = @instance.obfuscate_ips?(host)
208
+
209
+ assert result
210
+ end
211
+
212
+ test 'returns true when both IPv4 and IPv6 settings are enabled' do
213
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(false)
214
+ host = mock('host')
215
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv4_enabled').returns('true')
216
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv6_enabled').returns('true')
217
+
218
+ result = @instance.obfuscate_ips?(host)
219
+
220
+ assert result
221
+ end
222
+
223
+ test 'returns false when both IPv4 and IPv6 settings are disabled' do
224
+ Setting.expects(:[]).with(:obfuscate_inventory_ips).returns(false)
225
+ host = mock('host')
226
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv4_enabled').returns('false')
227
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscate_ipv6_enabled').returns('false')
228
+
229
+ result = @instance.obfuscate_ips?(host)
230
+
231
+ refute result
232
+ end
233
+ end
234
+
235
+ describe 'obfuscate_ip' do
236
+ test 'generates first IP when no existing obfuscated IPs' do
237
+ ips_dict = {}
238
+
239
+ result = @instance.obfuscate_ip('192.168.1.1', ips_dict)
240
+
241
+ assert_equal '10.230.230.1', result
242
+ end
243
+
244
+ test 'generates next sequential IP when existing obfuscated IPs present' do
245
+ ips_dict = { '192.168.1.1' => '10.230.230.5', '192.168.1.2' => '10.230.230.10' }
246
+
247
+ result = @instance.obfuscate_ip('192.168.1.3', ips_dict)
248
+
249
+ assert_equal '10.230.230.11', result
250
+ end
251
+
252
+ test 'handles mixed IP ranges correctly' do
253
+ ips_dict = { '192.168.1.1' => '10.230.230.255', '192.168.1.2' => '10.230.230.1' }
254
+
255
+ result = @instance.obfuscate_ip('192.168.1.3', ips_dict)
256
+
257
+ assert_equal '10.230.231.0', result
258
+ end
259
+
260
+ test 'generates valid IP addresses' do
261
+ ips_dict = {}
262
+
263
+ result = @instance.obfuscate_ip('any.ip.address', ips_dict)
264
+
265
+ assert_match(/\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/, result)
266
+ assert_nothing_raised { IPAddr.new(result) }
267
+ end
268
+ end
269
+
270
+ describe 'obfuscated_ips' do
271
+ test 'handles invalid JSON in insights_client fact gracefully' do
272
+ host = mock('host')
273
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns('invalid json')
274
+
275
+ result = @instance.obfuscated_ips(host)
276
+
277
+ assert_equal '10.230.230.1', result['192.168.1.1']
278
+ end
279
+
280
+ test 'handles empty insights_client fact' do
281
+ host = mock('host')
282
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns('[]')
283
+
284
+ result = @instance.obfuscated_ips(host)
285
+
286
+ assert_equal '10.230.230.1', result['192.168.1.1']
287
+ end
288
+
289
+ test 'preserves existing obfuscated IPs and generates new ones' do
290
+ host = mock('host')
291
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns(
292
+ '[{"original": "192.168.1.1", "obfuscated": "10.230.230.5"}]'
293
+ )
294
+
295
+ result = @instance.obfuscated_ips(host)
296
+
297
+ assert_equal '10.230.230.5', result['192.168.1.1']
298
+ assert_equal '10.230.230.6', result['192.168.1.2']
299
+ end
300
+
301
+ test 'default_proc generates unique sequential IPs' do
302
+ host = mock('host')
303
+ @instance.expects(:fact_value).with(host, 'insights_client::obfuscated_ipv4').returns(nil)
304
+
305
+ result = @instance.obfuscated_ips(host)
306
+
307
+ ip1 = result['192.168.1.1']
308
+ ip2 = result['192.168.1.2']
309
+ ip3 = result['192.168.1.3']
310
+
311
+ assert_equal '10.230.230.1', ip1
312
+ assert_equal '10.230.230.2', ip2
313
+ assert_equal '10.230.230.3', ip3
314
+ end
315
+ end
51
316
  end
@@ -56,9 +56,10 @@ class SliceGeneratorTest < ActiveSupport::TestCase
56
56
  'dmi::system::product_name',
57
57
  'dmi::chassis::asset_tag',
58
58
  'insights_client::obfuscate_hostname_enabled',
59
+ 'insights_client::obfuscated_hostname',
60
+ 'insights_client::obfuscate_ipv4_enabled',
61
+ 'insights_client::obfuscated_ipv4',
59
62
  'insights_client::hostname',
60
- 'insights_client::obfuscate_ip_enabled',
61
- 'insights_client::ips',
62
63
  'insights_id',
63
64
  ]
64
65
  end
@@ -210,14 +211,38 @@ class SliceGeneratorTest < ActiveSupport::TestCase
210
211
  assert_equal 1, generator.hosts_count
211
212
  end
212
213
 
213
- test 'generates obfuscated ip_address fields with inisghts-client' do
214
+ test 'does not obfuscate fqdn when insights_client obfuscate_hostname_enabled fact is missing and obfuscate_inventory_hostnames setting is false' do
215
+ # Create a host and obfuscated_hostname fact, but do NOT create the obfuscate_hostname_enabled fact
216
+ obfuscated_hostname_data = [
217
+ { 'original' => @host.fqdn, 'obfuscated' => '0dd449d0a027.example.com' },
218
+ ]
219
+ obfuscated_hostname_value = JSON.generate(obfuscated_hostname_data)
220
+ FactoryBot.create(:fact_value,
221
+ fact_name: fact_names['insights_client::obfuscated_hostname'],
222
+ value: obfuscated_hostname_value,
223
+ host: @host)
224
+ # Do NOT create the 'insights_client::obfuscate_hostname_enabled' fact
225
+
226
+ batch = Host.where(id: @host.id).in_batches.first
227
+ generator = create_generator(batch)
228
+
229
+ json_str = generator.render
230
+ actual = JSON.parse(json_str.join("\n"))
231
+
232
+ assert_not_nil(actual_host = actual['hosts'].first)
233
+ assert_equal @host.fqdn, actual_host['fqdn'], "FQDN should not be obfuscated when obfuscate_hostname_enabled is missing and setting is false"
234
+ assert_not_nil(actual_facts = actual_host['facts'].first['facts'])
235
+ assert_not_equal true, actual_facts['is_hostname_obfuscated']
236
+ end
237
+
238
+ test 'generates obfuscated ip_address fields when insights-client facts are present' do
214
239
  nic = FactoryBot.build(:nic_managed)
215
240
  @host.interfaces << nic
216
241
 
217
- FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::obfuscate_ip_enabled'], value: 'true', host: @host)
242
+ FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::obfuscate_ipv4_enabled'], value: 'true', host: @host)
218
243
  FactoryBot.create(
219
244
  :fact_value,
220
- fact_name: fact_names['insights_client::ips'],
245
+ fact_name: fact_names['insights_client::obfuscated_ipv4'],
221
246
  value: "[{\"obfuscated\": \"10.230.230.100\", \"original\": \"#{nic.ip}\"}]",
222
247
  host: @host
223
248
  )
@@ -240,9 +265,17 @@ class SliceGeneratorTest < ActiveSupport::TestCase
240
265
  assert_equal 1, generator.hosts_count
241
266
  end
242
267
 
243
- test 'obfuscates fqdn when instructed by insights-client' do
268
+ test 'obfuscates fqdn when insights-client facts are present' do
269
+ obfuscated_hostname_data = [
270
+ { 'original' => @host.fqdn, 'obfuscated' => '0dd449d0a027.example.com' },
271
+ { 'original' => 'satellite.theforeman.org', 'obfuscated' => 'host2.example.com' },
272
+ ]
273
+ obfuscated_hostname_value = JSON.generate(obfuscated_hostname_data)
274
+ FactoryBot.create(:fact_value,
275
+ fact_name: fact_names['insights_client::obfuscated_hostname'],
276
+ value: obfuscated_hostname_value,
277
+ host: @host)
244
278
  FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::obfuscate_hostname_enabled'], value: 'true', host: @host)
245
- FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::hostname'], value: 'obfuscated_name', host: @host)
246
279
 
247
280
  batch = Host.where(id: @host.id).in_batches.first
248
281
  generator = create_generator(batch)
@@ -252,7 +285,7 @@ class SliceGeneratorTest < ActiveSupport::TestCase
252
285
 
253
286
  assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
254
287
  assert_not_nil(actual_host = actual['hosts'].first)
255
- assert_equal 'obfuscated_name', actual_host['fqdn']
288
+ assert_equal obfuscated_hostname_data.first['obfuscated'], actual_host['fqdn']
256
289
  assert_equal '1234', actual_host['account']
257
290
  assert_not_nil(actual_facts = actual_host['facts'].first['facts'])
258
291
  assert_equal true, actual_facts['is_hostname_obfuscated']
@@ -279,9 +312,35 @@ class SliceGeneratorTest < ActiveSupport::TestCase
279
312
  assert_equal 1, generator.hosts_count
280
313
  end
281
314
 
282
- test 'does not obfuscate fqdn when insights-client sets to false' do
315
+ test 'obfuscates host fqdn with insights-client when setting set' do
316
+ Setting[:obfuscate_inventory_hostnames] = true
317
+ FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::hostname'], value: @host.fqdn, host: @host)
318
+
319
+ batch = Host.where(id: @host.id).in_batches.first
320
+ generator = create_generator(batch)
321
+
322
+ json_str = generator.render
323
+ actual = JSON.parse(json_str.join("\n"))
324
+
325
+ obfuscated_fqdn = Digest::SHA1.hexdigest(@host.fqdn) + '.example.com'
326
+
327
+ assert_equal '00000000-0000-0000-0000-000000000000', actual['report_slice_id']
328
+ assert_not_nil(actual_host = actual['hosts'].first)
329
+ assert_equal obfuscated_fqdn, actual_host['fqdn']
330
+ assert_equal '1234', actual_host['account']
331
+ assert_not_nil(actual_facts = actual_host['facts'].first['facts'])
332
+ assert_equal true, actual_facts['is_hostname_obfuscated']
333
+ assert_equal 1, generator.hosts_count
334
+ end
335
+
336
+ test 'does not obfuscate fqdn when host fact from insights-client has a value of false' do
337
+ obfuscated_hostname_data = [
338
+ { 'original' => @host.fqdn, 'obfuscated' => '0dd449d0a027.example.com' },
339
+ { 'original' => 'satellite.theforeman.org', 'obfuscated' => 'host2.example.com' },
340
+ ]
341
+ obfuscated_hostname_value = JSON.generate(obfuscated_hostname_data)
283
342
  FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::obfuscate_hostname_enabled'], value: 'false', host: @host)
284
- FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::hostname'], value: 'obfuscated_name', host: @host)
343
+ FactoryBot.create(:fact_value, fact_name: fact_names['insights_client::obfuscated_hostname'], value: obfuscated_hostname_value, host: @host)
285
344
 
286
345
  batch = Host.where(id: @host.id).in_batches.first
287
346
  generator = create_generator(batch)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_rh_cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.4.3
4
+ version: 11.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Red Hat Cloud team