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 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