train-core 3.10.8 → 3.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/train/audit_log.rb +21 -0
- data/lib/train/logger_ext.rb +8 -0
- data/lib/train/options.rb +30 -1
- data/lib/train/plugins/base_connection.rb +25 -9
- data/lib/train/plugins/transport.rb +6 -0
- data/lib/train/version.rb +1 -1
- data/lib/train.rb +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b10470a7175d568ce7d0df7a3497deb7709ad5ca210f542654f3c560585bacb
|
4
|
+
data.tar.gz: 6d91bd6042737e52411a58c30b1c9127bf6a402402e1800abe708ca02434e7ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ce7f3b1b9c3bd621adbdecbb2e11b3815621f97e50ad24567eb70eeaa8b8b5e239e1a6393efec0d732960f3ad9d46072fff176ea40cf6108f6a1f1102bc5685
|
7
|
+
data.tar.gz: ee8265ac08e6c4ed064229cef212471fa3cb52f4be75e478342993a5901b591d545f21ef46cce35c39e734d8e283fe6d26d4f76b6fa98b5c7428649fb83a2c32
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Train
|
2
|
+
class AuditLog
|
3
|
+
# Default values for audit log options are set in the options.rb
|
4
|
+
def self.create(options = {})
|
5
|
+
# Load monkey-patch to disable leading comment in logfiles
|
6
|
+
require_relative "logger_ext"
|
7
|
+
|
8
|
+
logger = Logger.new(options[:audit_log_location], options[:audit_log_frequency], options[:audit_log_size])
|
9
|
+
logger.level = options[:level] || Logger::INFO
|
10
|
+
logger.progname = options[:audit_log_app_name]
|
11
|
+
logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
12
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
13
|
+
{
|
14
|
+
timestamp: datetime.to_s,
|
15
|
+
app: progname,
|
16
|
+
}.merge(msg).compact.to_json + $/
|
17
|
+
end
|
18
|
+
logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
|
2
|
+
# Part of Audit Log.
|
3
|
+
# The default logger implementation injects a comment as the first line of the
|
4
|
+
# log file, which makes it an invalid JSON file. No way to disable that other than monkey-patching.
|
5
|
+
|
6
|
+
class Logger::LogDevice
|
7
|
+
def add_log_header(file); end
|
8
|
+
end
|
data/lib/train/options.rb
CHANGED
@@ -34,6 +34,18 @@ module Train
|
|
34
34
|
@default_options
|
35
35
|
end
|
36
36
|
|
37
|
+
# Created separate method to set the default audit log options so that it will be handled separately
|
38
|
+
# and will not break any existing functionality
|
39
|
+
def default_audit_log_options
|
40
|
+
{
|
41
|
+
enable_audit_log: { default: false },
|
42
|
+
audit_log_location: { required: true, default: nil },
|
43
|
+
audit_log_app_name: { default: "train" },
|
44
|
+
audit_log_size: { default: nil },
|
45
|
+
audit_log_frequency: { default: 0 },
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
37
49
|
def include_options(other)
|
38
50
|
unless other.respond_to?(:default_options)
|
39
51
|
raise "Trying to include options from module #{other.inspect}, "\
|
@@ -47,13 +59,18 @@ module Train
|
|
47
59
|
# @return [Hash] options, which created this Transport
|
48
60
|
attr_reader :options
|
49
61
|
|
62
|
+
def default_audit_log_options
|
63
|
+
self.class.default_audit_log_options
|
64
|
+
end
|
65
|
+
|
50
66
|
def default_options
|
51
67
|
self.class.default_options
|
52
68
|
end
|
53
69
|
|
54
70
|
def merge_options(base, opts)
|
55
71
|
res = base.merge(opts || {})
|
56
|
-
|
72
|
+
# Also merge the default audit log options into the options so that those are available at the time of validation.
|
73
|
+
default_options.merge(default_audit_log_options).each do |field, hm|
|
57
74
|
next unless res[field].nil? && hm.key?(:default)
|
58
75
|
|
59
76
|
default = hm[:default]
|
@@ -78,6 +95,18 @@ module Train
|
|
78
95
|
end
|
79
96
|
opts
|
80
97
|
end
|
98
|
+
|
99
|
+
# Introduced this method to validate only audit log options and avoiding call to validate_options so
|
100
|
+
# that it will no break existing implementation.
|
101
|
+
def validate_audit_log_options(opts)
|
102
|
+
default_audit_log_options.each do |field, hm|
|
103
|
+
if opts[field].nil? && hm[:required]
|
104
|
+
raise Train::ClientError,
|
105
|
+
"You must provide a value for #{field.to_s.inspect}."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
opts
|
109
|
+
end
|
81
110
|
end
|
82
111
|
end
|
83
112
|
end
|
@@ -3,6 +3,7 @@ require_relative "../extras"
|
|
3
3
|
require_relative "../file"
|
4
4
|
require "fileutils" unless defined?(FileUtils)
|
5
5
|
require "logger"
|
6
|
+
require_relative "../audit_log"
|
6
7
|
|
7
8
|
class Train::Plugins::Transport
|
8
9
|
# A Connection instance can be generated and re-generated, given new
|
@@ -20,10 +21,21 @@ class Train::Plugins::Transport
|
|
20
21
|
# @yield [self] yields itself for block-style invocation
|
21
22
|
def initialize(options = nil)
|
22
23
|
@options = options || {}
|
24
|
+
|
23
25
|
@logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)
|
24
26
|
Train::Platforms::Detect::Specifications::OS.load
|
25
27
|
Train::Platforms::Detect::Specifications::Api.load
|
26
28
|
|
29
|
+
# In run_command all options are not accessible as some of them gets deleted in transit.
|
30
|
+
# To make the data like hostname, username available to aduit logs dup the options
|
31
|
+
@audit_log_data = options.dup || {}
|
32
|
+
# For transport other than local all audit log options accessible inside transport_options key
|
33
|
+
if !@options.empty? && @options[:transport_options] && @options[:transport_options][:enable_audit_log]
|
34
|
+
@audit_log = Train::AuditLog.create(options[:transport_options])
|
35
|
+
elsif !@options.empty? && @options[:enable_audit_log]
|
36
|
+
@audit_log = Train::AuditLog.create(@options)
|
37
|
+
end
|
38
|
+
|
27
39
|
# default caching options
|
28
40
|
@cache_enabled = {
|
29
41
|
file: true,
|
@@ -140,11 +152,14 @@ class Train::Plugins::Transport
|
|
140
152
|
# Some implementations do not accept an opts argument.
|
141
153
|
# We cannot update all implementations to accept opts due to them being separate plugins.
|
142
154
|
# Therefore here we check the implementation's arity to maintain compatibility.
|
155
|
+
@audit_log.info({ type: "cmd", command: "#{cmd}", user: @audit_log_data[:username], hostname: @audit_log_data[:hostname] }) if @audit_log
|
156
|
+
|
143
157
|
case method(:run_command_via_connection).arity.abs
|
144
158
|
when 1
|
145
159
|
return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)
|
146
160
|
|
147
161
|
@cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)
|
162
|
+
|
148
163
|
when 2
|
149
164
|
return run_command_via_connection(cmd, opts, &data_handler) unless cache_enabled?(:command)
|
150
165
|
|
@@ -157,29 +172,31 @@ class Train::Plugins::Transport
|
|
157
172
|
# This is the main file call for all connections. This will call the private
|
158
173
|
# file_via_connection on the connection with optional caching
|
159
174
|
def file(path, *args)
|
175
|
+
@audit_log.info({ type: "file", path: "#{path}", user: @audit_log_data[:username], hostname: @audit_log_data[:hostname] }) if @audit_log
|
160
176
|
return file_via_connection(path, *args) unless cache_enabled?(:file)
|
161
177
|
|
162
178
|
@cache[:file][path] ||= file_via_connection(path, *args)
|
163
179
|
end
|
164
180
|
|
165
|
-
# Uploads local files
|
181
|
+
# Uploads local files to remote host.
|
166
182
|
#
|
167
|
-
# @param locals [Array<String>]
|
183
|
+
# @param locals [String, Array<String>] path to local files
|
168
184
|
# @param remote [String] path to remote destination
|
169
185
|
# @raise [TransportFailed] if the files could not all be uploaded
|
170
186
|
# successfully, which may vary by implementation
|
171
187
|
def upload(locals, remote)
|
172
|
-
|
173
|
-
|
188
|
+
remote_directory = file(remote).directory?
|
189
|
+
|
190
|
+
if locals.is_a?(Array) && !remote_directory
|
191
|
+
raise Train::TransportError, "#{self.class} expects remote directory as second upload parameter for multi-file uploads"
|
174
192
|
end
|
175
193
|
|
176
194
|
Array(locals).each do |local|
|
177
|
-
|
178
|
-
remote_file
|
179
|
-
|
195
|
+
remote_file = remote_directory ? File.join(remote, File.basename(local)) : remote
|
196
|
+
@audit_log.info({ type: "file upload", source: local, destination: remote_file, user: @audit_log_data[:username], hostname: @audit_log_data[:hostname] }) if @audit_log
|
180
197
|
logger.debug("Attempting to upload '#{local}' as file #{remote_file}")
|
181
198
|
|
182
|
-
file(remote_file).content =
|
199
|
+
file(remote_file).content = File.read(local)
|
183
200
|
end
|
184
201
|
end
|
185
202
|
|
@@ -197,7 +214,6 @@ class Train::Plugins::Transport
|
|
197
214
|
Array(remotes).each do |remote|
|
198
215
|
new_content = file(remote).content
|
199
216
|
local_file = File.join(local, File.basename(remote))
|
200
|
-
|
201
217
|
logger.debug("Attempting to download '#{remote}' as file #{local_file}")
|
202
218
|
|
203
219
|
File.open(local_file, "w") { |fp| fp.write(new_content) }
|
@@ -21,6 +21,12 @@ class Train::Plugins
|
|
21
21
|
def initialize(options = {})
|
22
22
|
@options = merge_options({}, options || {})
|
23
23
|
@logger = @options[:logger] || Logger.new($stdout, level: :fatal)
|
24
|
+
# Validates audit log configuration options if audit log is enabled
|
25
|
+
# The reason to implement different validate method for audit log options is
|
26
|
+
# to validate only audit log options and not to break any existing validate_option implementation.
|
27
|
+
if !@options.empty? && @options[:enable_audit_log]
|
28
|
+
validate_audit_log_options(options)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
# Create a connection to the target. Options may be provided
|
data/lib/train/version.rb
CHANGED
data/lib/train.rb
CHANGED
@@ -26,7 +26,8 @@ module Train
|
|
26
26
|
# @return [Hash] map of default options
|
27
27
|
def self.options(name)
|
28
28
|
cls = load_transport(name)
|
29
|
-
|
29
|
+
# Merging default_audit_log_options so that they will get listed in the options that are available.
|
30
|
+
cls.default_options.merge(cls.default_audit_log_options) unless cls.nil?
|
30
31
|
end
|
31
32
|
|
32
33
|
# Load the transport plugin indicated by name. If the plugin is not
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: train-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef InSpec Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -127,6 +127,7 @@ extra_rdoc_files: []
|
|
127
127
|
files:
|
128
128
|
- LICENSE
|
129
129
|
- lib/train.rb
|
130
|
+
- lib/train/audit_log.rb
|
130
131
|
- lib/train/errors.rb
|
131
132
|
- lib/train/extras.rb
|
132
133
|
- lib/train/extras/command_wrapper.rb
|
@@ -142,6 +143,7 @@ files:
|
|
142
143
|
- lib/train/file/remote/unix.rb
|
143
144
|
- lib/train/file/remote/windows.rb
|
144
145
|
- lib/train/globals.rb
|
146
|
+
- lib/train/logger_ext.rb
|
145
147
|
- lib/train/options.rb
|
146
148
|
- lib/train/platforms.rb
|
147
149
|
- lib/train/platforms/common.rb
|