From 9fea092c4394aa11c766aa3403199dfd4ebf5127 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 11 Jan 2022 15:55:48 +0100 Subject: [PATCH] Cleanup RainLoop\Service with improved ?admin path detection --- dev/Common/Links.js | 12 +- plugins/README.md | 4 - .../libraries/RainLoop/Plugins/Manager.php | 2 +- .../0.0.0/app/libraries/RainLoop/Service.php | 116 ++++++++---------- .../app/libraries/RainLoop/ServiceActions.php | 11 +- 5 files changed, 63 insertions(+), 82 deletions(-) diff --git a/dev/Common/Links.js b/dev/Common/Links.js index b8769e56bf..8ed3a9da77 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -6,7 +6,11 @@ const HASH_PREFIX = '#/', SERVER_PREFIX = './?', VERSION = Settings.app('version'), - VERSION_PREFIX = Settings.app('webVersionPath') || 'snappymail/v/' + VERSION + '/'; + VERSION_PREFIX = Settings.app('webVersionPath') || 'snappymail/v/' + VERSION + '/', + + adminPath = () => rl.adminArea() && !Settings.app('adminHostUse'), + + prefix = () => SERVER_PREFIX + (adminPath() ? Settings.app('adminPath') : ''); export const SUB_QUERY_PREFIX = '&q[]=', @@ -20,9 +24,7 @@ export const /** * @returns {string} */ - logoutLink = () => (rl.adminArea() && !Settings.app('adminHostUse')) - ? SERVER_PREFIX + Settings.app('adminPath') - : ROOT, + logoutLink = () => adminPath() ? prefix() : ROOT, /** * @param {string} type @@ -49,7 +51,7 @@ export const * @param {string} type * @returns {string} */ - serverRequest = type => SERVER_PREFIX + '/' + type + '/' + SUB_QUERY_PREFIX + '/0/', + serverRequest = type => prefix() + '/' + type + '/' + SUB_QUERY_PREFIX + '/0/', /** * @param {string} lang diff --git a/plugins/README.md b/plugins/README.md index 2a830deeae..fe34753101 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -178,10 +178,6 @@ $Plugin->addHook('hook.name', 'functionName'); params: array &$aPaths -### filter.http-query - params: - string &$sQuery - ### filter.json-response params: string $sAction diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php index 9f679de2de..84def593fa 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php @@ -34,7 +34,7 @@ public function __construct(\RainLoop\Actions $oActions) $this->oLogger = null; $this->oActions = $oActions; - $oConfig = $this->oActions->Config(); + $oConfig = $oActions->Config(); $this->bIsEnabled = (bool) $oConfig->Get('plugins', 'enable', false); if ($this->bIsEnabled) { $sList = $oConfig->Get('plugins', 'enabled_list', ''); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php index 7da25e125e..dd808ca713 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Service.php @@ -2,31 +2,8 @@ namespace RainLoop; -class Service +abstract class Service { - /** - * @var \MailSo\Base\Http - */ - private $oHttp; - - /** - * @var \RainLoop\Actions - */ - private $oActions; - - /** - * @var \RainLoop\ServiceActions - */ - private $oServiceActions; - - function __construct() - { - $this->oHttp = \MailSo\Base\Http::SingletonInstance(); - $this->oActions = Api::Actions(); - - $this->oServiceActions = new ServiceActions($this->oHttp, $this->oActions); - } - /** * @staticvar bool $bOne */ @@ -35,23 +12,23 @@ public static function Handle() : bool static $bOne = null; if (null === $bOne) { - $bOne = (new self)->RunResult(); + $bOne = static::RunResult(); } return $bOne; } - public function RunResult() : bool + private static function RunResult() : bool { - if ($this->oActions->Config()->Get('debug', 'enable', false)) + $oConfig = Api::Config(); + + if ($oConfig->Get('debug', 'enable', false)) { \error_reporting(E_ALL); \ini_set('display_errors', 1); \ini_set('log_errors', 1); } - $oConfig = $this->oActions->Config(); - $sServer = \trim($oConfig->Get('security', 'custom_server_signature', '')); if (\strlen($sServer)) { @@ -64,7 +41,7 @@ public function RunResult() : bool // Google FLoC \header('Permissions-Policy: interest-cohort=()'); - $this->setCSP(); + static::setCSP(); $sXFrameOptionsHeader = \trim($oConfig->Get('security', 'x_frame_options_header', '')) ?: 'DENY'; \header('X-Frame-Options: '.$sXFrameOptionsHeader); @@ -72,9 +49,10 @@ public function RunResult() : bool $sXssProtectionOptionsHeader = \trim($oConfig->Get('security', 'x_xss_protection_header', '')) ?: '1; mode=block'; \header('X-XSS-Protection: '.$sXssProtectionOptionsHeader); - if ($oConfig->Get('labs', 'force_https', false) && !$this->oHttp->IsSecure()) + $oHttp = \MailSo\Base\Http::SingletonInstance(); + if ($oConfig->Get('labs', 'force_https', false) && !$oHttp->IsSecure()) { - \header('Location: https://'.$this->oHttp->GetHost(false, false).$this->oHttp->GetUrl()); + \header('Location: https://'.$oHttp->GetHost(false, false).$oHttp->GetUrl()); exit(0); } @@ -95,32 +73,36 @@ public function RunResult() : bool } } - $this->oActions->Plugins()->RunHook('filter.http-query', array(&$sQuery)); $aPaths = \explode('/', $sQuery); -// unset($aPaths[1]); // was the rlspecauth/AuthAccountHash token - $this->oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths)); $bAdmin = false; $sAdminPanelHost = $oConfig->Get('security', 'admin_panel_host', ''); if (empty($sAdminPanelHost)) { $bAdmin = !empty($aPaths[0]) && ($oConfig->Get('security', 'admin_panel_key', '') ?: 'admin') === $aPaths[0]; + $bAdmin && \array_shift($aPaths); } - else if (empty($aPaths[0]) && - \mb_strtolower($sAdminPanelHost) === \mb_strtolower($this->oHttp->GetHost())) + else if (empty($aPaths[0]) && \mb_strtolower($sAdminPanelHost) === \mb_strtolower($oHttp->GetHost())) { $bAdmin = true; } - if ($this->oHttp->IsPost()) + $oActions = Api::Actions(); +// $oActions = $bAdmin ? new ActionsAdmin() : Api::Actions(); + + $oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths)); + + if ($oHttp->IsPost()) { - $this->oHttp->ServerNoCache(); + $oHttp->ServerNoCache(); } + $oServiceActions = new ServiceActions($oHttp, $oActions); + if ($bAdmin && !$oConfig->Get('security', 'allow_admin_panel', true)) { \MailSo\Base\Http::StatusHeader(403); - echo $this->oServiceActions->ErrorTemplates('Access Denied.', + echo $oServiceActions->ErrorTemplates('Access Denied.', 'Access to the SnappyMail Admin Panel is not allowed!'); return false; @@ -128,11 +110,11 @@ public function RunResult() : bool $bIndex = true; $sResult = ''; - if (\count($aPaths) && !empty($aPaths[0]) && !$bAdmin && 'index' !== \strtolower($aPaths[0])) + if (\count($aPaths) && !empty($aPaths[0]) && 'index' !== \strtolower($aPaths[0])) { if (!\SnappyMail\HTTP\SecFetch::isSameOrigin()) { \MailSo\Base\Http::StatusHeader(403); - echo $this->oServiceActions->ErrorTemplates('Access Denied.', + echo $oServiceActions->ErrorTemplates('Access Denied.', "Disallowed Sec-Fetch Dest: " . ($_SERVER['HTTP_SEC_FETCH_DEST'] ?? '') . " Mode: " . ($_SERVER['HTTP_SEC_FETCH_MODE'] ?? '') . " @@ -145,13 +127,13 @@ public function RunResult() : bool $sMethodName = 'Service'.\preg_replace('/@.+$/', '', $aPaths[0]); $sMethodExtra = \strpos($aPaths[0], '@') ? \preg_replace('/^[^@]+@/', '', $aPaths[0]) : ''; - if (\method_exists($this->oServiceActions, $sMethodName) && - \is_callable(array($this->oServiceActions, $sMethodName))) + if (\method_exists($oServiceActions, $sMethodName) && + \is_callable(array($oServiceActions, $sMethodName))) { - $this->oServiceActions->SetQuery($sQuery)->SetPaths($aPaths); - $sResult = $this->oServiceActions->{$sMethodName}($sMethodExtra); + $oServiceActions->SetQuery($sQuery)->SetPaths($aPaths); + $sResult = $oServiceActions->{$sMethodName}($sMethodExtra); } - else if (!$this->oActions->Plugins()->RunAdditionalPart($aPaths[0], $aPaths)) + else if (!$oActions->Plugins()->RunAdditionalPart($aPaths[0], $aPaths)) { $bIndex = true; } @@ -161,11 +143,11 @@ public function RunResult() : bool { // if (!\SnappyMail\HTTP\SecFetch::isEntering()) { \header('Content-Type: text/html; charset=utf-8'); - $this->oHttp->ServerNoCache(); + $oHttp->ServerNoCache(); if (!\is_dir(APP_DATA_FOLDER_PATH) || !\is_writable(APP_DATA_FOLDER_PATH)) { - echo $this->oServiceActions->ErrorTemplates( + echo $oServiceActions->ErrorTemplates( 'Permission denied!', 'SnappyMail can not access the data folder "'.APP_DATA_FOLDER_PATH.'"' ); @@ -173,22 +155,22 @@ public function RunResult() : bool return false; } - $sLanguage = $this->oActions->GetLanguage($bAdmin); + $sLanguage = $oActions->GetLanguage($bAdmin); $sAppJsMin = $oConfig->Get('labs', 'use_app_debug_js', false) ? '' : '.min'; $sAppCssMin = $oConfig->Get('labs', 'use_app_debug_css', false) ? '' : '.min'; $sFaviconUrl = (string) $oConfig->Get('webmail', 'favicon_url', ''); - $sFaviconPngLink = $sFaviconUrl ? $sFaviconUrl : $this->oActions->StaticPath('apple-touch-icon.png'); - $sAppleTouchLink = $sFaviconUrl ? '' : $this->oActions->StaticPath('apple-touch-icon.png'); + $sFaviconPngLink = $sFaviconUrl ? $sFaviconUrl : $oActions->StaticPath('apple-touch-icon.png'); + $sAppleTouchLink = $sFaviconUrl ? '' : $oActions->StaticPath('apple-touch-icon.png'); $aTemplateParameters = array( '{{BaseAppFaviconPngLinkTag}}' => $sFaviconPngLink ? '' : '', '{{BaseAppFaviconTouchLinkTag}}' => $sAppleTouchLink ? '' : '', - '{{BaseAppMainCssLink}}' => $this->oActions->StaticPath('css/'.($bAdmin ? 'admin' : 'app').$sAppCssMin.'.css'), - '{{BaseAppThemeCssLink}}' => $this->oActions->ThemeLink($bAdmin), - '{{BaseAppManifestLink}}' => $this->oActions->StaticPath('manifest.json'), + '{{BaseAppMainCssLink}}' => $oActions->StaticPath('css/'.($bAdmin ? 'admin' : 'app').$sAppCssMin.'.css'), + '{{BaseAppThemeCssLink}}' => $oActions->ThemeLink($bAdmin), + '{{BaseAppManifestLink}}' => $oActions->StaticPath('manifest.json'), '{{LoadingDescriptionEsc}}' => \htmlspecialchars($oConfig->Get('webmail', 'loading_description', 'SnappyMail'), ENT_QUOTES|ENT_IGNORE, 'UTF-8'), '{{BaseAppAdmin}}' => $bAdmin ? 1 : 0 ); @@ -199,22 +181,22 @@ public function RunResult() : bool $sCacheFileName = 'TMPL:' . $sLanguage . \md5( \json_encode(array( $oConfig->Get('cache', 'index', ''), - $this->oActions->Plugins()->Hash(), + $oActions->Plugins()->Hash(), $sAppJsMin, $sAppCssMin, $aTemplateParameters, APP_VERSION )) ); - $sResult = $this->oActions->Cacher()->Get($sCacheFileName); + $sResult = $oActions->Cacher()->Get($sCacheFileName); } if ($sResult) { $sResult .= ''; } else { - $aTemplateParameters['{{BaseAppThemeCss}}'] = $this->oActions->compileCss($this->oActions->GetTheme($bAdmin), $bAdmin); - $aTemplateParameters['{{BaseLanguage}}'] = $this->oActions->compileLanguage($sLanguage, $bAdmin); - $aTemplateParameters['{{BaseTemplates}}'] = $this->oServiceActions->compileTemplates($bAdmin); + $aTemplateParameters['{{BaseAppThemeCss}}'] = $oActions->compileCss($oActions->GetTheme($bAdmin), $bAdmin); + $aTemplateParameters['{{BaseLanguage}}'] = $oActions->compileLanguage($sLanguage, $bAdmin); + $aTemplateParameters['{{BaseTemplates}}'] = $oServiceActions->compileTemplates($bAdmin); $aTemplateParameters['{{BaseAppBootCss}}'] = \file_get_contents(APP_VERSION_ROOT_PATH.'static/css/boot'.$sAppCssMin.'.css'); $aTemplateParameters['{{BaseAppBootScript}}'] = \file_get_contents(APP_VERSION_ROOT_PATH.'static/js'.($sAppJsMin ? '/min' : '').'/boot'.$sAppJsMin.'.js'); $sResult = \strtr(\file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/Index.html'), $aTemplateParameters); @@ -229,17 +211,17 @@ public function RunResult() : bool $sResult = Utils::ClearHtmlOutput($sResult); } if ($sCacheFileName) { - $this->oActions->Cacher()->Set($sCacheFileName, $sResult); + $oActions->Cacher()->Set($sCacheFileName, $sResult); } } $sScriptNonce = \SnappyMail\UUID::generate(); - $this->setCSP($sScriptNonce); + static::setCSP($sScriptNonce); $sResult = \str_replace('nonce=""', 'nonce="'.$sScriptNonce.'"', $sResult); /* \preg_match(']+>(.+)', $sResult, $script); $sScriptHash = 'sha256-'.\base64_encode(\hash('sha256', $script[1], true)); - $this->setCSP(null, $sScriptHash); + static::setCSP(null, $sScriptHash); */ } else if (!\headers_sent()) @@ -251,17 +233,17 @@ public function RunResult() : bool echo $sResult; unset($sResult); - $this->oActions->BootEnd(); + $oActions->BootEnd(); return true; } - private function setCSP(string $sScriptNonce = null) : void + private static function setCSP(string $sScriptNonce = null) : void { // "img-src https:" is allowed due to remote images in e-mails - $sContentSecurityPolicy = \trim($this->oActions->Config()->Get('security', 'content_security_policy', '')) + $sContentSecurityPolicy = \trim(Api::Config()->Get('security', 'content_security_policy', '')) ?: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: https: http:; style-src 'self' 'unsafe-inline'"; - if ($this->oActions->Config()->Get('security', 'use_local_proxy_for_external_images', '')) { + if (Api::Config()->Get('security', 'use_local_proxy_for_external_images', '')) { $sContentSecurityPolicy = \preg_replace('/(img-src[^;]+)\\shttps:(\\s|;|$)/D', '$1$2', $sContentSecurityPolicy); $sContentSecurityPolicy = \preg_replace('/(img-src[^;]+)\\shttp:(\\s|;|$)/D', '$1$2', $sContentSecurityPolicy); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index d1d9c239f4..177c3cb4e0 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -99,6 +99,7 @@ public function ServiceJson() : string } else if (!empty($sAction)) { + // if ($this->oActions instanceof ActionsAdmin) if (0 === \stripos($sAction, 'Admin') && 'AdminLogin' !== $sAction && 'AdminLogout' !== $sAction) { $this->oActions->IsAdminLoggined(); } @@ -335,7 +336,7 @@ public function ServiceProxyExternal() : string { $bResult = false; $sData = empty($this->aPaths[1]) ? '' : $this->aPaths[1]; - if (!empty($sData) && $this->oActions->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) + if (!empty($sData) && $this->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) { $this->oActions->verifyCacheByKey($sData); @@ -419,8 +420,8 @@ public function ServiceRaw() : string if (\strlen($sRawError)) { - $this->oActions->Logger()->Write($sRawError, \MailSo\Log\Enumerations\Type::ERROR); - $this->oActions->Logger()->WriteDump($this->aPaths, \MailSo\Log\Enumerations\Type::ERROR, 'PATHS'); + $this->Logger()->Write($sRawError, \MailSo\Log\Enumerations\Type::ERROR); + $this->Logger()->WriteDump($this->aPaths, \MailSo\Log\Enumerations\Type::ERROR, 'PATHS'); } if ($oException) @@ -702,7 +703,7 @@ public function ServiceSso() : string } catch (\Throwable $oException) { - $this->oActions->Logger()->WriteException($oException); + $this->Logger()->WriteException($oException); } } } @@ -736,7 +737,7 @@ public function ServiceRemoteAutoLogin() : string } catch (\Throwable $oException) { - $this->oActions->Logger()->WriteException($oException); + $this->Logger()->WriteException($oException); } }