1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\System;
9:
10: use Aurora\Modules\Core\Models\User;
11: use Aurora\Modules\Core\Models\Tenant;
12: use Aurora\System\Enums\DbType;
13: use Aurora\System\Console\Commands;
14: use Aurora\System\Exceptions\ApiException;
15: use Illuminate\Container\Container;
16:
17: /**
18: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
19: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
20: * @copyright Copyright (c) 2019, Afterlogic Corp.
21: */
22: if (!defined('AU_APP_ROOT_PATH')) {
23: define('AU_APP_ROOT_PATH', rtrim(realpath(dirname(__DIR__)), '\\/') . '/');
24: }
25:
26: define('AU_API_PATH_TO_AURORA', '/../');
27:
28: define('AU_API_CRLF', PHP_EOL);
29: define('AU_API_TAB', "\t");
30:
31: define('AU_API_SESSION_WEBMAIL_NAME', 'PHPWEBMAILSESSID');
32:
33: define('AU_API_HELPDESK_PUBLIC_NAME', '_helpdesk_');
34:
35: // timezone fix
36: $sDefaultTimeZone = function_exists('date_default_timezone_get')
37: ? @date_default_timezone_get() : 'US/Pacific';
38:
39: define('AU_API_SERVER_TIME_ZONE', ($sDefaultTimeZone && 0 < strlen($sDefaultTimeZone))
40: ? $sDefaultTimeZone : 'US/Pacific');
41:
42: if (defined('AU_API_SERVER_TIME_ZONE') && function_exists('date_default_timezone_set')) {
43: @date_default_timezone_set(AU_API_SERVER_TIME_ZONE);
44: }
45:
46: unset($sDefaultTimeZone);
47:
48: /**
49: * @package Api
50: */
51: class Api
52: {
53: /**
54: * @var \Aurora\System\Module\Manager
55: */
56: public static $oModuleManager;
57:
58: /**
59: * @var array
60: */
61: public static $aModuleDecorators;
62:
63: /**
64: * @var array
65: */
66: public static $aSecretWords = [];
67:
68: /**
69: * @var bool
70: */
71: public static $bIsValid;
72:
73: /**
74: * @deprecated
75: * @var string
76: */
77: public static $sSalt;
78:
79: /**
80: * @var string
81: */
82: public static $sEncryptionKey;
83:
84: /**
85: * @var array
86: */
87: public static $aI18N = null;
88:
89: /**
90: * @var array
91: */
92: public static $aClientI18N = [];
93:
94: /**
95: * @var bool
96: */
97: public static $bUseDbLog = false;
98:
99: /**
100: * @var bool
101: */
102: public static $bDebug = false;
103:
104: /**
105: * @var array
106: */
107: protected static $aUserSession = [];
108:
109: /**
110: * @var bool
111: */
112: protected static $__SKIP_CHECK_USER_ROLE__ = false;
113:
114: /**
115: * @var string
116: */
117: protected static $sLanguage = null;
118:
119: /**
120: * @var \Aurora\System\Settings
121: */
122: protected static $oSettings;
123:
124: /**
125: * @var boolean
126: */
127: protected static $bInitialized = false;
128:
129: /**
130: *
131: */
132: protected static $oAuthenticatedUser = null;
133:
134: /**
135: * @var \Illuminate\Container\Container
136: */
137: public static $oContainer = null;
138:
139: /**
140: * @var array
141: */
142: protected static $usersCache = [];
143:
144: /**
145: * @var array
146: */
147: protected static $tenantsCache = [];
148:
149: /**
150: * @deprecated
151: * @return string
152: */
153: public static function GetSaltPath()
154: {
155: return self::DataPath() . '/salt8.php';
156: }
157:
158: /**
159: *
160: * @return string
161: */
162: public static function GetEncryptionKeyPath()
163: {
164: return self::DataPath() . '/encryption_key.php';
165: }
166:
167: /**
168: *
169: */
170: public static function InitEncryptionKey()
171: {
172: $sEncryptionKey = '';
173: $sEncryptionKeyPath = self::GetEncryptionKeyPath();
174:
175: if (!@file_exists($sEncryptionKeyPath)) {
176: if (@file_exists(self::GetSaltPath())) {
177: include self::GetSaltPath();
178: $sEncryptionKey = self::$sSalt;
179: } else {
180: $sEncryptionKey = bin2hex(random_bytes(16));
181: }
182:
183: $sEncryptionKey = '<?php \\Aurora\\System\\Api::$sEncryptionKey = "' . $sEncryptionKey . '";';
184: if (@file_put_contents($sEncryptionKeyPath, $sEncryptionKey) && @file_exists(self::GetSaltPath())) {
185: @unlink(self::GetSaltPath());
186: }
187: }
188:
189: if (is_readable($sEncryptionKeyPath)) {
190: include_once $sEncryptionKeyPath;
191: } else {
192: throw new ApiException(Notifications::SystemNotConfigured, null, 'Check the read permission of the encryption key file');
193: }
194: }
195:
196: /**
197: *
198: */
199: public static function GetUserSession()
200: {
201: return self::$aUserSession;
202: }
203:
204: /**
205: *
206: */
207: public static function SetUserSession($aUserSession)
208: {
209: self::$oAuthenticatedUser = null;
210: return self::$aUserSession = $aUserSession;
211: }
212:
213: /**
214: *
215: */
216: public static function GrantAdminPrivileges()
217: {
218: self::$aUserSession['UserId'] = -1;
219: self::$aUserSession['AuthToken'] = '';
220: }
221:
222: public static function UseDbLogs($bUseDbLogs = false)
223: {
224: self::$bUseDbLog = $bUseDbLogs;
225: }
226:
227: /**
228: *
229: * @param bool $bGrantAdminPrivileges
230: */
231: public static function Init($bGrantAdminPrivileges = false)
232: {
233: if (!defined('AU_API_INIT')) {
234: $apiInitTimeStart = \microtime(true);
235:
236: include_once self::GetVendorPath() . 'autoload.php';
237: include_once 'bootstrap.php';
238:
239: if ($bGrantAdminPrivileges) {
240: self::GrantAdminPrivileges();
241: }
242:
243: self::InitEncryptionKey();
244: self::validateApi();
245: self::GetModuleManager()->loadModules();
246:
247: define('AU_API_INIT', microtime(true) - $apiInitTimeStart);
248: }
249: }
250:
251: /**
252: *
253: * @param bool $bSkip
254: * @return bool Previous state
255: */
256: public static function skipCheckUserRole($bSkip)
257: {
258: $bResult = self::$__SKIP_CHECK_USER_ROLE__;
259: self::$__SKIP_CHECK_USER_ROLE__ = $bSkip;
260: return $bResult;
261: }
262:
263: /**
264: *
265: * @return bool
266: */
267: public static function accessCheckIsSkipped()
268: {
269: return self::$__SKIP_CHECK_USER_ROLE__;
270: }
271:
272: public static function checkUserAccess($oUser)
273: {
274: if ($oUser) {
275: $oAuthUser = Api::getAuthenticatedUser();
276: switch ($oAuthUser->Role) {
277: case \Aurora\System\Enums\UserRole::TenantAdmin:
278: if ($oUser->IdTenant !== $oAuthUser->IdTenant) {
279: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
280: }
281: break;
282: case \Aurora\System\Enums\UserRole::NormalUser:
283: if ($oUser->Id !== $oAuthUser->Id) {
284: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
285: }
286: break;
287: }
288: }
289: }
290:
291: /**
292: * @param string $sWord
293: *
294: * @return void
295: */
296: public static function AddSecret($sWord)
297: {
298: if (0 < \strlen(\trim($sWord))) {
299: self::$aSecretWords[] = $sWord;
300: self::$aSecretWords = \array_unique(self::$aSecretWords);
301: }
302: }
303:
304: /**
305: * @param array $aValues
306: *
307: * @return string
308: */
309: public static function EncodeKeyValues(array $aValues)
310: {
311: return Utils::UrlSafeBase64Encode(
312: Utils::EncryptValue(@\json_encode($aValues))
313: );
314: }
315:
316: /**
317: * Decrypts a string that is encrypted serialized array data
318: *
319: * @param string $sEncryptedValues
320: *
321: * @return array
322: */
323: public static function DecodeKeyValues(string $sEncryptedValues)
324: {
325: $sEncryptedValues = Utils::UrlSafeBase64Decode(trim($sEncryptedValues));
326:
327: $sValue = Utils::DecryptValue($sEncryptedValues);
328:
329: $aResult = @\json_decode($sValue, true);
330:
331: return \is_array($aResult) ? $aResult : array();
332: }
333:
334: /**
335: *
336: * @return \Aurora\System\Module\Manager
337: */
338: public static function GetModuleManager()
339: {
340: if (!isset(self::$oModuleManager)) {
341: self::$oModuleManager = Module\Manager::createInstance();
342: self::$aModuleDecorators = [];
343: }
344:
345: return self::$oModuleManager;
346: }
347:
348: /**
349: *
350: * @param string $sModuleName
351: * @return \Aurora\System\Module\Decorator
352: */
353: public static function GetModuleDecorator($sModuleName)
354: {
355: if (!isset(self::$aModuleDecorators[$sModuleName]) && self::GetModule($sModuleName) !== false) {
356: self::$aModuleDecorators[$sModuleName] = new Module\Decorator($sModuleName);
357: }
358:
359: return isset(self::$aModuleDecorators[$sModuleName]) ? self::$aModuleDecorators[$sModuleName] : false;
360: }
361:
362: /**
363: *
364: * @param string $sModuleName
365: * @return \Aurora\System\Module\AbstractModule
366: */
367: public static function GetModule($sModuleName)
368: {
369: return self::GetModuleManager()->GetModule($sModuleName);
370: }
371:
372: /**
373: *
374: * @param string $sModuleName
375: * @return bool
376: */
377: public static function IsModuleLoaded($sModuleName)
378: {
379: return self::GetModuleManager()->isModuleLoaded($sModuleName);
380: }
381:
382: /**
383: *
384: * @return array
385: */
386: public static function GetModules()
387: {
388: return self::GetModuleManager()->GetModules();
389: }
390:
391: /**
392: *
393: * @param string $sMethodName
394: * @param array $aParameters
395: * @return mixed
396: */
397: public static function ExecuteMethod($sMethodName, $aParameters = array())
398: {
399: list($sModuleName, $sMethodName) = explode(Module\AbstractModule::$Delimiter, $sMethodName);
400: $oModule = self::GetModule($sModuleName);
401: if ($oModule instanceof Module\AbstractModule) {
402: return $oModule->CallMethod($sModuleName, $sMethodName, $aParameters);
403: }
404: }
405:
406: /**
407: * @return \MailSo\Cache\CacheClient
408: */
409: public static function Cacher()
410: {
411: static $oCacher = null;
412: if (null === $oCacher) {
413: $oCacher = \MailSo\Cache\CacheClient::NewInstance();
414: $oCacher->SetDriver(\MailSo\Cache\Drivers\File::NewInstance(self::DataPath() . '/cache'));
415: $oCacher->SetCacheIndex(self::Version());
416: }
417:
418: return $oCacher;
419: }
420:
421: /**
422: * @return UserSession
423: */
424: public static function UserSession()
425: {
426: static $oSession = null;
427: if (null === $oSession) {
428: $oSession = new UserSession();
429: }
430:
431: return $oSession;
432: }
433:
434: /**
435: * @return \Aurora\System\Settings
436: */
437: public static function &GetSettings($force = false)
438: {
439: if (null === self::$oSettings || $force) {
440: try {
441: $sSettingsPath = \Aurora\System\Api::DataPath() . '/settings/';
442: if (!\file_exists($sSettingsPath)) {
443: set_error_handler(function () {});
444: mkdir($sSettingsPath, 0777);
445: restore_error_handler();
446: if (!file_exists($sSettingsPath)) {
447: self::$oSettings = false;
448: return self::$oSettings;
449: }
450: }
451:
452: self::$oSettings = new \Aurora\System\Settings($sSettingsPath . 'config.json');
453: self::$oSettings->Load();
454: } catch (\Aurora\System\Exceptions\BaseException $oException) {
455: self::$oSettings = false;
456: }
457: }
458: return self::$oSettings;
459: }
460:
461: /**
462: * @return bool
463: */
464: public static function UpdateSettings()
465: {
466: $bResult = true;
467: try {
468: Api::Init();
469: Api::GetModuleManager()->SyncModulesConfigs();
470: Api::GetSettings()->SyncConfigs();
471: } catch (\Exception $e) {
472: $bResult = false;
473: }
474:
475: return $bResult;
476: }
477:
478: /**
479: * @return \PDO|false
480: */
481: public static function GetPDO()
482: {
483: static $oPdoCache = null;
484: if (null !== $oPdoCache) {
485: return $oPdoCache;
486: }
487:
488: $oPdo = false;
489: $oSettings = &self::GetSettings();
490: if ($oSettings) {
491: $sDbPort = '';
492: $sUnixSocket = '';
493:
494: $iDbType = $oSettings->DBType;
495: $sDbHost = $oSettings->DBHost;
496: $sDbName = $oSettings->DBName;
497: $sDbLogin = $oSettings->DBLogin;
498: $sDbPassword = $oSettings->DBPassword;
499:
500: $iPos = strpos($sDbHost, ':');
501: if (false !== $iPos && 0 < $iPos) {
502: $sAfter = substr($sDbHost, $iPos + 1);
503: $sDbHost = substr($sDbHost, 0, $iPos);
504:
505: if (is_numeric($sAfter)) {
506: $sDbPort = $sAfter;
507: } else {
508: $sUnixSocket = $sAfter;
509: }
510: }
511:
512: if (class_exists('PDO')) {
513: try {
514: $oPdo = @new \PDO((Enums\DbType::PostgreSQL === $iDbType ? 'pgsql' : 'mysql') . ':dbname=' . $sDbName .
515: (empty($sDbHost) ? '' : ';host=' . $sDbHost) .
516: (empty($sDbPort) ? '' : ';port=' . $sDbPort) .
517: (empty($sUnixSocket) ? '' : ';unix_socket=' . $sUnixSocket) . ';charset=utf8', $sDbLogin, $sDbPassword);
518:
519: if ($oPdo) {
520: $oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
521: $oPdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES utf8");
522: }
523: } catch (\Exception $oException) {
524: self::Log($oException->getMessage(), Enums\LogLevel::Error);
525: self::Log($oException->getTraceAsString(), Enums\LogLevel::Error);
526: $oPdo = false;
527: }
528: } else {
529: self::Log('Class PDO dosn\'t exist', Enums\LogLevel::Error);
530: }
531: }
532:
533: if (false !== $oPdo) {
534: $oPdoCache = $oPdo;
535: }
536:
537: return $oPdo;
538: }
539:
540: /**
541: * @return bool
542: */
543: public static function IsMobileApplication()
544: {
545: /* @var $oIntegrator \Aurora\System\Managers\Integrator */
546: $oIntegrator = \Aurora\System\Managers\Integrator::getInstance();
547:
548: return (bool) $oIntegrator /*&& $oApiCapability->isNotLite()*/ && 1 === $oIntegrator->isMobile(); // todo
549: }
550:
551: /**
552: * @param string $sNewLocation
553: */
554: public static function Location($sNewLocation)
555: {
556: self::Log('Location: ' . $sNewLocation);
557: @header('Location: ' . $sNewLocation);
558: }
559:
560: /**
561: * @param string $sNewLocation
562: */
563: public static function Location2($sNewLocation)
564: {
565: exit('<META HTTP-EQUIV="refresh" CONTENT="0; url=' . $sNewLocation . '">');
566: }
567:
568: /**
569: * @param string $sDesc
570: * @param string $sModuleName
571: */
572: public static function LogEvent($sDesc, $sModuleName = '')
573: {
574: Logger::LogEvent($sDesc, $sModuleName);
575: }
576:
577: /**
578: * @param mixed $mObject
579: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Full
580: * @param string $sFilePrefix = ''
581: */
582: public static function LogObject($mObject, $iLogLevel = Enums\LogLevel::Full, $sFilePrefix = '')
583: {
584: Logger::LogObject($mObject, $iLogLevel, $sFilePrefix);
585: }
586:
587: /**
588: * @param Exceptions\Exception $mObject
589: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Error
590: * @param string|null $sFilePrefix = null
591: */
592: public static function LogException($mObject, $iLogLevel = Enums\LogLevel::Error, $sFilePrefix = null)
593: {
594: $sFilePrefix = $sFilePrefix ?: Logger::$sErrorLogPrefix;
595: Logger::LogException($mObject, $iLogLevel, $sFilePrefix);
596: }
597:
598: /**
599: * @param string $sFilePrefix = ''
600: *
601: * @return string
602: */
603: public static function GetLogFileName($sFilePrefix = '', $iTimestamp = 0)
604: {
605: return Logger::GetLogFileName($sFilePrefix, $iTimestamp);
606: }
607:
608: public static function GetLogFileDir()
609: {
610: return Logger::GetLogFileDir();
611: }
612:
613: /**
614: * @return \MailSo\Log\Logger
615: */
616: public static function SystemLogger()
617: {
618: return Logger::SystemLogger();
619: }
620:
621: /**
622: * @param string $sDesc
623: * @param int $iLogLevel = \Aurora\System\Enums\LogLevel::Full
624: * @param string $sFilePrefix = ''
625: */
626: public static function Log($sDesc, $iLogLevel = Enums\LogLevel::Full, $sFilePrefix = '')
627: {
628: Logger::Log($sDesc, $iLogLevel, $sFilePrefix);
629: }
630:
631: /**
632: * @param string $sDesc
633: * @param string $sLogFile
634: */
635: public static function LogOnly($sDesc, $sLogFile)
636: {
637: Logger::LogOnly($sDesc, $sLogFile);
638: }
639:
640: public static function LogSql($query)
641: {
642: $sql = $query->toSql();
643: foreach($query->getBindings() as $binding) {
644: $value = is_numeric($binding) ? $binding : "'" . $binding . "'";
645: $sql = preg_replace('/\?/', $value, $sql, 1);
646: }
647:
648: Api::Log($sql, \Aurora\System\Enums\LogLevel::Full, 'sql-');
649: }
650:
651: public static function ClearLog($sFileFullPath)
652: {
653: return Logger::ClearLog($sFileFullPath);
654: }
655:
656: public static function RemoveSeparateLogs()
657: {
658: Logger::RemoveSeparateLogs();
659: }
660:
661: public static function removeOldLogs()
662: {
663: Logger::RemoveOldLogs();
664: }
665:
666: public static function GetLoggerGuid()
667: {
668: return Logger::GetLoggerGuid();
669: }
670:
671: /**
672: * @return string
673: */
674: public static function RootPath()
675: {
676: defined('AU_API_ROOTPATH') || define('AU_API_ROOTPATH', rtrim(dirname(__FILE__), '/\\') . '/');
677: return AU_API_ROOTPATH;
678: }
679:
680: /**
681: * @return string
682: */
683: public static function WebMailPath()
684: {
685: return self::RootPath() . ltrim(AU_API_PATH_TO_AURORA, '/');
686: }
687:
688: /**
689: * @return string
690: */
691: public static function GetVendorPath()
692: {
693: return self::RootPath() . '../vendor/';
694: }
695:
696: /**
697: * @return string
698: */
699: public static function VersionFull()
700: {
701: static $sVersion = null;
702: $sAppVersion = @file_get_contents(self::WebMailPath() . 'VERSION');
703:
704: $sVersion = (empty($sAppVersion)) ? '0.0.0' : $sAppVersion;
705:
706: return $sVersion;
707: }
708:
709: /**
710: * @return string
711: */
712: public static function Version()
713: {
714: static $sVersion = null;
715: if (null === $sVersion) {
716: preg_match('/[\d\.]+/', @file_get_contents(self::WebMailPath() . 'VERSION'), $matches);
717:
718: if (isset($matches[0])) {
719: $sAppVersion = preg_replace('/[^0-9]/', '', $matches[0]);
720: }
721:
722: $sVersion = (empty($sAppVersion)) ? '0.0.0' : $sAppVersion;
723: }
724: return $sVersion;
725: }
726:
727: /**
728: * @return string
729: */
730: public static function VersionJs()
731: {
732: $oSettings = &self::GetSettings();
733: $sAppVersion = @file_get_contents(self::WebMailPath() . 'VERSION');
734: $sAppVersion = empty($sAppVersion) ? '0.0.0' : $sAppVersion;
735:
736: return preg_replace('/[^0-9]/', '', $sAppVersion);
737: }
738:
739: /**
740: * @return string
741: */
742: public static function DataPath()
743: {
744: $dataPath = 'data';
745: if (!defined('AU_API_DATA_FOLDER') && @file_exists(self::WebMailPath() . 'inc_settings_path.php')) {
746: include self::WebMailPath() . 'inc_settings_path.php';
747: }
748: if (!defined('AU_API_DATA_FOLDER')) {
749: define('AU_API_DATA_FOLDER', Utils::GetFullPath($dataPath, self::WebMailPath()));
750: }
751: $sDataFullPath = defined('AU_API_DATA_FOLDER') ? AU_API_DATA_FOLDER : '';
752:
753: /**
754: if (!\file_exists($sDataFullPath))
755: {
756: \mkdir($sDataFullPath, 0777);
757: }
758: */
759: return $sDataFullPath;
760: }
761:
762: /**
763: * @return bool
764: */
765: protected static function validateApi()
766: {
767: $iResult = 1;
768:
769: $oSettings = &self::GetSettings();
770: $iResult &= $oSettings && ($oSettings instanceof AbstractSettings);
771:
772: self::$bIsValid = (bool) $iResult;
773:
774: return self::$bIsValid;
775: }
776:
777: /**
778: * @return bool
779: */
780: public static function IsValid()
781: {
782: return (bool)self::$bIsValid;
783: }
784:
785: /**
786: * @param string $sEmail
787: * @param string $sPassword
788: * @param string $sLogin = ''
789: * @return string
790: */
791: public static function GenerateSsoToken($sEmail, $sPassword, $sLogin = '')
792: {
793: $sSsoHash = \Illuminate\Support\Str::random(32);
794: return self::Cacher()->Set('SSO:' . $sSsoHash, self::EncodeKeyValues(array(
795: 'Email' => $sEmail,
796: 'Password' => $sPassword,
797: 'Login' => $sLogin
798: ))) ? $sSsoHash : '';
799: }
800:
801: /**
802: * @param string $sLangFile
803: * @return array
804: */
805: public static function convertIniToLang($sLangFile)
806: {
807: $aResultLang = false;
808:
809: $aLang = @\parse_ini_string(file_get_contents($sLangFile), true);
810: if (is_array($aLang)) {
811: $aResultLang = array();
812: foreach ($aLang as $sKey => $mValue) {
813: if (\is_array($mValue)) {
814: foreach ($mValue as $sSecKey => $mSecValue) {
815: $aResultLang[$sKey . '/' . $sSecKey] = $mSecValue;
816: }
817: } else {
818: $aResultLang[$sKey] = $mValue;
819: }
820: }
821: }
822:
823: return $aResultLang;
824: }
825:
826: /**
827: * @param mixed $mLang
828: * @param string $sData
829: * @param array|null $aParams = null
830: * @return array
831: */
832: public static function processTranslateParams($mLang, $sData, $aParams = null, $iPlural = null)
833: {
834: $sResult = $sData;
835: if ($mLang && isset($mLang[$sData])) {
836: $sResult = $mLang[$sData];
837: }
838:
839: if (isset($iPlural)) {
840: $aPluralParts = explode('|', $sResult);
841:
842: $sResult = ($aPluralParts && $aPluralParts[$iPlural]) ? $aPluralParts[$iPlural] : (
843: $aPluralParts && $aPluralParts[0] ? $aPluralParts[0] : $sResult
844: );
845: }
846:
847: if (null !== $aParams && is_array($aParams)) {
848: foreach ($aParams as $sKey => $sValue) {
849: $sResult = str_replace('%' . $sKey . '%', $sValue, $sResult);
850: }
851: }
852:
853: return $sResult;
854: }
855:
856: /**
857: *
858: * @param string $sLanguage
859: */
860: public static function SetLanguage($sLanguage)
861: {
862: self::$sLanguage = $sLanguage;
863: }
864:
865: /**
866: *
867: * @param bool $bForNewUser
868: * @return string
869: */
870: public static function GetLanguage($bForNewUser = false)
871: {
872: $sResult = null;
873: if (isset(self::$sLanguage)) {
874: $sResult = self::$sLanguage;
875: } else {
876: $iAuthUserId = self::getAuthenticatedUserId();
877: $bSuperAdmin = $iAuthUserId === -1;
878: $oModuleManager = self::GetModuleManager();
879:
880: $sResult = $oModuleManager->getModuleConfigValue('Core', 'Language');
881: if ($oModuleManager->getModuleConfigValue('Core', 'AutodetectLanguage', true)) {
882: $sResult = self::getBrowserLanguage();
883: }
884:
885: if ($bSuperAdmin) {
886: $oSettings = &self::GetSettings();
887: $sResult = $oSettings->AdminLanguage;
888: } elseif (!$bForNewUser) {
889: $oUser = self::getAuthenticatedUser();
890: if ($oUser) {
891: $sResult = $oUser->Language;
892: } elseif (isset($_COOKIE['aurora-lang-on-login'])) {
893: $sResult = $_COOKIE['aurora-lang-on-login'];
894: }
895: }
896: }
897:
898: return $sResult;
899: }
900:
901: protected static function getBrowserLanguage()
902: {
903: $aLanguages = array(
904: 'ar-dz' => 'Arabic', 'ar-bh' => 'Arabic', 'ar-eg' => 'Arabic', 'ar-iq' => 'Arabic', 'ar-jo' => 'Arabic', 'ar-kw' => 'Arabic',
905: 'ar-lb' => 'Arabic', 'ar-ly' => 'Arabic', 'ar-ma' => 'Arabic', 'ar-om' => 'Arabic', 'ar-qa' => 'Arabic', 'ar-sa' => 'Arabic',
906: 'ar-sy' => 'Arabic', 'ar-tn' => 'Arabic', 'ar-ae' => 'Arabic', 'ar-ye' => 'Arabic', 'ar' => 'Arabic',
907: 'bg' => 'Bulgarian',
908: 'zh-cn' => 'Chinese-Simplified', 'zh-hk' => 'Chinese-Simplified', 'zh-mo' => 'Chinese-Simplified', 'zh-sg' => 'Chinese-Simplified',
909: 'zh-tw' => 'Chinese-Simplified', 'zh' => 'Chinese-Simplified',
910: 'cs' => 'Czech',
911: 'da' => 'Danish',
912: 'nl-be' => 'Dutch', 'nl' => 'Dutch',
913: 'en-au' => 'English', 'en-bz' => 'English ', 'en-ca' => 'English', 'en-ie' => 'English', 'en-jm' => 'English',
914: 'en-nz' => 'English', 'en-ph' => 'English', 'en-za' => 'English', 'en-tt' => 'English', 'en-gb' => 'English',
915: 'en-us' => 'English', 'en-zw' => 'English', 'en' => 'English', 'us' => 'English',
916: 'et' => 'Estonian', 'fi' => 'Finnish',
917: 'fr-be' => 'French', 'fr-ca' => 'French', 'fr-lu' => 'French', 'fr-mc' => 'French', 'fr-ch' => 'French', 'fr' => 'French',
918: 'de-at' => 'German', 'de-li' => 'German', 'de-lu' => 'German', 'de-ch' => 'German', 'de' => 'German',
919: 'el' => 'Greek', 'he' => 'Hebrew', 'hu' => 'Hungarian', 'it-ch' => 'Italian', 'it' => 'Italian',
920: 'ja' => 'Japanese', 'ko' => 'Korean', 'lv' => 'Latvian', 'lt' => 'Lithuanian',
921: 'nb-no' => 'Norwegian', 'nn-no' => 'Norwegian', 'no' => 'Norwegian', 'pl' => 'Polish',
922: 'pt-br' => 'Portuguese-Brazil', 'pt' => 'Portuguese-Portuguese', 'pt-pt' => 'Portuguese-Portuguese',
923: 'ro-md' => 'Romanian', 'ro' => 'Romanian',
924: 'ru-md' => 'Russian', 'ru' => 'Russian', 'sr' => 'Serbian',
925: 'es-ar' => 'Spanish', 'es-bo' => 'Spanish', 'es-cl' => 'Spanish', 'es-co' => 'Spanish', 'es-cr' => 'Spanish',
926: 'es-do' => 'Spanish', 'es-ec' => 'Spanish', 'es-sv' => 'Spanish', 'es-gt' => 'Spanish', 'es-hn' => 'Spanish)',
927: 'es-mx' => 'Spanish', 'es-ni' => 'Spanish', 'es-pa' => 'Spanish', 'es-py' => 'Spanish', 'es-pe' => 'Spanish',
928: 'es-pr' => 'Spanish', 'es-us' => 'Spanish ', 'es-uy' => 'Spanish', 'es-ve' => 'Spanish', 'es' => 'Spanish',
929: 'sv-fi' => 'Swedish', 'sv' => 'Swedish', 'th' => 'Thai', 'tr' => 'Turkish', 'uk' => 'Ukrainian', 'vi' => 'Vietnamese', 'sl' => 'Slovenian'
930: );
931:
932: $sLanguage = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']) : 'en';
933: $aTempLanguages = preg_split('/[,;]+/', $sLanguage);
934: $sLanguage = !empty($aTempLanguages[0]) ? $aTempLanguages[0] : 'en';
935:
936: $sLanguageShort = substr($sLanguage, 0, 2);
937:
938: return \array_key_exists($sLanguage, $aLanguages) ? $aLanguages[$sLanguage] :
939: (\array_key_exists($sLanguageShort, $aLanguages) ? $aLanguages[$sLanguageShort] : '');
940: }
941:
942: /**
943: * @param string $sData
944: * @param array $aParams = null
945: * @param mixed $iPluralCount = null
946: *
947: * @return string
948: */
949: public static function ClientI18N($sData, $aParams = null, $iPluralCount = null)
950: {
951: $sLanguage = self::GetLanguage();
952:
953: $aLang = null;
954: if (isset(self::$aClientI18N[$sLanguage])) {
955: $aLang = self::$aClientI18N[$sLanguage];
956: } else {
957: self::$aClientI18N[$sLanguage] = false;
958:
959: $sLangFile = self::WebMailPath() . 'i18n/' . $sLanguage . '.ini';
960: if (!@file_exists($sLangFile)) {
961: $sLangFile = self::WebMailPath() . 'i18n/English.ini';
962: $sLangFile = @file_exists($sLangFile) ? $sLangFile : '';
963: }
964:
965: if (0 < strlen($sLangFile)) {
966: $aLang = self::convertIniToLang($sLangFile);
967: if (is_array($aLang)) {
968: self::$aClientI18N[$sLanguage] = $aLang;
969: }
970: }
971: }
972:
973: return isset($iPluralCount) ? self::processTranslateParams($aLang, $sData, $aParams, self::getPlural($sLanguage, $iPluralCount)) : self::processTranslateParams($aLang, $sData, $aParams);
974: }
975:
976: public static function getPlural($sLang = '', $iNumber = 0)
977: {
978: $iResult = 0;
979: $iNumber = (int) $iNumber;
980:
981: switch ($sLang) {
982: case 'Arabic':
983: $iResult = ($iNumber === 0 ? 0 : ($iNumber === 1 ? 1 : ($iNumber === 2 ? 2 : ($iNumber % 100 >= 3 && $iNumber % 100 <= 10 ? 3 : ($iNumber % 100 >= 11 ? 4 : 5)))));
984: break;
985: case 'Bulgarian':
986: $iResult = ($iNumber === 1 ? 0 : 1);
987: break;
988: case 'Chinese-Simplified':
989: $iResult = 0;
990: break;
991: case 'Chinese-Traditional':
992: $iResult = ($iNumber === 1 ? 0 : 1);
993: break;
994: case 'Czech':
995: $iResult = ($iNumber === 1) ? 0 : (($iNumber >= 2 && $iNumber <= 4) ? 1 : 2);
996: break;
997: case 'Danish':
998: $iResult = ($iNumber === 1 ? 0 : 1);
999: break;
1000: case 'Dutch':
1001: $iResult = ($iNumber === 1 ? 0 : 1);
1002: break;
1003: case 'English':
1004: $iResult = ($iNumber === 1 ? 0 : 1);
1005: break;
1006: case 'Estonian':
1007: $iResult = ($iNumber === 1 ? 0 : 1);
1008: break;
1009: case 'Finnish':
1010: $iResult = ($iNumber === 1 ? 0 : 1);
1011: break;
1012: case 'French':
1013: $iResult = ($iNumber === 1 ? 0 : 1);
1014: break;
1015: case 'German':
1016: $iResult = ($iNumber === 1 ? 0 : 1);
1017: break;
1018: case 'Greek':
1019: $iResult = ($iNumber === 1 ? 0 : 1);
1020: break;
1021: case 'Hebrew':
1022: $iResult = ($iNumber === 1 ? 0 : 1);
1023: break;
1024: case 'Hungarian':
1025: $iResult = ($iNumber === 1 ? 0 : 1);
1026: break;
1027: case 'Italian':
1028: $iResult = ($iNumber === 1 ? 0 : 1);
1029: break;
1030: case 'Japanese':
1031: $iResult = 0;
1032: break;
1033: case 'Korean':
1034: $iResult = 0;
1035: break;
1036: case 'Latvian':
1037: $iResult = ($iNumber % 10 === 1 && $iNumber % 100 !== 11 ? 0 : ($iNumber !== 0 ? 1 : 2));
1038: break;
1039: case 'Lithuanian':
1040: $iResult = ($iNumber % 10 === 1 && $iNumber % 100 !== 11 ? 0 : ($iNumber % 10 >= 2 && ($iNumber % 100 < 10 || $iNumber % 100 >= 20) ? 1 : 2));
1041: break;
1042: case 'Norwegian':
1043: $iResult = ($iNumber === 1 ? 0 : 1);
1044: break;
1045: case 'Persian':
1046: $iResult = 0;
1047: break;
1048: case 'Polish':
1049: $iResult = ($iNumber === 1 ? 0 : ($iNumber % 10 >= 2 && $iNumber % 10 <= 4 && ($iNumber % 100 < 10 || $iNumber % 100 >= 20) ? 1 : 2));
1050: break;
1051: case 'Portuguese-Portuguese':
1052: $iResult = ($iNumber === 1 ? 0 : 1);
1053: break;
1054: case 'Portuguese-Brazil':
1055: $iResult = ($iNumber === 1 ? 0 : 1);
1056: break;
1057: case 'Romanian':
1058: $iResult = ($iNumber === 1 ? 0 : (($iNumber === 0 || ($iNumber % 100 > 0 && $iNumber % 100 < 20)) ? 1 : 2));
1059: break;
1060: case 'Russian':
1061: $iResult = ($iNumber % 10 === 1 && $iNumber % 100 !== 11 ? 0 : ($iNumber % 10 >= 2 && $iNumber % 10 <= 4 && ($iNumber % 100 < 10 || $iNumber % 100 >= 20) ? 1 : 2));
1062: break;
1063: case 'Serbian':
1064: $iResult = ($iNumber % 10 === 1 && $iNumber % 100 !== 11 ? 0 : ($iNumber % 10 >= 2 && $iNumber % 10 <= 4 && ($iNumber % 100 < 10 || $iNumber % 100 >= 20) ? 1 : 2));
1065: break;
1066: case 'Slovenian':
1067: $iResult = (($iNumber % 10 === 1 && $iNumber % 100 !== 11) ? 0 : (($iNumber % 10 === 2 && $iNumber % 100 !== 12) ? 1 : 2));
1068: break;
1069: case 'Spanish':
1070: $iResult = ($iNumber === 1 ? 0 : 1);
1071: break;
1072: case 'Swedish':
1073: $iResult = ($iNumber === 1 ? 0 : 1);
1074: break;
1075: case 'Thai':
1076: $iResult = 0;
1077: break;
1078: case 'Turkish':
1079: $iResult = ($iNumber === 1 ? 0 : 1);
1080: break;
1081: case 'Ukrainian':
1082: $iResult = ($iNumber % 10 === 1 && $iNumber % 100 !== 11 ? 0 : ($iNumber % 10 >= 2 && $iNumber % 10 <= 4 && ($iNumber % 100 < 10 || $iNumber % 100 >= 20) ? 1 : 2));
1083: break;
1084: case 'Vietnamese':
1085: $iResult = 0;
1086: break;
1087: default:
1088: $iResult = 0;
1089: break;
1090: }
1091:
1092: return $iResult;
1093: }
1094:
1095: /**
1096: * @param string $sData
1097: * @param array $aParams = null
1098: *
1099: * @return string
1100: */
1101: public static function I18N($sData, $aParams = null, $sForceCustomInitialisationLang = '')
1102: {
1103: if (null === self::$aI18N) {
1104: self::$aI18N = false;
1105:
1106: $sLangFile = '';
1107: if (0 < strlen($sForceCustomInitialisationLang)) {
1108: $sLangFile = self::RootPath() . 'common/i18n/' . $sForceCustomInitialisationLang . '.ini';
1109: }
1110:
1111: if (0 === strlen($sLangFile) || !@file_exists($sLangFile)) {
1112: $sLangFile = self::RootPath() . 'common/i18n/English.ini';
1113: }
1114:
1115: if (0 < strlen($sLangFile) && @file_exists($sLangFile)) {
1116: $aResultLang = self::convertIniToLang($sLangFile);
1117: if (is_array($aResultLang)) {
1118: self::$aI18N = $aResultLang;
1119: }
1120: }
1121: }
1122:
1123: return self::processTranslateParams(self::$aI18N, $sData, $aParams);
1124: }
1125:
1126: /**
1127: * Checks if authenticated user has at least specified role.
1128: * @param int $iRole
1129: * @throws \Aurora\System\Exceptions\ApiException
1130: */
1131: public static function checkUserRoleIsAtLeast($iRole)
1132: {
1133: if (!self::$__SKIP_CHECK_USER_ROLE__) {
1134: $oUser = self::getAuthenticatedUser();
1135: $bUserRoleIsAtLeast = $oUser === null && $iRole === Enums\UserRole::Anonymous ||
1136: $oUser !== null && $oUser->Role === Enums\UserRole::Customer &&
1137: ($iRole === Enums\UserRole::Customer || $iRole === Enums\UserRole::Anonymous) ||
1138: $oUser !== null && $oUser->Role === Enums\UserRole::NormalUser &&
1139: ($iRole === Enums\UserRole::NormalUser || $iRole === Enums\UserRole::Customer || $iRole === Enums\UserRole::Anonymous) ||
1140: $oUser !== null && $oUser->Role === Enums\UserRole::TenantAdmin &&
1141: ($iRole === Enums\UserRole::TenantAdmin || $iRole === Enums\UserRole::NormalUser || $iRole === Enums\UserRole::Customer || $iRole === Enums\UserRole::Anonymous) ||
1142: $oUser !== null && $oUser->Role === Enums\UserRole::SuperAdmin &&
1143: ($iRole === Enums\UserRole::SuperAdmin || $iRole === Enums\UserRole::TenantAdmin || $iRole === Enums\UserRole::NormalUser || $iRole === Enums\UserRole::Customer || $iRole === Enums\UserRole::Anonymous);
1144: if (!$bUserRoleIsAtLeast) {
1145: throw new Exceptions\ApiException(Notifications::AccessDenied, null, 'AccessDenied');
1146: }
1147: }
1148: }
1149:
1150: public static function getAuthTokenFromHeaders()
1151: {
1152: $sResult = false;
1153: $sAuthHeader = \MailSo\Base\Http::SingletonInstance()->GetHeader('Authorization');
1154: if (!empty($sAuthHeader)) {
1155: $authHeaderData = explode(' ', $sAuthHeader);
1156:
1157: if (isset($authHeaderData[0]) && strtolower($authHeaderData[0]) === 'bearer' && isset($authHeaderData[1]) && !empty($authHeaderData[1])) {
1158: $sResult = $authHeaderData[1];
1159: }
1160: }
1161:
1162: return $sResult;
1163: }
1164:
1165: public static function requireAdminAuth()
1166: {
1167: $mResult = false;
1168: $response = new \Sabre\HTTP\Response();
1169: $basicAuth = new \Sabre\HTTP\Auth\Basic("Locked down area", \Sabre\HTTP\Sapi::getRequest(), $response);
1170: if (!$userPass = $basicAuth->getCredentials()) {
1171: $basicAuth->requireLogin();
1172: \Sabre\HTTP\Sapi::sendResponse($response);
1173: } elseif (!\Aurora\Modules\AdminAuth\Module::getInstance()->Login($userPass[0], $userPass[1])) {
1174: $basicAuth->requireLogin();
1175: \Sabre\HTTP\Sapi::sendResponse($response);
1176: } else {
1177: $mResult = true;
1178: }
1179:
1180: if (!$mResult) {
1181: $response->setBody('Unauthorized');
1182: \Sabre\HTTP\Sapi::sendResponse($response);
1183: exit;
1184: }
1185: }
1186:
1187: public static function getDeviceIdFromHeaders()
1188: {
1189: $sResult = false;
1190: $sDeviceIdHeader = \MailSo\Base\Http::SingletonInstance()->GetHeader('X-DeviceId');
1191: if (!empty($sDeviceIdHeader)) {
1192: $sResult = $sDeviceIdHeader;
1193: }
1194:
1195: return $sResult;
1196: }
1197:
1198: /**
1199: *
1200: * @return string
1201: */
1202: public static function getAuthToken()
1203: {
1204: $sAuthToken = $_COOKIE[Application::AUTH_TOKEN_KEY] ?? false;
1205: if (!$sAuthToken) {
1206: $sAuthToken = self::getAuthTokenFromHeaders();
1207: }
1208:
1209: return $sAuthToken;
1210: }
1211:
1212: /**
1213: *
1214: * @return \Aurora\Modules\Core\Models\User
1215: */
1216: public static function authorise($sAuthToken = '')
1217: {
1218: $oUser = null;
1219: $mUserId = false;
1220: try {
1221: if (isset(self::$aUserSession['UserId'])) {
1222: $mUserId = self::$aUserSession['UserId'];
1223: } else {
1224: $sAuthToken = empty($sAuthToken) ? self::getAuthToken() : $sAuthToken;
1225:
1226: $mUserId = self::getAuthenticatedUserId($sAuthToken);
1227: }
1228: $oUser = self::getUserById($mUserId);
1229: } catch (\Exception $oException) {
1230: }
1231: return $oUser;
1232: }
1233:
1234: public static function getAuthenticatedUserInfo($sAuthToken = '')
1235: {
1236: $mResult = false;
1237: if (empty($sAuthToken)) {
1238: if (is_array(self::$aUserSession) && isset(self::$aUserSession['AuthToken'])) {
1239: $sAuthToken = self::$aUserSession['AuthToken'];
1240: }
1241: }
1242: /* @var $oIntegrator \Aurora\System\Managers\Integrator */
1243: $oIntegrator = \Aurora\System\Managers\Integrator::getInstance();
1244: if ($oIntegrator) {
1245: $mResult = $oIntegrator->getAuthenticatedUserInfo($sAuthToken);
1246: }
1247:
1248: return $mResult;
1249: }
1250:
1251: public static function validateAuthToken($authToken = null)
1252: {
1253: if ($authToken === null) {
1254: $authToken = self::getAuthToken();
1255: }
1256: if ($authToken && !self::UserSession()->Get($authToken)) {
1257: throw new ApiException(Notifications::InvalidToken);
1258: }
1259: }
1260:
1261: public static function getCookiePath()
1262: {
1263: static $sPath = false;
1264:
1265: if (false === $sPath) {
1266: $sScriptName = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '';
1267: $aPath = explode('/', $sScriptName);
1268: $sLastPathItem = count($aPath) > 0 ? $aPath[count($aPath) - 1] : '';
1269: if (count($aPath) > 0 && ($sLastPathItem !== '' || strtolower(substr($sLastPathItem, -1)) === '.php')) {
1270: array_pop($aPath);
1271: }
1272: $sPath = implode('/', $aPath) . '/';
1273: }
1274:
1275: return $sPath;
1276: }
1277:
1278: public static function getCookieSecure()
1279: {
1280: return self::isHttps();
1281: }
1282:
1283: public static function isHttps()
1284: {
1285: return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
1286: (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443');
1287: }
1288:
1289: public static function getAuthenticatedUserId($sAuthToken = '')
1290: {
1291: $mResult = false;
1292: if (!empty($sAuthToken)) {
1293: $aInfo = \Aurora\System\Managers\Integrator::getInstance()->getAuthenticatedUserInfo($sAuthToken);
1294: if (!empty(self::$aUserSession['UserId']) && (int) $aInfo['userId'] === (int) self::$aUserSession['UserId']) {
1295: $mResult = (int) self::$aUserSession['UserId'];
1296: } else {
1297: $mResult = $aInfo['userId'];
1298: self::$aUserSession['UserId'] = (int) $mResult;
1299: self::$aUserSession['AuthToken'] = $sAuthToken;
1300: }
1301: } else {
1302: if (is_array(self::$aUserSession) && isset(self::$aUserSession['UserId'])) {
1303: $mResult = self::$aUserSession['UserId'];
1304: } else {
1305: $mResult = 0;
1306: }
1307: }
1308:
1309: return $mResult;
1310: }
1311:
1312: public static function getAuthenticatedUserPublicId($sAuthToken = '')
1313: {
1314: $iUserId = self::getAuthenticatedUserId($sAuthToken);
1315: return self::getUserPublicIdById($iUserId);
1316: }
1317:
1318: /**
1319: * @return void
1320: */
1321: public static function unsetAuthenticatedUser()
1322: {
1323: unset(self::$oAuthenticatedUser);
1324: }
1325:
1326: /**
1327: * @param string $sAuthToken
1328: * @param bool $bForce
1329: *
1330: * @return \Aurora\Modules\Core\Models\User
1331: */
1332: public static function getAuthenticatedUser($sAuthToken = '', $bForce = false)
1333: {
1334: $iUserId = 0;
1335: if (null === self::$oAuthenticatedUser || $bForce) {
1336: if (!empty($sAuthToken)) {
1337: $iUserId = self::getAuthenticatedUserId($sAuthToken); // called for saving in session
1338: } elseif (!empty(self::$aUserSession['UserId'])) {
1339: $iUserId = self::$aUserSession['UserId'];
1340: }
1341:
1342: self::$oAuthenticatedUser = self::getUserById($iUserId);
1343: }
1344: return self::$oAuthenticatedUser;
1345: }
1346:
1347: public static function getAuthenticatedUserAuthToken()
1348: {
1349: $mResult = false;
1350:
1351: if (is_array(self::$aUserSession) && isset(self::$aUserSession['AuthToken'])) {
1352: $mResult = self::$aUserSession['AuthToken'];
1353: }
1354:
1355: return $mResult;
1356: }
1357:
1358: /**
1359: * @param int $iUserId
1360: * @return string
1361: */
1362: public static function getUserUUIDById($iUserId)
1363: {
1364: $sUUID = '';
1365:
1366: if (\is_numeric($iUserId)) {
1367: $oUser = self::getUserById($iUserId);
1368:
1369: if ($oUser instanceof User) {
1370: $sUUID = $oUser->UUID;
1371: }
1372: } else {
1373: $sUUID = $iUserId;
1374: }
1375:
1376: return $sUUID;
1377: }
1378:
1379: /**
1380: * @param int $iUserId
1381: * @return string
1382: */
1383: public static function getUserPublicIdById($iUserId)
1384: {
1385: $sPublicId = '';
1386:
1387: if (\is_numeric($iUserId)) {
1388: $oUser = self::getUserById($iUserId);
1389: if ($oUser) {
1390: return $oUser->PublicId;
1391: }
1392: // @TODO: check if it's needed
1393: } else {
1394: $sPublicId = $iUserId;
1395: }
1396:
1397: return $sPublicId;
1398: }
1399:
1400: /**
1401: * @param string $sPublicId
1402: * @return int|false
1403: */
1404: public static function getUserIdByPublicId($sPublicId)
1405: {
1406: $iUserId = false;
1407:
1408: if (Api::GetSettings()->GetValue('AdminLogin') === $sPublicId) { // superadmin user
1409: return -1;
1410: }
1411:
1412: $user = self::getUserByPublicId($sPublicId);
1413: if ($user instanceof User) {
1414: $iUserId = $user->Id;
1415: }
1416:
1417: return $iUserId;
1418: }
1419:
1420: public static function getUserByPublicId($sPublicId, $bForce = false)
1421: {
1422: $result = null;
1423: if (!$bForce) {
1424: foreach (self::$usersCache as $user) {
1425: if ($user->PublicId === $sPublicId) {
1426: $result = $user;
1427: break;
1428: }
1429: }
1430: }
1431: if (!$result) {
1432: $result = User::where('PublicId', $sPublicId)->first();
1433: if ($result) {
1434: self::$usersCache[$result->Id] = $result;
1435: }
1436: }
1437:
1438: return $result;
1439: }
1440:
1441: public static function getUserById($iUserId, $bForce = false)
1442: {
1443: try {
1444: if (!isset(self::$usersCache[$iUserId]) || $bForce) {
1445: $oUser = Managers\Integrator::getUserByIdHelper($iUserId);
1446: if ($oUser) {
1447: self::$usersCache[$iUserId] = $oUser;
1448: }
1449: }
1450: } catch (\Exception $oEx) {
1451: self::LogException($oEx);
1452: }
1453:
1454: return self::$usersCache[$iUserId] ?? null;
1455: }
1456:
1457: public static function removeUserFromCache($iUserId)
1458: {
1459: if (!isset(self::$usersCache[$iUserId])) {
1460: unset(self::$usersCache[$iUserId]);
1461: }
1462: }
1463:
1464: public static function getTenantById($iTenantId, $bForce = false)
1465: {
1466: try {
1467: if (!isset(self::$tenantsCache[$iTenantId]) || $bForce) {
1468: $oTenant = Tenant::find($iTenantId);
1469: if ($oTenant) {
1470: self::$tenantsCache[$iTenantId] = $oTenant;
1471: }
1472: }
1473: } catch (\Exception $oEx) {
1474: self::LogException($oEx);
1475: }
1476:
1477: return self::$tenantsCache[$iTenantId] ?? null;
1478: }
1479:
1480: public static function getTenantByWebDomain()
1481: {
1482: static $bTenantInitialized = false;
1483: static $oTenant = null;
1484:
1485: if (!$bTenantInitialized) {
1486: if (!empty($_SERVER['SERVER_NAME'])) {
1487:
1488: foreach (self::$tenantsCache as $tenantCache) {
1489: if ($tenantCache->WebDomain === $_SERVER['SERVER_NAME']) {
1490: $oTenant = $tenantCache;
1491: break;
1492: }
1493: }
1494:
1495: if (!$oTenant) {
1496: $oTenant = Tenant::firstWhere('WebDomain', $_SERVER['SERVER_NAME']);
1497: if ($oTenant) {
1498: self::$tenantsCache[$oTenant->Id] = $oTenant;
1499: }
1500: }
1501: }
1502: $bTenantInitialized = true;
1503: }
1504:
1505: return $oTenant;
1506: }
1507:
1508: public static function removeTenantFromCache($iTenantId)
1509: {
1510: if (!isset(self::$tenantsCache[$iTenantId])) {
1511: unset(self::$tenantsCache[$iTenantId]);
1512: }
1513: }
1514:
1515: public static function setTenantName($sTenantName)
1516: {
1517: self::$aUserSession['TenantName'] = $sTenantName;
1518: }
1519:
1520: public static function setUserId($iUserId)
1521: {
1522: self::$aUserSession['UserId'] = (int) $iUserId;
1523: }
1524:
1525: public static function setAuthToken($sAuthToken)
1526: {
1527: self::$aUserSession['AuthToken'] = $sAuthToken;
1528: }
1529:
1530: public static function getCurrentTenant()
1531: {
1532: static $bTenantInitialized = false;
1533: static $oTenant = null;
1534:
1535: if (!$bTenantInitialized) {
1536: $oUser = self::getAuthenticatedUser();
1537:
1538: if ($oUser && !$oUser->isAdmin()) {
1539: $oTenant = self::getTenantById($oUser->IdTenant);
1540: }
1541:
1542: if (!$oUser && !$oTenant) {
1543: $oTenant = self::getTenantByWebDomain();
1544: }
1545:
1546: // $bTenantInitialized = true;
1547: }
1548:
1549: return $oTenant;
1550: }
1551:
1552: /**
1553: *
1554: * @return string
1555: */
1556: public static function getTenantName()
1557: {
1558: static $mResult = null;
1559:
1560: if (!isset($mResult)) {
1561: if (is_array(self::$aUserSession) && !empty(self::$aUserSession['TenantName'])) {
1562: $mResult = self::$aUserSession['TenantName'];
1563: } else {
1564: try {
1565: $oTenant = self::getCurrentTenant();
1566: if ($oTenant) {
1567: $mResult = $oTenant->Name;
1568: }
1569: } catch (\Exception $oEx) {
1570: $mResult = false;
1571: }
1572: }
1573: // $bTenantInitialized = true;
1574: }
1575:
1576: return $mResult;
1577: }
1578:
1579: public static function GetDbConfig($DbType, $DbHost, $DbName, $DbPrefix, $DbLogin, $DbPassword)
1580: {
1581: $aDbHost = \explode(':', $DbHost);
1582: if (isset($aDbHost[0])) {
1583: $DbHost = $aDbHost[0];
1584: }
1585: $aDbConfig = [
1586: 'driver' => DbType::PostgreSQL === $DbType ? 'pgsql' : 'mysql',
1587: 'host' => $DbHost,
1588: 'database' => $DbName,
1589: 'username' => $DbLogin,
1590: 'password' => $DbPassword,
1591: 'charset' => 'utf8mb4',
1592: 'collation' => 'utf8mb4_unicode_ci',
1593: 'prefix' => $DbPrefix,
1594: ];
1595: if (isset($aDbHost[1])) {
1596: $aDbConfig['port'] = $aDbHost[1];
1597: }
1598:
1599: return $aDbConfig;
1600: }
1601:
1602: public static function CreateContainer($force = false)
1603: {
1604: if (!isset(self::$oContainer) || $force) {
1605: // Instantiate the app container
1606: $appContainer = Container::getInstance();
1607:
1608: // Tell facade about the application instance
1609: \Illuminate\Support\Facades\Facade::setFacadeApplication($appContainer);
1610:
1611: $appContainer['app'] = $appContainer;
1612:
1613: $appContainer['config'] = new \Illuminate\Config\Repository();
1614:
1615: $oSettings = &Api::GetSettings();
1616: if ($oSettings) {
1617: $capsule = new \Illuminate\Database\Capsule\Manager();
1618: $appContainer['capsule'] = $capsule;
1619: $capsule->addConnection(
1620: self::GetDbConfig(
1621: $oSettings->DBType,
1622: $oSettings->DBHost,
1623: $oSettings->DBName,
1624: $oSettings->DBPrefix,
1625: $oSettings->DBLogin,
1626: $oSettings->DBPassword
1627: )
1628: );
1629:
1630: //Make this Capsule instance available globally.
1631: $capsule->setAsGlobal();
1632:
1633: // Setup the Eloquent ORM.
1634: $capsule->bootEloquent();
1635:
1636: $appContainer['connection'] = function ($ac) use ($capsule) {
1637: return $capsule->getConnection('default');
1638: };
1639:
1640: $appContainer['migration-table'] = 'migrations';
1641:
1642: $appContainer['filesystem'] = function ($ac) {
1643: return new \Illuminate\Filesystem\Filesystem();
1644: };
1645:
1646: $appContainer['resolver'] = function ($ac) {
1647: $r = new \Illuminate\Database\ConnectionResolver(['default' => $ac['connection']]);
1648: $r->setDefaultConnection('default');
1649: return $r;
1650: };
1651:
1652: $appContainer['migration-repo'] = function ($ac) {
1653: return new \Illuminate\Database\Migrations\DatabaseMigrationRepository($ac['resolver'], $ac['migration-table']);
1654: };
1655:
1656: $appContainer['migrator'] = function ($ac) {
1657: return new \Illuminate\Database\Migrations\Migrator($ac['migration-repo'], $ac['resolver'], $ac['filesystem']);
1658: };
1659:
1660: $appContainer['migration-creator'] = function ($ac) {
1661: return new \Illuminate\Database\Migrations\MigrationCreator($ac['filesystem'], \Aurora\Api::RootPath() . 'Console' . DIRECTORY_SEPARATOR . 'stubs');
1662: };
1663:
1664: $appContainer['composer'] = function ($ac) {
1665: return new \Illuminate\Support\Composer($ac['filesystem']);
1666: };
1667:
1668: $appContainer['console'] = function ($ac) {
1669: $consoleaApp = new \Symfony\Component\Console\Application();
1670:
1671: $events = new \Illuminate\Events\Dispatcher($ac);
1672:
1673: $consoleaApp = new \Illuminate\Console\Application($ac, $events, 'Version 1.0');
1674: $consoleaApp->setName('Aurora console app');
1675:
1676: $consoleaApp->add(new Commands\Migrations\InstallCommand($ac['migration-repo']));
1677: $consoleaApp->add(new Commands\Migrations\MigrateCommand($ac['migrator']));
1678: $consoleaApp->add(new Commands\Migrations\RollbackCommand($ac['migrator']));
1679: $consoleaApp->add(new Commands\Migrations\MigrateMakeCommand($ac['migration-creator'], $ac['composer']));
1680:
1681: $consoleaApp->add(new Commands\Seeds\SeedCommand($ac['resolver']));
1682: $consoleaApp->add(new Commands\Seeds\SeederMakeCommand($ac['filesystem'], $ac['composer']));
1683:
1684: $consoleaApp->add(new Commands\OrphansCommand());
1685:
1686: $consoleaApp->add(new Commands\ModelsCommand($ac));
1687:
1688: return $consoleaApp;
1689: };
1690:
1691: self::$oContainer = $appContainer;
1692: }
1693: }
1694: }
1695:
1696: /**
1697: * @return Container
1698: */
1699: public static function GetContainer($force = false)
1700: {
1701: self::CreateContainer($force);
1702:
1703: return self::$oContainer;
1704: }
1705:
1706: public static function CheckAccess(&$UserId)
1707: {
1708: if (self::accessCheckIsSkipped()) {
1709: return;
1710: }
1711: $bAccessDenied = true;
1712:
1713: $oAuthenticatedUser = self::getAuthenticatedUser();
1714:
1715: if ($UserId === null) {
1716: $iUserId = $oAuthenticatedUser->Id;
1717: } else {
1718: $iUserId = (int) $UserId;
1719:
1720: $iUserRole = $oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User ? $oAuthenticatedUser->Role : \Aurora\System\Enums\UserRole::Anonymous;
1721: switch ($iUserRole) {
1722: case (\Aurora\System\Enums\UserRole::SuperAdmin):
1723: // everything is allowed for SuperAdmin
1724: $UserId = $iUserId;
1725: $bAccessDenied = false;
1726: break;
1727: case (\Aurora\System\Enums\UserRole::TenantAdmin):
1728: // everything is allowed for TenantAdmin
1729: $oUser = \Aurora\Modules\Core\Module::getInstance()->GetUser($iUserId);
1730: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
1731: if ($oAuthenticatedUser->IdTenant === $oUser->IdTenant) {
1732: $UserId = $iUserId;
1733: $bAccessDenied = false;
1734: }
1735: }
1736: break;
1737: case (\Aurora\System\Enums\UserRole::NormalUser):
1738: // User identifier shoud be checked
1739: if ($iUserId === $oAuthenticatedUser->Id) {
1740: $UserId = $iUserId;
1741: $bAccessDenied = false;
1742: }
1743: break;
1744: case (\Aurora\System\Enums\UserRole::Customer):
1745: case (\Aurora\System\Enums\UserRole::Anonymous):
1746: // everything is forbidden for Customer and Anonymous users
1747: break;
1748: }
1749: if ($bAccessDenied) {
1750: throw new ApiException(\Aurora\System\Notifications::AccessDenied, null, 'AccessDenied');
1751: }
1752: }
1753: }
1754:
1755: public static function setCookie($name, $value, $expires = 0, $httpOnly = true, $sameSite = 'Strict')
1756: {
1757: @\setcookie(
1758: $name,
1759: $value,
1760: [
1761: 'expires' => $expires,
1762: 'path' => self::getCookiePath(),
1763: 'domain' => '',
1764: 'httponly' => $httpOnly,
1765: 'secure' => self::getCookieSecure(),
1766: 'samesite' => $sameSite // None || Lax || Strict
1767: ]
1768: );
1769: }
1770:
1771: public static function setAuthTokenCookie($authToken)
1772: {
1773: $iAuthTokenCookieExpireTime = (int) self::GetModuleManager()->getModuleConfigValue('Core', 'AuthTokenCookieExpireTime');
1774: $sSameSite = self::GetModuleManager()->getModuleConfigValue('Core', 'CookieSameSite', 'Strict');
1775:
1776: self::setCookie(
1777: \Aurora\System\Application::AUTH_TOKEN_KEY,
1778: $authToken,
1779: ($iAuthTokenCookieExpireTime === 0) ? 0 : \strtotime("+$iAuthTokenCookieExpireTime days"),
1780: true,
1781: $sSameSite
1782: );
1783: }
1784:
1785: public static function unsetAuthTokenCookie()
1786: {
1787: self::setCookie(
1788: \Aurora\System\Application::AUTH_TOKEN_KEY,
1789: '',
1790: -1,
1791: true,
1792: self::GetModuleManager()->getModuleConfigValue('Core', 'CookieSameSite', 'Strict')
1793: );
1794: }
1795: }
1796: