mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved account login/switch handling
This commit is contained in:
parent
3af8652175
commit
abe2af153d
7 changed files with 45 additions and 101 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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] ?: '',
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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])) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue