lib/conjur/command/audit.rb in conjur-cli-4.4.0 vs lib/conjur/command/audit.rb in conjur-cli-4.5.0
- old
+ new
@@ -1,43 +1,101 @@
require 'conjur/command'
require 'active_support/ordered_hash'
+require 'conjur/audit/follower'
class Conjur::Command
class Audit < self
self.prefix = 'audit'
class << self
private
+ SHORT_FORMATS = {
+ 'resource:check' => lambda{|e| "checked that they can #{e[:privilege]} #{e[:resource]} (#{e[:allowed]})" },
+ 'resource:create' => lambda{|e| "created resource #{e[:resource_id]} owned by #{e[:owner]}" },
+ 'resource:update' => lambda{|e| "gave #{e[:resource]} to #{e[:owner]}" },
+ 'resource:destroy' => lambda{|e| "destroyed resource #{e[:resource]}" },
+ 'resource:permit' => lambda{|e| "permitted #{e[:grantee]} to #{e[:privilege]} #{e[:resource]} (grant option: #{!!e[:grant_option]})" },
+ 'resource:deny' => lambda{|e| "denied #{e[:privilege]} from #{e[:grantee]} on #{e[:resource]}" },
+ 'resource:permitted_roles' => lambda{|e| "listed roles permitted to #{e[:permission]} on #{e[:resource]}" },
+ 'role:check' => lambda{|e| "checked that #{e[:role] == e[:conjur_user] ? 'they' : e[:role]} can #{e[:privilege]} #{e[:resource]} (#{e[:allowed]})" },
+ 'role:grant' => lambda{|e| "granted role #{e[:role]} to #{e[:member]} #{e[:admin_option] ? ' with ' : ' without '}admin" },
+ 'role:revoke' => lambda{|e| "revoked role #{e[:role]} from #{e[:member]}" },
+ 'role:create' => lambda{|e| "created role #{e[:role_id]}" }
+ }
+
+
+ def short_event_format e
+ e.symbolize_keys!
+ # hack: sometimes resource is a hash. We don't want that!
+ if e[:resource] && e[:resource].kind_of?(Hash)
+ e[:resource] = e[:resource]['id']
+ end
+ s = "[#{Time.at(e[:timestamp])}] "
+ s << " #{e[:conjur_user]}"
+ s << " (as #{e[:conjur_role]})" if e[:conjur_role] != e[:conjur_user]
+ formatter = SHORT_FORMATS["#{e[:asset]}:#{e[:action]}"]
+ if formatter
+ s << " " << formatter.call(e)
+ else
+ s << " unknown event: #{e[:asset]}:#{e[:action]}!"
+ end
+ s << " (failed with #{e[:error]})" if e[:error]
+ s
+ end
+
def extract_int_option(source, name, dest=nil)
if val = source[name]
raise "Expected an integer for #{name}, but got #{val}" unless /\d+/ =~ val
val.to_i.tap{ |i| dest[name] = i if dest }
end
end
def extract_audit_options options
- {}.tap do |opts|
- [:limit, :offset].each do |name|
- extract_int_option(options, name, opts)
- end
+ # Do a little song and dance to simplify testing
+ extracted = options.slice :follow, :short
+ [:limit, :offset].each do |name|
+ extract_int_option(options, name, extracted)
end
+ if extracted[:follow] && extracted[:offset]
+ exit_now! "--offset option not allowed for --follow", 1
+ end
+ extracted
end
- def show_audit_events events
- puts JSON.pretty_generate(events)
+ def show_audit_events events, options
+ events.reverse!
+ if options[:short]
+ events.each{|e| puts short_event_format(e)}
+ else
+ puts JSON.pretty_generate(events)
+ end
end
def audit_feed_command kind, &block
command kind do |c|
c.desc "Maximum number of events to fetch"
c.flag [:l, :limit]
c.desc "Offset of the first event to return"
c.flag [:o, :offset]
+ c.desc "Short output format"
+ c.switch [:s, :short]
+
+ c.desc "Follow events as they are generated"
+ c.switch [:f, :follow]
+
c.action do |global_options, options, args|
- opts = extract_audit_options options
- show_audit_events instance_exec(args, opts, &block)
+ options = extract_audit_options options
+ if options[:follow]
+ Conjur::Audit::Follower.new do |merge_options|
+ instance_exec(args, options.merge(merge_options), &block)
+ end.follow do |events|
+ show_audit_events events, options
+ end
+ else
+ show_audit_events instance_exec(args, options, &block), options
+ end
end
end
end
end
\ No newline at end of file