Improved account login/switch handling

This commit is contained in:
djmaze 2021-12-14 12:42:41 +01:00
parent 3af8652175
commit abe2af153d
7 changed files with 45 additions and 101 deletions

View file

@ -79,7 +79,7 @@ trait Accounts
throw new ClientException(Notifications::AccountDoesNotExist); throw new ClientException(Notifications::AccountDoesNotExist);
} }
$oNewAccount = $this->LoginProcess($sEmail, $sPassword, false, $oMainAccount); $oNewAccount = $this->LoginProcess($sEmail, $sPassword, false, false);
$aAccounts[$oNewAccount->Email()] = $oNewAccount->asTokenArray($oMainAccount); $aAccounts[$oNewAccount->Email()] = $oNewAccount->asTokenArray($oMainAccount);
$this->SetAccounts($oMainAccount, $aAccounts); $this->SetAccounts($oMainAccount, $aAccounts);

View file

@ -200,14 +200,9 @@ trait User
public function DoLogout() : array public function DoLogout() : array
{ {
$oAccount = $this->getAccountFromToken(false); $bMain = empty($_COOKIE[self::AUTH_ADDITIONAL_TOKEN_KEY]);
if ($oAccount) { $this->Logout($bMain);
if ($oAccount instanceof \RainLoop\Model\MainAccount) { $bMain && $this->ClearSignMeData();
$this->ClearSignMeData();
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
}
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
}
return $this->TrueResponse(__FUNCTION__); return $this->TrueResponse(__FUNCTION__);
} }

View file

