mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved user authentication system by supporting OpenSSL aes-256-cbc-hmac-sha1 encryption
This commit is contained in:
parent
99c72ad9e6
commit
8ca043b6e4
16 changed files with 468 additions and 450 deletions
|
|
@ -18,30 +18,22 @@ namespace MailSo\Base;
|
||||||
class Crypt
|
class Crypt
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function Encrypt(string $sString, string $sKey, string $sCipher = '') : string
|
public static function Encrypt(string $sString, string $sKey) : string
|
||||||
{
|
{
|
||||||
if (!\strlen($sString)) {
|
if (!\strlen($sString)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if ($sCipher && \is_callable('openssl_encrypt')) {
|
|
||||||
$iv = \str_pad('', \openssl_cipher_iv_length($sCipher), \sha1($sKey));
|
|
||||||
return \openssl_encrypt($sString, $sCipher, $sKey, OPENSSL_RAW_DATA, $iv);
|
|
||||||
}
|
|
||||||
if (\is_callable('xxtea_encrypt')) {
|
if (\is_callable('xxtea_encrypt')) {
|
||||||
return \xxtea_encrypt($sString, $sKey);
|
return \xxtea_encrypt($sString, $sKey);
|
||||||
}
|
}
|
||||||
return Xxtea::encrypt($sString, $sKey);
|
return Xxtea::encrypt($sString, $sKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Decrypt(string $sString, string $sKey, string $sCipher = '') : string
|
public static function Decrypt(string $sString, string $sKey) : string
|
||||||
{
|
{
|
||||||
if (!\strlen($sString)) {
|
if (!\strlen($sString)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if ($sCipher && \is_callable('openssl_encrypt')) {
|
|
||||||
$iv = \str_pad('', \openssl_cipher_iv_length($sCipher), \sha1($sKey));
|
|
||||||
return \openssl_decrypt($sString, $sCipher, $sKey, OPENSSL_RAW_DATA, $iv);
|
|
||||||
}
|
|
||||||
if (\is_callable('xxtea_decrypt')) {
|
if (\is_callable('xxtea_decrypt')) {
|
||||||
return \xxtea_decrypt($sString, $sKey);
|
return \xxtea_decrypt($sString, $sKey);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,20 @@ class Actions
|
||||||
use Actions\Themes;
|
use Actions\Themes;
|
||||||
|
|
||||||
const AUTH_MAILTO_TOKEN_KEY = 'smmailtoauth';
|
const AUTH_MAILTO_TOKEN_KEY = 'smmailtoauth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This 30 days cookie contains decrypt data,
|
||||||
|
* to decrypt a \RainLoop\Model\Account which is stored at
|
||||||
|
* /_data_/.../storage/DOMAIN/LOCAL/.sign_me/*
|
||||||
|
* Gets refreshed on each login
|
||||||
|
*/
|
||||||
const AUTH_SIGN_ME_TOKEN_KEY = 'smremember';
|
const AUTH_SIGN_ME_TOKEN_KEY = 'smremember';
|
||||||
const AUTH_SPEC_TOKEN_KEY = 'smspecauth';
|
|
||||||
|
/**
|
||||||
|
* This session cookie contains a \RainLoop\Model\Account
|
||||||
|
*/
|
||||||
|
const AUTH_SPEC_TOKEN_KEY = 'smaccount';
|
||||||
|
|
||||||
const AUTH_SPEC_LOGOUT_TOKEN_KEY = 'smspeclogout';
|
const AUTH_SPEC_LOGOUT_TOKEN_KEY = 'smspeclogout';
|
||||||
const AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY = 'smspeclogoutcmk';
|
const AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY = 'smspeclogoutcmk';
|
||||||
|
|
||||||
|
|
@ -130,7 +142,6 @@ class Actions
|
||||||
$this->oAddressBookProvider = null;
|
$this->oAddressBookProvider = null;
|
||||||
$this->oSuggestionsProvider = null;
|
$this->oSuggestionsProvider = null;
|
||||||
|
|
||||||
$this->sSpecAuthToken = '';
|
|
||||||
$this->bIsJson = false;
|
$this->bIsJson = false;
|
||||||
|
|
||||||
$oConfig = $this->Config();
|
$oConfig = $this->Config();
|
||||||
|
|
@ -309,7 +320,6 @@ class Actions
|
||||||
|
|
||||||
if (false !== \strpos($sLine, '{imap:') || false !== \strpos($sLine, '{smtp:')) {
|
if (false !== \strpos($sLine, '{imap:') || false !== \strpos($sLine, '{smtp:')) {
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
$this->getAuthAccountHash();
|
|
||||||
$oAccount = $this->getAccountFromToken(false);
|
$oAccount = $this->getAccountFromToken(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,7 +373,6 @@ class Actions
|
||||||
|
|
||||||
if (\preg_match('/\{user:(email|login|domain)\}/i', $sLine)) {
|
if (\preg_match('/\{user:(email|login|domain)\}/i', $sLine)) {
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
$this->getAuthAccountHash();
|
|
||||||
$oAccount = $this->getAccountFromToken(false);
|
$oAccount = $this->getAccountFromToken(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -871,7 +880,8 @@ class Actions
|
||||||
$aResult['IncLogin'] = $oAccount->IncLogin();
|
$aResult['IncLogin'] = $oAccount->IncLogin();
|
||||||
$aResult['OutLogin'] = $oAccount->OutLogin();
|
$aResult['OutLogin'] = $oAccount->OutLogin();
|
||||||
$aResult['AccountHash'] = $oAccount->Hash();
|
$aResult['AccountHash'] = $oAccount->Hash();
|
||||||
$aResult['AccountSignMe'] = $oAccount->SignMe();
|
// $aResult['AccountSignMe'] = $oAccount->SignMe();
|
||||||
|
$aResult['AccountSignMe'] = false;
|
||||||
$aResult['ContactsIsAllowed'] = $oAddressBookProvider->IsActive();
|
$aResult['ContactsIsAllowed'] = $oAddressBookProvider->IsActive();
|
||||||
$aResult['ContactsSyncIsAllowed'] = (bool)$oConfig->Get('contacts', 'allow_sync', false);
|
$aResult['ContactsSyncIsAllowed'] = (bool)$oConfig->Get('contacts', 'allow_sync', false);
|
||||||
$aResult['ContactsSyncInterval'] = (int)$oConfig->Get('contacts', 'sync_interval', 20);
|
$aResult['ContactsSyncInterval'] = (int)$oConfig->Get('contacts', 'sync_interval', 20);
|
||||||
|
|
@ -1066,17 +1076,6 @@ class Actions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AuthToken(Model\Account $oAccount): void
|
|
||||||
{
|
|
||||||
$this->SetAuthToken($oAccount);
|
|
||||||
|
|
||||||
$aAccounts = $this->GetAccounts($oAccount);
|
|
||||||
if (isset($aAccounts[$oAccount->Email()])) {
|
|
||||||
$aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken();
|
|
||||||
$this->SetAccounts($oAccount, $aAccounts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
|
|
@ -1202,7 +1201,7 @@ class Actions
|
||||||
{
|
{
|
||||||
$iResult = 0;
|
$iResult = 0;
|
||||||
|
|
||||||
$oAccount = $this->GetAccountFromCustomToken($sHash, false);
|
$oAccount = $this->GetAccountFromCustomToken($sHash);
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
try {
|
try {
|
||||||
$oMailClient = new \MailSo\Mail\MailClient();
|
$oMailClient = new \MailSo\Mail\MailClient();
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,9 @@ trait Accounts
|
||||||
|
|
||||||
$oAccountToChange = null;
|
$oAccountToChange = null;
|
||||||
if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) {
|
if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) {
|
||||||
$oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false, false);
|
$oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false);
|
||||||
if ($oAccountToChange) {
|
if ($oAccountToChange) {
|
||||||
$this->AuthToken($oAccountToChange);
|
$this->SetAuthToken($oAccountToChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -707,15 +707,12 @@ trait Messages
|
||||||
$this->Plugins()->RunHook('filter.smtp-hidden-rcpt', array($oAccount, $oMessage, &$aHiddenRcpt));
|
$this->Plugins()->RunHook('filter.smtp-hidden-rcpt', array($oAccount, $oMessage, &$aHiddenRcpt));
|
||||||
}
|
}
|
||||||
|
|
||||||
$bUsePhpMail = $oAccount->Domain()->OutUsePhpMail();
|
|
||||||
|
|
||||||
$oSmtpClient = new \MailSo\Smtp\SmtpClient();
|
$oSmtpClient = new \MailSo\Smtp\SmtpClient();
|
||||||
$oSmtpClient->SetLogger($this->Logger());
|
$oSmtpClient->SetLogger($this->Logger());
|
||||||
$oSmtpClient->SetTimeOuts(10, (int) \RainLoop\Api::Config()->Get('labs', 'smtp_timeout', 60));
|
$oSmtpClient->SetTimeOuts(10, (int) \RainLoop\Api::Config()->Get('labs', 'smtp_timeout', 60));
|
||||||
|
|
||||||
$oAccount->OutConnectAndLoginHelper(
|
$bUsePhpMail = false;
|
||||||
$this->Plugins(), $oSmtpClient, $this->Config(), $bUsePhpMail
|
$oAccount->OutConnectAndLoginHelper($this->Plugins(), $oSmtpClient, $this->Config(), $bUsePhpMail);
|
||||||
);
|
|
||||||
|
|
||||||
if ($bUsePhpMail)
|
if ($bUsePhpMail)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,24 +23,16 @@ trait User
|
||||||
$sEmail = \MailSo\Base\Utils::Trim($this->GetActionParam('Email', ''));
|
$sEmail = \MailSo\Base\Utils::Trim($this->GetActionParam('Email', ''));
|
||||||
$sPassword = $this->GetActionParam('Password', '');
|
$sPassword = $this->GetActionParam('Password', '');
|
||||||
$sLanguage = $this->GetActionParam('Language', '');
|
$sLanguage = $this->GetActionParam('Language', '');
|
||||||
$bSignMe = '1' === (string) $this->GetActionParam('SignMe', '0');
|
$bSignMe = !empty($this->GetActionParam('SignMe', 0));
|
||||||
|
|
||||||
$oAccount = null;
|
$oAccount = null;
|
||||||
|
|
||||||
$this->Logger()->AddSecret($sPassword);
|
$this->Logger()->AddSecret($sPassword);
|
||||||
|
|
||||||
try
|
$oAccount = $this->LoginProcess($sEmail, $sPassword, $bSignMe);
|
||||||
{
|
|
||||||
$oAccount = $this->LoginProcess($sEmail, $sPassword,
|
|
||||||
$bSignMe ? $this->generateSignMeToken($sEmail) : '');
|
|
||||||
$this->Plugins()->RunHook('login.success', array($oAccount));
|
$this->Plugins()->RunHook('login.success', array($oAccount));
|
||||||
}
|
|
||||||
catch (ClientException $oException)
|
|
||||||
{
|
|
||||||
throw $oException;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->AuthToken($oAccount);
|
$this->SetAuthToken($oAccount);
|
||||||
|
|
||||||
if ($oAccount && \strlen($sLanguage))
|
if ($oAccount && \strlen($sLanguage))
|
||||||
{
|
{
|
||||||
|
|
@ -211,13 +203,9 @@ trait User
|
||||||
$oAccount = $this->getAccountFromToken(false);
|
$oAccount = $this->getAccountFromToken(false);
|
||||||
if ($oAccount)
|
if ($oAccount)
|
||||||
{
|
{
|
||||||
if ($oAccount->SignMe())
|
|
||||||
{
|
|
||||||
$this->ClearSignMeData($oAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$oAccount->IsAdditionalAccount())
|
if (!$oAccount->IsAdditionalAccount())
|
||||||
{
|
{
|
||||||
|
$this->ClearSignMeData();
|
||||||
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -515,11 +503,6 @@ trait User
|
||||||
$this->SettingsProvider()->Save($oAccount, $oSettings) : false);
|
$this->SettingsProvider()->Save($oAccount, $oSettings) : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateSignMeToken(string $sEmail) : string
|
|
||||||
{
|
|
||||||
return \MailSo\Base\Utils::Sha1Rand(APP_SALT.$sEmail);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array
|
private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array
|
||||||
{
|
{
|
||||||
$aValues = $this->getDecodedRawKeyValue($sHash);
|
$aValues = $this->getDecodedRawKeyValue($sHash);
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,20 @@ use \RainLoop\Exceptions\ClientException;
|
||||||
|
|
||||||
trait UserAuth
|
trait UserAuth
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
const AUTH_SIGN_ME_TOKEN_KEY = 'smremember';
|
|
||||||
const AUTH_SPEC_TOKEN_KEY = 'smspecauth';
|
|
||||||
const AUTH_SPEC_LOGOUT_TOKEN_KEY = 'smspeclogout';
|
|
||||||
const AUTH_SPEC_LOGOUT_CUSTOM_MSG_KEY = 'smspeclogoutcmk';
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $sSpecAuthToken;
|
private $sSpecAuthToken = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Or use 'aes-256-xts' ?
|
||||||
|
*/
|
||||||
|
private static $sCipher = 'aes-256-cbc-hmac-sha1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function LoginProcess(string &$sEmail, string &$sPassword, string $sSignMeToken = ''): Account
|
public function LoginProcess(string &$sEmail, string &$sPassword, bool $bSignMe = false): Account
|
||||||
{
|
{
|
||||||
$sInputEmail = $sEmail;
|
$sInputEmail = $sEmail;
|
||||||
|
|
||||||
|
|
@ -117,7 +116,7 @@ trait UserAuth
|
||||||
$oAccount = null;
|
$oAccount = null;
|
||||||
$sClientCert = \trim($this->Config()->Get('ssl', 'client_cert', ''));
|
$sClientCert = \trim($this->Config()->Get('ssl', 'client_cert', ''));
|
||||||
try {
|
try {
|
||||||
$oAccount = $this->LoginProvide($sEmail, $sLogin, $sPassword, $sSignMeToken, $sClientCert, true);
|
$oAccount = Account::NewInstanceByLogin($this, $sEmail, $sLogin, $sPassword, $sClientCert, true);
|
||||||
|
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
throw new ClientException(Notifications::AuthError);
|
throw new ClientException(Notifications::AuthError);
|
||||||
|
|
@ -130,6 +129,7 @@ trait UserAuth
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->CheckMailConnection($oAccount, true);
|
$this->CheckMailConnection($oAccount, true);
|
||||||
|
$bSignMe && $this->SetSignMeToken($oAccount);
|
||||||
} catch (\Throwable $oException) {
|
} catch (\Throwable $oException) {
|
||||||
$this->loginErrorDelay();
|
$this->loginErrorDelay();
|
||||||
|
|
||||||
|
|
@ -142,107 +142,27 @@ trait UserAuth
|
||||||
/**
|
/**
|
||||||
* @throws \RainLoop\Exceptions\ClientException
|
* @throws \RainLoop\Exceptions\ClientException
|
||||||
*/
|
*/
|
||||||
public function GetAccountFromCustomToken(string $sToken, bool $bThrowExceptionOnFalse = true, bool $bValidateShortToken = true, bool $bQ = false): ?Account
|
public function GetAccountFromCustomToken(string $sToken, bool $bValidateShortToken = true, bool $bQ = false, bool $bThrowExceptionOnFalse = false): ?Account
|
||||||
{
|
{
|
||||||
$oResult = null;
|
|
||||||
if (!empty($sToken)) {
|
if (!empty($sToken)) {
|
||||||
$aAccountHash = $bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken);
|
$aToken = $bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken);
|
||||||
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0] && // simple token validation
|
return Account::NewInstanceFromTokenArray(
|
||||||
8 <= \count($aAccountHash) && // length checking
|
$this,
|
||||||
!empty($aAccountHash[7]) && // does short token exist
|
$bQ ? Utils::DecodeKeyValuesQ($sToken) : Utils::DecodeKeyValues($sToken),
|
||||||
(!$bValidateShortToken || Utils::GetShortToken() === $aAccountHash[7] || // check short token if needed
|
$bThrowExceptionOnFalse
|
||||||
(isset($aAccountHash[10]) && 0 < $aAccountHash[10] && \time() < $aAccountHash[10]))
|
);
|
||||||
) {
|
|
||||||
$oAccount = $this->LoginProvide($aAccountHash[1], $aAccountHash[2], $aAccountHash[3],
|
|
||||||
empty($aAccountHash[5]) ? '' : $aAccountHash[5], empty($aAccountHash[11]) ? '' : $aAccountHash[11], $bThrowExceptionOnFalse);
|
|
||||||
|
|
||||||
if ($oAccount) {
|
|
||||||
// init proxy user/password
|
|
||||||
if (!empty($aAccountHash[8]) && !empty($aAccountHash[9])) {
|
|
||||||
$oAccount->SetProxyAuthUser($aAccountHash[8]);
|
|
||||||
$oAccount->SetProxyAuthPassword($aAccountHash[9]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->Logger()->AddSecret($oAccount->Password());
|
if ($bThrowExceptionOnFalse) {
|
||||||
$this->Logger()->AddSecret($oAccount->ProxyAuthPassword());
|
throw new ClientException(\RainLoop\Notifications::AuthError);
|
||||||
|
|
||||||
$oAccount->SetParentEmail($aAccountHash[6]);
|
|
||||||
$oResult = $oAccount;
|
|
||||||
}
|
|
||||||
} else if ($bThrowExceptionOnFalse) {
|
|
||||||
throw new ClientException(Notifications::AuthError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bThrowExceptionOnFalse && !$oResult) {
|
return null;
|
||||||
throw new ClientException(Notifications::AuthError);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function LoginProvide(string $sEmail, string $sLogin, string $sPassword, string $sSignMeToken = '', string $sClientCert = '', bool $bThrowProvideException = false): ?Account
|
|
||||||
{
|
|
||||||
$oAccount = null;
|
|
||||||
if (\strlen($sEmail) && \strlen($sLogin) && \strlen($sPassword)) {
|
|
||||||
$oDomain = $this->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true);
|
|
||||||
if ($oDomain) {
|
|
||||||
if ($oDomain->ValidateWhiteList($sEmail, $sLogin)) {
|
|
||||||
$oAccount = new Account($sEmail, $sLogin, $sPassword, $oDomain, $sSignMeToken, '', '', $sClientCert);
|
|
||||||
$this->Plugins()->RunHook('filter.account', array($oAccount));
|
|
||||||
|
|
||||||
if ($bThrowProvideException && !$oAccount) {
|
|
||||||
throw new ClientException(Notifications::AuthError);
|
|
||||||
}
|
|
||||||
} else if ($bThrowProvideException) {
|
|
||||||
throw new ClientException(Notifications::AccountNotAllowed);
|
|
||||||
}
|
|
||||||
} else if ($bThrowProvideException) {
|
|
||||||
throw new ClientException(Notifications::DomainNotAllowed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSpecAuthTokenCookie(): string
|
|
||||||
{
|
|
||||||
return Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// rlspecauth / AuthAccountHash
|
|
||||||
public function getAuthAccountHash() : string
|
|
||||||
{
|
|
||||||
if ('' === $this->sSpecAuthToken && !\strlen($this->GetSpecAuthLogoutTokenWithDeletion())) {
|
|
||||||
$sAuthAccountHash = $this->GetSpecAuthTokenCookie() ?: $this->GetSpecAuthToken();
|
|
||||||
if (empty($sAuthAccountHash)) {
|
|
||||||
$oAccount = $this->GetAccountFromSignMeToken();
|
|
||||||
if ($oAccount) try
|
|
||||||
{
|
|
||||||
$this->CheckMailConnection($oAccount);
|
|
||||||
$this->AuthToken($oAccount);
|
|
||||||
$sAuthAccountHash = $this->GetSpecAuthToken();
|
|
||||||
}
|
|
||||||
catch (\Throwable $oException)
|
|
||||||
{
|
|
||||||
$oException = null;
|
|
||||||
$this->ClearSignMeData($oAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->SetSpecAuthToken($sAuthAccountHash);
|
|
||||||
}
|
|
||||||
return $this->GetSpecAuthToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getLocalAuthToken(): string
|
|
||||||
{
|
|
||||||
$sToken = $this->GetSpecAuthToken();
|
|
||||||
return !empty($sToken) && '_' === \substr($sToken, 0, 1) ? \substr($sToken, 1) : '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetShortLifeSpecAuthToken(int $iLife = 60): string
|
public function GetShortLifeSpecAuthToken(int $iLife = 60): string
|
||||||
{
|
{
|
||||||
$aAccountHash = Utils::DecodeKeyValues($this->getLocalAuthToken());
|
$aAccountHash = Utils::DecodeKeyValues($this->sSpecAuthToken);
|
||||||
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0]) {
|
if (!empty($aAccountHash[0]) && 'token' === $aAccountHash[0]) {
|
||||||
$aAccountHash[10] = \time() + $iLife;
|
$aAccountHash[10] = \time() + $iLife;
|
||||||
return Utils::EncodeKeyValues($aAccountHash);
|
return Utils::EncodeKeyValues($aAccountHash);
|
||||||
|
|
@ -256,126 +176,141 @@ trait UserAuth
|
||||||
*/
|
*/
|
||||||
public function getAccountFromToken(bool $bThrowExceptionOnFalse = true): ?Account
|
public function getAccountFromToken(bool $bThrowExceptionOnFalse = true): ?Account
|
||||||
{
|
{
|
||||||
return $this->GetAccountFromCustomToken($this->getLocalAuthToken(), $bThrowExceptionOnFalse, true, true);
|
if (!\is_string($this->sSpecAuthToken)) {
|
||||||
|
$this->sSpecAuthToken = '';
|
||||||
|
if (isset($_COOKIE[self::AUTH_SPEC_LOGOUT_TOKEN_KEY])) {
|
||||||
|
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
||||||
|
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
|
// Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
|
} else {
|
||||||
|
$sAuthAccountHash = Utils::GetCookie(self::AUTH_SPEC_TOKEN_KEY);
|
||||||
|
if (empty($sAuthAccountHash)) {
|
||||||
|
$oAccount = $this->GetAccountFromSignMeToken();
|
||||||
|
if ($oAccount) {
|
||||||
|
$this->SetAuthToken($oAccount);
|
||||||
|
$sAuthAccountHash = $this->sSpecAuthToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSpecAuthToken(): string
|
|
||||||
{
|
|
||||||
return $this->sSpecAuthToken;
|
|
||||||
}
|
}
|
||||||
|
$this->sSpecAuthToken = $sAuthAccountHash ?: '';
|
||||||
public function SetSpecAuthToken(string $sSpecAuthToken): self
|
}
|
||||||
{
|
}
|
||||||
$this->sSpecAuthToken = $sSpecAuthToken;
|
return $this->GetAccountFromCustomToken($this->sSpecAuthToken, true, true, $bThrowExceptionOnFalse);
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetAuthToken(Account $oAccount): void
|
public function SetAuthToken(Account $oAccount): void
|
||||||
{
|
{
|
||||||
$sSpecAuthToken = '_' . $oAccount->GetAuthTokenQ();
|
$sSpecAuthToken = $oAccount->GetAuthTokenQ();
|
||||||
|
|
||||||
$this->SetSpecAuthToken($sSpecAuthToken);
|
$this->sSpecAuthToken = $sSpecAuthToken;
|
||||||
Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken);
|
Utils::SetCookie(self::AUTH_SPEC_TOKEN_KEY, $sSpecAuthToken);
|
||||||
|
|
||||||
if ($oAccount->SignMe()) {
|
if (isset($aAccounts[$oAccount->Email()])) {
|
||||||
$this->SetSignMeToken($oAccount);
|
$aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken();
|
||||||
|
$this->SetAccounts($oAccount, $aAccounts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function GetSignMeToken(): ?array
|
private static function GetSignMeToken(): ?array
|
||||||
{
|
{
|
||||||
$sSignMeToken = Utils::GetCookie(self::AUTH_SIGN_ME_TOKEN_KEY, '');
|
$sSignMeToken = Utils::GetCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
||||||
return empty($sSignMeToken) ? null : Utils::DecodeKeyValuesQ($sSignMeToken);
|
if (empty($sSignMeToken)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$aResult = \SnappyMail\Crypt::DecryptCookie($sSignMeToken);
|
||||||
|
return \is_array($aResult) ? $aResult : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function SetSignMeTokenCookie(array $aData): void
|
||||||
|
{
|
||||||
|
Utils::SetCookie(
|
||||||
|
self::AUTH_SIGN_ME_TOKEN_KEY,
|
||||||
|
\SnappyMail\Crypt::EncryptCookie($aData),
|
||||||
|
\time() + 3600 * 24 * 30 // 30 days
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function SetSignMeToken(Account $oAccount): void
|
private function SetSignMeToken(Account $oAccount): void
|
||||||
{
|
{
|
||||||
$this->ClearSignMeData($oAccount);
|
$this->ClearSignMeData();
|
||||||
|
|
||||||
$uuid = \SnappyMail\UUID::generate();
|
$uuid = \SnappyMail\UUID::generate();
|
||||||
$salt = \sha1(\random_bytes(16));
|
$data = \SnappyMail\Crypt::EncryptRaw($oAccount);
|
||||||
Utils::SetCookie(self::AUTH_SIGN_ME_TOKEN_KEY,
|
|
||||||
Utils::EncodeKeyValuesQ(array(
|
if ($data['xxtea']) {
|
||||||
|
static::SetSignMeTokenCookie(array(
|
||||||
'e' => $oAccount->Email(),
|
'e' => $oAccount->Email(),
|
||||||
'u' => $uuid,
|
'u' => $uuid,
|
||||||
's' => $salt
|
's' => \base64_encode($data[1])
|
||||||
)),
|
));
|
||||||
\time() + 3600 * 24 * 30); // 30 days
|
} else {
|
||||||
|
static::SetSignMeTokenCookie(array(
|
||||||
|
'e' => $oAccount->Email(),
|
||||||
|
'u' => $uuid,
|
||||||
|
'i' => \base64_encode($data[1])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$this->StorageProvider()->Put($oAccount,
|
$this->StorageProvider()->Put(
|
||||||
|
$oAccount,
|
||||||
StorageType::SIGN_ME,
|
StorageType::SIGN_ME,
|
||||||
$uuid,
|
$uuid,
|
||||||
Utils::EncryptString($oAccount->GetAuthToken(), $salt)
|
$data[2]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetAccountFromSignMeToken(): ?Account
|
public function GetAccountFromSignMeToken(): ?Account
|
||||||
{
|
{
|
||||||
$oAccount = null;
|
|
||||||
|
|
||||||
$aTokenData = static::GetSignMeToken();
|
$aTokenData = static::GetSignMeToken();
|
||||||
if (!empty($aTokenData)) {
|
if (!empty($aTokenData)) {
|
||||||
if (!empty($aTokenData['e']) && !empty($aTokenData['u']) && !empty($aTokenData['s']) && \SnappyMail\UUID::isValid($aTokenData['u'])) {
|
$oAccount = null;
|
||||||
$sAuthToken = $this->StorageProvider()->Get($aTokenData['e'],
|
if (!empty($aTokenData['e']) && !empty($aTokenData['u']) && \SnappyMail\UUID::isValid($aTokenData['u'])) {
|
||||||
|
$sAuthToken = $this->StorageProvider()->Get(
|
||||||
|
$aTokenData['e'],
|
||||||
StorageType::SIGN_ME,
|
StorageType::SIGN_ME,
|
||||||
$aTokenData['u']
|
$aTokenData['u']
|
||||||
);
|
);
|
||||||
if (empty($sAuthToken)) {
|
if (empty($sAuthToken)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$sAuthToken = Utils::DecryptString($sAuthToken, $aTokenData['s']);
|
if (!empty($aTokenData['s'])) {
|
||||||
if (!empty($sAuthToken)) {
|
$aAccountHash = \SnappyMail\Crypt::XxteaDecrypt($sAuthToken, \base64_decode($aTokenData['s']));
|
||||||
$oAccount = $this->GetAccountFromCustomToken($sAuthToken, false, false, true);
|
} else if (!empty($aTokenData['i'])) {
|
||||||
}
|
$aAccountHash = \SnappyMail\Crypt::OpenSSLDecrypt($sAuthToken, \base64_decode($aTokenData['i']));
|
||||||
} 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 (!empty($aAccountHash) && \is_array($aAccountHash)) {
|
||||||
|
$oAccount = Account::NewInstanceFromTokenArray($this, $aAccountHash);
|
||||||
if ($oAccount) {
|
if ($oAccount) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->CheckMailConnection($oAccount);
|
||||||
// Update lifetime
|
// Update lifetime
|
||||||
$this->SetSignMeToken($oAccount);
|
$this->SetSignMeToken($oAccount);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oAccount;
|
return $oAccount;
|
||||||
}
|
}
|
||||||
|
catch (\Throwable $oException)
|
||||||
protected function ClearSignMeData(Account $oAccount) : void
|
|
||||||
{
|
{
|
||||||
if ($oAccount) {
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ClearSignMeData();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function ClearSignMeData() : void
|
||||||
|
{
|
||||||
|
if (isset($_COOKIE[self::AUTH_SIGN_ME_TOKEN_KEY])) {
|
||||||
$aTokenData = static::GetSignMeToken();
|
$aTokenData = static::GetSignMeToken();
|
||||||
if (!empty($aTokenData['u']) && \SnappyMail\UUID::isValid($aTokenData['u'])) {
|
if (!empty($aTokenData['e']) && !empty($aTokenData['u']) && \SnappyMail\UUID::isValid($aTokenData['u'])) {
|
||||||
$this->StorageProvider()->Clear($oAccount, 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);
|
||||||
$this->StorageProvider()->Clear($oAccount, StorageType::CONFIG, 'sign_me');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSpecAuthLogoutTokenWithDeletion(): string
|
|
||||||
{
|
|
||||||
$sResult = Utils::GetCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY, '');
|
|
||||||
if (\strlen($sResult)) {
|
|
||||||
Utils::ClearCookie(self::AUTH_SPEC_LOGOUT_TOKEN_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetAuthLogoutToken(): void
|
public function SetAuthLogoutToken(): void
|
||||||
{
|
{
|
||||||
\header('X-RainLoop-Action: Logout');
|
\header('X-RainLoop-Action: Logout');
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
namespace RainLoop\Model;
|
namespace RainLoop\Model;
|
||||||
|
|
||||||
class Account
|
use \RainLoop\Utils;
|
||||||
|
use \RainLoop\Exceptions\ClientException;
|
||||||
|
|
||||||
|
class Account implements \JsonSerializable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
|
@ -22,23 +25,18 @@ class Account
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $sProxyAuthUser;
|
private $sProxyAuthUser = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $sProxyAuthPassword;
|
private $sProxyAuthPassword = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $sClientCert;
|
private $sClientCert;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $sSignMeToken;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \RainLoop\Model\Domain
|
* @var \RainLoop\Model\Domain
|
||||||
*/
|
*/
|
||||||
|
|
@ -47,21 +45,7 @@ class Account
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $sParentEmail;
|
private $sParentEmail = '';
|
||||||
|
|
||||||
function __construct(string $sEmail, string $sLogin, string $sPassword, Domain $oDomain,
|
|
||||||
string $sSignMeToken = '', string $sProxyAuthUser = '', string $sProxyAuthPassword = '', string $sClientCert = '')
|
|
||||||
{
|
|
||||||
$this->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
|
||||||
$this->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
|
||||||
$this->sPassword = $sPassword;
|
|
||||||
$this->oDomain = $oDomain;
|
|
||||||
$this->sSignMeToken = $sSignMeToken;
|
|
||||||
$this->sProxyAuthUser = $sProxyAuthUser;
|
|
||||||
$this->sProxyAuthPassword = $sProxyAuthPassword;
|
|
||||||
$this->sClientCert = $sClientCert;
|
|
||||||
$this->sParentEmail = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Email() : string
|
public function Email() : string
|
||||||
{
|
{
|
||||||
|
|
@ -85,12 +69,12 @@ class Account
|
||||||
|
|
||||||
public function ParentEmailHelper() : string
|
public function ParentEmailHelper() : string
|
||||||
{
|
{
|
||||||
return \strlen($this->sParentEmail) ? $this->sParentEmail : $this->sEmail;
|
return $this->sParentEmail ?: $this->sEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsAdditionalAccount() : string
|
public function IsAdditionalAccount() : bool
|
||||||
{
|
{
|
||||||
return \strlen($this->sParentEmail);
|
return !empty($this->sParentEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IncLogin() : string
|
public function IncLogin() : string
|
||||||
|
|
@ -135,16 +119,6 @@ class Account
|
||||||
return $this->sClientCert;
|
return $this->sClientCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SignMe() : bool
|
|
||||||
{
|
|
||||||
return \strlen($this->sSignMeToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SignMeToken() : string
|
|
||||||
{
|
|
||||||
return $this->sSignMeToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Domain() : Domain
|
public function Domain() : Domain
|
||||||
{
|
{
|
||||||
return $this->oDomain;
|
return $this->oDomain;
|
||||||
|
|
@ -152,8 +126,13 @@ class Account
|
||||||
|
|
||||||
public function Hash() : string
|
public function Hash() : string
|
||||||
{
|
{
|
||||||
return \md5(APP_SALT.$this->Email().APP_SALT.$this->DomainIncHost().
|
return \md5(\implode(APP_SALT, [
|
||||||
APP_SALT.$this->DomainIncPort().APP_SALT.$this->Password().APP_SALT.'0'.APP_SALT.$this->ParentEmail().APP_SALT);
|
$this->sEmail,
|
||||||
|
$this->Domain()->IncHost(),
|
||||||
|
$this->Domain()->IncPort(),
|
||||||
|
$this->sPassword,
|
||||||
|
$this->sParentEmail
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetPassword(string $sPassword) : void
|
public function SetPassword(string $sPassword) : void
|
||||||
|
|
@ -176,106 +155,108 @@ class Account
|
||||||
$this->sProxyAuthPassword = $sProxyAuthPassword;
|
$this->sProxyAuthPassword = $sProxyAuthPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DomainIncHost() : string
|
public function jsonSerialize()
|
||||||
{
|
{
|
||||||
return $this->Domain()->IncHost();
|
return array(
|
||||||
}
|
'account', // 0
|
||||||
|
$this->sEmail, // 1
|
||||||
public function DomainIncPort() : int
|
$this->sLogin, // 2
|
||||||
{
|
$this->sPassword, // 3
|
||||||
return $this->Domain()->IncPort();
|
$this->sClientCert, // 4
|
||||||
}
|
$this->sParentEmail, // 5
|
||||||
|
$this->sProxyAuthUser, // 6
|
||||||
public function DomainIncSecure() : int
|
$this->sProxyAuthPassword // 7
|
||||||
{
|
);
|
||||||
return $this->Domain()->IncSecure();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainOutHost() : string
|
|
||||||
{
|
|
||||||
return $this->Domain()->OutHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainOutPort() : int
|
|
||||||
{
|
|
||||||
return $this->Domain()->OutPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainOutSecure() : int
|
|
||||||
{
|
|
||||||
return $this->Domain()->OutSecure();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainOutAuth() : bool
|
|
||||||
{
|
|
||||||
return $this->Domain()->OutAuth();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainSieveHost() : string
|
|
||||||
{
|
|
||||||
return $this->Domain()->SieveHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainSievePort() : int
|
|
||||||
{
|
|
||||||
return $this->Domain()->SievePort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DomainSieveSecure() : int
|
|
||||||
{
|
|
||||||
return $this->Domain()->SieveSecure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetAuthToken() : string
|
public function GetAuthToken() : string
|
||||||
{
|
{
|
||||||
return \RainLoop\Utils::EncodeKeyValues(array(
|
return Utils::EncodeKeyValues($this->jsonSerialize());
|
||||||
'token', // 0
|
|
||||||
$this->sEmail, // 1
|
|
||||||
$this->sLogin, // 2
|
|
||||||
$this->sPassword, // 3
|
|
||||||
\RainLoop\Utils::Fingerprint(), // 4
|
|
||||||
$this->sSignMeToken, // 5
|
|
||||||
$this->sParentEmail, // 6
|
|
||||||
\RainLoop\Utils::GetShortToken(), // 7
|
|
||||||
$this->sProxyAuthUser, // 8
|
|
||||||
$this->sProxyAuthPassword, // 9
|
|
||||||
0, // 10 // lifetime
|
|
||||||
$this->sClientCert // 11
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetAuthTokenQ() : string
|
public function GetAuthTokenQ() : string
|
||||||
{
|
{
|
||||||
return \RainLoop\Utils::EncodeKeyValuesQ(array(
|
return Utils::EncodeKeyValuesQ($this->jsonSerialize());
|
||||||
'token', // 0
|
return \MailSo\Base\Utils::UrlSafeBase64Encode(
|
||||||
$this->sEmail, // 1
|
\MailSo\Base\Crypt::Encrypt(\json_encode($this), \sha1(APP_SALT))
|
||||||
$this->sLogin, // 2
|
);
|
||||||
$this->sPassword, // 3
|
}
|
||||||
\RainLoop\Utils::Fingerprint(), // 4
|
|
||||||
$this->sSignMeToken, // 5
|
public static function NewInstanceByLogin(\RainLoop\Actions $oActions, string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', bool $bThrowException = false): ?self
|
||||||
$this->sParentEmail, // 6
|
{
|
||||||
\RainLoop\Utils::GetShortToken(), // 7
|
$oAccount = null;
|
||||||
$this->sProxyAuthUser, // 8
|
if (\strlen($sEmail) && \strlen($sLogin) && \strlen($sPassword)) {
|
||||||
$this->sProxyAuthPassword, // 9
|
$oDomain = $oActions->DomainProvider()->Load(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), true);
|
||||||
0, // 10 // lifetime
|
if ($oDomain) {
|
||||||
$this->sClientCert // 11
|
if ($oDomain->ValidateWhiteList($sEmail, $sLogin)) {
|
||||||
));
|
$oAccount = new self;
|
||||||
|
|
||||||
|
$oAccount->sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
||||||
|
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
|
||||||
|
$oAccount->sPassword = $sPassword;
|
||||||
|
$oAccount->oDomain = $oDomain;
|
||||||
|
$oAccount->sClientCert = $sClientCert;
|
||||||
|
|
||||||
|
$oActions->Plugins()->RunHook('filter.account', array($oAccount));
|
||||||
|
|
||||||
|
if ($bThrowException && !$oAccount) {
|
||||||
|
throw new ClientException(Notifications::AuthError);
|
||||||
|
}
|
||||||
|
} else if ($bThrowException) {
|
||||||
|
throw new ClientException(Notifications::AccountNotAllowed);
|
||||||
|
}
|
||||||
|
} else if ($bThrowException) {
|
||||||
|
throw new ClientException(Notifications::DomainNotAllowed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function NewInstanceFromTokenArray(
|
||||||
|
\RainLoop\Actions $oActions,
|
||||||
|
array $aAccountHash,
|
||||||
|
bool $bThrowExceptionOnFalse = false): ?self
|
||||||
|
{
|
||||||
|
if (!empty($aAccountHash[0]) && 'account' === $aAccountHash[0] && 8 === \count($aAccountHash)) {
|
||||||
|
$oAccount = static::NewInstanceByLogin(
|
||||||
|
$oActions,
|
||||||
|
$aAccountHash[1],
|
||||||
|
$aAccountHash[2],
|
||||||
|
$aAccountHash[3],
|
||||||
|
$aAccountHash[4] ?: '',
|
||||||
|
$bThrowExceptionOnFalse
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($oAccount) {
|
||||||
|
// init proxy user/password
|
||||||
|
if (!empty($aAccountHash[6]) && !empty($aAccountHash[7])) {
|
||||||
|
$oAccount->SetProxyAuthUser($aAccountHash[6]);
|
||||||
|
$oAccount->SetProxyAuthPassword($aAccountHash[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oActions->Logger()->AddSecret($oAccount->Password());
|
||||||
|
$oActions->Logger()->AddSecret($oAccount->ProxyAuthPassword());
|
||||||
|
|
||||||
|
$oAccount->SetParentEmail($aAccountHash[5]);
|
||||||
|
return $oAccount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IncConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Mail\MailClient $oMailClient, \RainLoop\Config\Application $oConfig) : bool
|
public function IncConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Mail\MailClient $oMailClient, \RainLoop\Config\Application $oConfig) : bool
|
||||||
{
|
{
|
||||||
$oImapClient = $oMailClient->ImapClient();
|
$oImapClient = $oMailClient->ImapClient();
|
||||||
|
|
||||||
$aImapCredentials = array(
|
$aImapCredentials = \array_merge(
|
||||||
'UseConnect' => true,
|
$this->Domain()->ImapSettings(),
|
||||||
'UseAuth' => true,
|
array(
|
||||||
'Host' => $this->DomainIncHost(),
|
|
||||||
'Port' => $this->DomainIncPort(),
|
|
||||||
'Secure' => $this->DomainIncSecure(),
|
|
||||||
'Login' => $this->IncLogin(),
|
'Login' => $this->IncLogin(),
|
||||||
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
||||||
'ClientCert' => $this->ClientCert(),
|
'ClientCert' => $this->ClientCert(),
|
||||||
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
|
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, &$aImapCredentials));
|
$oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, &$aImapCredentials));
|
||||||
|
|
@ -292,17 +273,15 @@ class Account
|
||||||
|
|
||||||
public function OutConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool
|
public function OutConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool
|
||||||
{
|
{
|
||||||
$aSmtpCredentials = array(
|
$aSmtpCredentials = \array_merge(
|
||||||
|
$this->Domain()->SmtpSettings(),
|
||||||
|
array(
|
||||||
'UseConnect' => !$bUsePhpMail,
|
'UseConnect' => !$bUsePhpMail,
|
||||||
'UseAuth' => $this->DomainOutAuth(),
|
|
||||||
'UsePhpMail' => $bUsePhpMail,
|
'UsePhpMail' => $bUsePhpMail,
|
||||||
'Ehlo' => \MailSo\Smtp\SmtpClient::EhloHelper(),
|
|
||||||
'Host' => $this->DomainOutHost(),
|
|
||||||
'Port' => $this->DomainOutPort(),
|
|
||||||
'Secure' => $this->DomainOutSecure(),
|
|
||||||
'Login' => $this->OutLogin(),
|
'Login' => $this->OutLogin(),
|
||||||
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
||||||
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
|
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, &$aSmtpCredentials));
|
$oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, &$aSmtpCredentials));
|
||||||
|
|
@ -321,16 +300,14 @@ class Account
|
||||||
|
|
||||||
public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\ManageSieveClient $oSieveClient, \RainLoop\Config\Application $oConfig)
|
public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\ManageSieveClient $oSieveClient, \RainLoop\Config\Application $oConfig)
|
||||||
{
|
{
|
||||||
$aSieveCredentials = array(
|
$aSieveCredentials = \array_merge(
|
||||||
'UseConnect' => true,
|
$this->Domain()->SieveSettings(),
|
||||||
'UseAuth' => true,
|
array(
|
||||||
'Host' => $this->DomainSieveHost(),
|
|
||||||
'Port' => $this->DomainSievePort(),
|
|
||||||
'Secure' => $this->DomainSieveSecure(),
|
|
||||||
'Login' => $this->IncLogin(),
|
'Login' => $this->IncLogin(),
|
||||||
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
|
||||||
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true),
|
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true),
|
||||||
'InitialAuthPlain' => !!$oConfig->Get('ssl', 'sieve_auth_plain_initial', true)
|
'InitialAuthPlain' => !!$oConfig->Get('ssl', 'sieve_auth_plain_initial', true)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, &$aSieveCredentials));
|
$oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, &$aSieveCredentials));
|
||||||
|
|
|
||||||
|
|
@ -396,27 +396,62 @@ class Domain implements \JsonSerializable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ImapSettings() : array
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'UseConnect' => true,
|
||||||
|
'UseAuth' => true,
|
||||||
|
'Host' => $this->sIncHost,
|
||||||
|
'Port' => $this->iIncPort,
|
||||||
|
'Secure' => $this->iIncSecure,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SmtpSettings() : array
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'UseConnect' => !$this->bOutUsePhpMail,
|
||||||
|
'UseAuth' => $this->bOutAuth,
|
||||||
|
'Host' => $this->sOutHost,
|
||||||
|
'Port' => $this->iOutPort,
|
||||||
|
'Secure' => $this->iOutSecure,
|
||||||
|
'Ehlo' => \MailSo\Smtp\SmtpClient::EhloHelper(),
|
||||||
|
'UsePhpMail' => $this->bOutUsePhpMail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SieveSettings() : array
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'UseConnect' => true,
|
||||||
|
'UseAuth' => true,
|
||||||
|
'Host' => $this->sSieveHost,
|
||||||
|
'Port' => $this->iSievePort,
|
||||||
|
'Secure' => $this->iSieveSecure
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function ToSimpleJSON() : array
|
public function ToSimpleJSON() : array
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
'Name' => $this->Name(),
|
'Name' => $this->sName,
|
||||||
'IncHost' => $this->IncHost(),
|
'IncHost' => $this->sIncHost,
|
||||||
'IncPort' => $this->IncPort(),
|
'IncPort' => $this->iIncPort,
|
||||||
'IncSecure' => $this->IncSecure(),
|
'IncSecure' => $this->iIncSecure,
|
||||||
'IncShortLogin' => $this->IncShortLogin(),
|
'IncShortLogin' => $this->bIncShortLogin,
|
||||||
'UseSieve' => $this->UseSieve(),
|
'UseSieve' => $this->bUseSieve,
|
||||||
'SieveHost' => $this->SieveHost(),
|
'SieveHost' => $this->sSieveHost,
|
||||||
'SievePort' => $this->SievePort(),
|
'SievePort' => $this->iSievePort,
|
||||||
'SieveSecure' => $this->SieveSecure(),
|
'SieveSecure' => $this->iSieveSecure,
|
||||||
'OutHost' => $this->OutHost(),
|
'OutHost' => $this->sOutHost,
|
||||||
'OutPort' => $this->OutPort(),
|
'OutPort' => $this->iOutPort,
|
||||||
'OutSecure' => $this->OutSecure(),
|
'OutSecure' => $this->iOutSecure,
|
||||||
'OutShortLogin' => $this->OutShortLogin(),
|
'OutShortLogin' => $this->bOutShortLogin,
|
||||||
'OutAuth' => $this->OutAuth(),
|
'OutAuth' => $this->bOutAuth,
|
||||||
'OutSetSender' => $this->OutSetSender(),
|
'OutSetSender' => $this->bOutSetSender,
|
||||||
'OutUsePhpMail' => $this->OutUsePhpMail(),
|
'OutUsePhpMail' => $this->bOutUsePhpMail,
|
||||||
'WhiteList' => $this->WhiteList(),
|
'WhiteList' => $this->sWhiteList,
|
||||||
'AliasName' => $this->AliasName()
|
'AliasName' => $this->sAliasName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,24 +459,24 @@ class Domain implements \JsonSerializable
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
// '@Object' => 'Object/Domain',
|
// '@Object' => 'Object/Domain',
|
||||||
'Name' => \MailSo\Base\Utils::IdnToUtf8($this->Name()),
|
'Name' => \MailSo\Base\Utils::IdnToUtf8($this->sName),
|
||||||
'IncHost' => \MailSo\Base\Utils::IdnToUtf8($this->IncHost()),
|
'IncHost' => \MailSo\Base\Utils::IdnToUtf8($this->sIncHost),
|
||||||
'IncPort' => $this->IncPort(),
|
'IncPort' => $this->iIncPort,
|
||||||
'IncSecure' => $this->IncSecure(),
|
'IncSecure' => $this->iIncSecure,
|
||||||
'IncShortLogin' => $this->IncShortLogin(),
|
'IncShortLogin' => $this->bIncShortLogin,
|
||||||
'UseSieve' => $this->UseSieve(),
|
'UseSieve' => $this->bUseSieve,
|
||||||
'SieveHost' => \MailSo\Base\Utils::IdnToUtf8($this->SieveHost()),
|
'SieveHost' => \MailSo\Base\Utils::IdnToUtf8($this->sSieveHost),
|
||||||
'SievePort' => $this->SievePort(),
|
'SievePort' => $this->iSievePort,
|
||||||
'SieveSecure' => $this->SieveSecure(),
|
'SieveSecure' => $this->iSieveSecure,
|
||||||
'OutHost' => \MailSo\Base\Utils::IdnToUtf8($this->OutHost()),
|
'OutHost' => \MailSo\Base\Utils::IdnToUtf8($this->sOutHost),
|
||||||
'OutPort' => $this->OutPort(),
|
'OutPort' => $this->iOutPort,
|
||||||
'OutSecure' => $this->OutSecure(),
|
'OutSecure' => $this->iOutSecure,
|
||||||
'OutShortLogin' => $this->OutShortLogin(),
|
'OutShortLogin' => $this->bOutShortLogin,
|
||||||
'OutAuth' => $this->OutAuth(),
|
'OutAuth' => $this->bOutAuth,
|
||||||
'OutSetSender' => $this->OutSetSender(),
|
'OutSetSender' => $this->bOutSetSender,
|
||||||
'OutUsePhpMail' => $this->OutUsePhpMail(),
|
'OutUsePhpMail' => $this->bOutUsePhpMail,
|
||||||
'WhiteList' => $this->WhiteList(),
|
'WhiteList' => $this->sWhiteList,
|
||||||
'AliasName' => $this->AliasName()
|
'AliasName' => $this->sAliasName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,54 +29,54 @@ class Storage extends \RainLoop\Providers\AbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
* @param mixed $sValue
|
* @param mixed $sValue
|
||||||
*/
|
*/
|
||||||
public function Put($oAccount, int $iStorageType, string $sKey, string $sValue) : bool
|
public function Put($mAccount, int $iStorageType, string $sKey, string $sValue) : bool
|
||||||
{
|
{
|
||||||
if (!$this->verifyAccount($oAccount, $iStorageType))
|
if (!$this->verifyAccount($mAccount, $iStorageType))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->oDriver->Put($oAccount, $iStorageType, $sKey, $sValue);
|
return $this->oDriver->Put($mAccount, $iStorageType, $sKey, $sValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
* @param mixed $mDefault = false
|
* @param mixed $mDefault = false
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function Get($oAccount, int $iStorageType, string $sKey, $mDefault = false)
|
public function Get($mAccount, int $iStorageType, string $sKey, $mDefault = false)
|
||||||
{
|
{
|
||||||
if (!$this->verifyAccount($oAccount, $iStorageType))
|
if (!$this->verifyAccount($mAccount, $iStorageType))
|
||||||
{
|
{
|
||||||
return $mDefault;
|
return $mDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->oDriver->Get($oAccount, $iStorageType, $sKey, $mDefault);
|
return $this->oDriver->Get($mAccount, $iStorageType, $sKey, $mDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
*/
|
*/
|
||||||
public function Clear($oAccount, int $iStorageType, string $sKey) : bool
|
public function Clear($mAccount, int $iStorageType, string $sKey) : bool
|
||||||
{
|
{
|
||||||
if (!$this->verifyAccount($oAccount, $iStorageType))
|
if (!$this->verifyAccount($mAccount, $iStorageType))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->oDriver->Clear($oAccount, $iStorageType, $sKey);
|
return $this->oDriver->Clear($mAccount, $iStorageType, $sKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string $oAccount
|
* @param \RainLoop\Model\Account|string $mAccount
|
||||||
*/
|
*/
|
||||||
public function DeleteStorage($oAccount) : bool
|
public function DeleteStorage($mAccount) : bool
|
||||||
{
|
{
|
||||||
return $this->oDriver->DeleteStorage($oAccount);
|
return $this->oDriver->DeleteStorage($mAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsActive() : bool
|
public function IsActive() : bool
|
||||||
|
|
|
||||||
|
|
@ -29,24 +29,24 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
*/
|
*/
|
||||||
public function Put($oAccount, int $iStorageType, string $sKey, string $sValue) : bool
|
public function Put($mAccount, int $iStorageType, string $sKey, string $sValue) : bool
|
||||||
{
|
{
|
||||||
$sFileName = $this->generateFileName($oAccount, $iStorageType, $sKey, true);
|
$sFileName = $this->generateFileName($mAccount, $iStorageType, $sKey, true);
|
||||||
return $sFileName && false !== \file_put_contents($sFileName, $sValue);
|
return $sFileName && false !== \file_put_contents($sFileName, $sValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
* @param mixed $mDefault = false
|
* @param mixed $mDefault = false
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function Get($oAccount, int $iStorageType, string $sKey, $mDefault = false)
|
public function Get($mAccount, int $iStorageType, string $sKey, $mDefault = false)
|
||||||
{
|
{
|
||||||
$mValue = false;
|
$mValue = false;
|
||||||
$sFileName = $this->generateFileName($oAccount, $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);
|
||||||
}
|
}
|
||||||
|
|
@ -55,12 +55,12 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string|null $oAccount
|
* @param \RainLoop\Model\Account|string|null $mAccount
|
||||||
*/
|
*/
|
||||||
public function Clear($oAccount, int $iStorageType, string $sKey) : bool
|
public function Clear($mAccount, int $iStorageType, string $sKey) : bool
|
||||||
{
|
{
|
||||||
$mResult = true;
|
$mResult = true;
|
||||||
$sFileName = $this->generateFileName($oAccount, $iStorageType, $sKey);
|
$sFileName = $this->generateFileName($mAccount, $iStorageType, $sKey);
|
||||||
if ($sFileName && \file_exists($sFileName)) {
|
if ($sFileName && \file_exists($sFileName)) {
|
||||||
$mResult = \unlink($sFileName);
|
$mResult = \unlink($sFileName);
|
||||||
}
|
}
|
||||||
|
|
@ -69,11 +69,11 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|string $oAccount
|
* @param \RainLoop\Model\Account|string $mAccount
|
||||||
*/
|
*/
|
||||||
public function DeleteStorage($oAccount) : bool
|
public function DeleteStorage($mAccount) : bool
|
||||||
{
|
{
|
||||||
$sPath = $this->generateFileName($oAccount, StorageType::CONFIG, '', false, true);
|
$sPath = $this->generateFileName($mAccount, StorageType::CONFIG, '', false, true);
|
||||||
if ($sPath && \is_dir($sPath)) {
|
if ($sPath && \is_dir($sPath)) {
|
||||||
\MailSo\Base\Utils::RecRmDir($sPath);
|
\MailSo\Base\Utils::RecRmDir($sPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,22 @@ namespace RainLoop\Providers\Storage;
|
||||||
interface IStorage
|
interface IStorage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|null $oAccount
|
* @param \RainLoop\Model\Account|null $mAccount
|
||||||
*/
|
*/
|
||||||
public function Put($oAccount, int $iStorageType, string $sKey, string $sValue) : bool;
|
public function Put($mAccount, int $iStorageType, string $sKey, string $sValue) : bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|null $oAccount
|
* @param \RainLoop\Model\Account|null $mAccount
|
||||||
* @param mixed $mDefault = false
|
* @param mixed $mDefault = false
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function Get($oAccount, int $iStorageType, string $sKey, $mDefault = false);
|
public function Get($mAccount, int $iStorageType, string $sKey, $mDefault = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \RainLoop\Model\Account|null $oAccount
|
* @param \RainLoop\Model\Account|null $mAccount
|
||||||
*/
|
*/
|
||||||
public function Clear($oAccount, int $iStorageType, string $sKey) : bool;
|
public function Clear($mAccount, int $iStorageType, string $sKey) : bool;
|
||||||
|
|
||||||
public function IsLocal() : bool;
|
public function IsLocal() : bool;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,6 @@ class Service
|
||||||
$bAdmin = true;
|
$bAdmin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$bAdmin || $this->oActions->getAuthAccountHash();
|
|
||||||
|
|
||||||
if ($this->oHttp->IsPost())
|
if ($this->oHttp->IsPost())
|
||||||
{
|
{
|
||||||
$this->oHttp->ServerNoCache();
|
$this->oHttp->ServerNoCache();
|
||||||
|
|
|
||||||
|
|
@ -684,7 +684,7 @@ class ServiceActions
|
||||||
{
|
{
|
||||||
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
|
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
|
||||||
|
|
||||||
if ($oAccount instanceof Model\Account && $aAdditionalOptions)
|
if ($aAdditionalOptions)
|
||||||
{
|
{
|
||||||
$bNeedToSettings = false;
|
$bNeedToSettings = false;
|
||||||
|
|
||||||
|
|
@ -711,7 +711,7 @@ class ServiceActions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->oActions->AuthToken($oAccount);
|
$this->oActions->SetAuthToken($oAccount);
|
||||||
|
|
||||||
$bLogout = !($oAccount instanceof Model\Account);
|
$bLogout = !($oAccount instanceof Model\Account);
|
||||||
}
|
}
|
||||||
|
|
@ -746,7 +746,7 @@ class ServiceActions
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
|
$oAccount = $this->oActions->LoginProcess($sEmail, $sPassword);
|
||||||
$this->oActions->AuthToken($oAccount);
|
$this->oActions->SetAuthToken($oAccount);
|
||||||
$bLogout = !($oAccount instanceof Model\Account);
|
$bLogout = !($oAccount instanceof Model\Account);
|
||||||
}
|
}
|
||||||
catch (\Throwable $oException)
|
catch (\Throwable $oException)
|
||||||
|
|
@ -824,13 +824,13 @@ class ServiceActions
|
||||||
$aAccounts = $this->oActions->GetAccounts($oAccount);
|
$aAccounts = $this->oActions->GetAccounts($oAccount);
|
||||||
if (isset($aAccounts[$sEmail]))
|
if (isset($aAccounts[$sEmail]))
|
||||||
{
|
{
|
||||||
$oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail], false, false);
|
$oAccountToLogin = $this->oActions->GetAccountFromCustomToken($aAccounts[$sEmail], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($oAccountToLogin)
|
if ($oAccountToLogin)
|
||||||
{
|
{
|
||||||
$this->oActions->AuthToken($oAccountToLogin);
|
$this->oActions->SetAuthToken($oAccountToLogin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,17 @@ class Utils
|
||||||
|
|
||||||
const
|
const
|
||||||
/**
|
/**
|
||||||
|
* 30 days cookie
|
||||||
* Used by: ServiceProxyExternal, compileLogParams, GetCsrfToken
|
* Used by: ServiceProxyExternal, compileLogParams, GetCsrfToken
|
||||||
* To preven CSRF attacks on all requests.
|
* To preven CSRF attacks on all requests.
|
||||||
*/
|
*/
|
||||||
CONNECTION_TOKEN = 'rltoken',
|
CONNECTION_TOKEN = 'smtoken',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Session cookie
|
||||||
* Used by: GetAuthToken, GetAuthTokenQ, GetAccountFromCustomToken, EncryptStringQ, DecryptStringQ
|
* Used by: GetAuthToken, GetAuthTokenQ, GetAccountFromCustomToken, EncryptStringQ, DecryptStringQ
|
||||||
*/
|
*/
|
||||||
SHORT_TOKEN = 'rlsession';
|
SHORT_TOKEN = 'smsession';
|
||||||
|
|
||||||
public static function EncryptString(string $sString, string $sKey) : string
|
public static function EncryptString(string $sString, string $sKey) : string
|
||||||
{
|
{
|
||||||
|
|
@ -96,14 +98,13 @@ class Utils
|
||||||
|
|
||||||
public static function Fingerprint() : string
|
public static function Fingerprint() : string
|
||||||
{
|
{
|
||||||
return \md5($_SERVER['HTTP_USER_AGENT'] ?: 'RainLoopFingerprint');
|
return \sha1(\preg_replace('/[^a-z]+/i', '', \explode(')', $_SERVER['HTTP_USER_AGENT'])[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetShortToken() : string
|
public static function GetShortToken() : string
|
||||||
{
|
{
|
||||||
$sToken = static::GetCookie(self::SHORT_TOKEN, null);
|
$sToken = static::GetCookie(self::SHORT_TOKEN, null);
|
||||||
if (!$sToken)
|
if (!$sToken) {
|
||||||
{
|
|
||||||
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
$sToken = \MailSo\Base\Utils::Sha1Rand(APP_SALT);
|
||||||
static::SetCookie(self::SHORT_TOKEN, $sToken, 0);
|
static::SetCookie(self::SHORT_TOKEN, $sToken, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
100
snappymail/v/0.0.0/app/libraries/snappymail/crypt.php
Normal file
100
snappymail/v/0.0.0/app/libraries/snappymail/crypt.php
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail;
|
||||||
|
|
||||||
|
abstract class Crypt
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Or use 'aes-256-xts' ?
|
||||||
|
*/
|
||||||
|
const CIPHER = 'aes-256-cbc-hmac-sha1';
|
||||||
|
|
||||||
|
private static function Passphrase() : string
|
||||||
|
{
|
||||||
|
return \sha1(\preg_replace('/[^a-z]+/i', '', \explode(')', $_SERVER['HTTP_USER_AGENT'])[0]) . APP_SALT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DecryptCookie($sData)
|
||||||
|
{
|
||||||
|
return \json_decode(
|
||||||
|
\zlib_decode(
|
||||||
|
\MailSo\Base\Crypt::Decrypt(
|
||||||
|
\MailSo\Base\Utils::UrlSafeBase64Decode($sData),
|
||||||
|
static::Passphrase()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function EncryptCookie($mData) : string
|
||||||
|
{
|
||||||
|
return \MailSo\Base\Utils::UrlSafeBase64Encode(
|
||||||
|
\MailSo\Base\Crypt::Encrypt(
|
||||||
|
\zlib_encode(
|
||||||
|
\json_encode($mData),
|
||||||
|
ZLIB_ENCODING_RAW,
|
||||||
|
9
|
||||||
|
),
|
||||||
|
static::Passphrase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function EncryptRaw($data) : array
|
||||||
|
{
|
||||||
|
if (\is_callable('openssl_encrypt')) {
|
||||||
|
$iv = \random_bytes(\openssl_cipher_iv_length(static::CIPHER));
|
||||||
|
$data = \openssl_encrypt(
|
||||||
|
\json_encode($data),
|
||||||
|
static::CIPHER,
|
||||||
|
static::Passphrase(),
|
||||||
|
OPENSSL_RAW_DATA,
|
||||||
|
$iv
|
||||||
|
);
|
||||||
|
return [static::CIPHER, $iv, $data];
|
||||||
|
}
|
||||||
|
|
||||||
|
$salt = \random_bytes(16);
|
||||||
|
return ['xxtea', $salt, static::XxteaEncrypt($data, $salt)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OpenSSLDecrypt(string $data, string $iv)
|
||||||
|
{
|
||||||
|
if (!$data || !$iv || !\is_callable('openssl_decrypt')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return \json_decode(\openssl_decrypt(
|
||||||
|
$data,
|
||||||
|
static::CIPHER,
|
||||||
|
static::Passphrase(),
|
||||||
|
OPENSSL_RAW_DATA,
|
||||||
|
$iv
|
||||||
|
), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function XxteaDecrypt(string $data, string $salt)
|
||||||
|
{
|
||||||
|
if (!$data || !$salt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$key = $salt . static::Passphrase();
|
||||||
|
return \json_decode(\is_callable('xxtea_decrypt')
|
||||||
|
? \xxtea_decrypt($data, $key)
|
||||||
|
: \MailSo\Base\Xxtea::decrypt($data, $key)
|
||||||
|
, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function XxteaEncrypt($data, string $salt) : string
|
||||||
|
{
|
||||||
|
if (!$data || !$salt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$data = \json_encode($data);
|
||||||
|
$key = $salt . static::Passphrase();
|
||||||
|
return \is_callable('xxtea_encrypt')
|
||||||
|
? \xxtea_encrypt($data, $key)
|
||||||
|
: \MailSo\Base\Xxtea::encrypt($data, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
'imagick' => extension_loaded('imagick'),
|
'imagick' => extension_loaded('imagick'),
|
||||||
'intl' => function_exists('idn_to_ascii'),
|
'intl' => function_exists('idn_to_ascii'),
|
||||||
'ldap' => extension_loaded('ldap'),
|
'ldap' => extension_loaded('ldap'),
|
||||||
|
'OpenSSL' => extension_loaded('openssl'),
|
||||||
'mysql' => extension_loaded('pdo_mysql'),
|
'mysql' => extension_loaded('pdo_mysql'),
|
||||||
'pgsql' => extension_loaded('pdo_pgsql'),
|
'pgsql' => extension_loaded('pdo_pgsql'),
|
||||||
'sqlite' => extension_loaded('pdo_sqlite'),
|
'sqlite' => extension_loaded('pdo_sqlite'),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue