Prevent authentication on backend if account is locked (#2243)
This commit is contained in:
parent
1bec61b68f
commit
652d8ba9bc
|
@ -853,25 +853,22 @@ sub attributes {
|
||||||
bruteForceProtectionTempo => {
|
bruteForceProtectionTempo => {
|
||||||
default => 30,
|
default => 30,
|
||||||
type => 'int',
|
type => 'int',
|
||||||
documentation =>
|
documentation => 'Lock time',
|
||||||
'Brute force attack protection -> Tempo before try again',
|
|
||||||
},
|
},
|
||||||
bruteForceProtectionMaxAge => {
|
bruteForceProtectionMaxAge => {
|
||||||
default => 300,
|
default => 300,
|
||||||
type => 'int',
|
type => 'int',
|
||||||
documentation =>
|
documentation => 'Max age between current and first failed login',
|
||||||
'Brute force attack protection -> Max age between last and first allowed failed login',
|
|
||||||
},
|
},
|
||||||
bruteForceProtectionMaxFailed => {
|
bruteForceProtectionMaxFailed => {
|
||||||
default => 3,
|
default => 3,
|
||||||
type => 'int',
|
type => 'int',
|
||||||
documentation =>
|
documentation => 'Max allowed failed login',
|
||||||
'Brute force attack protection -> Max allowed failed login',
|
|
||||||
},
|
},
|
||||||
bruteForceProtectionMaxLockTime => {
|
bruteForceProtectionMaxLockTime => {
|
||||||
default => 900,
|
default => 900,
|
||||||
type => 'int',
|
type => 'int',
|
||||||
documentation => 'Brute force attack protection -> Max lock time',
|
documentation => 'Max lock time',
|
||||||
},
|
},
|
||||||
bruteForceProtectionIncrementalTempo => {
|
bruteForceProtectionIncrementalTempo => {
|
||||||
default => 0,
|
default => 0,
|
||||||
|
@ -882,7 +879,7 @@ sub attributes {
|
||||||
},
|
},
|
||||||
bruteForceProtectionLockTimes => {
|
bruteForceProtectionLockTimes => {
|
||||||
type => 'text',
|
type => 'text',
|
||||||
default => '5, 15, 60, 300, 600',
|
default => '15, 30, 60, 300, 600',
|
||||||
documentation =>
|
documentation =>
|
||||||
'Incremental lock time values for brute force attack protection',
|
'Incremental lock time values for brute force attack protection',
|
||||||
},
|
},
|
||||||
|
|
|
@ -689,19 +689,19 @@ sub tests {
|
||||||
'"History" plugin is required to enable "BruteForceProtection" plugin'
|
'"History" plugin is required to enable "BruteForceProtection" plugin'
|
||||||
) unless ( $conf->{loginHistoryEnabled} );
|
) unless ( $conf->{loginHistoryEnabled} );
|
||||||
return ( 1,
|
return ( 1,
|
||||||
'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin'
|
'Number of failed logins must be higher than 1 to enable "BruteForceProtection" plugin'
|
||||||
) unless ( $conf->{failedLoginNumber} > 2 );
|
) unless ( $conf->{failedLoginNumber} > 1 );
|
||||||
return ( 1,
|
return ( 1,
|
||||||
'Number of failed logins history must be higher than allowed failed logins plus lock time values'
|
'Number of failed logins history must be higher or equal than allowed failed logins plus lock time values'
|
||||||
)
|
)
|
||||||
if ( $conf->{bruteForceProtectionIncrementalTempo}
|
if ( $conf->{bruteForceProtectionIncrementalTempo}
|
||||||
&& $conf->{failedLoginNumber} <=
|
&& $conf->{failedLoginNumber} <
|
||||||
$conf->{bruteForceProtectionMaxFailed} +
|
$conf->{bruteForceProtectionMaxFailed} +
|
||||||
$conf->{bruteForceProtectionLockTimes} );
|
$conf->{bruteForceProtectionLockTimes} );
|
||||||
return ( 1,
|
return ( 1,
|
||||||
'Number of failed logins history must be higher than allowed failed logins'
|
'Number of failed logins history must be higher or equal than allowed failed logins'
|
||||||
)
|
)
|
||||||
unless ( $conf->{failedLoginNumber} >
|
unless ( $conf->{failedLoginNumber} >=
|
||||||
$conf->{bruteForceProtectionMaxFailed} );
|
$conf->{bruteForceProtectionMaxFailed} );
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
|
|
@ -185,7 +185,7 @@ sub checkUnauthLogout {
|
||||||
$self->userLogger->info('Unauthenticated logout request');
|
$self->userLogger->info('Unauthenticated logout request');
|
||||||
$self->logger->debug('Cleaning pdata');
|
$self->logger->debug('Cleaning pdata');
|
||||||
$self->logger->debug("Removing $self->{conf}->{cookieName} cookie");
|
$self->logger->debug("Removing $self->{conf}->{cookieName} cookie");
|
||||||
$req->pdata({});
|
$req->pdata( {} );
|
||||||
$req->addCookie(
|
$req->addCookie(
|
||||||
$self->cookie(
|
$self->cookie(
|
||||||
name => $self->conf->{cookieName},
|
name => $self->conf->{cookieName},
|
||||||
|
@ -449,12 +449,13 @@ sub setGroups {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub setPersistentSessionInfo {
|
sub setPersistentSessionInfo {
|
||||||
my ( $self, $req ) = @_;
|
|
||||||
|
# $user passed by BruteForceProtection plugin
|
||||||
|
my ( $self, $req, $user ) = @_;
|
||||||
|
|
||||||
# Do not restore infos if session already opened
|
# Do not restore infos if session already opened
|
||||||
unless ( $req->id ) {
|
unless ( $req->id ) {
|
||||||
my $key = $req->{sessionInfo}->{ $self->conf->{whatToTrace} };
|
my $key = $req->{sessionInfo}->{ $self->conf->{whatToTrace} } || $user;
|
||||||
|
|
||||||
return PE_OK unless ( $key and length($key) );
|
return PE_OK unless ( $key and length($key) );
|
||||||
|
|
||||||
my $persistentSession = $self->getPersistentSession($key);
|
my $persistentSession = $self->getPersistentSession($key);
|
||||||
|
@ -593,9 +594,9 @@ sub secondFactor {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub storeHistory {
|
sub storeHistory {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req, $uid ) = @_; # $uid passed by BruteForceProtection plugin
|
||||||
if ( $self->conf->{loginHistoryEnabled} ) {
|
if ( $self->conf->{loginHistoryEnabled} ) {
|
||||||
$self->registerLogin($req);
|
$self->registerLogin( $req, $uid );
|
||||||
}
|
}
|
||||||
PE_OK;
|
PE_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1037,7 +1037,9 @@ sub tplParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub registerLogin {
|
sub registerLogin {
|
||||||
my ( $self, $req ) = @_;
|
|
||||||
|
# $user passed by BruteForceProtection plugin
|
||||||
|
my ( $self, $req, $uid ) = @_;
|
||||||
return
|
return
|
||||||
unless ( $self->conf->{loginHistoryEnabled}
|
unless ( $self->conf->{loginHistoryEnabled}
|
||||||
and defined $req->authResult );
|
and defined $req->authResult );
|
||||||
|
@ -1067,7 +1069,8 @@ sub registerLogin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->updatePersistentSession( $req, { 'loginHistory' => undef } );
|
$self->updatePersistentSession( $req, { 'loginHistory' => undef },
|
||||||
|
$uid );
|
||||||
delete $req->sessionInfo->{loginHistory};
|
delete $req->sessionInfo->{loginHistory};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,7 +1095,7 @@ sub registerLogin {
|
||||||
if ( scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
if ( scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
||||||
|
|
||||||
# Save into persistent session
|
# Save into persistent session
|
||||||
$self->updatePersistentSession( $req, { _loginHistory => $history, } );
|
$self->updatePersistentSession( $req, { _loginHistory => $history }, $uid );
|
||||||
|
|
||||||
PE_OK;
|
PE_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ our $VERSION = '2.0.10';
|
||||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||||
|
|
||||||
# INITIALIZATION
|
# INITIALIZATION
|
||||||
use constant afterSub => { storeHistory => 'run' };
|
use constant aroundSub => { authenticate => 'check' };
|
||||||
|
|
||||||
has lockTimes => (
|
has lockTimes => (
|
||||||
is => 'rw',
|
is => 'rw',
|
||||||
|
@ -25,8 +25,15 @@ has maxAge => (
|
||||||
isa => 'Int'
|
isa => 'Int'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
has maxFailed => (
|
||||||
|
is => 'rw',
|
||||||
|
isa => 'Int'
|
||||||
|
);
|
||||||
|
|
||||||
sub init {
|
sub init {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
$self->maxFailed( abs $self->conf->{bruteForceProtectionMaxFailed} );
|
||||||
|
|
||||||
if ( $self->conf->{disablePersistentStorage} ) {
|
if ( $self->conf->{disablePersistentStorage} ) {
|
||||||
$self->logger->error(
|
$self->logger->error(
|
||||||
'"BruteForceProtection" plugin enabled WITHOUT persistent session storage"'
|
'"BruteForceProtection" plugin enabled WITHOUT persistent session storage"'
|
||||||
|
@ -40,13 +47,11 @@ sub init {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unless ( $self->conf->{failedLoginNumber} >
|
unless ( $self->conf->{failedLoginNumber} > $self->maxFailed ) {
|
||||||
$self->conf->{bruteForceProtectionMaxFailed} )
|
|
||||||
{
|
|
||||||
$self->logger->error( 'Number of failed logins history ('
|
$self->logger->error( 'Number of failed logins history ('
|
||||||
. $self->conf->{failedLoginNumber}
|
. $self->conf->{failedLoginNumber}
|
||||||
. ') must be higher than allowed failed logins attempt ('
|
. ') must be higher than allowed failed logins attempt ('
|
||||||
. $self->conf->{bruteForceProtectionMaxFailed}
|
. $self->maxFailed
|
||||||
. ')' );
|
. ')' );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -62,21 +67,16 @@ sub init {
|
||||||
split /\s*,\s*/, $self->conf->{bruteForceProtectionLockTimes};
|
split /\s*,\s*/, $self->conf->{bruteForceProtectionLockTimes};
|
||||||
|
|
||||||
unless ($lockTimes) {
|
unless ($lockTimes) {
|
||||||
@{ $self->lockTimes } = ( 5, 15, 60, 300, 600 );
|
@{ $self->lockTimes } = ( 15, 30, 60, 300, 600 );
|
||||||
$lockTimes = 5;
|
$lockTimes = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (
|
for ( my $i = 1 ; $i < $self->maxFailed ; $i++ ) {
|
||||||
my $i = 1 ;
|
|
||||||
$i <= $self->conf->{bruteForceProtectionMaxFailed} ;
|
|
||||||
$i++
|
|
||||||
)
|
|
||||||
{
|
|
||||||
unshift @{ $self->lockTimes }, 0;
|
unshift @{ $self->lockTimes }, 0;
|
||||||
$lockTimes++;
|
$lockTimes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
unless ( $lockTimes < $self->conf->{failedLoginNumber} ) {
|
unless ( $lockTimes <= $self->conf->{failedLoginNumber} ) {
|
||||||
$self->logger->warn( 'Number failed logins history ('
|
$self->logger->warn( 'Number failed logins history ('
|
||||||
. $self->conf->{failedLoginNumber}
|
. $self->conf->{failedLoginNumber}
|
||||||
. ') must be higher than incremental lock time values plus allowed failed logins attempt ('
|
. ') must be higher than incremental lock time values plus allowed failed logins attempt ('
|
||||||
|
@ -91,76 +91,83 @@ sub init {
|
||||||
$self->maxAge($sum);
|
$self->maxAge($sum);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->maxAge( $self->conf->{bruteForceProtectionMaxAge} );
|
$self->maxAge( $self->conf->{bruteForceProtectionMaxAge} *
|
||||||
|
( 1 + $self->maxFailed ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# RUNNING METHOD
|
# RUNNING METHOD
|
||||||
sub run {
|
sub check {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $sub, $req ) = @_;
|
||||||
my $now = time;
|
my $now = time;
|
||||||
my $countFailed = my @failedLogins =
|
$self->p->setSessionInfo($req);
|
||||||
map { ( $now - $_->{_utime} ) < $self->maxAge ? $_ : () }
|
$self->logger->debug("Retrieve $req->{user} logins history");
|
||||||
@{ $req->sessionInfo->{_loginHistory}->{failedLogin} };
|
$self->p->setPersistentSessionInfo( $req, $req->{user} );
|
||||||
$self->logger->debug( ' Failed login maxAge = ' . $self->maxAge );
|
|
||||||
$self->logger->debug(
|
|
||||||
" Number of failed login(s) to take into account = $countFailed");
|
|
||||||
|
|
||||||
if ( $self->conf->{bruteForceProtectionIncrementalTempo} ) {
|
my $countFailed = my @failedLogins =
|
||||||
|
map { ( $now - $_->{_utime} ) <= $self->maxAge ? $_ : () }
|
||||||
|
@{ $req->sessionInfo->{_loginHistory}->{failedLogin} };
|
||||||
|
$self->logger->debug( ' -> Failed login maxAge = ' . $self->maxAge );
|
||||||
|
$self->logger->debug(
|
||||||
|
"Number of failed login(s) to take into account = $countFailed");
|
||||||
my $lastFailedLoginEpoch = $failedLogins[0]->{_utime} || undef;
|
my $lastFailedLoginEpoch = $failedLogins[0]->{_utime} || undef;
|
||||||
|
|
||||||
return PE_OK unless $lastFailedLoginEpoch;
|
if ( $self->conf->{bruteForceProtectionIncrementalTempo} ) {
|
||||||
|
return $sub->($req) unless $lastFailedLoginEpoch;
|
||||||
|
|
||||||
|
# Delta between current attempt and last failed login
|
||||||
my $delta = $now - $lastFailedLoginEpoch;
|
my $delta = $now - $lastFailedLoginEpoch;
|
||||||
$self->logger->debug(" -> Delta = $delta");
|
$self->logger->debug(" -> Delta = $delta");
|
||||||
|
|
||||||
|
# Time to wait
|
||||||
my $waitingTime = $self->lockTimes->[ $countFailed - 1 ]
|
my $waitingTime = $self->lockTimes->[ $countFailed - 1 ]
|
||||||
// $self->conf->{bruteForceProtectionMaxLockTime};
|
// $self->conf->{bruteForceProtectionMaxLockTime};
|
||||||
|
|
||||||
|
# Reach last tempo. Stop to increase waiting time
|
||||||
|
if ( $countFailed >= scalar @{ $self->lockTimes } ) {
|
||||||
|
$self->userLogger->warn(
|
||||||
|
"BruteForceProtection: Last lock time has been reached");
|
||||||
|
$self->logger->debug("Force waitingTime to last value");
|
||||||
|
$waitingTime =
|
||||||
|
$self->lockTimes->[ scalar @{ $self->lockTimes } - 1 ];
|
||||||
|
}
|
||||||
$self->logger->debug(" -> Waiting time = $waitingTime");
|
$self->logger->debug(" -> Waiting time = $waitingTime");
|
||||||
if ( $waitingTime && $delta <= $waitingTime ) {
|
|
||||||
$self->logger->debug("BruteForceProtection enabled");
|
# Delta < waitingTime => wait
|
||||||
$req->lockTime($waitingTime);
|
if ( $waitingTime && $delta < $waitingTime ) {
|
||||||
|
$self->userLogger->warn("BruteForceProtection enabled");
|
||||||
|
$req->authResult(PE_WAIT);
|
||||||
|
|
||||||
|
# Do not store failed login if last tempo or max tempo is reached
|
||||||
|
$self->p->registerLogin( $req, $req->{user} )
|
||||||
|
if ( $waitingTime < $self->conf->{bruteForceProtectionMaxLockTime}
|
||||||
|
&& $waitingTime <
|
||||||
|
$self->lockTimes->[ scalar @{ $self->lockTimes } - 1 ] );
|
||||||
|
$req->lockTime( $waitingTime - $delta );
|
||||||
return PE_WAIT;
|
return PE_WAIT;
|
||||||
}
|
}
|
||||||
return PE_OK;
|
return $sub->($req);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PE_OK
|
return $sub->($req)
|
||||||
if ( $countFailed <= $self->conf->{bruteForceProtectionMaxFailed} );
|
if ( $countFailed < $self->maxFailed );
|
||||||
|
|
||||||
my @lastFailedLoginEpoch = ();
|
# Delta between current attempt and last failed login
|
||||||
my $MaxAge = $self->maxAge + 1;
|
my $delta = $lastFailedLoginEpoch ? $now - $lastFailedLoginEpoch : 0;
|
||||||
|
|
||||||
# Auth_N-2 failed login epoch
|
|
||||||
foreach ( 0 .. $self->conf->{bruteForceProtectionMaxFailed} - 1 ) {
|
|
||||||
push @lastFailedLoginEpoch,
|
|
||||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]->{_utime}
|
|
||||||
if ( $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] );
|
|
||||||
}
|
|
||||||
|
|
||||||
# If Auth_N-MaxFailed older than MaxAge -> another try allowed
|
|
||||||
$MaxAge =
|
|
||||||
$lastFailedLoginEpoch[0] -
|
|
||||||
$lastFailedLoginEpoch[ $self->conf->{bruteForceProtectionMaxFailed} - 1 ]
|
|
||||||
if $self->conf->{bruteForceProtectionMaxFailed};
|
|
||||||
$self->logger->debug(" -> MaxAge = $MaxAge");
|
|
||||||
|
|
||||||
return PE_OK
|
|
||||||
if ( $MaxAge > $self->maxAge );
|
|
||||||
|
|
||||||
# Delta between the two last failed logins -> Auth_N - Auth_N-1
|
|
||||||
my $delta =
|
|
||||||
defined $lastFailedLoginEpoch[1] ? $now - $lastFailedLoginEpoch[1] : 0;
|
|
||||||
$self->logger->debug(" -> Delta = $delta");
|
$self->logger->debug(" -> Delta = $delta");
|
||||||
|
|
||||||
# Delta between the two last failed logins < Tempo => wait
|
# Delta < Tempo => wait
|
||||||
return PE_OK
|
return $sub->($req)
|
||||||
unless ( $delta <= $self->conf->{bruteForceProtectionTempo} );
|
unless ( $delta < $self->conf->{bruteForceProtectionTempo}
|
||||||
|
&& $countFailed );
|
||||||
|
|
||||||
# Account locked
|
# Account locked
|
||||||
$self->logger->debug("BruteForceProtection enabled");
|
$self->userLogger->warn("BruteForceProtection enabled");
|
||||||
$req->lockTime( $self->conf->{bruteForceProtectionTempo} );
|
$self->logger->debug(
|
||||||
|
" -> Waiting time = $self->{conf}->{bruteForceProtectionTempo}");
|
||||||
|
$req->lockTime( $self->conf->{bruteForceProtectionTempo} - $delta );
|
||||||
return PE_WAIT;
|
return PE_WAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ my $res;
|
||||||
|
|
||||||
my $client = LLNG::Manager::Test->new( {
|
my $client = LLNG::Manager::Test->new( {
|
||||||
ini => {
|
ini => {
|
||||||
logLevel => 'error',
|
logLevel => 'debug',
|
||||||
authentication => 'Demo',
|
authentication => 'Demo',
|
||||||
userDB => 'Same',
|
userDB => 'Same',
|
||||||
loginHistoryEnabled => 1,
|
loginHistoryEnabled => 1,
|
||||||
|
@ -118,11 +118,32 @@ ok(
|
||||||
),
|
),
|
||||||
'2nd Bad Auth query'
|
'2nd Bad Auth query'
|
||||||
);
|
);
|
||||||
|
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
|
||||||
|
'Rejected -> Protection enabled' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
ok( $res->[2]->[0] =~ m%5 <span trspan="seconds">seconds</span>%,
|
||||||
|
'LockTime = 5' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
count(3);
|
||||||
|
|
||||||
|
# Waiting
|
||||||
|
Time::Fake->offset("+6s");
|
||||||
|
|
||||||
|
## Third failed connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=ohwd'),
|
||||||
|
length => 23,
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'3rd Bad Auth query'
|
||||||
|
);
|
||||||
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
|
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
|
||||||
'Rejected -> Protection enabled' )
|
'Rejected -> Protection enabled' )
|
||||||
or print STDERR Dumper( $res->[2]->[0] );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
ok( $res->[2]->[0] =~ m%10 <span trspan="seconds">seconds</span>%,
|
ok( $res->[2]->[0] =~ m%10 <span trspan="seconds">seconds</span>%,
|
||||||
'LockTime = 10' )
|
'LockTime = 10**************' )
|
||||||
or print STDERR Dumper( $res->[2]->[0] );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
count(3);
|
count(3);
|
||||||
|
|
||||||
|
|
|
@ -224,14 +224,20 @@ $id1 = expectCookie($res);
|
||||||
|
|
||||||
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||||
or print STDERR Dumper( $res->[2]->[0] );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
ok( $res->[2]->[0] =~ /<caption trspan="lastFailedLoginsCaptionLabel">/, 'History found' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
ok( $res->[2]->[0] =~ /<caption trspan="lastLoginsCaptionLabel">/, 'History found' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||||
my @cf = ( $res->[2]->[0] =~ /PE5<\/td>/gs );
|
my @cf = ( $res->[2]->[0] =~ /PE5<\/td>/gs );
|
||||||
|
|
||||||
# History with 10 entries
|
# History with 8 entries
|
||||||
ok( @c == 10, ' -> Ten entries found' );
|
ok( @c == 8, ' -> Eight entries found' )
|
||||||
ok( @cf == 6, " -> Six 'failedLogin' entries found" );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
count(3);
|
ok( @cf == 4, " -> Four 'failedLogin' entries found" )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
count(5);
|
||||||
|
|
||||||
$client->logout($id1);
|
$client->logout($id1);
|
||||||
clean_sessions();
|
clean_sessions();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user