App-Spoor

 view release on metacpan or  search on metacpan

lib/App/Spoor/LoginEntryParser.pm  view on Meta::CPAN

package App::Spoor::LoginEntryParser;

use v5.10;
use strict;
use warnings;

=head1 NAME

App::Spoor::LoginEntryParser

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

This package contains the necessary functionality to parse CPanel error log entries.

=head1 SUBROUTINES/METHODS

=head2 parse

This subroutine accepts a single line from a CPanel login log (as a string) and returns a reference to a hash 
representation of that entry.

The hash representation contains the following elements:

=over 2

=item * type: This is hardcoded to 'login'

=item * event: This is hardcoded to 'login'.

=item * log_time: A DateTime instance representing the time of the log entry.

=item * context: The context within which the operation is being performed can be either 'mailbox', 'domain' or 'system'.

=item * scope: Can be one of 'webmaild', 'cpaneld' or 'whostmgrd'.

=item * ip: The ip logging in.

=item * status: Can be one of 'success', 'deferred' or 'failed'. 

=item * credential: The credential (email address/username) presented.

=item * possessor: In the case of an email address being provided, the domain user to which it belongs.

=item * message: This is only set if the entry contained additional info (generally on a non-successful login), e.g. "security token missing".

=item * endpoint: HTTP-related information, only present on a non-successful login.

=back

=cut

sub parse {
  use DateTime::Format::Strptime;

  my $log_entry = shift;
  my $date_parser = DateTime::Format::Strptime->new(pattern => '%Y-%m-%d %H:%M:%S %z', on_error => 'croak');
  my %response;
  if ($log_entry =~ /
    \A
    \[(?<timestamp>[^\]]+)\]\s
    info\s
    \[(?<scope>[^\]]+)\]\s
    (?<ip>\S+)\s
    -\s(?<credential_string>[^-]+)\s-\s
    SUCCESS
  /x) {

    %response = (
      type => 'login',
      event => 'login',
      log_time => $date_parser->parse_datetime($+{timestamp})->epoch(),
      scope => $+{scope},
      ip => $+{ip},
      status => 'success',
    );

    if ($+{credential_string} =~ /\A(?<credential>\S+)\s\(possessor: (?<possessor>[^\)]+)\)/) {
      $response{credential} = $+{credential};
      $response{possessor} = $+{possessor};
    } else {
      $response{credential} = $+{credential_string};
    }
  } elsif ($log_entry =~ /
    \A
    \[(?<timestamp>[^\]]+)\]\s
    info\s
    \[(?<scope>[^\]]+)\]\s
    (?<ip>\S+)\s
    -\s(?<credential>[^-]+)\s
    "(?<endpoint>[^"]+)"\s
    (?<status>[A-Z]+)\s
    [^:]+:\s(?<message>.+)
    \Z
  /x) {
    %response = (
      type => 'login',
      event => 'login',
      log_time => $date_parser->parse_datetime($+{timestamp})->epoch(),
      scope => $+{scope},
      ip => $+{ip},
      status => lc($+{status}),
      credential => $+{credential},
      message => $+{message},
      endpoint => $+{endpoint}
    );
  }

  if ($response{scope} eq 'webmaild' && $response{credential} =~ /@/) {
    $response{context} = 'mailbox';
  } elsif ($response{scope} eq 'webmaild') {
    $response{context} = 'domain';
  } elsif ($response{scope} eq 'cpaneld') {
    $response{context} = 'domain';
  } elsif ($response{scope} eq 'whostmgrd') {
    $response{context} = 'system';
  }



( run in 0.946 second using v1.01-cache-2.11-cpan-d8267643d1d )