@ -21,7 +21,7 @@ trait UserAuth
/** /**
* @throws \RainLoop\Exceptions\ClientException * @throws \RainLoop\Exceptions\ClientException
*/ */
public function LoginProcess(string &$sEmail, string &$sPassword, bool $bSignMe = false, Account $oMainAccount = null): Account public function LoginProcess(string &$sEmail, string &$sPassword, bool $bSignMe = false, bool $bMainAccount = true): Account
{ {
$sInputEmail = $sEmail; $sInputEmail = $sEmail;
@ -114,10 +114,10 @@ trait UserAuth
$oAccount = null; $oAccount = null;
$sClientCert = \trim($this->Config()->Get('ssl', 'client_cert', '')); $sClientCert = \trim($this->Config()->Get('ssl', 'client_cert', ''));
try { try {
if ($oMainAccount) { if ($bMainAccount) {
$oAccount = AdditionalAccount::NewInstanceByLogin($this, $sEmail, $sLogin, $sPassword, $sClientCert, true); $oAccount = MainAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, $sClientCert, true);
} else { } else {
$oAccount = MainAccount::NewInstanceByLogin($this, $sEmail, $sLogin, $sPassword, $sClientCert, true); $oAccount = AdditionalAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, $sClientCert, true);
} }
if (!$oAccount) { if (!$oAccount) {
@ -131,7 +131,7 @@ trait UserAuth
try { try {
$this->CheckMailConnection($oAccount, true); $this->CheckMailConnection($oAccount, true);
if (!$oMainAccount) { if ($bMainAccount) {
$bSignMe && $this->SetSignMeToken($oAccount); $bSignMe && $this->SetSignMeToken($oAccount);
$this->StorageProvider()->Put($oAccount, StorageType::SESSION, Utils::GetSessionToken(), 'true'); $this->StorageProvider()->Put($oAccount, StorageType::SESSION, Utils::GetSessionToken(), 'true');
} }
@ -144,6 +144,18 @@ trait UserAuth
return $oAccount; return $oAccount;
} }
private static function SetAccountCookie(string $sName, ?Account $oAccount)
{
if ($oAccount) {
Utils::SetCookie(
$sName,
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($oAccount))
);
} else {
Utils::ClearCookie($sName);
}
}
public function switchAccount(string $sEmail) : bool public function switchAccount(string $sEmail) : bool
{ {
$this->Http()->ServerNoCache(); $this->Http()->ServerNoCache();
@ -165,6 +177,9 @@ trait UserAuth
if (!$oAccountToLogin) { if (!$oAccountToLogin) {
throw new ClientException(Notifications::AccountSwitchFailed); throw new ClientException(Notifications::AccountSwitchFailed);
} }
// $this->CheckMailConnection($oAccountToLogin);
$this->SetAdditionalAuthToken($oAccountToLogin); $this->SetAdditionalAuthToken($oAccountToLogin);
return true; return true;
} }
@ -233,12 +248,10 @@ trait UserAuth
} else { } else {
$oMainAuthAccount && $this->StorageProvider()->Clear($oMainAuthAccount, StorageType::SESSION, $sToken); $oMainAuthAccount && $this->StorageProvider()->Clear($oMainAuthAccount, StorageType::SESSION, $sToken);
Utils::ClearCookie(Utils::SESSION_TOKEN); Utils::ClearCookie(Utils::SESSION_TOKEN);
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); $this->Logout(true);
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
} }
} else { } else {
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY); $this->Logout(true);
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
} }
} else { } else {
$oAccount = $this->GetAccountFromSignMeToken(); $oAccount = $this->GetAccountFromSignMeToken();
@ -252,7 +265,7 @@ trait UserAuth
} }
if ($this->oMainAuthAccount) { if ($this->oMainAuthAccount) {
$this->StorageProvider()->Put($this->oMainAuthAccount, StorageType::SESSION, $sToken, 'true'); $this->StorageProvider()->Put($this->oMainAuthAccount, StorageType::SESSION, Utils::GetSessionToken(), 'true');
} }
} }
@ -263,17 +276,13 @@ trait UserAuth
{ {
$this->oAdditionalAuthAccount = false; $this->oAdditionalAuthAccount = false;
$this->oMainAuthAccount = $oAccount; $this->oMainAuthAccount = $oAccount;
Utils::SetSecureCookie(self::AUTH_SPEC_TOKEN_KEY, $oAccount); static::SetAccountCookie(self::AUTH_SPEC_TOKEN_KEY, $oAccount);
} }
public function SetAdditionalAuthToken(?AdditionalAccount $oAccount): void public function SetAdditionalAuthToken(?AdditionalAccount $oAccount): void
{ {
$this->oAdditionalAuthAccount = $oAccount ?: false; $this->oAdditionalAuthAccount = $oAccount ?: false;
if ($oAccount) { static::SetAccountCookie(self::AUTH_ADDITIONAL_TOKEN_KEY, $oAccount);
Utils::SetSecureCookie(self::AUTH_ADDITIONAL_TOKEN_KEY, $oAccount);
} else {
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
}
} }
/** /**
@ -288,7 +297,6 @@ trait UserAuth
if (isset($aResult['e'], $aResult['u']) && \SnappyMail\UUID::isValid($aResult['u'])) { if (isset($aResult['e'], $aResult['u']) && \SnappyMail\UUID::isValid($aResult['u'])) {
return $aResult; return $aResult;
} }
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
} }
return null; return null;
} }
@ -350,10 +358,10 @@ trait UserAuth
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
} }
$this->ClearSignMeData();
} }
$this->ClearSignMeData();
return null; return null;
} }
@ -362,8 +370,8 @@ trait UserAuth
$aTokenData = static::GetSignMeToken(); $aTokenData = static::GetSignMeToken();
if ($aTokenData) { if ($aTokenData) {
$this->StorageProvider()->Clear($aTokenData['e'], StorageType::SIGN_ME, $aTokenData['u']); $this->StorageProvider()->Clear($aTokenData['e'], StorageType::SIGN_ME, $aTokenData['u']);
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
} }
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
} }
/** /**
@ -391,6 +399,12 @@ trait UserAuth
Utils::SetCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY, $sMessage); Utils::SetCookie(self::AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY, $sMessage);
} }
protected function Logout(bool $bMain) : void
{
Utils::ClearCookie(self::AUTH_ADDITIONAL_TOKEN_KEY);
$bMain && Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
}
/** /**
* @throws \RainLoop\Exceptions\ClientException * @throws \RainLoop\Exceptions\ClientException
*/ */

View file

