#!/usr/bin/perl
#

## 
#
# $Id: //depot/DEV/Emerald/src/plugin/samples/utils/parseAuditLog.pl#1 $
#
#  NOTICE:
#    Copyright 2009 Zyrion, Inc. All Rights Reserved. 
#    This software is the proprietary information of 
#    Zyrion, Inc. Use is subject to license terms.
#
#  DESCRIPTION:
#    Script to parse audit information logged by Traverse into
#    logs/webapp.log and generate a report in CSV format. The
#    audit information includes activity performed via the Web
#    Application as well as BVE API server.
#
#  OUTPUT:
#    The following information is recorded in the output file 
#    in comma-separated format:
#      Date, Time, Login User, Represented User, Operation, 
#        Object Type, Object Name, Additional Parameters
#    
##

use strict;
use File::Basename;
use Cwd 'abs_path';
use lib (abs_path(dirname($0)),
         abs_path(dirname($0)) . "/../../lib/perl",
         abs_path(dirname($0)) . "/../../../perl/modules",
         abs_path(dirname($0)) . "/../../../../fcots/perl/");
use Getopt::Long;
use NetVigil::Debug;
                           
## -- global variable declaration
##

use vars qw( $opt_log $opt_output $opt_overwrite $opt_help $opt_vers 
             $my_name $my_version );
                          
{ 
  no warnings 'qw';
  $my_name     = basename($0);
  $my_version  = (qw$Revision: #1 $)[1];
}
                                                                                                   
## -- parse cli parameters & validate
##

GetOptions ('input|i=s'	  => \$opt_log,
            'output|o=s'  => \$opt_output,
            'overwrite|w' => \$opt_overwrite,
            'version'     => \$opt_vers,
            'help'        => \$opt_help,
            'debug+'      => \$DEBUG);

if ($opt_vers) { &show_version; exit 0; }
if ($opt_help) { &show_usage; }
if (! $opt_log || !$opt_output) {
  &show_usage;
}

## -- main program
##

if (! -f $opt_log) {
  &error("unable to locate audit log: ${opt_log}");
  exit 1;
}

if (! open(AL, "< $opt_log")) {
  &error("unable to open audit log: ${opt_log}", "reason: " . $!);
  exit 1;
}

if (-f $opt_output && !$opt_overwrite) {
  &error("output file already exists: ${opt_output}", "use --overwrite option to replace");
  exit 1;
}

if (! open(CSV, "> $opt_output")) {
  &error("unable to open output file: ${opt_output}", "reason: " . $!);
  exit 1;
}

while (<AL>) {
  my ($audit_date, $audit_time, $audit_login, $audit_user, $audit_operation, $audit_object, $audit_info) = "";
  my @audit_message = ();

  chop;
  if (/CRUDS:/) {
    ($audit_date, $audit_time, undef, undef, undef, @audit_message) = split(/ /, $_);
    my $input_buffer = join(' ', @audit_message);
    # 2009-01-27 12:08:49,843 user.Login: (INFO) CRUDS: login operation performed on user by zyrion (172.16.250.2)
    if ($input_buffer =~ /(\w+) operation performed on ([\w\-]+) by (\S+)(.*)/) {
      $audit_operation = $1;
      $audit_object = $2;
      $audit_login = $3;
      $audit_info = $4;
    }
  }

  # data cleanup
  #
  $audit_time =~ s/,\d+$//;
  $audit_operation =~ tr/A-Z/a-z/;
  $audit_object = "department" if ($audit_object eq "account");
  ($audit_login, $audit_user) = split(/\//, $audit_login);

  # parse additional info
  #
  if ($audit_info =~ /\((.*)\)/) {
    $audit_info = $1;
    if ($audit_operation =~ /login|logout/i) {
      # special case for login/logout operations
      #
      $audit_info = "remoteHost=$audit_info";
    } elsif ($audit_info !~ /\S+=/) {
      # special case when a single parameter value is recorded
      #
      $audit_info = "name=$audit_info";
    }
  }
  if ($audit_info) {
    # "serialNumber=200190", "testName=Fa-0/17 Traffic Out", "testType=snmp", "subType=interface_bytes_rate", "deviceName=CONSDATP11SW1", "accountName=Secretaria de Hacienda y Credito Publico", "agentBatchMode=1", "agentPort=161", "agentCommunity=(hidden)", "agentVersion=2", "interval=300", "warningThreshold=75000", "criticalThreshold=90000", "shadowWarningThreshold=60000", "shadowCriticalThreshold=150000", "slaThreshold=25000", "units=kb/s", "actionName=None", "scheduleName=Default Schedule", "thresholdType=1", "suppressed=false", "isSuspended=false", "resultProcessDirective=3", "resultMultiplier=0.0080", "maxValue=100000","snmpOid=.1.3.6.1.2.1.31.1.1.1.10.10017"
    my $key_value = "";
    my %PARAM_HASH = ();
    my @PARAM_PAIR = split(/\"?\s*[,;]\s*\"?/, $audit_info);
    foreach my $nvpair (@PARAM_PAIR) {
      $nvpair =~ s/\"//g;
      if ($nvpair =~ /([^=]+)=(.*)/) {
        my $param_name = $1;
        my $param_value = $2;
        &debug("$param_name == $param_value");
        if ($param_name =~ /^$audit_object(name)?$/i || $param_name =~ /^name$/i) {
          $key_value = $param_value;
        } else {
          next if ($param_value =~ /^(null|\[\])?$/i);
          $param_name =~ s/ //g;
          $param_name =~ tr/A-Z/a-z/;
          $PARAM_HASH{$param_name} = $param_value;
        }
      }
    }
    $audit_info = ',"';
    if ($key_value) {
      $audit_info .= $key_value;
    }
    $audit_info .= '","';
    foreach my $param_name (sort keys %PARAM_HASH) {
      $audit_info .= "${param_name}=$PARAM_HASH{$param_name} ; ";
    }
    $audit_info =~ s/ ; $//;
    $audit_info .= '"';
  }

  # print record
  #
  if ($audit_login) {
    my $csv_record;
    $csv_record = '"';
    $csv_record .= join('","',
        $audit_date,
        $audit_time,
        $audit_login,
        ($audit_login ne $audit_user ? $audit_user : ""),
        $audit_operation,
        ($audit_operation =~ /^login|logout$/ ? "" : $audit_object)
      );
    $csv_record .= '"';
    $csv_record .= $audit_info;
    $csv_record .= "\n";
    print CSV $csv_record;
  }
}

# done!
#
close (AL);
close (CSV);

exit 0;

## --- function definitions
##

sub show_version {

  print "${my_name} (version: ${my_version})\n";

}

sub show_usage {

  print <<eofUsage
  
  usage: ${my_name} --input=log_file
            --output=report_file [ --overwrite ]

           log_file    = traverse audit log (webapp.log)
           report_file = save csv data in this file
         --overwrite   = replace report_file if it exists

eofUsage
;
  exit 1;
}

