Refactor user credential validation at login time

Changed:
- move login/password verification to LoginManager
- code cleanup

Signed-off-by: VirtualTam <virtualtam@flibidi.net>
This commit is contained in:
VirtualTam 2018-02-17 01:46:27 +01:00
parent 49f1832316
commit 63ea23c2a6
3 changed files with 146 additions and 111 deletions

View file

@ -8,20 +8,123 @@ class LoginManager
{ {
protected $globals = []; protected $globals = [];
protected $configManager = null; protected $configManager = null;
protected $sessionManager = null;
protected $banFile = ''; protected $banFile = '';
protected $isLoggedIn = false;
protected $openShaarli = false;
/** /**
* Constructor * Constructor
* *
* @param array $globals The $GLOBALS array (reference) * @param array $globals The $GLOBALS array (reference)
* @param ConfigManager $configManager Configuration Manager instance. * @param ConfigManager $configManager Configuration Manager instance
* @param SessionManager $sessionManager SessionManager instance
*/ */
public function __construct(& $globals, $configManager) public function __construct(& $globals, $configManager, $sessionManager)
{ {
$this->globals = &$globals; $this->globals = &$globals;
$this->configManager = $configManager; $this->configManager = $configManager;
$this->sessionManager = $sessionManager;
$this->banFile = $this->configManager->get('resource.ban_file', 'data/ipbans.php'); $this->banFile = $this->configManager->get('resource.ban_file', 'data/ipbans.php');
$this->readBanFile(); $this->readBanFile();
if ($this->configManager->get('security.open_shaarli')) {
$this->openShaarli = true;
}
}
/**
* Check user session state and validity (expiration)
*
* @param array $server The $_SERVER array
* @param array $session The $_SESSION array (reference)
* @param array $cookie The $_COOKIE array
* @param string $webPath Path on the server in which the cookie will be available on
* @param string $token Session token
*
* @return bool true if the user session is valid, false otherwise
*/
public function checkLoginState($server, & $session, $cookie, $webPath, $token)
{
if (! $this->configManager->exists('credentials.login')) {
// Shaarli is not configured yet
$this->isLoggedIn = false;
return;
}
if (isset($cookie[SessionManager::$LOGGED_IN_COOKIE])
&& $cookie[SessionManager::$LOGGED_IN_COOKIE] === $token
) {
$this->sessionManager->storeLoginInfo($server);
$this->isLoggedIn = true;
}
// Logout when:
// - the session does not exist on the server side
// - the session has expired
// - the client IP address has changed
if (empty($session['uid'])
|| ($this->configManager->get('security.session_protection_disabled') === false
&& $session['ip'] != client_ip_id($server))
|| time() >= $session['expires_on']
) {
$this->sessionManager->logout($webPath);
$this->isLoggedIn = false;
return;
}
// Extend session validity
if (! empty($session['longlastingsession'])) {
// "Stay signed in" is enabled
$session['expires_on'] = time() + $session['longlastingsession'];
} else {
$session['expires_on'] = time() + SessionManager::$INACTIVITY_TIMEOUT;
}
}
/**
* Return whether the user is currently logged in
*
* @return true when the user is logged in, false otherwise
*/
public function isLoggedIn()
{
if ($this->openShaarli) {
return true;
}
return $this->isLoggedIn;
}
/**
* Check user credentials are valid
*
* @param array $server The $_SERVER array
* @param string $login Username
* @param string $password Password
*
* @return bool true if the provided credentials are valid, false otherwise
*/
public function checkCredentials($server, $login, $password)
{
$hash = sha1($password . $login . $this->configManager->get('credentials.salt'));
if ($login != $this->configManager->get('credentials.login')
|| $hash != $this->configManager->get('credentials.hash')
) {
logm(
$this->configManager->get('resource.log'),
$server['REMOTE_ADDR'],
'Login failed for user ' . $login
);
return false;
}
$this->sessionManager->storeLoginInfo($server);
logm(
$this->configManager->get('resource.log'),
$server['REMOTE_ADDR'],
'Login successful'
);
return true;
} }
/** /**

144
index.php
View file

@ -121,8 +121,8 @@
} }
$conf = new ConfigManager(); $conf = new ConfigManager();
$loginManager = new LoginManager($GLOBALS, $conf);
$sessionManager = new SessionManager($_SESSION, $conf); $sessionManager = new SessionManager($_SESSION, $conf);
$loginManager = new LoginManager($GLOBALS, $conf, $sessionManager);
// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. // LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead.
if (! defined('LC_MESSAGES')) { if (! defined('LC_MESSAGES')) {
@ -178,88 +178,20 @@
// a token depending of deployment salt, user password, and the current ip // a token depending of deployment salt, user password, and the current ip
define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt')));
/** $loginManager->checkLoginState($_SERVER, $_SESSION, $_COOKIE, WEB_PATH, STAY_SIGNED_IN_TOKEN);
* Checking session state (i.e. is the user still logged in)
*
* @param ConfigManager $conf Configuration Manager instance.
* @param SessionManager $sessionManager SessionManager instance
*
* @return bool true if the user is logged in, false otherwise.
*/
function setup_login_state($conf, $sessionManager)
{
if ($conf->get('security.open_shaarli')) {
return true;
}
$userIsLoggedIn = false; // By default, we do not consider the user as logged in;
$loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met.
if (! $conf->exists('credentials.login')) {
$userIsLoggedIn = false; // Shaarli is not configured yet.
$loginFailure = true;
}
if (isset($_COOKIE[SessionManager::$LOGGED_IN_COOKIE])
&& $_COOKIE[SessionManager::$LOGGED_IN_COOKIE] === STAY_SIGNED_IN_TOKEN
&& !$loginFailure
) {
$sessionManager->storeLoginInfo($_SERVER);
$userIsLoggedIn = true;
}
// If session does not exist on server side, or IP address has changed, or session has expired, logout.
if (empty($_SESSION['uid'])
|| ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != client_ip_id($_SERVER))
|| time() >= $_SESSION['expires_on'])
{
$sessionManager->logout(WEB_PATH);
$userIsLoggedIn = false;
$loginFailure = true;
}
if (!empty($_SESSION['longlastingsession'])) {
$_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked.
} else {
$_SESSION['expires_on'] = time() + $sessionManager::$INACTIVITY_TIMEOUT;
}
if (!$loginFailure) {
$userIsLoggedIn = true;
}
return $userIsLoggedIn;
}
$userIsLoggedIn = setup_login_state($conf, $sessionManager);
// ------------------------------------------------------------------------------------------
// Session management
/** /**
* Check that user/password is correct. * Adapter function for PageBuilder
* *
* @param string $login Username * TODO: update PageBuilder and tests
* @param string $password User password
* @param ConfigManager $conf Configuration Manager instance.
* @param SessionManager $sessionManager SessionManager instance
*
* @return bool: authentication successful or not.
*/ */
function check_auth($login, $password, $conf, $sessionManager)
{
$hash = sha1($password . $login . $conf->get('credentials.salt'));
if ($login == $conf->get('credentials.login') && $hash == $conf->get('credentials.hash')) {
// Login/password is correct.
$sessionManager->storeLoginInfo($_SERVER);
logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login successful');
return true;
}
logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login failed for user '.$login);
return false;
}
// Returns true if the user is logged in.
function isLoggedIn() function isLoggedIn()
{ {
global $userIsLoggedIn; global $loginManager;
return $userIsLoggedIn; return $loginManager->isLoggedIn();
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
// Process login form: Check if login/password is correct. // Process login form: Check if login/password is correct.
if (isset($_POST['login'])) { if (isset($_POST['login'])) {
@ -268,7 +200,7 @@ function isLoggedIn()
} }
if (isset($_POST['password']) if (isset($_POST['password'])
&& $sessionManager->checkToken($_POST['token']) && $sessionManager->checkToken($_POST['token'])
&& (check_auth($_POST['login'], $_POST['password'], $conf, $sessionManager)) && $loginManager->checkCredentials($_SERVER, $_POST['login'], $_POST['password'])
) { ) {
// Login/password is OK. // Login/password is OK.
$loginManager->handleSuccessfulLogin($_SERVER); $loginManager->handleSuccessfulLogin($_SERVER);
@ -347,15 +279,16 @@ function isLoggedIn()
* Gives the last 7 days (which have links). * Gives the last 7 days (which have links).
* This RSS feed cannot be filtered. * This RSS feed cannot be filtered.
* *
* @param ConfigManager $conf Configuration Manager instance. * @param ConfigManager $conf Configuration Manager instance
* @param LoginManager $loginManager LoginManager instance
*/ */
function showDailyRSS($conf) { function showDailyRSS($conf, $loginManager) {
// Cache system // Cache system
$query = $_SERVER['QUERY_STRING']; $query = $_SERVER['QUERY_STRING'];
$cache = new CachedPage( $cache = new CachedPage(
$conf->get('config.PAGE_CACHE'), $conf->get('config.PAGE_CACHE'),
page_url($_SERVER), page_url($_SERVER),
startsWith($query,'do=dailyrss') && !isLoggedIn() startsWith($query,'do=dailyrss') && !$loginManager->isLoggedIn()
); );
$cached = $cache->cachedVersion(); $cached = $cache->cachedVersion();
if (!empty($cached)) { if (!empty($cached)) {
@ -367,7 +300,7 @@ function showDailyRSS($conf) {
// Read links from database (and filter private links if used it not logged in). // Read links from database (and filter private links if used it not logged in).
$LINKSDB = new LinkDB( $LINKSDB = new LinkDB(
$conf->get('resource.datastore'), $conf->get('resource.datastore'),
isLoggedIn(), $loginManager->isLoggedIn(),
$conf->get('privacy.hide_public_links'), $conf->get('privacy.hide_public_links'),
$conf->get('redirector.url'), $conf->get('redirector.url'),
$conf->get('redirector.encode_url') $conf->get('redirector.encode_url')
@ -509,7 +442,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager)
/* Hook is called before column construction so that plugins don't have /* Hook is called before column construction so that plugins don't have
to deal with columns. */ to deal with columns. */
$pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn())); $pluginManager->executeHooks('render_daily', $data, array('loggedin' => $loginManager->isLoggedIn()));
/* We need to spread the articles on 3 columns. /* We need to spread the articles on 3 columns.
I did not want to use a JavaScript lib like http://masonry.desandro.com/ I did not want to use a JavaScript lib like http://masonry.desandro.com/
@ -553,8 +486,8 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager)
* @param ConfigManager $conf Configuration Manager instance. * @param ConfigManager $conf Configuration Manager instance.
* @param PluginManager $pluginManager Plugin Manager instance. * @param PluginManager $pluginManager Plugin Manager instance.
*/ */
function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) {
buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager); // Compute list of links to display buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager, $loginManager);
$PAGE->renderPage('linklist'); $PAGE->renderPage('linklist');
} }
@ -574,7 +507,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
read_updates_file($conf->get('resource.updates')), read_updates_file($conf->get('resource.updates')),
$LINKSDB, $LINKSDB,
$conf, $conf,
isLoggedIn() $loginManager->isLoggedIn()
); );
try { try {
$newUpdates = $updater->update(); $newUpdates = $updater->update();
@ -596,11 +529,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
// Determine which page will be rendered. // Determine which page will be rendered.
$query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
$targetPage = Router::findPage($query, $_GET, isLoggedIn()); $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn());
if ( if (
// if the user isn't logged in // if the user isn't logged in
!isLoggedIn() && !$loginManager->isLoggedIn() &&
// and Shaarli doesn't have public content... // and Shaarli doesn't have public content...
$conf->get('privacy.hide_public_links') && $conf->get('privacy.hide_public_links') &&
// and is configured to enforce the login // and is configured to enforce the login
@ -628,7 +561,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
$pluginManager->executeHooks('render_' . $name, $plugin_data, $pluginManager->executeHooks('render_' . $name, $plugin_data,
array( array(
'target' => $targetPage, 'target' => $targetPage,
'loggedin' => isLoggedIn() 'loggedin' => $loginManager->isLoggedIn()
) )
); );
$PAGE->assign('plugins_' . $name, $plugin_data); $PAGE->assign('plugins_' . $name, $plugin_data);
@ -680,7 +613,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
$data = array( $data = array(
'linksToDisplay' => $linksToDisplay, 'linksToDisplay' => $linksToDisplay,
); );
$pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn())); $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => $loginManager->isLoggedIn()));
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$PAGE->assign($key, $value); $PAGE->assign($key, $value);
@ -727,7 +660,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
'search_tags' => $searchTags, 'search_tags' => $searchTags,
'tags' => $tagList, 'tags' => $tagList,
); );
$pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => $loginManager->isLoggedIn()));
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$PAGE->assign($key, $value); $PAGE->assign($key, $value);
@ -760,7 +693,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
'search_tags' => $searchTags, 'search_tags' => $searchTags,
'tags' => $tags, 'tags' => $tags,
]; ];
$pluginManager->executeHooks('render_taglist', $data, ['loggedin' => isLoggedIn()]); $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => $loginManager->isLoggedIn()]);
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$PAGE->assign($key, $value); $PAGE->assign($key, $value);
@ -787,7 +720,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
$cache = new CachedPage( $cache = new CachedPage(
$conf->get('resource.page_cache'), $conf->get('resource.page_cache'),
page_url($_SERVER), page_url($_SERVER),
startsWith($query,'do='. $targetPage) && !isLoggedIn() startsWith($query,'do='. $targetPage) && !$loginManager->isLoggedIn()
); );
$cached = $cache->cachedVersion(); $cached = $cache->cachedVersion();
if (!empty($cached)) { if (!empty($cached)) {
@ -796,15 +729,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
} }
// Generate data. // Generate data.
$feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn()); $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, $loginManager->isLoggedIn());
$feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0)));
$feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !$loginManager->isLoggedIn());
$feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks'));
$data = $feedGenerator->buildData(); $data = $feedGenerator->buildData();
// Process plugin hook. // Process plugin hook.
$pluginManager->executeHooks('render_feed', $data, array( $pluginManager->executeHooks('render_feed', $data, array(
'loggedin' => isLoggedIn(), 'loggedin' => $loginManager->isLoggedIn(),
'target' => $targetPage, 'target' => $targetPage,
)); ));
@ -952,7 +885,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
} }
// -------- Handle other actions allowed for non-logged in users: // -------- Handle other actions allowed for non-logged in users:
if (!isLoggedIn()) if (!$loginManager->isLoggedIn())
{ {
// User tries to post new link but is not logged in: // User tries to post new link but is not logged in:
// Show login screen, then redirect to ?post=... // Show login screen, then redirect to ?post=...
@ -968,7 +901,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
exit; exit;
} }
showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
if (isset($_GET['edit_link'])) { if (isset($_GET['edit_link'])) {
header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); header('Location: ?do=login&edit_link='. escape($_GET['edit_link']));
exit; exit;
@ -1019,7 +952,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
$conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
$conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt'))); $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt')));
try { try {
$conf->write(isLoggedIn()); $conf->write($loginManager->isLoggedIn());
} }
catch(Exception $e) { catch(Exception $e) {
error_log( error_log(
@ -1070,7 +1003,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
$conf->set('translation.language', escape($_POST['language'])); $conf->set('translation.language', escape($_POST['language']));
try { try {
$conf->write(isLoggedIn()); $conf->write($loginManager->isLoggedIn());
$history->updateSettings(); $history->updateSettings();
invalidateCaches($conf->get('resource.page_cache')); invalidateCaches($conf->get('resource.page_cache'));
} }
@ -1522,7 +1455,7 @@ function($a, $b) { return $a['order'] - $b['order']; }
else { else {
$conf->set('general.enabled_plugins', save_plugin_config($_POST)); $conf->set('general.enabled_plugins', save_plugin_config($_POST));
} }
$conf->write(isLoggedIn()); $conf->write($loginManager->isLoggedIn());
$history->updateSettings(); $history->updateSettings();
} }
catch (Exception $e) { catch (Exception $e) {
@ -1547,7 +1480,7 @@ function($a, $b) { return $a['order'] - $b['order']; }
} }
// -------- Otherwise, simply display search form and links: // -------- Otherwise, simply display search form and links:
showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
exit; exit;
} }
@ -1559,8 +1492,9 @@ function($a, $b) { return $a['order'] - $b['order']; }
* @param LinkDB $LINKSDB LinkDB instance. * @param LinkDB $LINKSDB LinkDB instance.
* @param ConfigManager $conf Configuration Manager instance. * @param ConfigManager $conf Configuration Manager instance.
* @param PluginManager $pluginManager Plugin Manager instance. * @param PluginManager $pluginManager Plugin Manager instance.
* @param LoginManager $loginManager LoginManager instance
*/ */
function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
{ {
// Used in templates // Used in templates
if (isset($_GET['searchtags'])) { if (isset($_GET['searchtags'])) {
@ -1599,8 +1533,6 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
$keys[] = $key; $keys[] = $key;
} }
// Select articles according to paging. // Select articles according to paging.
$pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
$pagecount = $pagecount == 0 ? 1 : $pagecount; $pagecount = $pagecount == 0 ? 1 : $pagecount;
@ -1681,7 +1613,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
$data['pagetitle'] .= '- '. $conf->get('general.title'); $data['pagetitle'] .= '- '. $conf->get('general.title');
} }
$pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn())); $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => $loginManager->isLoggedIn()));
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$PAGE->assign($key, $value); $PAGE->assign($key, $value);
@ -1952,7 +1884,7 @@ function install($conf, $sessionManager) {
); );
try { try {
// Everything is ok, let's create config file. // Everything is ok, let's create config file.
$conf->write(isLoggedIn()); $conf->write($loginManager->isLoggedIn());
} }
catch(Exception $e) { catch(Exception $e) {
error_log( error_log(
@ -2216,7 +2148,7 @@ function resizeImage($filepath)
$linkDb = new LinkDB( $linkDb = new LinkDB(
$conf->get('resource.datastore'), $conf->get('resource.datastore'),
isLoggedIn(), $loginManager->isLoggedIn(),
$conf->get('privacy.hide_public_links'), $conf->get('privacy.hide_public_links'),
$conf->get('redirector.url'), $conf->get('redirector.url'),
$conf->get('redirector.encode_url') $conf->get('redirector.encode_url')

View file

@ -38,7 +38,7 @@ public function setUp()
$this->globals = &$GLOBALS; $this->globals = &$GLOBALS;
unset($this->globals['IPBANS']); unset($this->globals['IPBANS']);
$this->loginManager = new LoginManager($this->globals, $this->configManager); $this->loginManager = new LoginManager($this->globals, $this->configManager, null);
$this->server['REMOTE_ADDR'] = $this->ipAddr; $this->server['REMOTE_ADDR'] = $this->ipAddr;
} }
@ -59,7 +59,7 @@ public function testReadBanFile()
$this->banFile, $this->banFile,
"<?php\n\$GLOBALS['IPBANS']=array('FAILURES' => array('127.0.0.1' => 99));\n?>" "<?php\n\$GLOBALS['IPBANS']=array('FAILURES' => array('127.0.0.1' => 99));\n?>"
); );
new LoginManager($this->globals, $this->configManager); new LoginManager($this->globals, $this->configManager, null);
$this->assertEquals(99, $this->globals['IPBANS']['FAILURES']['127.0.0.1']); $this->assertEquals(99, $this->globals['IPBANS']['FAILURES']['127.0.0.1']);
} }