foreman_rh_cloud 12.1.4 → 12.2.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 +4 -4
- data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +1 -2
- data/app/controllers/insights_cloud/ui_requests_controller.rb +99 -0
- data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +7 -13
- data/app/services/foreman_rh_cloud/gateway_request.rb +26 -0
- data/app/services/foreman_rh_cloud/insights_api_forwarder.rb +116 -0
- data/app/services/foreman_rh_cloud/tags_auth.rb +55 -0
- data/config/routes.rb +7 -0
- data/lib/foreman_rh_cloud/engine.rb +7 -1
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/insights_cloud.rb +8 -0
- data/package.json +5 -1
- data/test/controllers/insights_cloud/ui_requests_controller_test.rb +169 -0
- data/test/unit/services/foreman_rh_cloud/cloud_request_forwarder_test.rb +3 -3
- data/test/unit/services/foreman_rh_cloud/insights_api_forwarder_test.rb +176 -0
- data/test/unit/services/foreman_rh_cloud/tags_auth_test.rb +29 -0
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +30 -10
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +18 -11
- data/webpack/CVEsHostDetailsTab/index.js +2 -2
- data/webpack/CveDetailsPage/CveDetailsPage.js +20 -0
- data/webpack/CveDetailsPage/CveDetailsPage.test.js +31 -0
- data/webpack/CveDetailsPage/index.js +1 -0
- data/webpack/ForemanColumnExtensions/index.js +49 -14
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/InventoryFilter.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilter.test.js.snap +1 -0
- data/webpack/ForemanInventoryUpload/Components/InventorySettings/MinimalInventoryDropdown.js +2 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/PageTitle.js +8 -1
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +4 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorButton.js +3 -3
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorButton.test.js.snap +3 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudPingModal/index.js +10 -4
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/PageDescription/PageDescription.js +6 -6
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.js +2 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/__snapshots__/SettingsWarning.test.js.snap +2 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButton.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButton.test.js.snap +1 -0
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/InventoryAutoUpload.js +3 -1
- data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/__snapshots__/InventoryAutoUpload.test.js.snap +3 -0
- data/webpack/ForemanRhCloudFills.js +6 -3
- data/webpack/ForemanRhCloudPages.js +21 -3
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTable.js +1 -0
- data/webpack/InsightsCloudSync/Components/InsightsTable/SelectAllAlert.js +2 -0
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +1 -0
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +3 -0
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModalFooter.js +12 -2
- data/webpack/InsightsCloudSync/Components/RemediationModal/Resolutions.js +1 -0
- data/webpack/InsightsCloudSync/Components/ToolbarDropdown.js +6 -1
- data/webpack/InsightsCloudSync/InsightsCloudSync.js +39 -1
- data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +6 -50
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +51 -1
- data/webpack/InsightsVulnerability/InsightsVulnerabilityListPage.js +21 -0
- data/webpack/InsightsVulnerability/InsightsVulnerabilityListPage.test.js +20 -0
- data/webpack/InsightsVulnerabilityHostIndexExtensions/CVECountCell.js +45 -0
- data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +28 -0
- data/webpack/IopRecommendationDetails/IopRecommendationDetails.js +44 -0
- data/webpack/common/DropdownToggle.js +1 -0
- data/webpack/common/ScalprumModule/ScalprumContext.js +73 -0
- data/webpack/common/Switcher/SwitcherPF4.js +1 -0
- data/webpack/common/Switcher/__tests__/__snapshots__/SwitcherPF4.test.js.snap +1 -0
- data/webpack/common/Switcher/index.js +1 -0
- metadata +21 -4
- data/webpack/InsightsVulnerability/InsightsVulnerability.js +0 -13
- data/webpack/InsightsVulnerability/InsightsVulnerability.test.js +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32fdc4ecba1d5949bce50e394de7cdd097697902c8ba47f47b14e26c96e44881
|
4
|
+
data.tar.gz: 0de08119aa42cce06a73dd7bce46d730e023d40b7f54b621cb6a3cef50fb0b63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03ad7ffd1b43db38bdd6762d01b1bd88e7404702a43cd7be626a72892e73f498c990eab3a4cc1037fe63393cdc87d460cd1b87fa7770749e87b9e9a2bf949418
|
7
|
+
data.tar.gz: 403935258065a6cc0755b0d71ed9c3fa6a8652c59264c85da213aa21471d93af5efcde6a829f0e1606d949474b41ab6eaa7b9c573b0ecc74393dce786795f01f
|
@@ -15,9 +15,8 @@ module InsightsCloud::Api
|
|
15
15
|
|
16
16
|
# The method that "proxies" requests over to Cloud
|
17
17
|
def forward_request
|
18
|
-
certs = candlepin_id_cert @organization
|
19
18
|
begin
|
20
|
-
@cloud_response = ::ForemanRhCloud::CloudRequestForwarder.new.forward_request(request, controller_name, @branch_id,
|
19
|
+
@cloud_response = ::ForemanRhCloud::CloudRequestForwarder.new.forward_request(request, controller_name, @branch_id, @host)
|
21
20
|
rescue RestClient::Exceptions::Timeout => e
|
22
21
|
response_obj = e.response.presence || e.exception
|
23
22
|
return render json: { message: response_obj.to_s, error: response_obj.to_s }, status: :gateway_timeout
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module InsightsCloud
|
2
|
+
class UIRequestsController < ::ApplicationController
|
3
|
+
layout false
|
4
|
+
|
5
|
+
before_action :ensure_org, :ensure_loc, :only => [:forward_request]
|
6
|
+
|
7
|
+
# The method that "proxies" requests over to Cloud
|
8
|
+
def forward_request
|
9
|
+
begin
|
10
|
+
path_match = request.original_fullpath.match(%r{^/insights_cloud/(?<path>[^?]*)})&.[](:path)
|
11
|
+
path_to_forward = path_match || params.require(:path)
|
12
|
+
|
13
|
+
@cloud_response = ::ForemanRhCloud::InsightsApiForwarder.new.forward_request(
|
14
|
+
request,
|
15
|
+
path_to_forward,
|
16
|
+
controller_name,
|
17
|
+
User.current,
|
18
|
+
@organization,
|
19
|
+
@location
|
20
|
+
)
|
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.warn("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'
|
36
|
+
|
37
|
+
return render json: {
|
38
|
+
:message => message,
|
39
|
+
:error => response_obj.to_s,
|
40
|
+
:headers => {},
|
41
|
+
:response => response_obj,
|
42
|
+
}, status: code
|
43
|
+
rescue StandardError => e
|
44
|
+
# Catch any other exceptions here, such as Errno::ECONNREFUSED
|
45
|
+
logger.warn("Cloud request failed with exception: #{e}")
|
46
|
+
return render json: { error: e.to_s }, status: :bad_gateway
|
47
|
+
end
|
48
|
+
|
49
|
+
# Append redhat-specific headers
|
50
|
+
@cloud_response.headers.each do |key, _value|
|
51
|
+
assign_header(response, @cloud_response, key, false) if key.to_s.start_with?('x_rh_')
|
52
|
+
end
|
53
|
+
|
54
|
+
# Append general headers
|
55
|
+
assign_header(response, @cloud_response, :x_resource_count, true)
|
56
|
+
headers[Rack::ETAG] = @cloud_response.headers[:etag]
|
57
|
+
|
58
|
+
if @cloud_response.headers[:content_disposition]
|
59
|
+
# If there is a Content-Disposition header, it means we are forwarding binary data, send the raw data with proper
|
60
|
+
# content type
|
61
|
+
send_data @cloud_response, disposition: @cloud_response.headers[:content_disposition], type: @cloud_response.headers[:content_type]
|
62
|
+
elsif @cloud_response.headers[:content_type] =~ /zip/
|
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
|
65
|
+
send_data @cloud_response, type: @cloud_response.headers[:content_type]
|
66
|
+
else
|
67
|
+
render json: @cloud_response, status: @cloud_response.code
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def assign_header(res, cloud_res, header, transform)
|
72
|
+
header_content = cloud_res.headers[header]
|
73
|
+
return unless header_content
|
74
|
+
new_header = transform ? header.to_s.tr('_', '-') : header.to_s
|
75
|
+
res.headers[new_header] = header_content
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def ensure_org
|
81
|
+
@organization = Organization.current
|
82
|
+
return render_message 'Organization not found or invalid', :status => 400 unless @organization
|
83
|
+
end
|
84
|
+
|
85
|
+
def ensure_loc
|
86
|
+
@location = Location.current
|
87
|
+
return render_message 'Location not found or invalid', :status => 400 unless @location
|
88
|
+
end
|
89
|
+
|
90
|
+
def base_url
|
91
|
+
InsightsCloud.ui_base_url
|
92
|
+
end
|
93
|
+
|
94
|
+
def render_message(msg, render_options = {})
|
95
|
+
render_options[:json] = { :message => msg }
|
96
|
+
render render_options
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -3,8 +3,9 @@ require 'rest-client'
|
|
3
3
|
module ForemanRhCloud
|
4
4
|
class CloudRequestForwarder
|
5
5
|
include ForemanRhCloud::CloudRequest
|
6
|
+
include ForemanRhCloud::CertAuth
|
6
7
|
|
7
|
-
def forward_request(original_request, controller_name, branch_id,
|
8
|
+
def forward_request(original_request, controller_name, branch_id, host)
|
8
9
|
forward_params = prepare_forward_params(original_request, branch_id)
|
9
10
|
logger.debug("Request parameters for telemetry request: #{forward_params}")
|
10
11
|
|
@@ -12,14 +13,15 @@ module ForemanRhCloud
|
|
12
13
|
|
13
14
|
logger.debug("User agent for telemetry is: #{http_user_agent original_request}")
|
14
15
|
|
15
|
-
request_opts = prepare_request_opts(original_request, forward_payload, forward_params,
|
16
|
+
request_opts = prepare_request_opts(original_request, forward_payload, forward_params, host)
|
17
|
+
request_opts[:organization] = host.organization
|
16
18
|
|
17
19
|
logger.debug("Sending request to: #{request_opts[:url]}")
|
18
20
|
|
19
21
|
execute_cloud_request(request_opts)
|
20
22
|
end
|
21
23
|
|
22
|
-
def prepare_request_opts(original_request, forward_payload, forward_params,
|
24
|
+
def prepare_request_opts(original_request, forward_payload, forward_params, host)
|
23
25
|
base_params = {
|
24
26
|
method: original_request.method,
|
25
27
|
payload: forward_payload,
|
@@ -33,7 +35,7 @@ module ForemanRhCloud
|
|
33
35
|
),
|
34
36
|
}
|
35
37
|
requested_url = original_request.original_fullpath.end_with?('/') ? original_request.path + '/' : original_request.path
|
36
|
-
params = path_params(requested_url
|
38
|
+
params = path_params(requested_url)
|
37
39
|
|
38
40
|
if ForemanRhCloud.with_local_advisor_engine?
|
39
41
|
params[:ssl_ca_file] = ForemanRhCloud.ca_cert
|
@@ -66,31 +68,23 @@ module ForemanRhCloud
|
|
66
68
|
forward_params
|
67
69
|
end
|
68
70
|
|
69
|
-
def path_params(request_path
|
71
|
+
def path_params(request_path)
|
70
72
|
case request_path
|
71
73
|
when lightspeed?
|
72
74
|
{
|
73
75
|
url: ForemanRhCloud.cert_base_url + request_path,
|
74
|
-
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
|
75
|
-
ssl_client_key: OpenSSL::PKey.read(certs[:key]),
|
76
76
|
}
|
77
77
|
when platform_request?
|
78
78
|
{
|
79
79
|
url: ForemanRhCloud.cert_base_url + request_path.sub('/redhat_access/r/insights/platform', '/api'),
|
80
|
-
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
|
81
|
-
ssl_client_key: OpenSSL::PKey.read(certs[:key]),
|
82
80
|
}
|
83
81
|
when connection_test_request?
|
84
82
|
{
|
85
83
|
url: ForemanRhCloud.cert_base_url + '/api/apicast-tests/ping',
|
86
|
-
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
|
87
|
-
ssl_client_key: OpenSSL::PKey.read(certs[:key]),
|
88
84
|
}
|
89
85
|
else # Legacy insights API
|
90
86
|
{
|
91
87
|
url: ForemanRhCloud.legacy_insights_url + request_path.sub('/redhat_access/r/insights', '/r/insights'),
|
92
|
-
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
|
93
|
-
ssl_client_key: OpenSSL::PKey.read(certs[:key]),
|
94
88
|
ssl_ca_file: ForemanRhCloud.legacy_insights_ca,
|
95
89
|
}
|
96
90
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanRhCloud
|
2
|
+
module GatewayRequest
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include CloudRequest
|
6
|
+
|
7
|
+
def execute_cloud_request(params)
|
8
|
+
certs = params.delete(:certs) || foreman_certificates
|
9
|
+
final_params = {
|
10
|
+
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
|
11
|
+
ssl_client_key: OpenSSL::PKey.read(certs[:key]),
|
12
|
+
ssl_ca_file: Setting[:ssl_ca_file],
|
13
|
+
verify_ssl: OpenSSL::SSL::VERIFY_PEER,
|
14
|
+
}.deep_merge(params)
|
15
|
+
|
16
|
+
super(final_params)
|
17
|
+
end
|
18
|
+
|
19
|
+
def foreman_certificates
|
20
|
+
{
|
21
|
+
cert: File.read(Setting[:ssl_certificate]),
|
22
|
+
key: File.read(Setting[:ssl_priv_key]),
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
|
3
|
+
module ForemanRhCloud
|
4
|
+
class InsightsApiForwarder
|
5
|
+
include ForemanRhCloud::GatewayRequest
|
6
|
+
|
7
|
+
SCOPED_REQUESTS = [
|
8
|
+
%r{/api/vulnerability/v1/vulnerabilities/cves},
|
9
|
+
%r{/api/vulnerability/v1/dashbar},
|
10
|
+
%r{/api/vulnerability/v1/cves/[^/]+/affected_systems},
|
11
|
+
%r{/api/vulnerability/v1/systems/[^/]+/cves},
|
12
|
+
%r{/api/insights/.*},
|
13
|
+
%r{/api/inventory/.*},
|
14
|
+
%r{/api/tasks/.*},
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
def forward_request(original_request, path, controller_name, user, organization, location)
|
18
|
+
TagsAuth.new(user, organization, location, logger).update_tag if scope_request?(original_request, path)
|
19
|
+
|
20
|
+
forward_params = prepare_forward_params(original_request, path, user: user, organization: organization, location: location).to_a
|
21
|
+
logger.debug("Request parameters for UI request: #{forward_params}")
|
22
|
+
|
23
|
+
forward_payload = prepare_forward_payload(original_request, controller_name)
|
24
|
+
|
25
|
+
logger.debug("User agent for UI is: #{http_user_agent(original_request)}")
|
26
|
+
|
27
|
+
request_opts = prepare_request_opts(original_request, path, forward_payload, forward_params)
|
28
|
+
|
29
|
+
logger.debug("Sending request to: #{request_opts[:url]}")
|
30
|
+
|
31
|
+
execute_cloud_request(request_opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
def prepare_tags(user, organization, location)
|
35
|
+
[
|
36
|
+
TagsAuth.auth_tag_for(user, organization, location),
|
37
|
+
].map { |tag_value| [:tag, tag_value] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def prepare_request_opts(original_request, path, forward_payload, forward_params)
|
41
|
+
base_params = {
|
42
|
+
method: original_request.method,
|
43
|
+
payload: forward_payload,
|
44
|
+
headers: original_headers(original_request).merge(
|
45
|
+
{
|
46
|
+
params: RestClient::ParamsArray.new(forward_params),
|
47
|
+
user_agent: http_user_agent(original_request),
|
48
|
+
content_type: original_request.media_type.presence || original_request.format.to_s,
|
49
|
+
}
|
50
|
+
),
|
51
|
+
}
|
52
|
+
params = path_params(path)
|
53
|
+
|
54
|
+
base_params.merge(params)
|
55
|
+
end
|
56
|
+
|
57
|
+
def prepare_forward_payload(original_request, controller_name)
|
58
|
+
forward_payload = original_request.request_parameters[controller_name]
|
59
|
+
|
60
|
+
forward_payload = original_request.raw_post.clone if (original_request.post? || original_request.patch?) && original_request.raw_post
|
61
|
+
forward_payload = original_request.body.read if original_request.put?
|
62
|
+
|
63
|
+
forward_payload = original_request.params.slice(:file, :metadata) if original_request.params[:file]
|
64
|
+
|
65
|
+
# fix rails behaviour for http PATCH:
|
66
|
+
forward_payload = forward_payload.to_json if original_request.format.json? && original_request.patch? && forward_payload && !forward_payload.is_a?(String)
|
67
|
+
forward_payload
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_forward_params(original_request, path, user:, organization:, location:)
|
71
|
+
forward_params = original_request.query_parameters.to_a
|
72
|
+
|
73
|
+
forward_params += prepare_tags(user, organization, location) if scope_request?(original_request, path)
|
74
|
+
|
75
|
+
forward_params
|
76
|
+
end
|
77
|
+
|
78
|
+
def path_params(path)
|
79
|
+
{
|
80
|
+
url: "#{InsightsCloud.ui_base_url}/#{path}",
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def original_headers(original_request)
|
85
|
+
headers = {
|
86
|
+
if_none_match: original_request.if_none_match,
|
87
|
+
if_modified_since: original_request.if_modified_since,
|
88
|
+
}.compact
|
89
|
+
|
90
|
+
logger.debug("Sending headers: #{headers}")
|
91
|
+
headers
|
92
|
+
end
|
93
|
+
|
94
|
+
def scope_request?(original_request, path)
|
95
|
+
return false unless original_request.get?
|
96
|
+
|
97
|
+
SCOPED_REQUESTS.any? { |request_pattern| request_pattern.match?(path) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def core_app_name
|
101
|
+
BranchInfo.new.core_app_name
|
102
|
+
end
|
103
|
+
|
104
|
+
def core_app_version
|
105
|
+
BranchInfo.new.core_app_version
|
106
|
+
end
|
107
|
+
|
108
|
+
def http_user_agent(original_request)
|
109
|
+
"#{core_app_name}/#{core_app_version};#{ForemanRhCloud::Engine.engine_name}/#{ForemanRhCloud::VERSION};#{original_request.env['HTTP_USER_AGENT']}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def logger
|
113
|
+
Foreman::Logging.logger('app')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ForemanRhCloud
|
2
|
+
class TagsAuth
|
3
|
+
include GatewayRequest
|
4
|
+
|
5
|
+
TAG_NAMESPACE = 'sat_iam'.freeze
|
6
|
+
TAG_SHORT_NAME = 'scope'.freeze
|
7
|
+
TAG_NAME = "#{TAG_NAMESPACE}/#{TAG_SHORT_NAME}".freeze
|
8
|
+
|
9
|
+
def self.auth_tag_for(user, org, loc)
|
10
|
+
new(user, org, loc, nil).auth_tag
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :logger
|
14
|
+
|
15
|
+
def initialize(user, org, loc, logger)
|
16
|
+
@user = user
|
17
|
+
@org = org
|
18
|
+
@loc = loc
|
19
|
+
@logger = logger
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_tag
|
23
|
+
logger.debug("Updating tags for user: #{@user}, org: #{@org.name}, loc: #{@loc.name}")
|
24
|
+
|
25
|
+
params = {
|
26
|
+
method: :post,
|
27
|
+
url: "#{InsightsCloud.gateway_url}/tags",
|
28
|
+
headers: {
|
29
|
+
content_type: :json,
|
30
|
+
},
|
31
|
+
payload: tags_query_payload.to_json,
|
32
|
+
}
|
33
|
+
execute_cloud_request(params)
|
34
|
+
end
|
35
|
+
|
36
|
+
def allowed_hosts
|
37
|
+
Host.authorized_as(@user, nil, nil).where(organization: @org, location: @loc).joins(:subscription_facet).pluck('katello_subscription_facets.uuid')
|
38
|
+
end
|
39
|
+
|
40
|
+
def tags_query_payload
|
41
|
+
{
|
42
|
+
tags: [{ "namespace": TAG_NAMESPACE, "key": TAG_SHORT_NAME, "value": tag_value }],
|
43
|
+
host_id_list: allowed_hosts,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def tag_value
|
48
|
+
"U:\"#{@user.login}\"O:\"#{@org.name}\"L:\"#{@loc.name}\""
|
49
|
+
end
|
50
|
+
|
51
|
+
def auth_tag
|
52
|
+
"#{TAG_NAME}=#{tag_value}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/config/routes.rb
CHANGED
@@ -30,14 +30,21 @@ Rails.application.routes.draw do
|
|
30
30
|
match 'hits/:host_id', to: 'hits#show', via: :get
|
31
31
|
|
32
32
|
post ':organization_id/parameter', to: 'settings#set_org_parameter', constraints: { organization_id: %r{[^\/]+} }
|
33
|
+
|
34
|
+
match '/*path', constraints: { path: %r{api/[^\?]+} }, to: 'ui_requests#forward_request', via: :all
|
33
35
|
end
|
34
36
|
|
35
37
|
namespace :foreman_rh_cloud do
|
36
38
|
unless ForemanRhCloud.with_local_advisor_engine?
|
37
39
|
get 'inventory_upload', to: '/react#index'
|
38
40
|
end
|
41
|
+
if ForemanRhCloud.with_local_advisor_engine?
|
42
|
+
get 'recommendations', to: '/react#index'
|
43
|
+
get 'recommendations/:rule_id', to: '/react#index'
|
44
|
+
end
|
39
45
|
get 'insights_cloud', to: '/react#index' # Uses foreman's react controller
|
40
46
|
get 'insights_vulnerability', to: '/react#index'
|
47
|
+
get 'insights_vulnerability/:cve_id', to: '/react#index'
|
41
48
|
end
|
42
49
|
|
43
50
|
scope :module => :'insights_cloud/api', :path => :redhat_access do
|
@@ -84,6 +84,7 @@ module ForemanRhCloud
|
|
84
84
|
'/foreman_rh_cloud/insights_cloud': [:index], # for bookmarks and later for showing the page
|
85
85
|
'insights_cloud/hits': [:index, :show, :auto_complete_search, :resolutions],
|
86
86
|
'insights_cloud/settings': [:index, :show],
|
87
|
+
'insights_cloud/ui_requests': [:forward_request],
|
87
88
|
'react': [:index],
|
88
89
|
},
|
89
90
|
:resource_type => ::InsightsHit.name
|
@@ -199,7 +200,7 @@ module ForemanRhCloud
|
|
199
200
|
RemoteExecutionFeature.register(
|
200
201
|
:rh_cloud_connector_run_playbook,
|
201
202
|
N_('Run RH Cloud playbook'),
|
202
|
-
description: N_('Run playbook
|
203
|
+
description: N_('Run playbook generated by Red Hat remediations app'),
|
203
204
|
host_action_button: false,
|
204
205
|
provided_inputs: ['playbook_url', 'report_url', 'correlation_id', 'report_interval']
|
205
206
|
)
|
@@ -228,10 +229,15 @@ module ForemanRhCloud
|
|
228
229
|
Katello::Api::V2::OrganizationsController.before_action(:local_find_taxonomy, only: :download_debug_certificate)
|
229
230
|
|
230
231
|
Katello::Api::V2::RepositoriesController.include Foreman::Controller::SmartProxyAuth
|
232
|
+
# patch the callbacks order for :index, since find_product has to run after the user is already initialized
|
233
|
+
Katello::Api::V2::RepositoriesController.skip_before_action(:find_product, only: :index)
|
234
|
+
Katello::Api::V2::RepositoriesController.skip_before_action(:find_optional_organization, only: :index)
|
231
235
|
Katello::Api::V2::RepositoriesController.add_smart_proxy_filters(
|
232
236
|
:index,
|
233
237
|
features: ForemanRhCloud.on_prem_smart_proxy_features
|
234
238
|
)
|
239
|
+
Katello::Api::V2::RepositoriesController.before_action(:find_product, only: :index)
|
240
|
+
Katello::Api::V2::RepositoriesController.before_action(:find_optional_organization, only: :index)
|
235
241
|
end
|
236
242
|
end
|
237
243
|
|
data/lib/insights_cloud.rb
CHANGED
@@ -21,6 +21,14 @@ module InsightsCloud
|
|
21
21
|
ForemanRhCloud.cert_base_url + '/api/remediations/v1/playbook'
|
22
22
|
end
|
23
23
|
|
24
|
+
def self.gateway_url
|
25
|
+
ForemanRhCloud.cert_base_url
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.ui_base_url
|
29
|
+
InsightsCloud.gateway_url
|
30
|
+
end
|
31
|
+
|
24
32
|
def self.remediation_rule_id(rule_id)
|
25
33
|
"advisor:#{rule_id}"
|
26
34
|
end
|
data/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "foreman_rh_cloud",
|
3
|
-
"version": "12.
|
3
|
+
"version": "12.2.0",
|
4
4
|
"description": "Inventory Upload =============",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
@@ -21,6 +21,10 @@
|
|
21
21
|
"peerDependencies": {
|
22
22
|
"@theforeman/vendor": ">= 15.0.1"
|
23
23
|
},
|
24
|
+
"dependencies": {
|
25
|
+
"@scalprum/react-core": "^0.9.3",
|
26
|
+
"@scalprum/core": "^0.8.1"
|
27
|
+
},
|
24
28
|
"devDependencies": {
|
25
29
|
"@babel/core": "^7.7.0",
|
26
30
|
"@theforeman/builder": ">= 15.0.1",
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
module InsightsCloud
|
5
|
+
class UIRequestsControllerTest < ActionController::TestCase
|
6
|
+
include KatelloCVEHelper
|
7
|
+
|
8
|
+
setup do
|
9
|
+
FactoryBot.create(:common_parameter, name: InsightsCloud.enable_client_param, key_type: 'boolean', value: true)
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#forward_request' do
|
13
|
+
include MockCerts
|
14
|
+
|
15
|
+
setup do
|
16
|
+
@body = 'Cloud response body'
|
17
|
+
@http_req = RestClient::Request.new(:method => 'GET', :url => 'http://test.theforeman.org')
|
18
|
+
|
19
|
+
@org = FactoryBot.create(:organization)
|
20
|
+
@loc = FactoryBot.create(:location)
|
21
|
+
host = FactoryBot.create(:host, :with_subscription, :organization => @org)
|
22
|
+
User.current = ::Katello::CpConsumerUser.new(:uuid => host.subscription_facet.uuid, :login => host.subscription_facet.uuid)
|
23
|
+
InsightsCloud::UIRequestsController.any_instance.stubs(:upstream_owner).returns({ 'uuid' => 'abcdefg' })
|
24
|
+
ForemanRhCloud::TagsAuth.any_instance.stubs(:execute_cloud_request)
|
25
|
+
|
26
|
+
setup_certs_expectation do
|
27
|
+
ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:foreman_certificates)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should respond with response from cloud" do
|
32
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
33
|
+
net_http_resp.add_field 'Set-Cookie', 'Monster'
|
34
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
35
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
|
36
|
+
|
37
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
38
|
+
assert_equal @body, @response.body
|
39
|
+
end
|
40
|
+
|
41
|
+
test "should handle timeout from cloud" do
|
42
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.
|
43
|
+
stubs(:forward_request).
|
44
|
+
raises(RestClient::Exceptions::OpenTimeout.new("Timed out connecting to server"))
|
45
|
+
|
46
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
47
|
+
request_response = JSON.parse(@response.body)
|
48
|
+
# I can't get @response.status to take a nil value so I'm not asserting for that
|
49
|
+
|
50
|
+
assert_equal 'Timed out connecting to server', request_response['error']
|
51
|
+
end
|
52
|
+
|
53
|
+
test "should add headers to response from cloud" do
|
54
|
+
x_resource_count = '101'
|
55
|
+
x_rh_insights_request_id = '202'
|
56
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
57
|
+
net_http_resp['x_resource_count'] = x_resource_count
|
58
|
+
net_http_resp['x_rh_insights_request_id'] = x_rh_insights_request_id
|
59
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
60
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
|
61
|
+
|
62
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
63
|
+
assert_equal x_resource_count, @response.headers['x-resource-count']
|
64
|
+
assert_equal x_rh_insights_request_id, @response.headers['x_rh_insights_request_id']
|
65
|
+
end
|
66
|
+
|
67
|
+
test "should extract path from original_fullpath when URL starts with insights_cloud" do
|
68
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
69
|
+
# Simulate a request with insights_cloud in the path
|
70
|
+
@request.stubs(:original_fullpath).returns('/insights_cloud/api/vulnerability/v1/cves?search=test')
|
71
|
+
|
72
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
73
|
+
::ForemanRhCloud::InsightsApiForwarder
|
74
|
+
.any_instance
|
75
|
+
.expects(:forward_request)
|
76
|
+
.returns(res)
|
77
|
+
.with { |_, path_to_forward| _(path_to_forward).must_equal('api/vulnerability/v1/cves') }
|
78
|
+
|
79
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
80
|
+
|
81
|
+
assert_equal @body, @response.body
|
82
|
+
end
|
83
|
+
|
84
|
+
test "should set etag header to response from cloud" do
|
85
|
+
etag = '12345'
|
86
|
+
req = RestClient::Request.new(:method => 'GET', :url => 'http://test.theforeman.org', :headers => { "If-None-Match": etag })
|
87
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
88
|
+
net_http_resp[Rack::ETAG] = etag
|
89
|
+
res = RestClient::Response.create(@body, net_http_resp, req)
|
90
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
|
91
|
+
|
92
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
93
|
+
assert_equal etag, @response.headers[Rack::ETAG]
|
94
|
+
end
|
95
|
+
|
96
|
+
test "should set content type header to response from cloud" do
|
97
|
+
req = RestClient::Request.new(:method => 'GET', :url => 'http://test.theforeman.org')
|
98
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 200, "OK")
|
99
|
+
net_http_resp[:content_type] = 'application/zip'
|
100
|
+
res = RestClient::Response.create(@body, net_http_resp, req)
|
101
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:forward_request).returns(res)
|
102
|
+
|
103
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
104
|
+
assert_equal net_http_resp[:content_type], @response.headers['Content-Type']
|
105
|
+
end
|
106
|
+
|
107
|
+
test "should handle StandardError" do
|
108
|
+
error_message = "Connection refused"
|
109
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(Errno::ECONNREFUSED.new)
|
110
|
+
|
111
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
112
|
+
assert_equal 502, @response.status
|
113
|
+
body = JSON.parse(@response.body)
|
114
|
+
assert_equal error_message, body['error']
|
115
|
+
end
|
116
|
+
|
117
|
+
test "should handle 304 cloud" do
|
118
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 304, "Not Modified")
|
119
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
120
|
+
|
121
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::NotModified.new(res))
|
122
|
+
|
123
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
124
|
+
assert_equal 304, @response.status
|
125
|
+
assert_equal 'Cloud request not modified', JSON.parse(@response.body)['message']
|
126
|
+
end
|
127
|
+
|
128
|
+
test "should handle RestClient::Exceptions::Timeout" do
|
129
|
+
timeout_message = "execution expired"
|
130
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::Exceptions::Timeout.new(timeout_message))
|
131
|
+
|
132
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
133
|
+
assert_equal 504, @response.status
|
134
|
+
body = JSON.parse(@response.body)
|
135
|
+
assert_equal timeout_message, body['message']
|
136
|
+
assert_equal timeout_message, body['error']
|
137
|
+
end
|
138
|
+
|
139
|
+
test "should handle failed authentication to cloud" do
|
140
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 401, "Unauthorized")
|
141
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
142
|
+
|
143
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::Unauthorized.new(res))
|
144
|
+
|
145
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
146
|
+
assert_equal 401, @response.status
|
147
|
+
assert_equal 'Authentication to the Insights Service failed.', JSON.parse(@response.body)['message']
|
148
|
+
end
|
149
|
+
|
150
|
+
test "should forward errors to the client" do
|
151
|
+
net_http_resp = Net::HTTPResponse.new(1.0, 500, "TEST_RESPONSE")
|
152
|
+
res = RestClient::Response.create(@body, net_http_resp, @http_req)
|
153
|
+
::ForemanRhCloud::InsightsApiForwarder.any_instance.stubs(:execute_cloud_request).raises(RestClient::InternalServerError.new(res))
|
154
|
+
|
155
|
+
get :forward_request, params: { "controller" => "vulnerabilities", "path" => "api/vulnerability/v1/cves" }, session: set_session
|
156
|
+
assert_equal 500, @response.status
|
157
|
+
assert_equal 'Cloud request failed', JSON.parse(@response.body)['message']
|
158
|
+
assert_match(/#{@body}/, JSON.parse(@response.body)['response'])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_session
|
163
|
+
set_session_user.merge(
|
164
|
+
organization_id: @org.id,
|
165
|
+
location_id: @loc.id
|
166
|
+
)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|