From aed4d9e4c200f9e250feb38cd8cdb917a6a3faf7 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 8 Nov 2021 12:07:44 +0100 Subject: [PATCH] Resolve #126 and nice for #151 --- .../libraries/RainLoop/Actions/UserAuth.php | 126 +++++++++++------- .../Storage/Enumerations/StorageType.php | 1 + .../Providers/Storage/FileStorage.php | 9 +- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php index 723c367a8..f5f61fd82 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/UserAuth.php @@ -209,35 +209,6 @@ trait UserAuth return Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY, ''); } - public function GetAccountFromSignMeToken(): ?Account - { - $oAccount = null; - - $sSignMeToken = Utils::GetCookie(self::AUTH_SIGN_ME_TOKEN_KEY, ''); - if (!empty($sSignMeToken)) { - $aTokenData = Utils::DecodeKeyValuesQ($sSignMeToken); - if (!empty($aTokenData['e']) && !empty($aTokenData['t'])) { - $sTokenSettings = $this->StorageProvider()->Get($aTokenData['e'], - StorageType::CONFIG, - 'sign_me' - ); - - if (!empty($sTokenSettings)) { - $aSignMeData = Utils::DecodeKeyValuesQ($sTokenSettings); - if (!empty($aSignMeData['AuthToken']) && - !empty($aSignMeData['SignMeToken']) && - $aSignMeData['SignMeToken'] === $aTokenData['t']) { - $oAccount = $this->GetAccountFromCustomToken($aSignMeData['AuthToken'], false, false, true); - } - } - } - } else { - Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); - } - - return $oAccount; - } - // rlspecauth / AuthAccountHash public function getAuthAccountHash() : string { @@ -306,34 +277,89 @@ trait UserAuth $this->SetSpecAuthToken($sSpecAuthToken); Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken); - if ($oAccount->SignMe() && \strlen($oAccount->SignMeToken())) { - Utils::SetCookie(self::AUTH_SIGN_ME_TOKEN_KEY, - Utils::EncodeKeyValuesQ(array( - 'e' => $oAccount->Email(), - 't' => $oAccount->SignMeToken() - )), - \time() + 60 * 60 * 24 * 30); - - $this->StorageProvider()->Put($oAccount, - StorageType::CONFIG, - 'sign_me', - Utils::EncodeKeyValuesQ(array( - 'Time' => \time(), - 'AuthToken' => $oAccount->GetAuthTokenQ(), - 'SignMeToken' => $oAccount->SignMeToken() - )) - ); + if ($oAccount->SignMe()) { + $this->SetSignMeToken($oAccount); } } + private static function GetSignMeToken(): ?array + { + $sSignMeToken = Utils::GetCookie(self::AUTH_SIGN_ME_TOKEN_KEY, ''); + return empty($sSignMeToken) ? null : Utils::DecodeKeyValuesQ($sSignMeToken); + } + + private function SetSignMeToken(Account $oAccount): void + { + $this->ClearSignMeData($oAccount); + + $uuid = \SnappyMail\UUID::generate(); + Utils::SetCookie(self::AUTH_SIGN_ME_TOKEN_KEY, + Utils::EncodeKeyValuesQ(array( + 'e' => $oAccount->Email(), + 'u' => $uuid + )), + \time() + 3600 * 24 * 30); // 30 days + + $this->StorageProvider()->Put($oAccount, + StorageType::SIGN_ME, + $uuid, + Utils::EncryptString($oAccount->GetAuthToken(), \sha1(APP_SALT . $uuid)) + ); + } + + public function GetAccountFromSignMeToken(): ?Account + { + $oAccount = null; + + $aTokenData = static::GetSignMeToken(); + if (!empty($aTokenData)) { + if (!empty($aTokenData['e']) && !empty($aTokenData['u']) && \SnappyMail\UUID::isValid($aTokenData['u'])) { + $sAuthToken = $this->StorageProvider()->Get($aTokenData['e'], + StorageType::SIGN_ME, + $aTokenData['u'] + ); + if (empty($sAuthToken)) { + return null; + } + $sAuthToken = Utils::DecryptString($sAuthToken, \sha1(APP_SALT . $aTokenData['u'])); + if (!empty($sAuthToken)) { + $oAccount = $this->GetAccountFromCustomToken($sAuthToken, false, false, true); + } + } else if (!empty($aTokenData['e']) && !empty($aTokenData['t'])) { + // This is old, see https://github.com/the-djmaze/snappymail/issues/126 + $sTokenSettings = $this->StorageProvider()->Get($aTokenData['e'], + StorageType::CONFIG, + 'sign_me' + ); + if (!empty($sTokenSettings)) { + $aSignMeData = Utils::DecodeKeyValuesQ($sTokenSettings); + if (!empty($aSignMeData['AuthToken']) && + !empty($aSignMeData['SignMeToken']) && + $aSignMeData['SignMeToken'] === $aTokenData['t']) { + $oAccount = $this->GetAccountFromCustomToken($aSignMeData['AuthToken'], false, false, true); + } + } + } + if ($oAccount) { + // Update lifetime + $this->SetSignMeToken($oAccount); + } + } else { + Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); + } + + return $oAccount; + } + protected function ClearSignMeData(Account $oAccount) : void { if ($oAccount) { + $aTokenData = static::GetSignMeToken(); + if (!empty($aTokenData['u']) && \SnappyMail\UUID::isValid($aTokenData['u'])) { + $this->StorageProvider()->Clear($oAccount, StorageType::SIGN_ME, $aTokenData['u']); + } Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY); - $this->StorageProvider()->Clear($oAccount, - StorageType::CONFIG, - 'sign_me' - ); + $this->StorageProvider()->Clear($oAccount, StorageType::CONFIG, 'sign_me'); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/Enumerations/StorageType.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/Enumerations/StorageType.php index f7173f972..faae86558 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/Enumerations/StorageType.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/Enumerations/StorageType.php @@ -7,4 +7,5 @@ class StorageType const USER = 1; const CONFIG = 2; const NOBODY = 3; + const SIGN_ME = 4; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/FileStorage.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/FileStorage.php index 5ab03d8f3..9496f7842 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/FileStorage.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Storage/FileStorage.php @@ -85,7 +85,7 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage */ public function DeleteStorage($oAccount) : bool { - $sPath = $this->generateFileName($oAccount, StorageType::USER, '', false, true); + $sPath = $this->generateFileName($oAccount, StorageType::CONFIG, '', false, true); if ($sPath && \is_dir($sPath)) { \MailSo\Base\Utils::RecRmDir($sPath); } @@ -141,6 +141,8 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage case StorageType::NOBODY: $sFilePath = $this->sDataPath.'/__nobody__/'.\sha1($sKey ?: \time()); break; + case StorageType::SIGN_ME: + $sSubEmail = '.sign_me'; case StorageType::CONFIG: if (empty($sEmail)) { return ''; @@ -165,6 +167,11 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage } } + // CleanupSignMeData + if (StorageType::SIGN_ME === $iStorageType && $sKey && 0 === \random_int(0, 25) && \is_dir($sFilePath)) { + \MailSo\Base\Utils::RecTimeDirRemove(\is_dir($sFilePath), 3600 * 24 * 30); // 30 days + } + return $sFilePath; }