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