Improved FileStorage dedugging with expired session token fix

This commit is contained in:
djmaze 2021-12-28 10:44:56 +01:00
parent f2388bb936
commit 997dc87305
4 changed files with 46 additions and 39 deletions

View file

@ -237,7 +237,7 @@ trait UserAuth
* Server side control/kickout of logged in sessions * Server side control/kickout of logged in sessions
* https://github.com/the-djmaze/snappymail/issues/151 * https://github.com/the-djmaze/snappymail/issues/151
*/ */
if (!isset($_COOKIE[Utils::SESSION_TOKEN])) { if (empty($_COOKIE[Utils::SESSION_TOKEN])) {
// \MailSo\Base\Http::StatusHeader(401); // \MailSo\Base\Http::StatusHeader(401);
$this->Logout(true); $this->Logout(true);
// $sAdditionalMessage = $this->StaticI18N('SESSION_UNDEFINED'); // $sAdditionalMessage = $this->StaticI18N('SESSION_UNDEFINED');
@ -248,11 +248,19 @@ trait UserAuth
$aData, $aData,
$bThrowExceptionOnFalse $bThrowExceptionOnFalse
); );
$sToken = Utils::GetSessionToken(); $oMainAuthAccount || \SnappyMail\LOG::notice('TOKENS', 'AUTH_SPEC_TOKEN_KEY invalid');
if ($oMainAuthAccount && $this->StorageProvider()->Get($oMainAuthAccount, StorageType::SESSION, $sToken)) { $sToken = $oMainAuthAccount ? Utils::GetSessionToken(false) : null;
$sTokenValue = $sToken ? $this->StorageProvider()->Get($oMainAuthAccount, StorageType::SESSION, $sToken) : null;
if ($oMainAuthAccount && $sTokenValue) {
$this->oMainAuthAccount = $oMainAuthAccount; $this->oMainAuthAccount = $oMainAuthAccount;
} else { } else {
$oMainAuthAccount && $this->StorageProvider()->Clear($oMainAuthAccount, StorageType::SESSION, $sToken); if ($oMainAuthAccount) {
$sToken || \SnappyMail\LOG::notice('TOKENS', 'SESSION_TOKEN not found');
if ($sToken) {
$oMainAuthAccount && $this->StorageProvider()->Clear($oMainAuthAccount, StorageType::SESSION, $sToken);
$sTokenValue || \SnappyMail\LOG::notice('TOKENS', 'SESSION_TOKEN value invalid: ' . \gettype($sTokenValue));
}
}
Utils::ClearCookie(Utils::SESSION_TOKEN); Utils::ClearCookie(Utils::SESSION_TOKEN);
// \MailSo\Base\Http::StatusHeader(401); // \MailSo\Base\Http::StatusHeader(401);
$this->Logout(true); $this->Logout(true);
@ -266,10 +274,7 @@ trait UserAuth
} }
} }
if ($this->oMainAuthAccount) { if ($bThrowExceptionOnFalse && !$this->oMainAuthAccount) {
// Extend session cookie lifetime
$this->StorageProvider()->Put($this->oMainAuthAccount, StorageType::SESSION, Utils::GetSessionToken(), 'true');
} else if ($bThrowExceptionOnFalse) {
throw new ClientException(Notifications::InvalidToken, null, 'Account undefined'); throw new ClientException(Notifications::InvalidToken, null, 'Account undefined');
} }
} }

View file

