169 lines
5.1 KiB
Perl
169 lines
5.1 KiB
Perl
package Lemonldap::NG::Portal::Plugins::NewLocationWarning;
|
|
|
|
use strict;
|
|
use Mouse;
|
|
use POSIX qw(strftime);
|
|
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
|
|
use List::MoreUtils qw/uniq/;
|
|
|
|
our $VERSION = '2.0.14';
|
|
|
|
has locationAttribute => ( is => 'rw' );
|
|
has locationDisplayAttribute => ( is => 'rw' );
|
|
has locationMaxValues => ( is => 'rw' );
|
|
has mailSessionKey => (
|
|
is => 'rw',
|
|
lazy => 1,
|
|
default => sub {
|
|
return
|
|
$_[0]->{conf}->{newLocationWarningMailAttribute}
|
|
|| $_[0]->{conf}->{mailSessionKey}
|
|
|| 'mail';
|
|
}
|
|
);
|
|
|
|
extends qw(
|
|
Lemonldap::NG::Portal::Lib::SMTP
|
|
Lemonldap::NG::Portal::Main::Plugin
|
|
);
|
|
|
|
# Entrypoint
|
|
use constant afterSub => { setLocalGroups => 'checkNewLocation' };
|
|
use constant endAuth => 'sendWarningEmail';
|
|
|
|
sub init {
|
|
my ($self) = @_;
|
|
|
|
if ( $self->conf->{disablePersistentStorage} ) {
|
|
$self->logger->error(
|
|
'"NewLocationWarning" plugin enabled WITHOUT persistent session storage"'
|
|
);
|
|
return 0;
|
|
}
|
|
unless ( $self->conf->{loginHistoryEnabled} ) {
|
|
$self->logger->error(
|
|
'"NewLocationWarning" plugin enabled WITHOUT "History" plugin');
|
|
return 0;
|
|
}
|
|
|
|
$self->locationAttribute( $self->conf->{newLocationWarningLocationAttribute}
|
|
|| 'ipAddr' );
|
|
$self->locationDisplayAttribute(
|
|
$self->conf->{newLocationWarningLocationDisplayAttribute}
|
|
|| $self->locationAttribute );
|
|
$self->locationMaxValues( $self->conf->{newLocationWarningMaxValues} || 0 );
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub checkNewLocation {
|
|
my ( $self, $req ) = @_;
|
|
my $successLogin = $req->sessionInfo->{_loginHistory}->{successLogin} || [];
|
|
my $location = $req->sessionInfo->{ $self->locationAttribute };
|
|
|
|
$self->logger->debug( "Could not find location of user " . $req->user )
|
|
unless $location;
|
|
|
|
# Get all non-empty, unique values of location attribute through list of
|
|
# successful logins
|
|
my @envHistory =
|
|
grep { $_ // "" }
|
|
uniq( map { $_->{ $self->locationAttribute } // "" } @{$successLogin} );
|
|
|
|
# Only consider some of the past unique locations
|
|
my $maxLocations = $self->locationMaxValues;
|
|
splice @envHistory, $maxLocations
|
|
if ( $maxLocations and ( scalar @envHistory > $maxLocations ) );
|
|
|
|
if ( grep { $_ eq $location } @envHistory ) {
|
|
$self->userLogger->debug(
|
|
"User " . $req->user . " logged in from known location $location" );
|
|
}
|
|
else {
|
|
# Not the first location in history, warn if new location
|
|
if (@envHistory) {
|
|
$self->userLogger->info( "User "
|
|
. $req->user
|
|
. " logged in from unknown location $location" );
|
|
my $riskLevel = ( $req->sessionInfo->{_riskLevel} || 0 ) + 1;
|
|
$req->sessionInfo->{_riskLevel} = $riskLevel;
|
|
$req->sessionInfo->{_riskDetails}->{newLocation} =
|
|
$req->sessionInfo->{ $self->locationDisplayAttribute };
|
|
}
|
|
else {
|
|
$self->userLogger->info( "User "
|
|
. $req->user
|
|
. " logged with empty location history from location $location"
|
|
);
|
|
}
|
|
}
|
|
return PE_OK;
|
|
}
|
|
|
|
sub sendWarningEmail {
|
|
my ( $self, $req ) = @_;
|
|
return $self->_sendMail($req)
|
|
if $req->sessionInfo->{_riskDetails}->{newLocation};
|
|
|
|
return PE_OK;
|
|
}
|
|
|
|
sub _sendMail {
|
|
my ( $self, $req ) = @_;
|
|
my $date = strftime( '%F %X', localtime );
|
|
my $location = $req->sessionInfo->{_riskDetails}->{newLocation};
|
|
my $ua = $req->env->{HTTP_USER_AGENT};
|
|
my $mail = $req->sessionInfo->{ $self->mailSessionKey };
|
|
|
|
# Build mail content
|
|
my $tr = $self->translate($req);
|
|
my $subject = $self->conf->{newLocationWarningMailSubject};
|
|
unless ($subject) {
|
|
$self->logger->debug('Use default warning subject');
|
|
$subject = 'newLocationWarningMailSubject';
|
|
$tr->( \$subject );
|
|
}
|
|
my ( $body, $html );
|
|
if ( $self->conf->{newLocationWarningMailBody} ) {
|
|
|
|
# We use a specific text message, no html
|
|
$self->logger->debug('Use specific warning body message');
|
|
$body = $self->conf->{newLocationWarningMailBody};
|
|
|
|
# Replace variables in body
|
|
$body =~ s/\$ua\b/$ua/ge;
|
|
$body =~ s/\$location\b/$location/ge;
|
|
$body =~ s/\$date\b/$date/ge;
|
|
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
|
|
}
|
|
else {
|
|
|
|
# Use HTML template
|
|
$body = $self->loadMailTemplate(
|
|
$req,
|
|
'mail_new_location_warning',
|
|
filter => $tr,
|
|
params => {
|
|
location => $location,
|
|
date => $date,
|
|
ua => $ua
|
|
},
|
|
);
|
|
$html = 1;
|
|
}
|
|
if ( $mail && $subject && $body ) {
|
|
$self->logger->warn("User $mail is signing in from a new location");
|
|
|
|
# Send mail
|
|
$self->logger->debug('Unable to send new location warning mail')
|
|
unless ( $self->send_mail( $mail, $subject, $body, $html ) );
|
|
}
|
|
else {
|
|
$self->logger->error(
|
|
'Unable to send new location warning mail: missing parameter(s)');
|
|
}
|
|
return PE_OK;
|
|
}
|
|
|
|
1;
|