foreman_remote_execution 3.3.7 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/app/controllers/job_invocations_controller.rb +22 -8
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/lib/foreman_remote_execution/renderer/scope/input.rb +35 -0
- data/app/models/job_invocation.rb +6 -3
- data/app/models/job_invocation_composer.rb +2 -2
- data/app/models/remote_execution_provider.rb +2 -2
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/ssh_execution_provider.rb +1 -1
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/app/views/job_invocations/_tab_hosts.html.erb +1 -20
- data/app/views/job_invocations/show.html.erb +3 -0
- data/app/views/job_invocations/show.json.erb +2 -1
- data/db/migrate/20200623073022_rename_sudo_password_to_effective_user_password.rb +34 -0
- data/db/seeds.d/20-permissions.rb +9 -0
- data/lib/foreman_remote_execution/engine.rb +4 -10
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +64 -1
- data/test/functional/job_invocations_controller_test.rb +71 -0
- data/test/support/remote_execution_helper.rb +5 -0
- data/test/unit/actions/run_host_job_test.rb +3 -3
- data/test/unit/actions/run_hosts_job_test.rb +1 -1
- data/test/unit/job_invocation_composer_test.rb +5 -5
- data/test/unit/remote_execution_provider_test.rb +6 -6
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +2 -0
- data/webpack/__mocks__/foremanReact/constants.js +21 -0
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +2 -0
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors.js +1 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +21 -15
- data/webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js +10 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +62 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +6 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +10 -2
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js +9 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +26 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +17 -2
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +68 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +11 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +35 -19
- data/webpack/react_app/components/TargetingHosts/index.js +73 -13
- metadata +17 -3
- data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93860edb8974a0691383b3a199a1a2d079d10401d7780b99aa6275b75125eb58
|
4
|
+
data.tar.gz: d333f8eb85a42b36694dee932447d4364b57ed2c095ad5a055d1e7adf69a9866
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70c300e719d6587639719a4d95aa23da2d3f3a16d51692392c789b70cd381b31320789db0f45b57c129e4b90d36bd1a8ddadd29db4f38f2d9e40a39219b1d130
|
7
|
+
data.tar.gz: 1feab0c5cfd3fee6054a004eba812b7257df80a2dce50aa200d15dd20797f24f8a614d802668ac6daf043c189b7caa8e8d5f0d4a997726764c80c91173d9437b
|
data/.github/workflows/ci.yml
CHANGED
@@ -52,16 +52,18 @@ class JobInvocationsController < ApplicationController
|
|
52
52
|
|
53
53
|
def show
|
54
54
|
@job_invocation = resource_base.includes(:template_invocations => :run_host_job_task).find(params[:id])
|
55
|
-
@auto_refresh = @job_invocation.task.try(:pending?)
|
56
|
-
@resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
57
|
-
# There's no need to do the joining if we're not filtering
|
58
|
-
unless params[:search].nil?
|
59
|
-
@resource_base = @resource_base.joins(:template_invocations)
|
60
|
-
.where(:template_invocations => { :job_invocation_id => @job_invocation.id})
|
61
|
-
end
|
62
|
-
@hosts = resource_base_search_and_page
|
63
55
|
@job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
|
64
56
|
@job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
|
57
|
+
@auto_refresh = @job_invocation.task.try(:pending?)
|
58
|
+
|
59
|
+
respond_to do |format|
|
60
|
+
format.json do
|
61
|
+
targeting_hosts_resources
|
62
|
+
end
|
63
|
+
|
64
|
+
format.html
|
65
|
+
format.js
|
66
|
+
end
|
65
67
|
end
|
66
68
|
|
67
69
|
def index
|
@@ -153,4 +155,16 @@ class JobInvocationsController < ApplicationController
|
|
153
155
|
JobInvocationComposer.from_ui_params(with_triggering)
|
154
156
|
end
|
155
157
|
end
|
158
|
+
|
159
|
+
def targeting_hosts_resources
|
160
|
+
@auto_refresh = @job_invocation.task.try(:pending?)
|
161
|
+
@resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
162
|
+
|
163
|
+
unless params[:search].nil?
|
164
|
+
@resource_base = @resource_base.joins(:template_invocations)
|
165
|
+
.where(:template_invocations => { :job_invocation_id => @job_invocation.id})
|
166
|
+
end
|
167
|
+
@hosts = resource_base_search_and_page
|
168
|
+
@total_hosts = resource_base_with_search.size
|
169
|
+
end
|
156
170
|
end
|
@@ -60,7 +60,7 @@ module Actions
|
|
60
60
|
def secrets(host, job_invocation, provider)
|
61
61
|
job_secrets = { :ssh_password => job_invocation.password,
|
62
62
|
:key_passphrase => job_invocation.key_passphrase,
|
63
|
-
:
|
63
|
+
:effective_user_password => job_invocation.effective_user_password }
|
64
64
|
|
65
65
|
job_secrets.merge(provider.secrets(host)) { |_key, job_secret, provider_secret| job_secret || provider_secret }
|
66
66
|
end
|
@@ -47,7 +47,7 @@ module Actions
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def finalize
|
50
|
-
job_invocation.password = job_invocation.key_passphrase = job_invocation.
|
50
|
+
job_invocation.password = job_invocation.key_passphrase = job_invocation.effective_user_password = nil
|
51
51
|
job_invocation.save!
|
52
52
|
|
53
53
|
Rails.logger.debug "cleaning cache for keys that begin with 'job_invocation_#{job_invocation.id}'"
|
@@ -3,14 +3,33 @@ module ForemanRemoteExecution
|
|
3
3
|
module Scope
|
4
4
|
class Input < ::Foreman::Renderer::Scope::Template
|
5
5
|
include Foreman::Renderer::Scope::Macros::HostTemplate
|
6
|
+
extend ApipieDSL::Class
|
6
7
|
|
7
8
|
attr_reader :template, :host, :invocation, :input_template_instance, :current_user
|
8
9
|
delegate :input, to: :input_template_instance
|
9
10
|
|
11
|
+
apipie :class, 'Macros related to template rendering' do
|
12
|
+
name 'Template Input Render'
|
13
|
+
sections only: %w[all jobs]
|
14
|
+
end
|
15
|
+
|
16
|
+
apipie :method, 'Always raises an error with a description provided as an argument' do
|
17
|
+
desc 'This method is useful for aborting script execution if some of the conditions are not met'
|
18
|
+
required :message, String, desc: 'Description for the error'
|
19
|
+
raises error: ::InputTemplateRenderer::RenderError, desc: 'The error is always being raised'
|
20
|
+
returns nil, desc: "Doesn't return anything"
|
21
|
+
example "<%
|
22
|
+
@host.operatingsystem #=> nil
|
23
|
+
render_error(N_('Unsupported or no operating system found for this host.')) unless @host.operatingsystem #=> InputTemplateRenderer::RenderError is raised and the execution of the script is aborted
|
24
|
+
%>"
|
25
|
+
end
|
10
26
|
def render_error(message)
|
11
27
|
raise ::InputTemplateRenderer::RenderError.new(message)
|
12
28
|
end
|
13
29
|
|
30
|
+
apipie :method, 'Check whether the template in preview mode or not' do
|
31
|
+
returns one_of: [true, false], desc: 'Returns true if the template in preview mode, false otherwise'
|
32
|
+
end
|
14
33
|
def preview?
|
15
34
|
!!@preview
|
16
35
|
end
|
@@ -23,6 +42,16 @@ module ForemanRemoteExecution
|
|
23
42
|
Rails.cache.fetch(cache_key, &block)
|
24
43
|
end
|
25
44
|
|
45
|
+
# rubocop:disable Lint/InterpolationCheck
|
46
|
+
apipie :method, 'Render template by given name' do
|
47
|
+
required :template_name, String, desc: 'name of the template to render'
|
48
|
+
optional :input_values, Hash, desc: 'key:value list of input values for the template'
|
49
|
+
optional :options, Hash, desc: 'Additional options such as :with_foreign_input_set. Set to true if a foreign input set should be considered when rendering the template'
|
50
|
+
raises error: StandardError, desc: 'raises an error if there is no template or template input with such name'
|
51
|
+
returns String, desc: 'Rendered template'
|
52
|
+
example '<%= render_template("Run Command - Ansible Default", command: "yum -y group install #{input("package")}") %>'
|
53
|
+
end
|
54
|
+
# rubocop:enable Lint/InterpolationCheck
|
26
55
|
def render_template(template_name, input_values = {}, options = {})
|
27
56
|
options.assert_valid_keys(:with_foreign_input_set)
|
28
57
|
with_foreign_input_set = options.fetch(:with_foreign_input_set, true)
|
@@ -59,6 +88,12 @@ module ForemanRemoteExecution
|
|
59
88
|
input_values.merge(overrides).with_indifferent_access
|
60
89
|
end
|
61
90
|
|
91
|
+
apipie :method, 'Returns the value of template input' do
|
92
|
+
required :name, String, desc: 'name of the template input'
|
93
|
+
raises error: UndefinedInput, desc: 'when there is no input with such name defined for the current template'
|
94
|
+
returns Object, desc: 'The value of template input'
|
95
|
+
example 'input("Include Facts") #=> "yes"'
|
96
|
+
end
|
62
97
|
def input(name)
|
63
98
|
return template_input_values[name.to_s] if template_input_values.key?(name.to_s)
|
64
99
|
|
@@ -41,7 +41,10 @@ class JobInvocation < ApplicationRecord
|
|
41
41
|
has_many :template_invocation_tasks, :through => :template_invocations,
|
42
42
|
:class_name => 'ForemanTasks::Task',
|
43
43
|
:source => 'run_host_job_task'
|
44
|
-
|
44
|
+
has_one :user, through: :task
|
45
|
+
scoped_search relation: :user, on: :login, rename: 'user', complete_value: true,
|
46
|
+
value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
|
47
|
+
special_values: [:current_user], aliases: ['owner'], :only_explicit => true
|
45
48
|
scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
|
46
49
|
scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
|
47
50
|
scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
|
@@ -70,7 +73,7 @@ class JobInvocation < ApplicationRecord
|
|
70
73
|
|
71
74
|
delegate :start_at, :to => :task, :allow_nil => true
|
72
75
|
|
73
|
-
encrypts :password, :key_passphrase, :
|
76
|
+
encrypts :password, :key_passphrase, :effective_user_password
|
74
77
|
|
75
78
|
def self.search_by_status(key, operator, value)
|
76
79
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
@@ -139,7 +142,7 @@ class JobInvocation < ApplicationRecord
|
|
139
142
|
invocation.pattern_template_invocations = self.pattern_template_invocations.map(&:deep_clone)
|
140
143
|
invocation.password = self.password
|
141
144
|
invocation.key_passphrase = self.key_passphrase
|
142
|
-
invocation.
|
145
|
+
invocation.effective_user_password = self.effective_user_password
|
143
146
|
end
|
144
147
|
end
|
145
148
|
|
@@ -15,7 +15,7 @@ class JobInvocationComposer
|
|
15
15
|
:description_format => job_invocation_base[:description_format],
|
16
16
|
:password => blank_to_nil(job_invocation_base[:password]),
|
17
17
|
:key_passphrase => blank_to_nil(job_invocation_base[:key_passphrase]),
|
18
|
-
:
|
18
|
+
:effective_user_password => blank_to_nil(job_invocation_base[:effective_user_password]),
|
19
19
|
:concurrency_control => concurrency_control_params,
|
20
20
|
:execution_timeout_interval => execution_timeout_interval,
|
21
21
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
@@ -348,7 +348,7 @@ class JobInvocationComposer
|
|
348
348
|
job_invocation.execution_timeout_interval = params[:execution_timeout_interval]
|
349
349
|
job_invocation.password = params[:password]
|
350
350
|
job_invocation.key_passphrase = params[:key_passphrase]
|
351
|
-
job_invocation.
|
351
|
+
job_invocation.effective_user_password = params[:effective_user_password]
|
352
352
|
|
353
353
|
if @reruns && job_invocation.targeting.static?
|
354
354
|
job_invocation.targeting.host_ids = JobInvocation.find(@reruns).targeting.host_ids
|
@@ -57,8 +57,8 @@ class RemoteExecutionProvider
|
|
57
57
|
[true, 'true', 'True', 'TRUE', '1'].include?(setting)
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
61
|
-
host_setting(host, :
|
60
|
+
def effective_user_password(host)
|
61
|
+
host_setting(host, :remote_execution_effective_user_password)
|
62
62
|
end
|
63
63
|
|
64
64
|
def effective_interfaces(host)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Setting::RemoteExecution < Setting
|
2
2
|
|
3
|
-
::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_cockpit_url remote_execution_form_job_template}
|
3
|
+
::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_effective_user_password remote_execution_cockpit_url remote_execution_form_job_template}
|
4
4
|
|
5
5
|
def self.default_settings
|
6
6
|
[
|
@@ -27,7 +27,7 @@ class Setting::RemoteExecution < Setting
|
|
27
27
|
N_('Effective User Method'),
|
28
28
|
nil,
|
29
29
|
{ :collection => proc { Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] } }),
|
30
|
-
self.set('
|
30
|
+
self.set('remote_execution_effective_user_password', N_("Effective user password"), '', N_("Effective user password"), nil, {:encrypted => true}),
|
31
31
|
self.set('remote_execution_sync_templates',
|
32
32
|
N_('Whether we should sync templates from disk when running db:seed.'),
|
33
33
|
true,
|
@@ -32,7 +32,7 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
32
32
|
{
|
33
33
|
:ssh_password => ssh_password(host),
|
34
34
|
:key_passphrase => ssh_key_passphrase(host),
|
35
|
-
:
|
35
|
+
:effective_user_password => effective_user_password(host),
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
@@ -95,7 +95,7 @@
|
|
95
95
|
<div class="advanced hidden">
|
96
96
|
<%= password_f f, :password, :placeholder => '*****', :label => _('Password'), :label_help => N_('Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
97
97
|
<%= password_f f, :key_passphrase, :placeholder => '*****', :label => _('Private key passphrase'), :label_help => N_('Key passhprase is only applicable for SSH provider. Other providers ignore this field. <br> Passphrase is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
98
|
-
<%= password_f f, :
|
98
|
+
<%= password_f f, :effective_user_password, :placeholder => '*****', :label => _('Effective user password'), :label_help => N_('Effective user password is only applicable for SSH provider. Other providers ignore this field. <br> Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
99
99
|
</div>
|
100
100
|
|
101
101
|
<div class="advanced hidden">
|
@@ -1,24 +1,5 @@
|
|
1
1
|
<% if job_invocation.resolved? %>
|
2
|
-
<%=
|
3
|
-
<div class="row">
|
4
|
-
<div class="title_filter col-md-6">
|
5
|
-
<div class="input-group">
|
6
|
-
<%= autocomplete_f(f, :search, value: params[:search].try(:squeeze, " "), placeholder: _("Filter") + ' ...', path: hosts_path, only_input: true) %>
|
7
|
-
<span class="input-group-btn">
|
8
|
-
<button class="btn btn-default" type="submit">
|
9
|
-
<%= icon_text('search', content_tag(:span, _('Search'), :class => 'hidden-xs', :kind => 'fa')) %>
|
10
|
-
</button>
|
11
|
-
</span>
|
12
|
-
</div>
|
13
|
-
</div>
|
14
|
-
</div>
|
15
|
-
<% end %>
|
16
|
-
<br>
|
17
|
-
|
18
|
-
<div id="targeting_hosts">
|
19
|
-
<%= mount_react_component('TargetingHosts', '#targeting_hosts') %>
|
20
|
-
</div>
|
21
|
-
<%= will_paginate_with_info @hosts, :container => true %>
|
2
|
+
<%= react_component('TargetingHosts' ) %>
|
22
3
|
<% else %>
|
23
4
|
<div class="alert alert-warning">
|
24
5
|
<%=
|
@@ -2,6 +2,9 @@
|
|
2
2
|
<% stylesheet 'foreman_remote_execution/foreman_remote_execution' %>
|
3
3
|
<% javascript 'charts', 'foreman_remote_execution/template_invocation' %>
|
4
4
|
<% javascript *webpack_asset_paths('foreman_remote_execution', :extension => 'js') %>
|
5
|
+
<% content_for(:stylesheets) do %>
|
6
|
+
<%= webpacked_plugins_css_for :foreman_remote_execution %>
|
7
|
+
<% end %>
|
5
8
|
|
6
9
|
<%= breadcrumbs name_field: 'description' %>
|
7
10
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class RenameSudoPasswordToEffectiveUserPassword < ActiveRecord::Migration[6.0]
|
2
|
+
def up
|
3
|
+
rename_column :job_invocations, :sudo_password, :effective_user_password
|
4
|
+
|
5
|
+
Parameter.where(name: 'remote_execution_sudo_password').each do |parameter|
|
6
|
+
record = Parameter.find_by(type: parameter.type, reference_id: parameter.reference_id, name: "remote_execution_effective_user_password")
|
7
|
+
if record.nil?
|
8
|
+
parameter.update(name: "remote_execution_effective_user_password")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
return unless (password = Setting.find_by(:name => 'remote_execution_sudo_password').try(:value))
|
13
|
+
|
14
|
+
Setting.find_by(:name => 'remote_execution_effective_user_password').update(value: password)
|
15
|
+
|
16
|
+
Setting.find_by(:name => 'remote_execution_sudo_password').delete
|
17
|
+
end
|
18
|
+
|
19
|
+
def down
|
20
|
+
rename_column :job_invocations, :effective_user_password, :sudo_password
|
21
|
+
|
22
|
+
Parameter.where(name: 'remote_execution_effective_user_password').each do |parameter|
|
23
|
+
record = Parameter.find_by(type: parameter.type, reference_id: parameter.reference_id, name: "remote_execution_sudo_password")
|
24
|
+
if record.nil?
|
25
|
+
parameter.update(name: "remote_execution_sudo_password")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
return unless (password = Setting.find_by(:name => 'remote_execution_effective_user_password').try(:value))
|
30
|
+
|
31
|
+
Setting.create!(name: 'remote_execution_sudo_password', value: password, description: 'Sudo password', category: 'Setting::RemoteExecution', settings_type: 'string', full_name: 'Sudo password',encrypted: true, default: nil)
|
32
|
+
Setting.find_by(:name => 'remote_execution_effective_user_password').delete
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
view_permission = Permission.find_by(name: "view_job_invocations", resource_type: 'JobInvocation')
|
2
|
+
default_role = Role.default
|
3
|
+
|
4
|
+
# the view_permissions can be nil in tests: skipping in that case
|
5
|
+
if view_permission && !default_role.permissions.include?(view_permission)
|
6
|
+
default_role.filters.create(:search => 'user = current_user') do |filter|
|
7
|
+
filter.filterings.build { |f| f.permission = view_permission }
|
8
|
+
end
|
9
|
+
end
|
@@ -32,15 +32,6 @@ module ForemanRemoteExecution
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
# A workaround for https://projects.theforeman.org/issues/30685
|
36
|
-
initializer 'foreman_remote_execution.rails_loading_workaround' do
|
37
|
-
# Without this, in production environment the module gets prepended too
|
38
|
-
# late and the extensions do not get applied
|
39
|
-
# TODO: Remove this and from config.to_prepare once there is an extension
|
40
|
-
# point in Foreman
|
41
|
-
ProvisioningTemplatesHelper.prepend ForemanRemoteExecution::JobTemplatesExtensions
|
42
|
-
end
|
43
|
-
|
44
35
|
initializer 'foreman_remote_execution.apipie' do
|
45
36
|
Apipie.configuration.checksum_path += ['/api/']
|
46
37
|
end
|
@@ -53,9 +44,12 @@ module ForemanRemoteExecution
|
|
53
44
|
|
54
45
|
initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |_app|
|
55
46
|
Foreman::Plugin.register :foreman_remote_execution do
|
56
|
-
requires_foreman '>=
|
47
|
+
requires_foreman '>= 2.2'
|
57
48
|
|
58
49
|
apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
|
50
|
+
ApipieDSL.configuration.dsl_classes_matchers += [
|
51
|
+
"#{ForemanRemoteExecution::Engine.root}/app/lib/foreman_remote_execution/renderer/**/*.rb",
|
52
|
+
]
|
59
53
|
|
60
54
|
automatic_assets(false)
|
61
55
|
precompile_assets(*assets_to_precompile)
|
@@ -38,10 +38,11 @@ module Api
|
|
38
38
|
|
39
39
|
test 'should see only permitted hosts' do
|
40
40
|
@user = FactoryBot.create(:user, admin: false)
|
41
|
+
@invocation.task.update(user: @user)
|
41
42
|
setup_user('view', 'job_invocations', nil, @user)
|
42
43
|
setup_user('view', 'hosts', 'name ~ nope.example.com', @user)
|
43
44
|
|
44
|
-
get :show, params: { :id => @invocation.id }, session:
|
45
|
+
get :show, params: { :id => @invocation.id }, session: prepare_user(@user)
|
45
46
|
assert_response :success
|
46
47
|
response = ActiveSupport::JSON.decode(@response.body)
|
47
48
|
assert_equal response['targeting']['hosts'], []
|
@@ -298,6 +299,68 @@ module Api
|
|
298
299
|
post :rerun, params: { :id => @invocation.id }
|
299
300
|
assert_response 404
|
300
301
|
end
|
302
|
+
|
303
|
+
describe 'restricted access' do
|
304
|
+
setup do
|
305
|
+
@admin = FactoryBot.create(:user, mail: 'admin@test.foreman.com', admin: true)
|
306
|
+
@user = FactoryBot.create(:user, mail: 'user@test.foreman.com', admin: false)
|
307
|
+
@invocation = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
|
308
|
+
@invocation2 = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
|
309
|
+
|
310
|
+
@invocation.task.update(user: @admin)
|
311
|
+
@invocation2.task.update(user: @user)
|
312
|
+
|
313
|
+
setup_user 'view', 'hosts', nil, @user
|
314
|
+
setup_user 'view', 'job_invocations', 'user = current_user', @user
|
315
|
+
setup_user 'create', 'job_invocations', 'user = current_user', @user
|
316
|
+
setup_user 'cancel', 'job_invocations', 'user = current_user', @user
|
317
|
+
end
|
318
|
+
|
319
|
+
let(:host) { @invocation.targeting.hosts.first }
|
320
|
+
let(:host2) { @invocation2.targeting.hosts.first }
|
321
|
+
|
322
|
+
context 'without user filter' do
|
323
|
+
test '#index' do
|
324
|
+
get :index, session: prepare_user(@admin)
|
325
|
+
assert_response :success
|
326
|
+
assert JSON.parse(@response.body)['results'].size >= 2
|
327
|
+
end
|
328
|
+
|
329
|
+
test '#show' do
|
330
|
+
get :show, params: { id: @invocation2.id }, session: prepare_user(@admin)
|
331
|
+
assert_response :success
|
332
|
+
end
|
333
|
+
|
334
|
+
test '#output' do
|
335
|
+
get :output, params: { job_invocation_id: @invocation2.id, host_id: host2.id }, session: prepare_user(@admin)
|
336
|
+
assert_response :success
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context 'with user filter' do
|
341
|
+
test '#index' do
|
342
|
+
get :index, session: prepare_user(@user)
|
343
|
+
assert_response :success
|
344
|
+
assert_equal 1, JSON.parse(@response.body)['results'].size
|
345
|
+
end
|
346
|
+
|
347
|
+
test '#show' do
|
348
|
+
get :show, params: { id: @invocation.id }, session: prepare_user(@user)
|
349
|
+
assert_response :not_found
|
350
|
+
end
|
351
|
+
|
352
|
+
test '#output' do
|
353
|
+
get :output, params: { job_invocation_id: @invocation.id, host_id: host.id }, session: prepare_user(@user)
|
354
|
+
assert_response :not_found
|
355
|
+
assert_includes @response.body, 'Job invocation not found'
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def prepare_user(user)
|
361
|
+
User.current = user
|
362
|
+
set_session_user(user)
|
363
|
+
end
|
301
364
|
end
|
302
365
|
end
|
303
366
|
end
|