parent
ca5e98da48
commit
b38a1b0209
15 changed files with 170 additions and 124 deletions
|
@ -4,21 +4,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a message to a text file
|
* Format log using provided data.
|
||||||
*
|
*
|
||||||
* The log format is compatible with fail2ban.
|
|
||||||
*
|
|
||||||
* @param string $logFile where to write the logs
|
|
||||||
* @param string $clientIp the client's remote IPv4/IPv6 address
|
|
||||||
* @param string $message the message to log
|
* @param string $message the message to log
|
||||||
|
* @param string|null $clientIp the client's remote IPv4/IPv6 address
|
||||||
|
*
|
||||||
|
* @return string Formatted message to log
|
||||||
*/
|
*/
|
||||||
function logm($logFile, $clientIp, $message)
|
function format_log(string $message, string $clientIp = null): string
|
||||||
{
|
{
|
||||||
file_put_contents(
|
$out = $message;
|
||||||
$logFile,
|
|
||||||
date('Y/m/d H:i:s').' - '.$clientIp.' - '.strval($message).PHP_EOL,
|
if (!empty($clientIp)) {
|
||||||
FILE_APPEND
|
// Note: we keep the first dash to avoid breaking fail2ban configs
|
||||||
);
|
$out = '- ' . $clientIp . ' - ' . $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
namespace Shaarli\Container;
|
namespace Shaarli\Container;
|
||||||
|
|
||||||
use malkusch\lock\mutex\FlockMutex;
|
use malkusch\lock\mutex\FlockMutex;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\Bookmark\BookmarkFileService;
|
use Shaarli\Bookmark\BookmarkFileService;
|
||||||
use Shaarli\Bookmark\BookmarkServiceInterface;
|
use Shaarli\Bookmark\BookmarkServiceInterface;
|
||||||
use Shaarli\Config\ConfigManager;
|
use Shaarli\Config\ConfigManager;
|
||||||
|
@ -49,6 +50,9 @@ class ContainerBuilder
|
||||||
/** @var LoginManager */
|
/** @var LoginManager */
|
||||||
protected $login;
|
protected $login;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
/** @var string|null */
|
/** @var string|null */
|
||||||
protected $basePath = null;
|
protected $basePath = null;
|
||||||
|
|
||||||
|
@ -56,12 +60,14 @@ public function __construct(
|
||||||
ConfigManager $conf,
|
ConfigManager $conf,
|
||||||
SessionManager $session,
|
SessionManager $session,
|
||||||
CookieManager $cookieManager,
|
CookieManager $cookieManager,
|
||||||
LoginManager $login
|
LoginManager $login,
|
||||||
|
LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
$this->conf = $conf;
|
$this->conf = $conf;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
$this->login = $login;
|
$this->login = $login;
|
||||||
$this->cookieManager = $cookieManager;
|
$this->cookieManager = $cookieManager;
|
||||||
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function build(): ShaarliContainer
|
public function build(): ShaarliContainer
|
||||||
|
@ -72,6 +78,7 @@ public function build(): ShaarliContainer
|
||||||
$container['sessionManager'] = $this->session;
|
$container['sessionManager'] = $this->session;
|
||||||
$container['cookieManager'] = $this->cookieManager;
|
$container['cookieManager'] = $this->cookieManager;
|
||||||
$container['loginManager'] = $this->login;
|
$container['loginManager'] = $this->login;
|
||||||
|
$container['logger'] = $this->logger;
|
||||||
$container['basePath'] = $this->basePath;
|
$container['basePath'] = $this->basePath;
|
||||||
|
|
||||||
$container['plugins'] = function (ShaarliContainer $container): PluginManager {
|
$container['plugins'] = function (ShaarliContainer $container): PluginManager {
|
||||||
|
@ -99,6 +106,7 @@ public function build(): ShaarliContainer
|
||||||
return new PageBuilder(
|
return new PageBuilder(
|
||||||
$container->conf,
|
$container->conf,
|
||||||
$container->sessionManager->getSession(),
|
$container->sessionManager->getSession(),
|
||||||
|
$container->logger,
|
||||||
$container->bookmarkService,
|
$container->bookmarkService,
|
||||||
$container->sessionManager->generateToken(),
|
$container->sessionManager->generateToken(),
|
||||||
$container->loginManager->isLoggedIn()
|
$container->loginManager->isLoggedIn()
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
namespace Shaarli\Container;
|
namespace Shaarli\Container;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\Bookmark\BookmarkServiceInterface;
|
use Shaarli\Bookmark\BookmarkServiceInterface;
|
||||||
use Shaarli\Config\ConfigManager;
|
use Shaarli\Config\ConfigManager;
|
||||||
use Shaarli\Feed\FeedBuilder;
|
use Shaarli\Feed\FeedBuilder;
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
* @property History $history
|
* @property History $history
|
||||||
* @property HttpAccess $httpAccess
|
* @property HttpAccess $httpAccess
|
||||||
* @property LoginManager $loginManager
|
* @property LoginManager $loginManager
|
||||||
|
* @property LoggerInterface $logger
|
||||||
* @property MetadataRetriever $metadataRetriever
|
* @property MetadataRetriever $metadataRetriever
|
||||||
* @property NetscapeBookmarkUtils $netscapeBookmarkUtils
|
* @property NetscapeBookmarkUtils $netscapeBookmarkUtils
|
||||||
* @property callable $notFoundHandler Overrides default Slim exception display
|
* @property callable $notFoundHandler Overrides default Slim exception display
|
||||||
|
|
|
@ -65,7 +65,6 @@ public function login(Request $request, Response $response): Response
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->container->loginManager->checkCredentials(
|
if (!$this->container->loginManager->checkCredentials(
|
||||||
$this->container->environment['REMOTE_ADDR'],
|
|
||||||
client_ip_id($this->container->environment),
|
client_ip_id($this->container->environment),
|
||||||
$request->getParam('login'),
|
$request->getParam('login'),
|
||||||
$request->getParam('password')
|
$request->getParam('password')
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Shaarli\Render;
|
namespace Shaarli\Render;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use exceptions\MissingBasePathException;
|
use Psr\Log\LoggerInterface;
|
||||||
use RainTPL;
|
use RainTPL;
|
||||||
use Shaarli\ApplicationUtils;
|
use Shaarli\ApplicationUtils;
|
||||||
use Shaarli\Bookmark\BookmarkServiceInterface;
|
use Shaarli\Bookmark\BookmarkServiceInterface;
|
||||||
|
@ -35,6 +35,9 @@ class PageBuilder
|
||||||
*/
|
*/
|
||||||
protected $session;
|
protected $session;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var BookmarkServiceInterface $bookmarkService instance.
|
* @var BookmarkServiceInterface $bookmarkService instance.
|
||||||
*/
|
*/
|
||||||
|
@ -56,15 +59,23 @@ class PageBuilder
|
||||||
*
|
*
|
||||||
* @param ConfigManager $conf Configuration Manager instance (reference).
|
* @param ConfigManager $conf Configuration Manager instance (reference).
|
||||||
* @param array $session $_SESSION array
|
* @param array $session $_SESSION array
|
||||||
* @param BookmarkServiceInterface $linkDB instance.
|
* @param LoggerInterface $logger
|
||||||
* @param string $token Session token
|
* @param null $linkDB instance.
|
||||||
|
* @param null $token Session token
|
||||||
* @param bool $isLoggedIn
|
* @param bool $isLoggedIn
|
||||||
*/
|
*/
|
||||||
public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
|
public function __construct(
|
||||||
{
|
ConfigManager &$conf,
|
||||||
|
array $session,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
$linkDB = null,
|
||||||
|
$token = null,
|
||||||
|
$isLoggedIn = false
|
||||||
|
) {
|
||||||
$this->tpl = false;
|
$this->tpl = false;
|
||||||
$this->conf = $conf;
|
$this->conf = $conf;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
|
$this->logger = $logger;
|
||||||
$this->bookmarkService = $linkDB;
|
$this->bookmarkService = $linkDB;
|
||||||
$this->token = $token;
|
$this->token = $token;
|
||||||
$this->isLoggedIn = $isLoggedIn;
|
$this->isLoggedIn = $isLoggedIn;
|
||||||
|
@ -98,7 +109,7 @@ private function initialize()
|
||||||
$this->tpl->assign('newVersion', escape($version));
|
$this->tpl->assign('newVersion', escape($version));
|
||||||
$this->tpl->assign('versionError', '');
|
$this->tpl->assign('versionError', '');
|
||||||
} catch (Exception $exc) {
|
} catch (Exception $exc) {
|
||||||
logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage());
|
$this->logger->error(format_log('Error: ' . $exc->getMessage(), client_ip_id($_SERVER)));
|
||||||
$this->tpl->assign('newVersion', '');
|
$this->tpl->assign('newVersion', '');
|
||||||
$this->tpl->assign('versionError', escape($exc->getMessage()));
|
$this->tpl->assign('versionError', escape($exc->getMessage()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
namespace Shaarli\Security;
|
namespace Shaarli\Security;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\FileUtils;
|
use Shaarli\FileUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,8 +29,8 @@ class BanManager
|
||||||
/** @var string Path to the file containing IP bans and failures */
|
/** @var string Path to the file containing IP bans and failures */
|
||||||
protected $banFile;
|
protected $banFile;
|
||||||
|
|
||||||
/** @var string Path to the log file, used to log bans */
|
/** @var LoggerInterface Path to the log file, used to log bans */
|
||||||
protected $logFile;
|
protected $logger;
|
||||||
|
|
||||||
/** @var array List of IP with their associated number of failed attempts */
|
/** @var array List of IP with their associated number of failed attempts */
|
||||||
protected $failures = [];
|
protected $failures = [];
|
||||||
|
@ -44,14 +45,15 @@ class BanManager
|
||||||
* @param int $nbAttempts Number of allowed failed attempt before the ban
|
* @param int $nbAttempts Number of allowed failed attempt before the ban
|
||||||
* @param int $banDuration Ban duration in seconds
|
* @param int $banDuration Ban duration in seconds
|
||||||
* @param string $banFile Path to the file containing IP bans and failures
|
* @param string $banFile Path to the file containing IP bans and failures
|
||||||
* @param string $logFile Path to the log file, used to log bans
|
* @param LoggerInterface $logger PSR-3 logger to save login attempts in log directory
|
||||||
*/
|
*/
|
||||||
public function __construct($trustedProxies, $nbAttempts, $banDuration, $banFile, $logFile) {
|
public function __construct($trustedProxies, $nbAttempts, $banDuration, $banFile, LoggerInterface $logger) {
|
||||||
$this->trustedProxies = $trustedProxies;
|
$this->trustedProxies = $trustedProxies;
|
||||||
$this->nbAttempts = $nbAttempts;
|
$this->nbAttempts = $nbAttempts;
|
||||||
$this->banDuration = $banDuration;
|
$this->banDuration = $banDuration;
|
||||||
$this->banFile = $banFile;
|
$this->banFile = $banFile;
|
||||||
$this->logFile = $logFile;
|
$this->logger = $logger;
|
||||||
|
|
||||||
$this->readBanFile();
|
$this->readBanFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,11 +80,7 @@ public function handleFailedAttempt($server)
|
||||||
|
|
||||||
if ($this->failures[$ip] >= $this->nbAttempts) {
|
if ($this->failures[$ip] >= $this->nbAttempts) {
|
||||||
$this->bans[$ip] = time() + $this->banDuration;
|
$this->bans[$ip] = time() + $this->banDuration;
|
||||||
logm(
|
$this->logger->info(format_log('IP address banned from login: '. $ip, $ip));
|
||||||
$this->logFile,
|
|
||||||
$server['REMOTE_ADDR'],
|
|
||||||
'IP address banned from login: '. $ip
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
$this->writeBanFile();
|
$this->writeBanFile();
|
||||||
}
|
}
|
||||||
|
@ -138,7 +136,7 @@ public function isBanned($server)
|
||||||
unset($this->failures[$ip]);
|
unset($this->failures[$ip]);
|
||||||
}
|
}
|
||||||
unset($this->bans[$ip]);
|
unset($this->bans[$ip]);
|
||||||
logm($this->logFile, $server['REMOTE_ADDR'], 'Ban lifted for: '. $ip);
|
$this->logger->info(format_log('Ban lifted for: '. $ip, $ip));
|
||||||
|
|
||||||
$this->writeBanFile();
|
$this->writeBanFile();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
namespace Shaarli\Security;
|
namespace Shaarli\Security;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\Config\ConfigManager;
|
use Shaarli\Config\ConfigManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +32,8 @@ class LoginManager
|
||||||
protected $staySignedInToken = '';
|
protected $staySignedInToken = '';
|
||||||
/** @var CookieManager */
|
/** @var CookieManager */
|
||||||
protected $cookieManager;
|
protected $cookieManager;
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -38,19 +41,21 @@ class LoginManager
|
||||||
* @param ConfigManager $configManager Configuration Manager instance
|
* @param ConfigManager $configManager Configuration Manager instance
|
||||||
* @param SessionManager $sessionManager SessionManager instance
|
* @param SessionManager $sessionManager SessionManager instance
|
||||||
* @param CookieManager $cookieManager CookieManager instance
|
* @param CookieManager $cookieManager CookieManager instance
|
||||||
|
* @param BanManager $banManager
|
||||||
|
* @param LoggerInterface $logger Used to log login attempts
|
||||||
*/
|
*/
|
||||||
public function __construct($configManager, $sessionManager, $cookieManager)
|
public function __construct(
|
||||||
{
|
ConfigManager $configManager,
|
||||||
|
SessionManager $sessionManager,
|
||||||
|
CookieManager $cookieManager,
|
||||||
|
BanManager $banManager,
|
||||||
|
LoggerInterface $logger
|
||||||
|
) {
|
||||||
$this->configManager = $configManager;
|
$this->configManager = $configManager;
|
||||||
$this->sessionManager = $sessionManager;
|
$this->sessionManager = $sessionManager;
|
||||||
$this->cookieManager = $cookieManager;
|
$this->cookieManager = $cookieManager;
|
||||||
$this->banManager = new BanManager(
|
$this->banManager = $banManager;
|
||||||
$this->configManager->get('security.trusted_proxies', []),
|
$this->logger = $logger;
|
||||||
$this->configManager->get('security.ban_after'),
|
|
||||||
$this->configManager->get('security.ban_duration'),
|
|
||||||
$this->configManager->get('resource.ban_file', 'data/ipbans.php'),
|
|
||||||
$this->configManager->get('resource.log')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->configManager->get('security.open_shaarli') === true) {
|
if ($this->configManager->get('security.open_shaarli') === true) {
|
||||||
$this->openShaarli = true;
|
$this->openShaarli = true;
|
||||||
|
@ -129,48 +134,34 @@ public function isLoggedIn(): bool
|
||||||
/**
|
/**
|
||||||
* Check user credentials are valid
|
* Check user credentials are valid
|
||||||
*
|
*
|
||||||
* @param string $remoteIp Remote client IP address
|
|
||||||
* @param string $clientIpId Client IP address identifier
|
* @param string $clientIpId Client IP address identifier
|
||||||
* @param string $login Username
|
* @param string $login Username
|
||||||
* @param string $password Password
|
* @param string $password Password
|
||||||
*
|
*
|
||||||
* @return bool true if the provided credentials are valid, false otherwise
|
* @return bool true if the provided credentials are valid, false otherwise
|
||||||
*/
|
*/
|
||||||
public function checkCredentials($remoteIp, $clientIpId, $login, $password)
|
public function checkCredentials($clientIpId, $login, $password)
|
||||||
{
|
{
|
||||||
// Check login matches config
|
|
||||||
if ($login !== $this->configManager->get('credentials.login')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check credentials
|
// Check credentials
|
||||||
try {
|
try {
|
||||||
$useLdapLogin = !empty($this->configManager->get('ldap.host'));
|
$useLdapLogin = !empty($this->configManager->get('ldap.host'));
|
||||||
if ((false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
|
if ($login === $this->configManager->get('credentials.login')
|
||||||
|
&& (
|
||||||
|
(false === $useLdapLogin && $this->checkCredentialsFromLocalConfig($login, $password))
|
||||||
|| (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
|
|| (true === $useLdapLogin && $this->checkCredentialsFromLdap($login, $password))
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
$this->sessionManager->storeLoginInfo($clientIpId);
|
$this->sessionManager->storeLoginInfo($clientIpId);
|
||||||
logm(
|
$this->logger->info(format_log('Login successful', $clientIpId));
|
||||||
$this->configManager->get('resource.log'),
|
|
||||||
$remoteIp,
|
|
||||||
'Login successful'
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} catch(Exception $exception) {
|
||||||
catch(Exception $exception) {
|
$this->logger->info(format_log('Exception while checking credentials: ' . $exception, $clientIpId));
|
||||||
logm(
|
|
||||||
$this->configManager->get('resource.log'),
|
|
||||||
$remoteIp,
|
|
||||||
'Exception while checking credentials: ' . $exception
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logm(
|
$this->logger->info(format_log('Login failed for user ' . $login, $clientIpId));
|
||||||
$this->configManager->get('resource.log'),
|
|
||||||
$remoteIp,
|
|
||||||
'Login failed for user ' . $login
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
index.php
19
index.php
|
@ -25,9 +25,12 @@
|
||||||
|
|
||||||
require_once __DIR__ . '/init.php';
|
require_once __DIR__ . '/init.php';
|
||||||
|
|
||||||
|
use Katzgrau\KLogger\Logger;
|
||||||
|
use Psr\Log\LogLevel;
|
||||||
use Shaarli\Config\ConfigManager;
|
use Shaarli\Config\ConfigManager;
|
||||||
use Shaarli\Container\ContainerBuilder;
|
use Shaarli\Container\ContainerBuilder;
|
||||||
use Shaarli\Languages;
|
use Shaarli\Languages;
|
||||||
|
use Shaarli\Security\BanManager;
|
||||||
use Shaarli\Security\CookieManager;
|
use Shaarli\Security\CookieManager;
|
||||||
use Shaarli\Security\LoginManager;
|
use Shaarli\Security\LoginManager;
|
||||||
use Shaarli\Security\SessionManager;
|
use Shaarli\Security\SessionManager;
|
||||||
|
@ -48,10 +51,22 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$logger = new Logger(
|
||||||
|
dirname($conf->get('resource.log')),
|
||||||
|
!$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
|
||||||
|
['filename' => basename($conf->get('resource.log'))]
|
||||||
|
);
|
||||||
$sessionManager = new SessionManager($_SESSION, $conf, session_save_path());
|
$sessionManager = new SessionManager($_SESSION, $conf, session_save_path());
|
||||||
$sessionManager->initialize();
|
$sessionManager->initialize();
|
||||||
$cookieManager = new CookieManager($_COOKIE);
|
$cookieManager = new CookieManager($_COOKIE);
|
||||||
$loginManager = new LoginManager($conf, $sessionManager, $cookieManager);
|
$banManager = new BanManager(
|
||||||
|
$conf->get('security.trusted_proxies', []),
|
||||||
|
$conf->get('security.ban_after'),
|
||||||
|
$conf->get('security.ban_duration'),
|
||||||
|
$conf->get('resource.ban_file', 'data/ipbans.php'),
|
||||||
|
$logger
|
||||||
|
);
|
||||||
|
$loginManager = new LoginManager($conf, $sessionManager, $cookieManager, $banManager, $logger);
|
||||||
$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
|
$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
|
||||||
|
|
||||||
// Sniff browser language and set date format accordingly.
|
// Sniff browser language and set date format accordingly.
|
||||||
|
@ -71,7 +86,7 @@
|
||||||
|
|
||||||
$loginManager->checkLoginState(client_ip_id($_SERVER));
|
$loginManager->checkLoginState(client_ip_id($_SERVER));
|
||||||
|
|
||||||
$containerBuilder = new ContainerBuilder($conf, $sessionManager, $cookieManager, $loginManager);
|
$containerBuilder = new ContainerBuilder($conf, $sessionManager, $cookieManager, $loginManager, $logger);
|
||||||
$container = $containerBuilder->build();
|
$container = $containerBuilder->build();
|
||||||
$app = new App($container);
|
$app = new App($container);
|
||||||
|
|
||||||
|
|
|
@ -63,41 +63,25 @@ protected function getLastLogEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message to a file - IPv4 client address
|
* Format a log a message - IPv4 client address
|
||||||
*/
|
*/
|
||||||
public function testLogmIp4()
|
public function testFormatLogIp4()
|
||||||
{
|
{
|
||||||
$logMessage = 'IPv4 client connected';
|
$message = 'IPv4 client connected';
|
||||||
logm(self::$testLogFile, '127.0.0.1', $logMessage);
|
$log = format_log($message, '127.0.0.1');
|
||||||
list($date, $ip, $message) = $this->getLastLogEntry();
|
|
||||||
|
|
||||||
$this->assertInstanceOf(
|
static::assertSame('- 127.0.0.1 - IPv4 client connected', $log);
|
||||||
'DateTime',
|
|
||||||
DateTime::createFromFormat(self::$dateFormat, $date)
|
|
||||||
);
|
|
||||||
$this->assertTrue(
|
|
||||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false
|
|
||||||
);
|
|
||||||
$this->assertEquals($logMessage, $message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message to a file - IPv6 client address
|
* Format a log a message - IPv6 client address
|
||||||
*/
|
*/
|
||||||
public function testLogmIp6()
|
public function testFormatLogIp6()
|
||||||
{
|
{
|
||||||
$logMessage = 'IPv6 client connected';
|
$message = 'IPv6 client connected';
|
||||||
logm(self::$testLogFile, '2001:db8::ff00:42:8329', $logMessage);
|
$log = format_log($message, '2001:db8::ff00:42:8329');
|
||||||
list($date, $ip, $message) = $this->getLastLogEntry();
|
|
||||||
|
|
||||||
$this->assertInstanceOf(
|
static::assertSame('- 2001:db8::ff00:42:8329 - IPv6 client connected', $log);
|
||||||
'DateTime',
|
|
||||||
DateTime::createFromFormat(self::$dateFormat, $date)
|
|
||||||
);
|
|
||||||
$this->assertTrue(
|
|
||||||
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false
|
|
||||||
);
|
|
||||||
$this->assertEquals($logMessage, $message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
namespace Shaarli\Container;
|
namespace Shaarli\Container;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\Bookmark\BookmarkServiceInterface;
|
use Shaarli\Bookmark\BookmarkServiceInterface;
|
||||||
use Shaarli\Config\ConfigManager;
|
use Shaarli\Config\ConfigManager;
|
||||||
use Shaarli\Feed\FeedBuilder;
|
use Shaarli\Feed\FeedBuilder;
|
||||||
|
@ -55,7 +56,8 @@ public function setUp(): void
|
||||||
$this->conf,
|
$this->conf,
|
||||||
$this->sessionManager,
|
$this->sessionManager,
|
||||||
$this->cookieManager,
|
$this->cookieManager,
|
||||||
$this->loginManager
|
$this->loginManager,
|
||||||
|
$this->createMock(LoggerInterface::class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +75,7 @@ public function testBuildContainer(): void
|
||||||
static::assertInstanceOf(History::class, $container->history);
|
static::assertInstanceOf(History::class, $container->history);
|
||||||
static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
|
static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
|
||||||
static::assertInstanceOf(LoginManager::class, $container->loginManager);
|
static::assertInstanceOf(LoginManager::class, $container->loginManager);
|
||||||
|
static::assertInstanceOf(LoggerInterface::class, $container->logger);
|
||||||
static::assertInstanceOf(MetadataRetriever::class, $container->metadataRetriever);
|
static::assertInstanceOf(MetadataRetriever::class, $container->metadataRetriever);
|
||||||
static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils);
|
static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils);
|
||||||
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
|
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
|
||||||
|
|
|
@ -195,7 +195,7 @@ public function testProcessLoginWithValidParameters(): void
|
||||||
$this->container->loginManager
|
$this->container->loginManager
|
||||||
->expects(static::once())
|
->expects(static::once())
|
||||||
->method('checkCredentials')
|
->method('checkCredentials')
|
||||||
->with('1.2.3.4', '1.2.3.4', 'bob', 'pass')
|
->with('1.2.3.4', 'bob', 'pass')
|
||||||
->willReturn(true)
|
->willReturn(true)
|
||||||
;
|
;
|
||||||
$this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
|
$this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
namespace Shaarli\Security;
|
namespace Shaarli\Security;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Shaarli\FileUtils;
|
use Shaarli\FileUtils;
|
||||||
use Shaarli\TestCase;
|
use Shaarli\TestCase;
|
||||||
|
|
||||||
|
@ -387,7 +388,7 @@ protected function getNewBanManagerInstance()
|
||||||
3,
|
3,
|
||||||
1800,
|
1800,
|
||||||
$this->banFile,
|
$this->banFile,
|
||||||
$this->logFile
|
$this->createMock(LoggerInterface::class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace Shaarli\Security;
|
namespace Shaarli\Security;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Shaarli\FakeConfigManager;
|
||||||
use Shaarli\TestCase;
|
use Shaarli\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +11,7 @@
|
||||||
*/
|
*/
|
||||||
class LoginManagerTest extends TestCase
|
class LoginManagerTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var \FakeConfigManager Configuration Manager instance */
|
/** @var FakeConfigManager Configuration Manager instance */
|
||||||
protected $configManager = null;
|
protected $configManager = null;
|
||||||
|
|
||||||
/** @var LoginManager Login Manager instance */
|
/** @var LoginManager Login Manager instance */
|
||||||
|
@ -60,6 +62,9 @@ class LoginManagerTest extends TestCase
|
||||||
/** @var CookieManager */
|
/** @var CookieManager */
|
||||||
protected $cookieManager;
|
protected $cookieManager;
|
||||||
|
|
||||||
|
/** @var BanManager */
|
||||||
|
protected $banManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare or reset test resources
|
* Prepare or reset test resources
|
||||||
*/
|
*/
|
||||||
|
@ -71,7 +76,7 @@ protected function setUp(): void
|
||||||
|
|
||||||
$this->passwordHash = sha1($this->password . $this->login . $this->salt);
|
$this->passwordHash = sha1($this->password . $this->login . $this->salt);
|
||||||
|
|
||||||
$this->configManager = new \FakeConfigManager([
|
$this->configManager = new FakeConfigManager([
|
||||||
'credentials.login' => $this->login,
|
'credentials.login' => $this->login,
|
||||||
'credentials.hash' => $this->passwordHash,
|
'credentials.hash' => $this->passwordHash,
|
||||||
'credentials.salt' => $this->salt,
|
'credentials.salt' => $this->salt,
|
||||||
|
@ -91,18 +96,29 @@ protected function setUp(): void
|
||||||
return $this->cookie[$key] ?? null;
|
return $this->cookie[$key] ?? null;
|
||||||
});
|
});
|
||||||
$this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path');
|
$this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path');
|
||||||
$this->loginManager = new LoginManager($this->configManager, $this->sessionManager, $this->cookieManager);
|
$this->banManager = $this->createMock(BanManager::class);
|
||||||
|
$this->loginManager = new LoginManager(
|
||||||
|
$this->configManager,
|
||||||
|
$this->sessionManager,
|
||||||
|
$this->cookieManager,
|
||||||
|
$this->banManager,
|
||||||
|
$this->createMock(LoggerInterface::class)
|
||||||
|
);
|
||||||
$this->server['REMOTE_ADDR'] = $this->ipAddr;
|
$this->server['REMOTE_ADDR'] = $this->ipAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record a failed login attempt
|
* Record a failed login attempt
|
||||||
*/
|
*/
|
||||||
public function testHandleFailedLogin()
|
public function testHandleFailedLogin(): void
|
||||||
{
|
{
|
||||||
|
$this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
|
||||||
|
$this->banManager->method('isBanned')->willReturn(true);
|
||||||
|
|
||||||
$this->loginManager->handleFailedLogin($this->server);
|
$this->loginManager->handleFailedLogin($this->server);
|
||||||
$this->loginManager->handleFailedLogin($this->server);
|
$this->loginManager->handleFailedLogin($this->server);
|
||||||
$this->assertFalse($this->loginManager->canLogin($this->server));
|
|
||||||
|
static::assertFalse($this->loginManager->canLogin($this->server));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,8 +130,13 @@ public function testHandleFailedLoginBehindTrustedProxy()
|
||||||
'REMOTE_ADDR' => $this->trustedProxy,
|
'REMOTE_ADDR' => $this->trustedProxy,
|
||||||
'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
|
'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
|
||||||
|
$this->banManager->method('isBanned')->willReturn(true);
|
||||||
|
|
||||||
$this->loginManager->handleFailedLogin($server);
|
$this->loginManager->handleFailedLogin($server);
|
||||||
$this->loginManager->handleFailedLogin($server);
|
$this->loginManager->handleFailedLogin($server);
|
||||||
|
|
||||||
$this->assertFalse($this->loginManager->canLogin($server));
|
$this->assertFalse($this->loginManager->canLogin($server));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,10 +217,16 @@ public function testGenerateStaySignedInTokenSessionProtectionDisabled()
|
||||||
*/
|
*/
|
||||||
public function testCheckLoginStateNotConfigured()
|
public function testCheckLoginStateNotConfigured()
|
||||||
{
|
{
|
||||||
$configManager = new \FakeConfigManager([
|
$configManager = new FakeConfigManager([
|
||||||
'resource.ban_file' => $this->banFile,
|
'resource.ban_file' => $this->banFile,
|
||||||
]);
|
]);
|
||||||
$loginManager = new LoginManager($configManager, null, $this->cookieManager);
|
$loginManager = new LoginManager(
|
||||||
|
$configManager,
|
||||||
|
$this->sessionManager,
|
||||||
|
$this->cookieManager,
|
||||||
|
$this->banManager,
|
||||||
|
$this->createMock(LoggerInterface::class)
|
||||||
|
);
|
||||||
$loginManager->checkLoginState('');
|
$loginManager->checkLoginState('');
|
||||||
|
|
||||||
$this->assertFalse($loginManager->isLoggedIn());
|
$this->assertFalse($loginManager->isLoggedIn());
|
||||||
|
@ -270,7 +297,7 @@ public function testCheckLoginStateClientIpChanged()
|
||||||
public function testCheckCredentialsWrongLogin()
|
public function testCheckCredentialsWrongLogin()
|
||||||
{
|
{
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$this->loginManager->checkCredentials('', '', 'b4dl0g1n', $this->password)
|
$this->loginManager->checkCredentials('', 'b4dl0g1n', $this->password)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +307,7 @@ public function testCheckCredentialsWrongLogin()
|
||||||
public function testCheckCredentialsWrongPassword()
|
public function testCheckCredentialsWrongPassword()
|
||||||
{
|
{
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$this->loginManager->checkCredentials('', '', $this->login, 'b4dp455wd')
|
$this->loginManager->checkCredentials('', $this->login, 'b4dp455wd')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +317,7 @@ public function testCheckCredentialsWrongPassword()
|
||||||
public function testCheckCredentialsWrongLoginAndPassword()
|
public function testCheckCredentialsWrongLoginAndPassword()
|
||||||
{
|
{
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$this->loginManager->checkCredentials('', '', 'b4dl0g1n', 'b4dp455wd')
|
$this->loginManager->checkCredentials('', 'b4dl0g1n', 'b4dp455wd')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +327,7 @@ public function testCheckCredentialsWrongLoginAndPassword()
|
||||||
public function testCheckCredentialsGoodLoginAndPassword()
|
public function testCheckCredentialsGoodLoginAndPassword()
|
||||||
{
|
{
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$this->loginManager->checkCredentials('', '', $this->login, $this->password)
|
$this->loginManager->checkCredentials('', $this->login, $this->password)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +338,7 @@ public function testCheckCredentialsFromUnreachableLdap()
|
||||||
{
|
{
|
||||||
$this->configManager->set('ldap.host', 'dummy');
|
$this->configManager->set('ldap.host', 'dummy');
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$this->loginManager->checkCredentials('', '', $this->login, $this->password)
|
$this->loginManager->checkCredentials('', $this->login, $this->password)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Shaarli\Security;
|
namespace Shaarli\Security;
|
||||||
|
|
||||||
|
use Shaarli\FakeConfigManager;
|
||||||
use Shaarli\TestCase;
|
use Shaarli\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +13,7 @@ class SessionManagerTest extends TestCase
|
||||||
/** @var array Session ID hashes */
|
/** @var array Session ID hashes */
|
||||||
protected static $sidHashes = null;
|
protected static $sidHashes = null;
|
||||||
|
|
||||||
/** @var \FakeConfigManager ConfigManager substitute for testing */
|
/** @var FakeConfigManager ConfigManager substitute for testing */
|
||||||
protected $conf = null;
|
protected $conf = null;
|
||||||
|
|
||||||
/** @var array $_SESSION array for testing */
|
/** @var array $_SESSION array for testing */
|
||||||
|
@ -34,7 +35,7 @@ public static function setUpBeforeClass(): void
|
||||||
*/
|
*/
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->conf = new \FakeConfigManager([
|
$this->conf = new FakeConfigManager([
|
||||||
'credentials.login' => 'johndoe',
|
'credentials.login' => 'johndoe',
|
||||||
'credentials.salt' => 'salt',
|
'credentials.salt' => 'salt',
|
||||||
'security.session_protection_disabled' => false,
|
'security.session_protection_disabled' => false,
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace Shaarli;
|
||||||
|
|
||||||
|
use Shaarli\Config\ConfigManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fake ConfigManager
|
* Fake ConfigManager
|
||||||
*/
|
*/
|
||||||
class FakeConfigManager
|
class FakeConfigManager extends ConfigManager
|
||||||
{
|
{
|
||||||
protected $values = [];
|
protected $values = [];
|
||||||
|
|
||||||
|
@ -23,7 +27,7 @@ public function __construct($values = [])
|
||||||
* @param string $key Key of the value to set
|
* @param string $key Key of the value to set
|
||||||
* @param mixed $value Value to set
|
* @param mixed $value Value to set
|
||||||
*/
|
*/
|
||||||
public function set($key, $value)
|
public function set($key, $value, $write = false, $isLoggedIn = false)
|
||||||
{
|
{
|
||||||
$this->values[$key] = $value;
|
$this->values[$key] = $value;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +39,7 @@ public function set($key, $value)
|
||||||
*
|
*
|
||||||
* @return mixed The value if set, else the name of the key
|
* @return mixed The value if set, else the name of the key
|
||||||
*/
|
*/
|
||||||
public function get($key)
|
public function get($key, $default = '')
|
||||||
{
|
{
|
||||||
if (isset($this->values[$key])) {
|
if (isset($this->values[$key])) {
|
||||||
return $this->values[$key];
|
return $this->values[$key];
|
||||||
|
|
Loading…
Reference in a new issue