@ -143,20 +143,7 @@ abstract class Account implements \JsonSerializable
); );
} }
/** public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
* @throws \RainLoop\Exceptions\ClientException
*/
public static function NewInstanceFromAuthToken(\RainLoop\Actions $oActions, string $sToken): ?self
{
return empty($sToken) ? null
: static::NewInstanceFromTokenArray(
$oActions,
Utils::DecodeKeyValues($sToken),
false
);
}
public static function NewInstanceByLogin(\RainLoop\Actions $oActions,
string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '',
bool $bThrowException = false): ?self bool $bThrowException = false): ?self
{ {
@ -176,7 +163,7 @@ abstract class Account implements \JsonSerializable
$oActions->Plugins()->RunHook('filter.account', array($oAccount)); $oActions->Plugins()->RunHook('filter.account', array($oAccount));
if ($bThrowException && !$oAccount) { if ($bThrowException && !$oAccount) {
throw new ClientException(Notifications::AuthError); throw new ClientException(Notifications::AccountFilterError);
} }
} else if ($bThrowException) { } else if ($bThrowException) {
throw new ClientException(Notifications::AccountNotAllowed); throw new ClientException(Notifications::AccountNotAllowed);
@ -195,7 +182,7 @@ abstract class Account implements \JsonSerializable
bool $bThrowExceptionOnFalse = false): ?self bool $bThrowExceptionOnFalse = false): ?self
{ {
if (!empty($aAccountHash[0]) && 'account' === $aAccountHash[0] && 7 <= \count($aAccountHash)) { if (!empty($aAccountHash[0]) && 'account' === $aAccountHash[0] && 7 <= \count($aAccountHash)) {
$oAccount = static::NewInstanceByLogin( $oAccount = static::NewInstanceFromCredentials(
$oActions, $oActions,
$aAccountHash[1] ?: '', $aAccountHash[1] ?: '',
$aAccountHash[2] ?: '', $aAccountHash[2] ?: '',

View file

@ -47,6 +47,7 @@ class Notifications
const AccountAlreadyExists = 801; const AccountAlreadyExists = 801;
const AccountDoesNotExist = 802; const AccountDoesNotExist = 802;
const AccountSwitchFailed = 803; const AccountSwitchFailed = 803;
const AccountFilterError = 804;
const MailServerError = 901; const MailServerError = 901;
const ClientViewError = 902; const ClientViewError = 902;

View file

@ -717,7 +717,7 @@ class ServiceActions
$this->oActions->SetAuthToken($oAccount); $this->oActions->SetAuthToken($oAccount);
$bLogout = !($oAccount instanceof Model\Account); $bLogout = !($oAccount instanceof Model\MainAccount);
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
@ -751,7 +751,7 @@ class ServiceActions
{ {
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword); $oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
$this->oActions->SetAuthToken($oAccount); $this->oActions->SetAuthToken($oAccount);
$bLogout = !($oAccount instanceof Model\Account); $bLogout = !($oAccount instanceof Model\MainAccount);
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
@ -768,49 +768,6 @@ class ServiceActions
return ''; return '';
} }
public function ServiceExternalLogin() : string
{
$this->oHttp->ServerNoCache();
$oException = null;
$oAccount = null;
$bLogout = true;
switch (\strtolower($_REQUEST['Output'] ?? 'Redirect'))
{
case 'json':
\header('Content-Type: application/json; charset=utf-8');
$aResult = array(
'Action' => 'ExternalLogin',
'Result' => $oAccount instanceof Model\Account ? true : false,
'ErrorCode' => 0
);
if (!$aResult['Result'])
{
if ($oException instanceof Exceptions\ClientException)
{
$aResult['ErrorCode'] = $oException->getCode();
}
else
{
$aResult['ErrorCode'] = Notifications::AuthError;
}
}
return \MailSo\Base\Utils::Php2js($aResult, $this->Logger());
case 'redirect':
default:
$this->oActions->Location('./');
break;
}
return '';
}
/** /**
* @return mixed * @return mixed
*/ */

View file

@ -77,7 +77,7 @@ class Utils
$sToken = static::GetCookie(self::SESSION_TOKEN, null); $sToken = static::GetCookie(self::SESSION_TOKEN, null);
if (!$sToken) { if (!$sToken) {
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT); $sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
static::SetCookie(self::SESSION_TOKEN, $sToken, 0); static::SetCookie(self::SESSION_TOKEN, $sToken);
} }
return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT); return \sha1('Session'.APP_SALT.$sToken.'Token'.APP_SALT);
@ -149,16 +149,6 @@ class Utils
)); ));
} }
public static function SetSecureCookie(string $sName, $mValue, int $iExpire = 0, bool $bHttpOnly = true)
{
static::SetCookie(
$sName,
\MailSo\Base\Utils::UrlSafeBase64Encode(\SnappyMail\Crypt::EncryptToJSON($mValue)),
$iExpire,
true
);
}
public static function ClearCookie(string $sName) public static function ClearCookie(string $sName)
{ {
if (isset($_COOKIE[$sName])) { if (isset($_COOKIE[$sName])) {