train-core 3.10.8 → 3.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd9123fc8c77d6d2913633bb544aae171f440d3a0558bacda3136fa62d856661
4
- data.tar.gz: b8e558c2c7fa1479725228f1745ff5ed610f6975babc43cb07bbfb6289f41806
3
+ metadata.gz: 0b10470a7175d568ce7d0df7a3497deb7709ad5ca210f542654f3c560585bacb
4
+ data.tar.gz: 6d91bd6042737e52411a58c30b1c9127bf6a402402e1800abe708ca02434e7ec
5
5
  SHA512:
6
- metadata.gz: 0ed3576117bf704f08d0170e3f9c9d08e20da634300f02b986aaa8fb18094d0b027d9943bcc04043d986102e641ed7c09a4ed163ca6b9f8de1488d3063043721
7
- data.tar.gz: deb114924bc9f537bb8a8c4df1f0b48936c8b7e081539109b886b4c26f994a6f96b214d514d2109ab75b526e2328473fb56f323e0746a28f8cf4cfce4df8e334
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
- default_options.each do |field, hm|
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 or directories to remote host.
181
+ # Uploads local files to remote host.
166
182
  #
167
- # @param locals [Array<String>] paths to local files or directories
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
- unless file(remote).directory?
173
- raise TransportError, "#{self.class} expects remote directory as second upload parameter"
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
- new_content = File.read(local)
178
- remote_file = File.join(remote, File.basename(local))
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 = new_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
@@ -2,5 +2,5 @@
2
2
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
3
3
 
4
4
  module Train
5
- VERSION = "3.10.8".freeze
5
+ VERSION = "3.11.0".freeze
6
6
  end
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
- cls.default_options unless cls.nil?
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.10.8
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-06-23 00:00:00.000000000 Z
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