foreman_remote_execution 3.3.7 → 4.0.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/.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
|