@ -19,13 +19,9 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/ */
private function verifyAccount($mAccount, int $iStorageType) : bool private function verifyAccount($mAccount, int $iStorageType) : bool
{ {
if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && return \RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY === $iStorageType
!($mAccount instanceof \RainLoop\Model\Account || \is_string($mAccount))) || $mAccount instanceof \RainLoop\Model\Account
{ || \is_string($mAccount);
return false;
}
return true;
} }
/** /**
@ -34,12 +30,9 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/ */
public function Put($mAccount, int $iStorageType, string $sKey, string $sValue) : bool public function Put($mAccount, int $iStorageType, string $sKey, string $sValue) : bool
{ {
if (!$this->verifyAccount($mAccount, $iStorageType)) return $this->verifyAccount($mAccount, $iStorageType)
{ ? $this->oDriver->Put($mAccount, $iStorageType, $sKey, $sValue)
return false; : false;
}
return $this->oDriver->Put($mAccount, $iStorageType, $sKey, $sValue);
} }
/** /**
@ -50,12 +43,9 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/ */
public function Get($mAccount, int $iStorageType, string $sKey, $mDefault = false) public function Get($mAccount, int $iStorageType, string $sKey, $mDefault = false)
{ {
if (!$this->verifyAccount($mAccount, $iStorageType)) return $this->verifyAccount($mAccount, $iStorageType)
{ ? $this->oDriver->Get($mAccount, $iStorageType, $sKey, $mDefault)
return $mDefault; : $mDefault;
}
return $this->oDriver->Get($mAccount, $iStorageType, $sKey, $mDefault);
} }
/** /**
@ -63,12 +53,9 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/ */
public function Clear($mAccount, int $iStorageType, string $sKey) : bool public function Clear($mAccount, int $iStorageType, string $sKey) : bool
{ {
if (!$this->verifyAccount($mAccount, $iStorageType)) return $this->verifyAccount($mAccount, $iStorageType)
{ ? $this->oDriver->Clear($mAccount, $iStorageType, $sKey)
return false; : false;
}
return $this->oDriver->Clear($mAccount, $iStorageType, $sKey);
} }
/** /**

View file

@ -38,7 +38,7 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
$sFileName && \RainLoop\Utils::saveFile($sFileName, $sValue); $sFileName && \RainLoop\Utils::saveFile($sFileName, $sValue);
return true; return true;
} catch (\Throwable $e) { } catch (\Throwable $e) {
\error_log("{$e->getMessage()}: {$sFileName}"); \SnappyMail\LOG::warning('FileStorage', $e->getMessage());
} }
return false; return false;
} }
@ -55,6 +55,10 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
$sFileName = $this->generateFileName($mAccount, $iStorageType, $sKey); $sFileName = $this->generateFileName($mAccount, $iStorageType, $sKey);
if ($sFileName && \file_exists($sFileName)) { if ($sFileName && \file_exists($sFileName)) {
$mValue = \file_get_contents($sFileName); $mValue = \file_get_contents($sFileName);
// Update mtime to prevent garbage collection
if (StorageType::SESSION === $iStorageType) {
\touch($sFileName);
}
} }
return false === $mValue ? $mDefault : $mValue; return false === $mValue ? $mDefault : $mValue;
} }
@ -154,6 +158,7 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
public function GC() : void public function GC() : void
{ {
\clearstatcache();
foreach (\glob("{$this->sDataPath}/*", GLOB_ONLYDIR) as $sDomain) { foreach (\glob("{$this->sDataPath}/*", GLOB_ONLYDIR) as $sDomain) {
foreach (\glob("{$sDomain}/*", GLOB_ONLYDIR) as $sLocal) { foreach (\glob("{$sDomain}/*", GLOB_ONLYDIR) as $sLocal) {
\MailSo\Base\Utils::RecTimeDirRemove("{$sLocal}/.sign_me", 3600 * 24 * 30); // 30 days \MailSo\Base\Utils::RecTimeDirRemove("{$sLocal}/.sign_me", 3600 * 24 * 30); // 30 days

View file

@ -59,7 +59,7 @@ class Utils
{ {
return \SnappyMail\Crypt::DecryptUrlSafe( return \SnappyMail\Crypt::DecryptUrlSafe(
$sEncodedValues, $sEncodedValues,
\sha1(APP_SALT.$sCustomKey.'Q'.static::GetSessionToken()) \sha1(APP_SALT.$sCustomKey.'Q'.static::GetSessionToken(false))
) ?: null; ) ?: null;
} }
@ -72,14 +72,17 @@ class Utils
} }
} }
public static function GetSessionToken() : string public static function GetSessionToken(bool $generate = true) : ?string
{ {
$sToken = static::GetCookie(self::SESSION_TOKEN, null); $sToken = static::GetCookie(self::SESSION_TOKEN, null);
if (!$sToken) { if (!$sToken) {
if (!$generate) {
return null;
}
\SnappyMail\LOG::debug('TOKENS', 'New SESSION_TOKEN');
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT); $sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
static::SetCookie(self::SESSION_TOKEN, $sToken); static::SetCookie(self::SESSION_TOKEN, $sToken);
} }
return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT); return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT);
} }
@ -225,11 +228,18 @@ class Utils
{ {
$dir = \dirname($filename); $dir = \dirname($filename);
if (!\is_dir($dir) && !\mkdir($dir, 0700, true)) { if (!\is_dir($dir) && !\mkdir($dir, 0700, true)) {
throw new \RainLoop\Exceptions\Exception('Failed to create directory "'.$dir.'"'); throw new Exceptions\Exception('Failed to create directory "'.$dir.'"');
} }
if (false === \file_put_contents($filename, $data)) { if (false === \file_put_contents($filename, $data)) {
throw new \RainLoop\Exceptions\Exception('Failed to save file "'.$filename.'"'); throw new Exceptions\Exception('Failed to save file "'.$filename.'"');
} }
\clearstatcache();
\chmod($filename, 0600); \chmod($filename, 0600);
/*
try {
} catch (\Throwable $oException) {
throw new Exceptions\Exception($oException->getMessage() . ': ' . \error_get_last()['message']);
}
*/
} }
} }