Skip to content

Commit

Permalink
Integrate audit log functionality - initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com>
  • Loading branch information
Vasu1105 committed Oct 17, 2023
1 parent 7ca6c2d commit 99bda39
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
29 changes: 29 additions & 0 deletions lib/train/audit_log.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module Train
class AuditLog
# TODO:
# Configurable values log location, log file size, log frequency((available options, daily, weeks, monthly))

# Default values for audit log options are set in the options.rb
def self.create(options = {})
logger = Logger.new(options[:audit_log_location], options[:audit_log_frequency], options[:audit_log_size])
logger.level = options[:level] || Logger::INFO
logger.progname = options[:audi_log_app_name]
logger.datetime_format = "%Y-%m-%d %H:%M:%S"
logger.formatter = proc do |severity, datetime, progname, msg|
{
level: severity,
timestamp: datetime.to_s,
app: progname,
type: msg[:type],
command: msg[:command],
path: msg[:path],
source: msg[:source],
destination: msg[:destination],
hostname: msg[:hostname],
user: msg[:user],
}.compact.to_json + $/
end
logger
end
end
end
14 changes: 14 additions & 0 deletions lib/train/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,23 @@ def option(name, conf = nil, &block)

def default_options
@default_options = {} unless defined? @default_options
# TODO: This is hacky way to set the default options for audit log for all type of transport.
@default_options.merge!(default_audit_log_options)
@default_options
end

def default_audit_log_options
# TODO: What should be the default audit log location if any of the application using train does not set it?
# should we keep it to $stdout.
{
enable_audit_log: { default: false },
audit_log_location: { default: $stdout },
audit_log_app_name: { default: "train" },
audit_log_size: { default: 2000000 },
audit_log_frequency: { default: "daily" },
}
end

def include_options(other)
unless other.respond_to?(:default_options)
raise "Trying to include options from module #{other.inspect}, "\
Expand Down
17 changes: 14 additions & 3 deletions lib/train/plugins/base_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative "../file"
require "fileutils" unless defined?(FileUtils)
require "logger"
require_relative "../audit_log"

class Train::Plugins::Transport
# A Connection instance can be generated and re-generated, given new
Expand All @@ -20,10 +21,17 @@ class BaseConnection
# @yield [self] yields itself for block-style invocation
def initialize(options = nil)
@options = options || {}
@logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)

Train::Platforms::Detect::Specifications::OS.load
Train::Platforms::Detect::Specifications::Api.load

@logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)

# In run_command all options are not accessible as some of them gets deleted in transit.
# To make the data like hostname, username available to aduit logs dup the options
@audit_log_data = options.dup || {}
@audit_log = Train::AuditLog.create(options) if @options[:enable_audit_log]

# default caching options
@cache_enabled = {
file: true,
Expand Down Expand Up @@ -140,11 +148,14 @@ def run_command(cmd, opts = {}, &data_handler)
# Some implementations do not accept an opts argument.
# We cannot update all implementations to accept opts due to them being separate plugins.
# Therefore here we check the implementation's arity to maintain compatibility.
@audit_log.info({ type: "cmd", command: "#{cmd}", user: "#{@audit_log_data[:username]}", hostname: "#{@audit_log_data[:hostname]}" }) if @audit_log

case method(:run_command_via_connection).arity.abs
when 1
return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)

@cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)

when 2
return run_command_via_connection(cmd, opts, &data_handler) unless cache_enabled?(:command)

Expand All @@ -157,6 +168,7 @@ def run_command(cmd, opts = {}, &data_handler)
# This is the main file call for all connections. This will call the private
# file_via_connection on the connection with optional caching
def file(path, *args)
@audit_log.info({ type: "file", path: "#{path}", user: "#{@audit_log_data[:username]}", hostname: "#{@audit_log_data[:hostname]}" }) if @audit_log
return file_via_connection(path, *args) unless cache_enabled?(:file)

@cache[:file][path] ||= file_via_connection(path, *args)
Expand All @@ -177,7 +189,7 @@ def upload(locals, remote)

Array(locals).each do |local|
remote_file = remote_directory ? File.join(remote, File.basename(local)) : remote

@audit_log.info({ type: "file upload", source: "#{local}", destination: "#{remote_file}", user: "#{@audit_log_data[:username]}", hostname: "#{@audit_log_data[:hostname]}" }) if @audit_log
logger.debug("Attempting to upload '#{local}' as file #{remote_file}")

file(remote_file).content = File.read(local)
Expand All @@ -198,7 +210,6 @@ def download(remotes, local)
Array(remotes).each do |remote|
new_content = file(remote).content
local_file = File.join(local, File.basename(remote))

logger.debug("Attempting to download '#{remote}' as file #{local_file}")

File.open(local_file, "w") { |fp| fp.write(new_content) }
Expand Down

0 comments on commit 99bda39

Please sign in to comment.