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\Modules\Core;
9:
10: use Aurora\Api;
11: use Aurora\Modules\Contacts\Enums\StorageType;
12: use Aurora\Modules\Contacts\Module as ContactsModule;
13: use Aurora\Modules\Core\Enums\ErrorCodes;
14: use Aurora\Modules\Core\Models\Group;
15: use Aurora\Modules\Core\Models\User;
16: use Aurora\Modules\Core\Models\UserBlock;
17: use Aurora\System\Enums\UserRole;
18: use Aurora\System\Exceptions\ApiException;
19: use Aurora\System\Notifications;
20: use Illuminate\Database\Eloquent\Builder;
21: use Symfony\Component\Console\Input\ArrayInput;
22: use Symfony\Component\Console\Output\NullOutput;
23: use Symfony\Component\Console\Output\BufferedOutput;
24: use Aurora\System\Logger;
25: use Aurora\System\Managers\Integrator;
26:
27: /**
28: * System module that provides core functionality such as User management, Tenants management.
29: *
30: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
31: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
32: * @copyright Copyright (c) 2023, Afterlogic Corp.
33: *
34: * @property Settings $oModuleSettings
35: *
36: * @package Modules
37: */
38: class Module extends \Aurora\System\Module\AbstractModule
39: {
40: protected $oTenantsManager = null;
41:
42: protected $oChannelsManager = null;
43:
44: protected $oUsersManager = null;
45:
46: protected $oIntegratorManager = null;
47:
48: /**
49: * @return Module
50: */
51: public static function getInstance()
52: {
53: return parent::getInstance();
54: }
55:
56: /**
57: * @return Module
58: */
59: public static function Decorator()
60: {
61: return parent::Decorator();
62: }
63:
64: /**
65: * @return Settings
66: */
67: public function getModuleSettings()
68: {
69: return $this->oModuleSettings;
70: }
71:
72: /**
73: * @return Managers\Tenants
74: */
75: public function getTenantsManager()
76: {
77: if ($this->oTenantsManager === null) {
78: $this->oTenantsManager = new Managers\Tenants($this);
79: }
80:
81: return $this->oTenantsManager;
82: }
83:
84: /**
85: * @return Managers\Channels
86: */
87: public function getChannelsManager()
88: {
89: if ($this->oChannelsManager === null) {
90: $this->oChannelsManager = new Managers\Channels($this);
91: }
92:
93: return $this->oChannelsManager;
94: }
95:
96: /**
97: * @return Managers\Users
98: */
99: public function getUsersManager()
100: {
101: if ($this->oUsersManager === null) {
102: $this->oUsersManager = new Managers\Users($this);
103: }
104:
105: return $this->oUsersManager;
106: }
107:
108: /**
109: * @return \Aurora\System\Managers\Integrator
110: */
111: public function getIntegratorManager()
112: {
113: if ($this->oIntegratorManager === null) {
114: $this->oIntegratorManager = new \Aurora\System\Managers\Integrator();
115: }
116:
117: return $this->oIntegratorManager;
118: }
119:
120: /***** private functions *****/
121: /**
122: * Initializes Core Module.
123: *
124: * @ignore
125: */
126: public function init()
127: {
128: $this->aErrors = [
129: Enums\ErrorCodes::ChannelDoesNotExist => $this->i18N('ERROR_CHANNEL_NOT_EXISTS'),
130: Enums\ErrorCodes::TenantAlreadyExists => $this->i18N('ERROR_TENANT_ALREADY_EXISTS'),
131: Enums\ErrorCodes::GroupAlreadyExists => $this->i18N('ERROR_GROUP_ALREADY_EXISTS'),
132: Enums\ErrorCodes::MySqlConfigError => 'Please make sure your PHP/MySQL environment meets the minimal system requirements.',
133: ];
134:
135: \Aurora\System\Router::getInstance()->registerArray(
136: self::GetName(),
137: [
138: 'api' => [$this, 'EntryApi'],
139: 'ping' => [$this, 'EntryPing'],
140: 'pull' => [$this, 'EntryPull'],
141: 'mobile' => [$this, 'EntryMobile'],
142: 'sso' => [$this, 'EntrySso'],
143: 'postlogin' => [$this, 'EntryPostlogin'],
144: 'file-cache' => [$this, 'EntryFileCache']
145: ]
146: );
147:
148: \Aurora\System\EventEmitter::getInstance()->onAny(
149: [
150: ['CreateAccount', [$this, 'onCreateAccount'], 100],
151: ['Core::GetCompatibilities::after', [$this, 'onAfterGetCompatibilities']],
152: ['System::RunEntry::before', [$this, 'onBeforeRunEntry'], 100]
153: ]
154: );
155:
156: $this->denyMethodsCallByWebApi([
157: 'Authenticate',
158: 'UpdateUserObject',
159: 'GetUserByUUID',
160: 'GetUserByPublicId',
161: 'GetAdminUser',
162: 'GetTenantWithoutRoleCheck',
163: 'GetTenantName',
164: 'GetTenantIdByName',
165: 'GetDefaultGlobalTenant',
166: 'UpdateTenantObject',
167: 'GetUserWithoutRoleCheck',
168: 'UpdateTokensValidFromTimestamp',
169: 'GetAccountUsedToAuthorize',
170: 'GetDigestHash',
171: 'VerifyPassword',
172: 'SetAuthDataAndGetAuthToken',
173: 'IsModuleDisabledForObject',
174: 'GetBlockedUser',
175: 'BlockUser',
176: 'IsBlockedUser',
177: 'GetAllGroup',
178: 'CheckIpReputation'
179: ]);
180: }
181:
182: /**
183: *
184: * @return mixed
185: */
186: private function getUploadData()
187: {
188: $mResult = false;
189: $oFile = null;
190: if (count($_FILES) > 0) {
191: $oFile = current($_FILES);
192: }
193: if (isset($oFile, $oFile['name'], $oFile['tmp_name'], $oFile['size'], $oFile['type'])) {
194: $iError = (isset($oFile['error'])) ? (int) $oFile['error'] : UPLOAD_ERR_OK;
195: $mResult = (UPLOAD_ERR_OK === $iError) ? $oFile : false;
196: }
197:
198: return $mResult;
199: }
200:
201: /**
202: * Is called by CreateAccount event. Finds or creates and returns User for new account.
203: *
204: * @ignore
205: * @param array $Args {
206: * *int* **UserId** Identifier of existing user.
207: * *int* **TenantId** Identifier of tenant for creating new user in it.
208: * *int* **$PublicId** New user name.
209: * }
210: * @param Models\User $Result
211: */
212: public function onCreateAccount(&$Args, &$Result)
213: {
214: $oUser = null;
215:
216: if (isset($Args['UserId']) && (int)$Args['UserId'] > 0) {
217: $oUser = $this->getUsersManager()->getUser($Args['UserId']);
218: } else {
219: $Email = (isset($Args['Email'])) ? $Args['Email'] : '';
220: $PublicId = (isset($Args['PublicId'])) ? $Args['PublicId'] : '';
221: $sPublicId = null;
222: if (!empty($PublicId)) {
223: $sPublicId = $PublicId;
224: } elseif (!empty($Email)) {
225: $sPublicId = $Email;
226: }
227: if (!empty($sPublicId)) {
228: $oUser = $this->getUsersManager()->getUserByPublicId($sPublicId);
229: }
230: if (!isset($oUser)) {
231: $bPrevState = Api::skipCheckUserRole(true);
232: $iUserId = self::Decorator()->CreateUser(isset($Args['TenantId']) ? (int) $Args['TenantId'] : 0, $sPublicId);
233: Api::skipCheckUserRole($bPrevState);
234: $oUser = $this->getUsersManager()->getUser($iUserId);
235: }
236:
237: if (isset($oUser) && isset($oUser->Id)) {
238: $Args['UserId'] = $oUser->Id;
239: }
240: }
241:
242: $Result = $oUser;
243: }
244:
245: /**
246: * @ignore
247: * @param array $aArgs
248: * @param array $mResult
249: */
250: public function onAfterGetCompatibilities($aArgs, &$mResult)
251: {
252: $aCompatibility['php.version'] = phpversion();
253: $aCompatibility['php.version.valid'] = (int) (version_compare($aCompatibility['php.version'], '7.2.5') > -1);
254:
255: $aCompatibility['safe-mode'] = @ini_get('safe_mode');
256: $aCompatibility['safe-mode.valid'] = is_numeric($aCompatibility['safe-mode'])
257: ? !((bool) $aCompatibility['safe-mode'])
258: : ('off' === strtolower($aCompatibility['safe-mode']) || empty($aCompatibility['safe-mode']));
259:
260: $aCompatibility['mysql.valid'] = (int) extension_loaded('mysql');
261: $aCompatibility['pdo.valid'] = (int)
262: ((bool) extension_loaded('pdo') && (bool) extension_loaded('pdo_mysql'));
263:
264: $aCompatibility['mysqlnd.valid'] = (int) (
265: function_exists('mysqli_fetch_all') &&
266: strpos(mysqli_get_client_info(), "mysqlnd") !== false
267: );
268:
269: $aCompatibility['socket.valid'] = (int) function_exists('fsockopen');
270: $aCompatibility['iconv.valid'] = (int) function_exists('iconv');
271: $aCompatibility['curl.valid'] = (int) function_exists('curl_init');
272: $aCompatibility['mbstring.valid'] = (int) function_exists('mb_detect_encoding');
273: $aCompatibility['openssl.valid'] = (int) extension_loaded('openssl');
274: $aCompatibility['xml.valid'] = (int) (class_exists('DOMDocument') && function_exists('xml_parser_create'));
275: $aCompatibility['json.valid'] = (int) function_exists('json_decode');
276: $aCompatibility['gd.valid'] = (int) extension_loaded('gd');
277:
278: $aCompatibility['ini-get.valid'] = (int) function_exists('ini_get');
279: $aCompatibility['ini-set.valid'] = (int) function_exists('ini_set');
280: $aCompatibility['set-time-limit.valid'] = (int) function_exists('set_time_limit');
281:
282: $aCompatibility['session.valid'] = (int) (function_exists('session_start') && isset($_SESSION['checksessionindex']));
283:
284: $dataPath = Api::DataPath();
285:
286: $aCompatibility['data.dir'] = $dataPath;
287: $aCompatibility['data.dir.valid'] = (int) (@is_dir($aCompatibility['data.dir']) && @is_writable($aCompatibility['data.dir']));
288:
289: $sTempPathName = '_must_be_deleted_' . md5(time());
290:
291: $aCompatibility['data.dir.create'] =
292: (int) @mkdir($aCompatibility['data.dir'] . '/' . $sTempPathName);
293: $aCompatibility['data.file.create'] =
294: (int) (bool) @fopen($aCompatibility['data.dir'] . '/' . $sTempPathName . '/' . $sTempPathName . '.test', 'w+');
295: $aCompatibility['data.file.delete'] =
296: (int) (bool) @unlink($aCompatibility['data.dir'] . '/' . $sTempPathName . '/' . $sTempPathName . '.test');
297: $aCompatibility['data.dir.delete'] =
298: (int) @rmdir($aCompatibility['data.dir'] . '/' . $sTempPathName);
299:
300:
301: $oSettings = &Api::GetSettings();
302:
303: $aCompatibility['settings.file'] = $oSettings ? $oSettings->GetPath() : '';
304:
305: $aCompatibility['settings.file.exist'] = (int) @file_exists($aCompatibility['settings.file']);
306: $aCompatibility['settings.file.read'] = (int) @is_readable($aCompatibility['settings.file']);
307: $aCompatibility['settings.file.write'] = (int) @is_writable($aCompatibility['settings.file']);
308:
309: $aCompatibilities = [
310: [
311: 'Name' => 'PHP version',
312: 'Result' => $aCompatibility['php.version.valid'],
313: 'Value' => $aCompatibility['php.version.valid']
314: ? 'OK'
315: : [$aCompatibility['php.version'] . ' detected, 7.2.5 or above required.',
316: 'You need to upgrade PHP engine installed on your server.
317: If it\'s a dedicated or your local server, you can download the latest version of PHP from its
318: <a href="http://php.net/downloads.php" target="_blank">official site</a> and install it yourself.
319: In case of a shared hosting, you need to ask your hosting provider to perform the upgrade.']
320: ],
321: [
322: 'Name' => 'Safe Mode is off',
323: 'Result' => $aCompatibility['safe-mode.valid'],
324: 'Value' => ($aCompatibility['safe-mode.valid'])
325: ? 'OK'
326: : ['Error, safe_mode is enabled.',
327: 'You need to <a href="http://php.net/manual/en/ini.sect.safe-mode.php" target="_blank">disable it in your php.ini</a>
328: or contact your hosting provider and ask to do this.']
329: ],
330: [
331: 'Name' => 'PDO MySQL Extension',
332: 'Result' => $aCompatibility['pdo.valid'],
333: 'Value' => ($aCompatibility['pdo.valid'])
334: ? 'OK'
335: : ['Error, PHP PDO MySQL extension not detected.',
336: 'You need to install this PHP extension or enable it in php.ini file.']
337: ],
338: [
339: 'Name' => 'MySQL Native Driver (mysqlnd)',
340: 'Result' => $aCompatibility['mysqlnd.valid'],
341: 'Value' => ($aCompatibility['mysqlnd.valid'])
342: ? 'OK'
343: : ['Error, MySQL Native Driver not found.',
344: 'You need to install this PHP extension or enable it in php.ini file.']
345: ],
346: [
347: 'Name' => 'Iconv Extension',
348: 'Result' => $aCompatibility['iconv.valid'],
349: 'Value' => ($aCompatibility['iconv.valid'])
350: ? 'OK'
351: : ['Error, iconv extension not detected.',
352: 'You need to install this PHP extension or enable it in php.ini file.']
353: ],
354: [
355: 'Name' => 'Multibyte String Extension',
356: 'Result' => $aCompatibility['mbstring.valid'],
357: 'Value' => ($aCompatibility['mbstring.valid'])
358: ? 'OK'
359: : ['Error, mb_string extension not detected.',
360: 'You need to install this PHP extension or enable it in php.ini file.']
361: ],
362: [
363: 'Name' => 'CURL Extension',
364: 'Result' => $aCompatibility['curl.valid'],
365: 'Value' => ($aCompatibility['curl.valid'])
366: ? 'OK'
367: : ['Error, curl extension not detected.',
368: 'You need to install this PHP extension or enable it in php.ini file.']
369: ],
370: [
371: 'Name' => 'JSON Extension',
372: 'Result' => $aCompatibility['json.valid'],
373: 'Value' => ($aCompatibility['json.valid'])
374: ? 'OK'
375: : ['Error, JSON extension not detected.',
376: 'You need to install this PHP extension or enable it in php.ini file.']
377: ],
378: [
379: 'Name' => 'XML/DOM Extension',
380: 'Result' => $aCompatibility['xml.valid'],
381: 'Value' => ($aCompatibility['xml.valid'])
382: ? 'OK'
383: : ['Error, xml (DOM) extension not detected.',
384: 'You need to install this PHP extension or enable it in php.ini file.']
385: ],
386: [
387: 'Name' => 'GD Extension',
388: 'Result' => $aCompatibility['gd.valid'],
389: 'Value' => ($aCompatibility['gd.valid'])
390: ? 'OK'
391: : ['Error, GD extension not detected.',
392: 'You need to install this PHP extension or enable it in php.ini file.']
393: ],
394: [
395: 'Name' => 'Sockets',
396: 'Result' => $aCompatibility['socket.valid'],
397: 'Value' => ($aCompatibility['socket.valid'])
398: ? 'OK'
399: : ['Error, creating network sockets must be enabled.', '
400: To enable sockets, you should remove fsockopen function from the list of prohibited functions in disable_functions directive of your php.ini file.
401: In case of a shared hosting, you need to ask your hosting provider to do this.']
402: ],
403: [
404: 'Name' => 'SSL (OpenSSL extension)',
405: 'Result' => $aCompatibility['openssl.valid'],
406: 'Value' => ($aCompatibility['openssl.valid'])
407: ? 'OK'
408: : ['SSL connections (like Gmail) will not be available. ', '
409: You need to enable OpenSSL support in your PHP configuration and make sure OpenSSL library is installed on your server.
410: For instructions, please refer to the official PHP documentation. In case of a shared hosting,
411: you need to ask your hosting provider to enable OpenSSL support.
412: You may ignore this if you\'re not going to connect to SSL-only mail servers (like Gmail).']
413: ],
414: [
415: 'Name' => 'Setting memory limits',
416: 'Result' => $aCompatibility['ini-get.valid'],
417: 'Value' => ($aCompatibility['ini-get.valid'] && $aCompatibility['ini-set.valid'])
418: ? 'OK'
419: : ['Opening large e-mails may fail.', '
420: You need to enable setting memory limits in your PHP configuration, i.e. remove ini_get and ini_set functions
421: from the list of prohibited functions in disable_functions directive of your php.ini file.
422: In case of a shared hosting, you need to ask your hosting provider to do this.']
423: ],
424: [
425: 'Name' => 'Setting script timeout',
426: 'Result' => $aCompatibility['set-time-limit.valid'],
427: 'Value' => ($aCompatibility['set-time-limit.valid'])
428: ? 'OK'
429: : ['Downloading large mailboxes may fail.', '
430: To enable setting script timeout, you should remove set_time_limit function from the list
431: of prohibited functions in disable_functions directive of your php.ini file.
432: In case of a shared hosting, you need to ask your hosting provider to do this.']
433: ],
434: [
435: 'Name' => 'WebMail data directory',
436: 'Result' => $aCompatibility['data.dir.valid'],
437: 'Value' => ($aCompatibility['data.dir.valid'])
438: ? 'Found'
439: : ['Error, data directory path discovery failure.']
440: ],
441: [
442: 'Name' => 'Creating/deleting directories',
443: 'Result' => $aCompatibility['data.dir.create'] && $aCompatibility['data.dir.delete'],
444: 'Value' => ($aCompatibility['data.dir.create'] && $aCompatibility['data.dir.delete'])
445: ? 'OK'
446: : ['Error, can\'t create/delete sub-directories in the data directory.', '
447: You need to grant read/write permission over data directory and all its contents to your web server user.
448: For instructions, please refer to this section of documentation and our
449: <a href="https://afterlogic.com/docs/webmail-pro-8/troubleshooting/troubleshooting-issues-with-data-directory" target="_blank">FAQ</a>.']
450: ],
451: [
452: 'Name' => 'Creating/deleting files',
453: 'Result' => $aCompatibility['data.file.create'] && $aCompatibility['data.file.delete'],
454: 'Value' => ($aCompatibility['data.file.create'] && $aCompatibility['data.file.delete'])
455: ? 'OK'
456: : ['Error, can\'t create/delete files in the data directory.', '
457: You need to grant read/write permission over data directory and all its contents to your web server user.
458: For instructions, please refer to this section of documentation and our
459: <a href="https://afterlogic.com/docs/webmail-pro-8/troubleshooting/troubleshooting-issues-with-data-directory" target="_blank">FAQ</a>.']
460: ],
461: [
462: 'Name' => 'WebMail Settings File',
463: 'Result' => $aCompatibility['settings.file.exist'],
464: 'Value' => ($aCompatibility['settings.file.exist'])
465: ? 'Found'
466: : ['Not Found, can\'t find "' . $aCompatibility['settings.file'] . '" file.', '
467: Make sure you completely copied the data directory with all its contents from installation package.
468: By default, the data directory is webmail subdirectory, and if it\'s not the case make sure its location matches one specified in inc_settings_path.php file.']
469: ],
470: [
471: 'Name' => 'Read/write settings file',
472: 'Result' => $aCompatibility['settings.file.read'] && $aCompatibility['settings.file.write'],
473: 'Value' => ($aCompatibility['settings.file.read'] && $aCompatibility['settings.file.write'])
474: ? 'OK / OK'
475: : ['Not Found, can\'t find "' . $aCompatibility['settings.file'] . '" file.', '
476: You should grant read/write permission over settings file to your web server user.
477: For instructions, please refer to this section of documentation and our
478: <a href="https://afterlogic.com/docs/webmail-pro-8/troubleshooting/troubleshooting-issues-with-data-directory" target="_blank">FAQ</a>.']
479: ],
480: ];
481:
482: $mResult[self::GetName()] = $aCompatibilities;
483: }
484:
485: public function onBeforeRunEntry($aArgs, &$mResult)
486: {
487: \Aurora\Api::removeOldLogs();
488:
489: return $this->redirectToHttps($aArgs['EntryName'], $mResult);
490: }
491:
492: /**
493: * Recursively deletes temporary files and folders on time.
494: *
495: * @param string $sTempPath Path to the temporary folder.
496: * @param int $iTime2Kill Interval in seconds at which files needs removing.
497: * @param int $iNow Current Unix timestamp.
498: */
499: protected function removeDirByTime($sTempPath, $iTime2Kill, $iNow)
500: {
501: $iFileCount = 0;
502: if (@is_dir($sTempPath)) {
503: $rDirH = @opendir($sTempPath);
504: if ($rDirH) {
505: while (($sFile = @readdir($rDirH)) !== false) {
506: if ('.' !== $sFile && '..' !== $sFile) {
507: if (@is_dir($sTempPath . '/' . $sFile)) {
508: $this->removeDirByTime($sTempPath . '/' . $sFile, $iTime2Kill, $iNow);
509: } else {
510: $iFileCount++;
511: }
512: }
513: }
514: @closedir($rDirH);
515: }
516:
517: if ($iFileCount > 0) {
518: if ($this->removeFilesByTime($sTempPath, $iTime2Kill, $iNow)) {
519: @rmdir($sTempPath);
520: }
521: } else {
522: @rmdir($sTempPath);
523: }
524: }
525: }
526:
527: /**
528: * Recursively deletes temporary files on time.
529: *
530: * @param string $sTempPath Path to the temporary folder.
531: * @param int $iTime2Kill Interval in seconds at which files needs removing.
532: * @param int $iNow Current Unix timestamp.
533: *
534: * @return bool
535: */
536: protected function removeFilesByTime($sTempPath, $iTime2Kill, $iNow)
537: {
538: $bResult = true;
539: if (@is_dir($sTempPath)) {
540: $rDirH = @opendir($sTempPath);
541: if ($rDirH) {
542: while (($sFile = @readdir($rDirH)) !== false) {
543: if ($sFile !== '.' && $sFile !== '..') {
544: if ($iNow - filemtime($sTempPath . '/' . $sFile) > $iTime2Kill) {
545: @unlink($sTempPath . '/' . $sFile);
546: } else {
547: $bResult = false;
548: }
549: }
550: }
551: @closedir($rDirH);
552: }
553: }
554: return $bResult;
555: }
556:
557: protected function redirectToHttps($sEntryName, $mResult)
558: {
559: $oSettings = &\Aurora\Api::GetSettings();
560: if ($oSettings) {
561: $bRedirectToHttps = $oSettings->RedirectToHttps;
562:
563: $bHttps = \Aurora\Api::isHttps();
564: if ($bRedirectToHttps && !$bHttps) {
565: if (\strtolower($sEntryName) !== 'api') {
566: \header("Location: https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
567: } else {
568: $mResult = [
569: 'ErrorCode' => 110
570: ];
571: return true;
572: }
573: }
574: }
575: }
576: /***** private functions *****/
577:
578: /***** static functions *****/
579: /**
580: * @ignore
581: * @return bool
582: */
583: private function deleteTree($dir)
584: {
585: $files = array_diff(scandir($dir), array('.','..'));
586:
587: foreach ($files as $file) {
588: (is_dir("$dir/$file")) ? $this->deleteTree("$dir/$file") : unlink("$dir/$file");
589: }
590:
591: return rmdir($dir);
592: }
593: /***** static functions *****/
594:
595: /***** public functions *****/
596: /**
597: *
598: * @return string
599: * @throws ApiException
600: */
601: public function EntryApi()
602: {
603: @ob_start();
604:
605: if (!is_writable(Api::DataPath())) {
606: throw new ApiException(Notifications::SystemNotConfigured, null, 'Check the write permission of the data folder');
607: }
608:
609: $aResponseItem = null;
610: $sModule = $this->oHttp->GetPost('Module', null);
611: $sMethod = $this->oHttp->GetPost('Method', null);
612: $sParameters = $this->oHttp->GetPost('Parameters', null);
613: $sFormat = $this->oHttp->GetPost('Format', null);
614: $sTenantName = $this->oHttp->GetPost('TenantName', null);
615:
616: if (isset($sModule, $sMethod)) {
617: $oModule = Api::GetModule($sModule);
618: if ($oModule instanceof \Aurora\System\Module\AbstractModule) {
619: try {
620: Api::Log(" ");
621: Api::Log(" ===== API: " . $sModule . '::' . $sMethod);
622:
623: Api::validateAuthToken();
624:
625: if (!empty($sMethod)) {
626: Api::setTenantName($sTenantName);
627:
628: $aParameters = [];
629: if (isset($sParameters) && \is_string($sParameters) && !empty($sParameters)) {
630: $aParameters = @\json_decode($sParameters, true);
631:
632: if (json_last_error() !== JSON_ERROR_NONE) {
633: throw new ApiException(
634: Notifications::InvalidInputParameter,
635: null,
636: 'InvalidInputParameter'
637: );
638: }
639:
640: if (!\is_array($aParameters)) {
641: $aParameters = array($aParameters);
642: }
643: }
644:
645: $mUploadData = $this->getUploadData();
646: if (\is_array($mUploadData)) {
647: $aParameters['UploadData'] = $mUploadData;
648: }
649:
650: $oModule->CallMethod(
651: $sMethod,
652: $aParameters,
653: true
654: );
655:
656: $oLastException = Api::GetModuleManager()->GetLastException();
657: if (isset($oLastException)) {
658: throw $oLastException;
659: }
660:
661: $aResponseItem = $oModule->DefaultResponse(
662: $sMethod,
663: Api::GetModuleManager()->GetResults()
664: );
665: }
666:
667: if (!\is_array($aResponseItem)) {
668: throw new ApiException(
669: Notifications::UnknownError,
670: null,
671: 'UnknownError'
672: );
673: }
674: } catch (\Exception $oException) {
675: Api::LogException($oException);
676:
677: $aAdditionalParams = null;
678: if ($oException instanceof ApiException) {
679: if (!$oException->GetModule()) {
680: $oException = new ApiException(
681: $oException->getCode(),
682: $oException->getPrevious(),
683: $oException->getMessage(),
684: $oException->GetObjectParams(),
685: $oModule
686: );
687: }
688: $aAdditionalParams = $oException->GetObjectParams();
689: }
690:
691: $aResponseItem = $oModule->ExceptionResponse(
692: $sMethod,
693: $oException,
694: $aAdditionalParams
695: );
696: }
697: } else {
698: $oException = new ApiException(
699: Notifications::ModuleNotFound,
700: null,
701: 'Module not found'
702: );
703: $aResponseItem = $this->ExceptionResponse(
704: $sMethod,
705: $oException
706: );
707: }
708: } else {
709: $oException = new ApiException(
710: Notifications::InvalidInputParameter,
711: null,
712: 'Invalid input parameter'
713: );
714: $aResponseItem = $this->ExceptionResponse(
715: $sMethod,
716: $oException
717: );
718: }
719:
720: if (isset($aResponseItem['Parameters'])) {
721: unset($aResponseItem['Parameters']);
722: }
723:
724: return \Aurora\System\Managers\Response::GetJsonFromObject($sFormat, $aResponseItem);
725: }
726:
727: /**
728: * @ignore
729: */
730: public function EntryMobile()
731: {
732: $oApiIntegrator = $this->getIntegratorManager();
733: $oApiIntegrator->setMobile(true);
734:
735: Api::Location('./');
736: }
737:
738: /**
739: * @ignore
740: */
741: public function EntrySso()
742: {
743: try {
744: $sHash = $this->oHttp->GetRequest('hash');
745: if (!empty($sHash)) {
746: $sData = Api::Cacher()->get('SSO:' . $sHash, true);
747: $aData = Api::DecodeKeyValues($sData);
748:
749: if (isset($aData['Password'], $aData['Email'])) {
750: $sLanguage = $this->oHttp->GetRequest('lang');
751: $aResult = self::Decorator()->Login($aData['Email'], $aData['Password'], $sLanguage);
752:
753: // if (is_array($aResult) && isset($aResult['AuthToken'])) {
754: // Api::setAuthTokenCookie($aResult['AuthToken']);
755: // } else {
756: // Api::unsetAuthTokenCookie();
757: // }
758: }
759: } else {
760: self::Decorator()->Logout();
761: }
762: } catch (\Exception $oExc) {
763: Api::LogException($oExc);
764: }
765:
766: Api::Location('./');
767: }
768:
769: /**
770: * @ignore
771: */
772: public function EntryPostlogin()
773: {
774: if ($this->oModuleSettings->AllowPostLogin) {
775: $sEmail = trim((string) $this->oHttp->GetRequest('Email', ''));
776: $sLogin = (string) $this->oHttp->GetRequest('Login', '');
777: $sPassword = (string) $this->oHttp->GetRequest('Password', '');
778:
779: if ($sLogin === '') {
780: $sLogin = $sEmail;
781: }
782:
783: $aResult = self::Decorator()->Login($sLogin, $sPassword);
784: // if (is_array($aResult) && isset($aResult['AuthToken'])) {
785: // Api::setAuthTokenCookie($aResult['AuthToken']);
786: // }
787:
788: Api::Location('./');
789: }
790: }
791:
792: public function EntryFileCache()
793: {
794: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
795:
796: $sRawKey = \Aurora\System\Router::getItemByIndex(1, '');
797: $sAction = \Aurora\System\Router::getItemByIndex(2, '');
798: $aValues = Api::DecodeKeyValues($sRawKey);
799:
800: $bDownload = true;
801: $bThumbnail = false;
802:
803: switch ($sAction) {
804: case 'view':
805: $bDownload = false;
806: $bThumbnail = false;
807: break;
808: case 'thumb':
809: $bDownload = false;
810: $bThumbnail = true;
811: break;
812: default:
813: $bDownload = true;
814: $bThumbnail = false;
815: break;
816: }
817:
818: $iUserId = (isset($aValues['UserId'])) ? $aValues['UserId'] : 0;
819:
820: if (isset($aValues['TempFile'], $aValues['TempName'], $aValues['Name'])) {
821: $sModule = isset($aValues['Module']) && !empty($aValues['Module']) ? $aValues['Module'] : 'System';
822: $sUUID = Api::getUserUUIDById($iUserId);
823: $oApiFileCache = new \Aurora\System\Managers\Filecache();
824: $mResult = $oApiFileCache->getFile($sUUID, $aValues['TempName'], '', $sModule);
825:
826: if (is_resource($mResult)) {
827: $sFileName = $aValues['Name'];
828: $sContentType = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName);
829: $sFileName = \Aurora\System\Utils::clearFileName($sFileName, $sContentType);
830:
831: \Aurora\System\Utils::OutputFileResource($sUUID, $sContentType, $sFileName, $mResult, $bThumbnail, $bDownload);
832: }
833: }
834: }
835:
836: public function IsModuleExists($Module)
837: {
838: return Api::GetModuleManager()->ModuleExists($Module);
839: }
840:
841: /**
842: *
843: * @return string
844: */
845: public function GetVersion()
846: {
847: return Api::Version();
848: }
849:
850: /**
851: * Clears temporary files by cron.
852: *
853: * @ignore
854: * @todo check if it works.
855: *
856: * @return bool
857: */
858: protected function ClearTempFiles()
859: {
860: $sTempPath = Api::DataPath() . '/temp';
861: if (@is_dir($sTempPath)) {
862: $iNow = time();
863:
864: $iTime2Run = $this->oModuleSettings->CronTimeToRunSeconds;
865: $iTime2Kill = $this->oModuleSettings->CronTimeToKillSeconds;
866: $sDataFile = $this->oModuleSettings->CronTimeFile;
867:
868: $iFiletTime = -1;
869: if (@file_exists(Api::DataPath() . '/' . $sDataFile)) {
870: $iFiletTime = (int) @file_get_contents(Api::DataPath() . '/' . $sDataFile);
871: }
872:
873: if ($iFiletTime === -1 || $iNow - $iFiletTime > $iTime2Run) {
874: $this->removeDirByTime($sTempPath, $iTime2Kill, $iNow);
875: @file_put_contents(Api::DataPath() . '/' . $sDataFile, $iNow);
876: }
877: }
878:
879: return true;
880: }
881:
882: /**
883: * !Not public
884: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
885: *
886: * Updates user by object.
887: *
888: * @param Models\User $oUser
889: * @return bool
890: */
891: public function UpdateUserObject($oUser)
892: {
893: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
894:
895: return $this->getUsersManager()->updateUser($oUser);
896: }
897:
898: /**
899: * !Not public
900: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
901: *
902: * Returns user object.
903: *
904: * @param int|string $UserId User identifier or UUID.
905: * @return Models\User
906: */
907: public function GetUserWithoutRoleCheck($UserId = '')
908: {
909: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
910:
911: $oUser = $this->getUsersManager()->getUser($UserId);
912:
913: return $oUser ? $oUser : null;
914: }
915:
916: /**
917: * !Not public
918: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
919: *
920: * Returns user object.
921: *
922: * @param int $UUID User uuid identifier.
923: * @return Models\User
924: */
925: public function GetUserByUUID($UUID)
926: {
927: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
928:
929: $oUser = $this->getUsersManager()->getUser($UUID);
930:
931: return $oUser ? $oUser : null;
932: }
933:
934: /**
935: * !Not public
936: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
937: *
938: * Returns user object.
939: *
940: * @param string $PublicId User public identifier.
941: * @return Models\User
942: */
943: public function GetUserByPublicId($PublicId)
944: {
945: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
946:
947: $oUser = $this->getUsersManager()->getUserByPublicId($PublicId);
948:
949: return $oUser ? $oUser : null;
950: }
951:
952: /**
953: * !Not public
954: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
955: *
956: * Creates and returns user with super administrator role.
957: *
958: * @deprecated sinse version 9.7.8
959: *
960: * @return Models\User
961: */
962: public function GetAdminUser()
963: {
964: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
965:
966: return Integrator::GetAdminUser();
967: }
968:
969: /**
970: * !Not public
971: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
972: *
973: * Returns tenant object by identifier.
974: *
975: * @param int $Id Tenant identifier.
976: * @return Models\Tenant|null
977: */
978: public function GetTenantWithoutRoleCheck($Id)
979: {
980: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
981:
982: $oTenant = $this->getTenantsManager()->getTenantById($Id);
983:
984: return $oTenant ? $oTenant : null;
985: }
986:
987: /**
988: * !Not public
989: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
990: *
991: * Returns tenant identifier by tenant name.
992: *
993: * @param string $TenantName Tenant name.
994: * @return int|null
995: */
996: public function GetTenantIdByName($TenantName = '')
997: {
998: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
999:
1000: $iTenantId = $this->getTenantsManager()->getTenantIdByName((string) $TenantName);
1001:
1002: return $iTenantId ? $iTenantId : null;
1003: }
1004:
1005: /**
1006: * !Not public
1007: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
1008: *
1009: * Returns current tenant name.
1010: *
1011: * @return string
1012: */
1013: public function GetTenantName()
1014: {
1015: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1016:
1017: $sTenant = '';
1018:
1019: $oUser = Api::getAuthenticatedUser();
1020: if ($oUser) {
1021: $oTenant = self::Decorator()->GetTenantWithoutRoleCheck($oUser->IdTenant);
1022: if ($oTenant) {
1023: $sTenant = $oTenant->Name;
1024:
1025: $sPostTenant = $this->oHttp->GetPost('TenantName', '');
1026: if (!empty($sPostTenant) && !empty($sTenant) && $sPostTenant !== $sTenant) {
1027: $sTenant = '';
1028: }
1029: }
1030: } else {
1031: $sTenant = $this->oHttp->GetRequest('tenant', '');
1032: }
1033: Api::setTenantName($sTenant);
1034: return $sTenant;
1035: }
1036:
1037: /**
1038: * !Not public
1039: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
1040: *
1041: * Returns default global tenant.
1042: *
1043: * @return Models\Tenant
1044: */
1045: public function GetDefaultGlobalTenant()
1046: {
1047: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1048:
1049: $oTenant = $this->getTenantsManager()->getDefaultGlobalTenant();
1050:
1051: return $oTenant ? $oTenant : null;
1052: }
1053:
1054: /**
1055: * !Not public
1056: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
1057: *
1058: * Updates tenant.
1059: *
1060: * @param Models\Tenant $oTenant
1061: * @return bool
1062: */
1063: public function UpdateTenantObject($oTenant)
1064: {
1065: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1066:
1067: return $this->getTenantsManager()->updateTenant($oTenant);
1068: }
1069:
1070: /**
1071: * !Not public
1072: * This method is restricted to be called by web API (see denyMethodsCallByWebApi method).
1073: *
1074: * @param Models\User $oUser
1075: * @return int
1076: */
1077: public function UpdateTokensValidFromTimestamp($oUser)
1078: {
1079: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1080:
1081: $oUser->TokensValidFromTimestamp = time();
1082: $this->getUsersManager()->updateUser($oUser);
1083: return $oUser->TokensValidFromTimestamp;
1084: }
1085: /***** public functions *****/
1086:
1087: /***** public functions might be called with web API *****/
1088: /**
1089: * @apiDefine Core Core Module
1090: * System module that provides core functionality such as User management, Tenants management
1091: */
1092:
1093: /**
1094: * @api {post} ?/Api/ DoServerInitializations
1095: * @apiName DoServerInitializations
1096: * @apiGroup Core
1097: * @apiDescription Does some pending actions to be executed when you log in.
1098: *
1099: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
1100: * @apiHeaderExample {json} Header-Example:
1101: * {
1102: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
1103: * }
1104: *
1105: * @apiParam {string=Core} Module Module name.
1106: * @apiParam {string=DoServerInitializations} Method Method name.
1107: *
1108: * @apiParamExample {json} Request-Example:
1109: * {
1110: * Module: 'Core',
1111: * Method: 'DoServerInitializations'
1112: * }
1113: *
1114: * @apiSuccess {object[]} Result Array of response objects.
1115: * @apiSuccess {string} Result.Module Module name.
1116: * @apiSuccess {string} Result.Method Method name.
1117: * @apiSuccess {bool} Result.Result Indicates if server initializations were made successfully.
1118: * @apiSuccess {int} [Result.ErrorCode] Error code.
1119: *
1120: * @apiSuccessExample {json} Success response example:
1121: * {
1122: * Module: 'Core',
1123: * Method: 'DoServerInitializations',
1124: * Result: true
1125: * }
1126: *
1127: * @apiSuccessExample {json} Error response example:
1128: * {
1129: * Module: 'Core',
1130: * Method: 'DoServerInitializations',
1131: * Result: false,
1132: * ErrorCode: 102
1133: * }
1134: */
1135: /**
1136: * Does some pending actions to be executed when you log in.
1137: *
1138: * @return bool
1139: */
1140: public function DoServerInitializations($Timezone = '')
1141: {
1142: Api::checkUserRoleIsAtLeast(UserRole::Customer);
1143: $result = true;
1144:
1145: $oCacher = Api::Cacher();
1146:
1147: $bDoGC = false;
1148: if ($oCacher && $oCacher->IsInited()) {
1149: $iTime = $oCacher->GetTimer('Cache/ClearFileCache');
1150: if (0 === $iTime || $iTime + 60 * 60 * 24 < time()) {
1151: if ($oCacher->SetTimer('Cache/ClearFileCache')) {
1152: $bDoGC = true;
1153: }
1154: }
1155: }
1156:
1157: if ($bDoGC) {
1158: Api::Log('GC: FileCache / Start');
1159: $oApiFileCache = new \Aurora\System\Managers\Filecache();
1160: $oApiFileCache->gc();
1161: $oCacher->gc();
1162: Api::Log('GC: FileCache / End');
1163: }
1164:
1165: return $result;
1166: }
1167:
1168: /**
1169: * @api {post} ?/Api/ Ping
1170: * @apiName Ping
1171: * @apiGroup Core
1172: * @apiDescription Method is used for checking Internet connection.
1173: *
1174: * @apiParam {string=Core} Module Module name.
1175: * @apiParam {string=Ping} Method Method name.
1176: *
1177: * @apiParamExample {json} Request-Example:
1178: * {
1179: * Module: 'Core',
1180: * Method: 'Ping'
1181: * }
1182: *
1183: * @apiSuccess {object[]} Result Array of response objects.
1184: * @apiSuccess {string} Result.Module Module name.
1185: * @apiSuccess {string} Result.Method Method name.
1186: * @apiSuccess {string} Result.Result Just a string to indicate that connection to backend is working.
1187: * @apiSuccess {int} [Result.ErrorCode] Error code.
1188: *
1189: * @apiSuccessExample {json} Success response example:
1190: * {
1191: * Module: 'Core',
1192: * Method: 'Ping',
1193: * Result: 'Pong'
1194: * }
1195: */
1196: /**
1197: * Method is used for checking Internet connection.
1198: *
1199: * @return 'Pong'
1200: */
1201: public function Ping()
1202: {
1203: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
1204:
1205: return 'Pong';
1206: }
1207:
1208: /**
1209: * @api {post} ?/Api/ GetAppData
1210: * @apiName GetAppData
1211: * @apiGroup Core
1212: * @apiDescription Obtains a list of settings for each module for the current user.
1213: *
1214: * @apiParam {string=Core} Module Module name.
1215: * @apiParam {string=GetAppData} Method Method name.
1216: *
1217: * @apiParamExample {json} Request-Example:
1218: * {
1219: * Module: 'Core',
1220: * Method: 'GetAppData'
1221: * }
1222: *
1223: * @apiSuccess {object[]} Result Array of response objects.
1224: * @apiSuccess {string} Result.Module Module name.
1225: * @apiSuccess {string} Result.Method Method name.
1226: * @apiSuccess {string} Result.Result List of settings for each module for the current user.
1227: * @apiSuccess {int} [Result.ErrorCode] Error code.
1228: *
1229: * @apiSuccessExample {json} Success response example:
1230: * {
1231: * Module: 'Core',
1232: * Method: 'GetAppData',
1233: * Result: {
1234: * User: {Id: 0, Role: 4, Name: "", PublicId: ""},
1235: * Core: { ... },
1236: * Contacts: { ... },
1237: * ...
1238: * CoreWebclient: { ... },
1239: * ...
1240: * }
1241: * }
1242: */
1243: /**
1244: * Obtains a list of settings for each module for the current user.
1245: *
1246: * @return array
1247: */
1248: public function GetAppData()
1249: {
1250: $oApiIntegrator = $this->getIntegratorManager();
1251: return $oApiIntegrator->appData();
1252: }
1253:
1254: /**
1255: * @api {post} ?/Api/ GetSettings
1256: * @apiName GetSettings
1257: * @apiGroup Core
1258: * @apiDescription Obtains list of module settings for authenticated user.
1259: *
1260: * @apiHeader {string} [Authorization] "Bearer " + Authentication token which was received as the result of Core.Login method.
1261: * @apiHeaderExample {json} Header-Example:
1262: * {
1263: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
1264: * }
1265: *
1266: * @apiParam {string=Core} Module Module name.
1267: * @apiParam {string=GetSettings} Method Method name.
1268: *
1269: * @apiParamExample {json} Request-Example:
1270: * {
1271: * Module: 'Core',
1272: * Method: 'GetSettings'
1273: * }
1274: *
1275: * @apiSuccess {object[]} Result Array of response objects.
1276: * @apiSuccess {string} Result.Module Module name.
1277: * @apiSuccess {string} Result.Method Method name.
1278: * @apiSuccess {mixed} Result.Result List of module settings in case of success, otherwise **false**.
1279: * @apiSuccess {string} Result.Result.SiteName Site name.
1280: * @apiSuccess {string} Result.Result.Language Language of interface.
1281: * @apiSuccess {int} Result.Result.TimeFormat Time format.
1282: * @apiSuccess {string} Result.Result.DateFormat Date format.
1283: * @apiSuccess {bool} Result.Result.AutodetectLanguage Indicates if language should be taken from browser.
1284: * @apiSuccess {object} Result.Result.EUserRole Enumeration with user roles.
1285: * @apiSuccess {string} [Result.Result.DBHost] Database host is returned only if super administrator is authenticated.
1286: * @apiSuccess {string} [Result.Result.DBName] Database name is returned only if super administrator is authenticated.
1287: * @apiSuccess {string} [Result.Result.DBLogin] Database login is returned only if super administrator is authenticated.
1288: * @apiSuccess {string} [Result.Result.AdminLogin] Super administrator login is returned only if super administrator is authenticated.
1289: * @apiSuccess {bool} [Result.Result.AdminHasPassword] Indicates if super administrator has set up password. It is returned only if super administrator is authenticated.
1290: * @apiSuccess {string} [Result.Result.AdminLanguage] Super administrator language is returned only if super administrator is authenticated.
1291: * @apiSuccess {bool} [Result.Result.IsSystemConfigured] Indicates if 'data' folder exist and writable and encryption key was generated.
1292: * @apiSuccess {bool} [Result.Result.EncryptionKeyNotEmpty] Indicates if encryption key was generated. It is returned only if super administrator is authenticated.
1293: * @apiSuccess {bool} [Result.Result.EnableLogging] Indicates if logging is enabled. It is returned only if super administrator is authenticated.
1294: * @apiSuccess {bool} [Result.Result.EnableEventLogging] Indicates if event logging is enabled. It is returned only if super administrator is authenticated.
1295: * @apiSuccess {string} [Result.Result.LoggingLevel] Value of logging level. It is returned only if super administrator is authenticated.
1296: * @apiSuccess {int} [Result.ErrorCode] Error code.
1297: *
1298: * @apiSuccessExample {json} Success response example:
1299: * {
1300: * Module: 'Core',
1301: * Method: 'GetSettings',
1302: * Result: { SiteName: "Aurora Cloud", Language: "English", TimeFormat: 1, DateFormat: "MM/DD/YYYY",
1303: * EUserRole: { SuperAdmin: 0, TenantAdmin: 1, NormalUser: 2, Customer: 3, Anonymous: 4 } }
1304: * }
1305: *
1306: * @apiSuccessExample {json} Error response example:
1307: * {
1308: * Module: 'Core',
1309: * Method: 'GetSettings',
1310: * Result: false,
1311: * ErrorCode: 102
1312: * }
1313: */
1314: /**
1315: * Obtains list of module settings for authenticated user.
1316: *
1317: * @return array
1318: */
1319: public function GetSettings()
1320: {
1321: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
1322:
1323: $oUser = Api::getAuthenticatedUser();
1324:
1325: $oApiIntegrator = $this->getIntegratorManager();
1326: $iLastErrorCode = $oApiIntegrator->getLastErrorCode();
1327: if (0 < $iLastErrorCode) {
1328: $oApiIntegrator->clearLastErrorCode();
1329: }
1330:
1331: $oSettings = &Api::GetSettings();
1332:
1333: $aSettings = array(
1334: 'AutodetectLanguage' => $this->oModuleSettings->AutodetectLanguage,
1335: 'UserSelectsDateFormat' => $this->oModuleSettings->UserSelectsDateFormat,
1336: 'DateFormat' => $this->oModuleSettings->DateFormat,
1337: 'DateFormatList' => $this->oModuleSettings->DateFormatList,
1338: 'EUserRole' => (new UserRole())->getMap(),
1339: 'Language' => Api::GetLanguage(),
1340: 'ShortLanguage' => \Aurora\System\Utils::ConvertLanguageNameToShort(Api::GetLanguage()),
1341: 'LanguageList' => $oApiIntegrator->getLanguageList(),
1342: 'LastErrorCode' => $iLastErrorCode,
1343: 'SiteName' => $this->oModuleSettings->SiteName,
1344: 'SocialName' => '',
1345: 'TenantName' => Api::getTenantName(),
1346: 'EnableMultiTenant' => $oSettings->EnableMultiTenant,
1347: 'TimeFormat' => $this->oModuleSettings->TimeFormat,
1348: 'UserId' => Api::getAuthenticatedUserId(),
1349: 'IsSystemConfigured' => is_writable(Api::DataPath()) &&
1350: (file_exists(Api::GetEncryptionKeyPath()) && strlen(@file_get_contents(Api::GetEncryptionKeyPath()))),
1351: 'Version' => Api::VersionFull(),
1352: 'ProductName' => $this->oModuleSettings->ProductName,
1353: 'PasswordMinLength' => $oSettings->PasswordMinLength,
1354: 'PasswordMustBeComplex' => $oSettings->PasswordMustBeComplex,
1355: 'CookiePath' => Api::getCookiePath(),
1356: 'CookieSecure' => Api::getCookieSecure(),
1357: 'AuthTokenCookieExpireTime' => $this->oModuleSettings->AuthTokenCookieExpireTime,
1358: 'StoreAuthTokenInDB' => $oSettings->StoreAuthTokenInDB,
1359: 'AvailableClientModules' => $oApiIntegrator->GetClientModuleNames(),
1360: 'AvailableBackendModules' => $oApiIntegrator->GetBackendModules(),
1361: 'AllowGroups' => $this->oModuleSettings->AllowGroups,
1362: );
1363:
1364: if ($oSettings && ($oUser instanceof User) && $oUser->Role === UserRole::SuperAdmin) {
1365: $sAdminPassword = $oSettings->AdminPassword;
1366:
1367: $aSettings = array_merge($aSettings, array(
1368: 'DBHost' => $oSettings->DBHost,
1369: 'DBName' => $oSettings->DBName,
1370: 'DBLogin' => $oSettings->DBLogin,
1371: 'AdminLogin' => $oSettings->AdminLogin,
1372: 'AdminHasPassword' => !empty($sAdminPassword),
1373: 'AdminLanguage' => $oSettings->AdminLanguage,
1374: 'CommonLanguage' => $this->oModuleSettings->Language,
1375: 'EncryptionKeyNotEmpty' => file_exists(Api::GetEncryptionKeyPath()) && strlen(@file_get_contents(Api::GetEncryptionKeyPath())),
1376: 'EnableLogging' => $oSettings->EnableLogging,
1377: 'EnableEventLogging' => $oSettings->EnableEventLogging,
1378: 'LoggingLevel' => $oSettings->LoggingLevel,
1379: 'LogFilesData' => $this->GetLogFilesData(),
1380: 'ELogLevel' => (new \Aurora\System\Enums\LogLevel())->getMap()
1381: ));
1382: }
1383:
1384: if (($oUser instanceof User) && $oUser->isNormalOrTenant()) {
1385: if ($oUser->DateFormat !== '') {
1386: $aSettings['DateFormat'] = $oUser->DateFormat;
1387: }
1388: $aSettings['TimeFormat'] = $oUser->TimeFormat;
1389: $aSettings['Timezone'] = $oUser->DefaultTimeZone;
1390: }
1391:
1392: return $aSettings;
1393: }
1394:
1395: /**
1396: * @api {post} ?/Api/ UpdateSettings
1397: * @apiName UpdateSettings
1398: * @apiGroup Core
1399: * @apiDescription Updates specified settings if super administrator is authenticated.
1400: *
1401: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
1402: * @apiHeaderExample {json} Header-Example:
1403: * {
1404: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
1405: * }
1406: *
1407: * @apiParam {string=Core} Module Module name.
1408: * @apiParam {string=UpdateSettings} Method Method name.
1409: * @apiParam {string} Parameters JSON.stringified object <br>
1410: * {<br>
1411: * &emsp; **DbLogin** *string* Database login.<br>
1412: * &emsp; **DbPassword** *string* Database password.<br>
1413: * &emsp; **DbName** *string* Database name.<br>
1414: * &emsp; **DbHost** *string* Database host.<br>
1415: * &emsp; **AdminLogin** *string* Login for super administrator.<br>
1416: * &emsp; **Password** *string* Current password for super administrator.<br>
1417: * &emsp; **NewPassword** *string* New password for super administrator.<br>
1418: * &emsp; **AdminLanguage** *string* Language for super administrator.<br>
1419: * &emsp; **Language** *string* Language that is used on login and for new users.<br>
1420: * &emsp; **AutodetectLanguage** *bool* Indicates if browser language should be used on login and for new users.<br>
1421: * &emsp; **TimeFormat** *int* Time format that is used for new users.<br>
1422: * &emsp; **EnableLogging** *bool* Indicates if logs are enabled.<br>
1423: * &emsp; **EnableEventLogging** *bool* Indicates if events logs are enabled.<br>
1424: * &emsp; **LoggingLevel** *int* Specify logging level.<br>
1425: * }
1426: *
1427: * @apiParamExample {json} Request-Example:
1428: * {
1429: * Module: 'Core',
1430: * Method: 'UpdateSettings',
1431: * Parameters: '{ DbLogin: "login_value", DbPassword: "password_value",
1432: * DbName: "db_name_value", DbHost: "host_value", AdminLogin: "admin_login_value",
1433: * Password: "admin_pass_value", NewPassword: "admin_pass_value" }'
1434: * }
1435: *
1436: * @apiSuccess {object[]} Result Array of response objects.
1437: * @apiSuccess {string} Result.Module Module name.
1438: * @apiSuccess {string} Result.Method Method name.
1439: * @apiSuccess {bool} Result.Result Indicates if settings were updated successfully.
1440: * @apiSuccess {int} [Result.ErrorCode] Error code.
1441: *
1442: * @apiSuccessExample {json} Success response example:
1443: * {
1444: * Module: 'Core',
1445: * Method: 'UpdateSettings',
1446: * Result: true
1447: * }
1448: *
1449: * @apiSuccessExample {json} Error response example:
1450: * {
1451: * Module: 'Core',
1452: * Method: 'UpdateSettings',
1453: * Result: false,
1454: * ErrorCode: 102
1455: * }
1456: */
1457: /**
1458: * Updates specified settings if super administrator is authenticated.
1459: *
1460: * @param string $DbLogin Database login.
1461: * @param string $DbPassword Database password.
1462: * @param string $DbName Database name.
1463: * @param string $DbHost Database host.
1464: * @param string $AdminLogin Login for super administrator.
1465: * @param string $Password Current password for super administrator.
1466: * @param string $NewPassword New password for super administrator.
1467: * @param string $AdminLanguage Language for super administrator.
1468: * @param string $SiteName Site name.
1469: * @param string $Language Language that is used on login and for new users.
1470: * @param bool $AutodetectLanguage Indicates if browser language should be used on login and for new users.
1471: * @param int $TimeFormat Time format that is used for new users.
1472: * @param bool $EnableLogging Indicates if logs are enabled.
1473: * @param bool $EnableEventLogging Indicates if events logs are enabled.
1474: * @param int $LoggingLevel Specify logging level.
1475: * @return bool
1476: * @throws ApiException
1477: */
1478: public function UpdateSettings(
1479: $DbLogin = null,
1480: $DbPassword = null,
1481: $DbName = null,
1482: $DbHost = null,
1483: $AdminLogin = null,
1484: $Password = null,
1485: $NewPassword = null,
1486: $AdminLanguage = null,
1487: $SiteName = null,
1488: $Language = null,
1489: $AutodetectLanguage = null,
1490: $TimeFormat = null,
1491: $DateFormat = null,
1492: $EnableLogging = null,
1493: $EnableEventLogging = null,
1494: $LoggingLevel = null
1495: ) {
1496: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
1497:
1498: $oUser = Api::getAuthenticatedUser();
1499:
1500: if ($oUser->Role === UserRole::SuperAdmin) {
1501: if ($SiteName !== null || $Language !== null || $TimeFormat !== null || $AutodetectLanguage !== null) {
1502: if ($SiteName !== null) {
1503: $this->setConfig('SiteName', $SiteName);
1504: }
1505: if ($AutodetectLanguage !== null) {
1506: $this->setConfig('AutodetectLanguage', $AutodetectLanguage);
1507: }
1508: if ($Language !== null) {
1509: $this->setConfig('Language', $Language);
1510: }
1511: if ($TimeFormat !== null) {
1512: $this->setConfig('TimeFormat', (int) $TimeFormat);
1513: }
1514: $this->saveModuleConfig();
1515: }
1516: $oSettings = &Api::GetSettings();
1517: if ($DbLogin !== null) {
1518: $oSettings->DBLogin = $DbLogin;
1519: }
1520: if ($DbPassword !== null) {
1521: $oSettings->DBPassword = $DbPassword;
1522: }
1523: if ($DbName !== null) {
1524: $oSettings->DBName = $DbName;
1525: }
1526: if ($DbHost !== null) {
1527: $oSettings->DBHost = $DbHost;
1528: }
1529: if ($AdminLogin !== null && $AdminLogin !== $oSettings->AdminLogin) {
1530: $aArgs = array(
1531: 'Login' => $AdminLogin
1532: );
1533: $this->broadcastEvent(
1534: 'CheckAccountExists',
1535: $aArgs
1536: );
1537:
1538: $oSettings->AdminLogin = $AdminLogin;
1539: }
1540:
1541: $sAdminPassword = $oSettings->AdminPassword;
1542: if ((empty($sAdminPassword) && empty($Password) || !empty($Password)) && !empty($NewPassword)) {
1543: if (empty($sAdminPassword) || password_verify($Password, $sAdminPassword)) {
1544: $oSettings->AdminPassword = password_hash(trim($NewPassword), PASSWORD_BCRYPT);
1545: } else {
1546: throw new ApiException(Notifications::AccountOldPasswordNotCorrect);
1547: }
1548: }
1549:
1550: if ($AdminLanguage !== null) {
1551: $oSettings->AdminLanguage = $AdminLanguage;
1552: }
1553: if ($EnableLogging !== null) {
1554: $oSettings->EnableLogging = $EnableLogging;
1555: }
1556: if ($EnableEventLogging !== null) {
1557: $oSettings->EnableEventLogging = $EnableEventLogging;
1558: }
1559: if ($LoggingLevel !== null) {
1560: $oSettings->LoggingLevel = $LoggingLevel;
1561: }
1562: return $oSettings->Save();
1563: }
1564:
1565: if ($oUser->isNormalOrTenant()) {
1566: if ($Language !== null) {
1567: $oUser->Language = $Language;
1568: }
1569: if ($TimeFormat !== null) {
1570: $oUser->TimeFormat = $TimeFormat;
1571: }
1572: if ($DateFormat !== null) {
1573: $oUser->DateFormat = $DateFormat;
1574: }
1575: return $this->UpdateUserObject($oUser);
1576: }
1577:
1578: return false;
1579: }
1580:
1581: public function UpdateLoggingSettings($EnableLogging = null, $EnableEventLogging = null, $LoggingLevel = null)
1582: {
1583: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
1584:
1585: $oSettings = &Api::GetSettings();
1586:
1587: if ($EnableLogging !== null) {
1588: $oSettings->EnableLogging = $EnableLogging;
1589: }
1590: if ($EnableEventLogging !== null) {
1591: $oSettings->EnableEventLogging = $EnableEventLogging;
1592: }
1593: if ($LoggingLevel !== null) {
1594: $oSettings->LoggingLevel = $LoggingLevel;
1595: }
1596:
1597: return $oSettings->Save();
1598: }
1599:
1600: /**
1601: * @ignore
1602: * Turns on or turns off mobile version.
1603: * @param bool $Mobile Indicates if mobile version should be turned on or turned off.
1604: * @return bool
1605: */
1606: public function SetMobile($Mobile)
1607: {
1608: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
1609: $oIntegrator = $this->getIntegratorManager();
1610: return $oIntegrator ? $oIntegrator->setMobile($Mobile) : false;
1611: }
1612:
1613: /**
1614: * @api {post} ?/Api/ CreateTables
1615: * @apiName CreateTables
1616: * @apiGroup Core
1617: * @apiDescription Creates tables reqired for module work. Creates first channel and tenant if it is necessary.
1618: *
1619: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
1620: * @apiHeaderExample {json} Header-Example:
1621: * {
1622: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
1623: * }
1624: *
1625: * @apiParam {string=Core} Module Module name.
1626: * @apiParam {string=CreateTables} Method Method name.
1627: *
1628: * @apiParamExample {json} Request-Example:
1629: * {
1630: * Module: 'Core',
1631: * Method: 'CreateTables'
1632: * }
1633: *
1634: * @apiSuccess {object[]} Result Array of response objects.
1635: * @apiSuccess {string} Result.Module Module name.
1636: * @apiSuccess {string} Result.Method Method name.
1637: * @apiSuccess {bool} Result.Result Indicates if tables was created successfully.
1638: * @apiSuccess {int} [Result.ErrorCode] Error code.
1639: *
1640: * @apiSuccessExample {json} Success response example:
1641: * {
1642: * Module: 'Core',
1643: * Method: 'CreateTables',
1644: * Result: true
1645: * }
1646: *
1647: * @apiSuccessExample {json} Error response example:
1648: * {
1649: * Module: 'Core',
1650: * Method: 'CreateTables',
1651: * Result: false,
1652: * ErrorCode: 102
1653: * }
1654: */
1655: /**
1656: * Creates tables required for module work. Creates first channel and tenant if it is necessary.
1657: *
1658: * @return bool
1659: */
1660: public function CreateTables()
1661: {
1662: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
1663:
1664: if (!function_exists('mysqli_fetch_all')) {
1665: throw new ApiException(0, null, 'Please make sure your PHP/MySQL environment meets the minimal system requirements.');
1666: }
1667:
1668: $bResult = false;
1669:
1670: try {
1671: $container = \Aurora\Api::GetContainer();
1672:
1673: $oPdo = $container['connection']->getPdo();
1674: if ($oPdo && strpos($oPdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), 'mysqlnd') === false) {
1675: throw new ApiException(Enums\ErrorCodes::MySqlConfigError, null, 'MySqlConfigError');
1676: }
1677:
1678: $container['console']->setAutoExit(false);
1679:
1680: $container['console']->find('migrate')
1681: ->run(new ArrayInput([
1682: '--force' => true,
1683: '--seed' => true
1684: ]), new NullOutput());
1685:
1686: $bResult = true;
1687: } catch (\Exception $oEx) {
1688: Api::LogException($oEx);
1689: if ($oEx instanceof ApiException) {
1690: throw $oEx;
1691: }
1692: }
1693:
1694: return $bResult;
1695: }
1696:
1697: public function GetOrphans()
1698: {
1699: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
1700:
1701: $bResult = false;
1702:
1703: try {
1704: $container = \Aurora\Api::GetContainer();
1705: $container['console']->setAutoExit(false);
1706:
1707: $output = new BufferedOutput();
1708: $container['console']->find('get-orphans')
1709: ->run(new ArrayInput([]), $output);
1710:
1711: $content = array_filter(explode(PHP_EOL, $output->fetch()));
1712: $bResult = $content;
1713: } catch (\Exception $oEx) {
1714: Api::LogException($oEx);
1715: }
1716:
1717: return $bResult;
1718: }
1719:
1720: /**
1721: * Updates config files.
1722: * @return boolean
1723: */
1724: public function UpdateConfig()
1725: {
1726: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
1727:
1728: return Api::UpdateSettings();
1729: }
1730:
1731: /**
1732: * @api {post} ?/Api/ TestDbConnection
1733: * @apiName TestDbConnection
1734: * @apiGroup Core
1735: * @apiDescription Tests connection to database with specified credentials.
1736: *
1737: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
1738: * @apiHeaderExample {json} Header-Example:
1739: * {
1740: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
1741: * }
1742: *
1743: * @apiParam {string=Core} Module Module name.
1744: * @apiParam {string=TestDbConnection} Method Method name.
1745: * @apiParam {string} Parameters JSON.stringified object <br>
1746: * {<br>
1747: * &emsp; **DbLogin** *string* Database login.<br>
1748: * &emsp; **DbName** *string* Database name.<br>
1749: * &emsp; **DbHost** *string* Database host.<br>
1750: * &emsp; **DbPassword** *string* Database password.<br>
1751: * }
1752: *
1753: * @apiParamExample {json} Request-Example:
1754: * {
1755: * Module: 'Core',
1756: * Method: 'TestDbConnection',
1757: * Parameters: '{ DbLogin: "db_login_value", DbName: "db_name_value", DbHost: "db_host_value",
1758: * DbPassword: "db_pass_value" }'
1759: * }
1760: *
1761: * @apiSuccess {object[]} Result Array of response objects.
1762: * @apiSuccess {string} Result.Module Module name.
1763: * @apiSuccess {string} Result.Method Method name.
1764: * @apiSuccess {bool} Result.Result Indicates if test of database connection was successful.
1765: * @apiSuccess {int} [Result.ErrorCode] Error code.
1766: *
1767: * @apiSuccessExample {json} Success response example:
1768: * {
1769: * Module: 'Core',
1770: * Method: 'TestDbConnection',
1771: * Result: true
1772: * }
1773: *
1774: * @apiSuccessExample {json} Error response example:
1775: * {
1776: * Module: 'Core',
1777: * Method: 'TestDbConnection',
1778: * Result: false,
1779: * ErrorCode: 102
1780: * }
1781: */
1782: /**
1783: * Tests connection to database with specified credentials.
1784: *
1785: * @param string $DbLogin Database login.
1786: * @param string $DbName Database name.
1787: * @param string $DbHost Database host.
1788: * @param string $DbPassword Database password.
1789: * @return bool
1790: */
1791: public function TestDbConnection($DbLogin, $DbName, $DbHost, $DbPassword = null)
1792: {
1793: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
1794: if (!function_exists('mysqli_fetch_all')) {
1795: throw new ApiException(0, null, 'Please make sure your PHP/MySQL environment meets the minimal system requirements.');
1796: }
1797:
1798: if (empty($DbName) || empty($DbHost) || empty($DbLogin)) {
1799: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
1800: }
1801:
1802: $oPdo = null;
1803: $oSettings = &Api::GetSettings();
1804: if ($oSettings) {
1805: if ($DbPassword === null) {
1806: $DbPassword = $oSettings->DBPassword;
1807: }
1808: $capsule = new \Illuminate\Database\Capsule\Manager();
1809: $capsule->addConnection(Api::GetDbConfig(
1810: $oSettings->DBType,
1811: $DbHost,
1812: $DbName,
1813: $oSettings->DBPrefix,
1814: $DbLogin,
1815: $DbPassword
1816: ));
1817: $oPdo = $capsule->getConnection()->getPdo();
1818:
1819: if ($oPdo && strpos($oPdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), 'mysqlnd') === false) {
1820: throw new ApiException(Enums\ErrorCodes::MySqlConfigError, null, 'MySqlConfigError');
1821: }
1822: }
1823:
1824: return $oPdo instanceof \PDO;
1825: }
1826:
1827: /**
1828: * Obtains authenticated account.
1829: *
1830: * @param string $AuthToken
1831: */
1832: public function GetAuthenticatedAccount($AuthToken)
1833: {
1834: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
1835:
1836: $aUserInfo = Api::getAuthenticatedUserInfo($AuthToken);
1837: $oAccount = call_user_func_array([$aUserInfo['accountType'], 'find'], [(int)$aUserInfo['account']]);
1838:
1839: return $oAccount;
1840: }
1841:
1842: /**
1843: * Obtains all accounts from all modules for authenticated user.
1844: *
1845: * @param string $AuthToken
1846: * @param string $Type
1847: * @return array
1848: */
1849: public function GetAccounts($AuthToken, $Type = '')
1850: {
1851: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
1852:
1853: $aUserInfo = Api::getAuthenticatedUserInfo($AuthToken);
1854:
1855: $aResult = [];
1856: if (isset($aUserInfo['userId'])) {
1857: $aArgs = array(
1858: 'UserId' => $aUserInfo['userId']
1859: );
1860:
1861: $this->broadcastEvent(
1862: 'GetAccounts',
1863: $aArgs,
1864: $aResult
1865: );
1866: }
1867:
1868: if (!empty($Type)) {
1869: $aTempResult = [];
1870: foreach ($aResult as $aItem) {
1871: if ($aItem['Type'] === $Type) {
1872: $aTempResult[] = $aItem;
1873: }
1874: }
1875: $aResult = $aTempResult;
1876: }
1877:
1878: return $aResult;
1879: }
1880:
1881: /**
1882: * Obtains all accounts from all modules by user.
1883: *
1884: * @param int $UserId
1885: * @param string $Type
1886: * @return array
1887: */
1888: public function GetUserAccounts($UserId, $Type = '')
1889: {
1890: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
1891: $aResult = [];
1892:
1893: $oAuthenticatedUser = Api::getAuthenticatedUser();
1894:
1895: // reset user id to authenticated user id if authenticated user exist
1896: // if user is not authenticated then checkUserRoleIsAtLeast will throw exception
1897: if ($oAuthenticatedUser) {
1898: $UserId = $oAuthenticatedUser->Id;
1899: }
1900:
1901: if ($UserId) {
1902: $aArgs = array(
1903: 'UserId' => $UserId
1904: );
1905:
1906: $this->broadcastEvent(
1907: 'GetAccounts',
1908: $aArgs,
1909: $aResult
1910: );
1911: if (!empty($Type)) {
1912: $aTempResult = [];
1913: foreach ($aResult as $aItem) {
1914: if ($aItem['Type'] === $Type) {
1915: $aTempResult[] = $aItem;
1916: }
1917: }
1918: $aResult = $aTempResult;
1919: }
1920: }
1921:
1922: return $aResult;
1923: }
1924:
1925: /**
1926: * @param string $sEmail
1927: * @param string $sIp
1928: *
1929: * @throws ApiException
1930: */
1931: public function IsBlockedUser($sEmail, $sIp)
1932: {
1933: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1934:
1935: $bEnableFailedLoginBlock = $this->oModuleSettings->EnableFailedLoginBlock;
1936: $iLoginBlockAvailableTriesCount = $this->oModuleSettings->LoginBlockAvailableTriesCount;
1937: $iLoginBlockDurationMinutes = $this->oModuleSettings->LoginBlockDurationMinutes;
1938:
1939: if ($bEnableFailedLoginBlock) {
1940: try {
1941: $oBlockedUser = $this->GetBlockedUser($sEmail, $sIp);
1942: if ($oBlockedUser) {
1943: if ($oBlockedUser->ErrorLoginsCount >= $iLoginBlockAvailableTriesCount) {
1944: $iBlockTime = (time() - $oBlockedUser->Time) / 60;
1945: if ($iBlockTime > $iLoginBlockDurationMinutes) {
1946: $oBlockedUser->delete();
1947: } else {
1948: $this->BlockUser($sEmail, $sIp);
1949: throw new ApiException(
1950: 1000,
1951: null,
1952: $this->i18N("BLOCKED_USER_MESSAGE_ERROR", [
1953: "N" => $iLoginBlockAvailableTriesCount,
1954: "M" => ceil($iLoginBlockDurationMinutes - $iBlockTime)
1955: ])
1956: );
1957: }
1958: }
1959: } elseif ($this->CheckIpReputation($sIp)) {
1960: $this->BlockUser($sEmail, $sIp, true);
1961:
1962: throw new ApiException(
1963: 1000,
1964: null,
1965: $this->i18N("BLOCKED_USER_IP_REPUTATION_MESSAGE_ERROR")
1966: );
1967: }
1968: } catch (\Aurora\System\Exceptions\DbException $oEx) {
1969: Api::LogException($oEx);
1970: }
1971: }
1972: }
1973:
1974: /**
1975: * @param string $sEmail
1976: * @param string $sIp
1977: *
1978: * @return Models\UserBlock|false
1979: */
1980: public function GetBlockedUser($sEmail, $sIp)
1981: {
1982: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
1983:
1984: $mResult = false;
1985:
1986: if ($this->oModuleSettings->EnableFailedLoginBlock) {
1987: try {
1988: $mResult = Models\UserBlock::where('Email', $sEmail)->where('IpAddress', $sIp)->first();
1989: } catch (\Exception $oEx) {
1990: $mResult = false;
1991: }
1992: }
1993:
1994: return $mResult;
1995: }
1996:
1997: /**
1998: * @param string $sIp
1999: *
2000: * @return bool
2001: */
2002: public function CheckIpReputation($sIp)
2003: {
2004: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2005:
2006: $mResult = false;
2007:
2008: if ($this->oModuleSettings->EnableFailedLoginBlock && is_numeric($this->oModuleSettings->LoginBlockIpReputationThreshold) && $this->oModuleSettings->LoginBlockIpReputationThreshold > 0) {
2009: $iLoginBlockAvailableTriesCount = $this->oModuleSettings->LoginBlockAvailableTriesCount;
2010:
2011: $count = Models\UserBlock::where('IpAddress', $sIp)->where('ErrorLoginsCount', '>=', $iLoginBlockAvailableTriesCount)->count();
2012:
2013: $mResult = $count >= $this->oModuleSettings->LoginBlockIpReputationThreshold;
2014: }
2015:
2016: return $mResult;
2017: }
2018:
2019: /**
2020: * @param string $sIp
2021: * @param string $sIp
2022: * @param bool $bMaxErrorLoginsCount
2023: */
2024: public function BlockUser($sEmail, $sIp, $bMaxErrorLoginsCount = false)
2025: {
2026: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2027:
2028: if ($this->oModuleSettings->EnableFailedLoginBlock) {
2029:
2030: try {
2031: $oBlockedUser = $this->GetBlockedUser($sEmail, $sIp);
2032: if (!$oBlockedUser) {
2033: $oBlockedUser = new Models\UserBlock();
2034: $oBlockedUser->Email = $sEmail;
2035: $oBlockedUser->IpAddress = $sIp;
2036: }
2037: $iUserId = Api::getUserIdByPublicId($sEmail);
2038: if ($iUserId) {
2039: $oBlockedUser->UserId = $iUserId;
2040: if ($bMaxErrorLoginsCount) {
2041: $oBlockedUser->ErrorLoginsCount = $this->oModuleSettings->LoginBlockAvailableTriesCount;
2042: } else {
2043: $oBlockedUser->ErrorLoginsCount++;
2044: }
2045: $oBlockedUser->Time = time();
2046:
2047: $oBlockedUser->save();
2048: }
2049: } catch (\Exception $oEx) {
2050: Api::LogException($oEx);
2051: }
2052: }
2053: }
2054:
2055: /**
2056: * @param string $Login
2057: * @param string $Password
2058: * @param bool $SignMe
2059: *
2060: * @return mixed
2061: */
2062: public function Authenticate($Login, $Password, $SignMe = false)
2063: {
2064: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2065:
2066: $sIp = \Aurora\System\Utils::getClientIp();
2067: $this->Decorator()->IsBlockedUser($Login, $sIp);
2068:
2069: $mResult = false;
2070: $aArgs = array(
2071: 'Login' => $Login,
2072: 'Password' => $Password,
2073: 'SignMe' => $SignMe
2074: );
2075:
2076: try {
2077: $this->broadcastEvent(
2078: 'Login',
2079: $aArgs,
2080: $mResult
2081: );
2082: } catch (\Exception $oException) {
2083: Api::GetModuleManager()->SetLastException($oException);
2084: }
2085:
2086: if (!$mResult) {
2087: $this->Decorator()->BlockUser($Login, $sIp);
2088: $this->Decorator()->IsBlockedUser($Login, $sIp);
2089: } else {
2090: $oBlockedUser = $this->Decorator()->GetBlockedUser($Login, $sIp);
2091: if ($oBlockedUser) {
2092: $oBlockedUser->delete();
2093: }
2094: }
2095:
2096: return $mResult;
2097: }
2098:
2099: /**
2100: *
2101: */
2102: public function SetAuthDataAndGetAuthToken($aAuthData, $Language = '', $SignMe = false)
2103: {
2104: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2105:
2106: $mResult = false;
2107: if ($aAuthData && is_array($aAuthData)) {
2108: $mResult = $aAuthData;
2109: if (isset($aAuthData['token'])) {
2110: $iTime = $SignMe ? 0 : time();
2111: $iAuthTokenExpirationLifetimeDays = \Aurora\Api::GetSettings()->AuthTokenExpirationLifetimeDays;
2112: $iExpire = 0;
2113: if ($iAuthTokenExpirationLifetimeDays > 0) {
2114: $iExpire = time() + ($iAuthTokenExpirationLifetimeDays * 24 * 60 * 60);
2115: }
2116:
2117: $sAuthToken = Api::UserSession()->Set($aAuthData, $iTime, $iExpire);
2118:
2119: //this will store user data in static variable of Api class for later usage
2120: $oUser = Api::getAuthenticatedUser($sAuthToken, true);
2121: if ($oUser) {
2122: if ($oUser->Role !== UserRole::SuperAdmin) {
2123: // If User is super admin don't try to detect tenant. It will try to connect to DB.
2124: // Super admin should be able to log in without connecting to DB.
2125: $oTenant = Api::getTenantByWebDomain();
2126: if ($oTenant && $oUser->IdTenant !== $oTenant->Id) {
2127: throw new ApiException(Notifications::AuthError, null, 'AuthError');
2128: }
2129: }
2130:
2131: if ($Language !== '' && $oUser->Language !== $Language) {
2132: $oUser->Language = $Language;
2133: }
2134:
2135: $oUser->LastLogin = date('Y-m-d H:i:s');
2136: $oUser->LoginsCount = $oUser->LoginsCount + 1;
2137:
2138: $this->getUsersManager()->updateUser($oUser);
2139: Api::LogEvent('login-success: ' . $oUser->PublicId, self::GetName());
2140: $mResult = [
2141: \Aurora\System\Application::AUTH_TOKEN_KEY => $sAuthToken
2142: ];
2143: } else {
2144: throw new ApiException(Notifications::AuthError, null, 'AuthError');
2145: }
2146: }
2147: } else {
2148: Api::LogEvent('login-failed', self::GetName());
2149: Api::GetModuleManager()->SetLastException(
2150: new ApiException(Notifications::AuthError, null, 'AuthError')
2151: );
2152: }
2153:
2154: return $mResult;
2155: }
2156:
2157: /**
2158: * @api {post} ?/Api/ Login
2159: * @apiName Login
2160: * @apiGroup Core
2161: * @apiDescription Broadcasts event Login to other modules, gets responses from them and returns AuthToken.
2162: *
2163: * @apiParam {string=Core} Module Module name.
2164: * @apiParam {string=Login} Method Method name.
2165: * @apiParam {string} Parameters JSON.stringified object <br>
2166: * {<br>
2167: * &emsp; **Login** *string* Account login.<br>
2168: * &emsp; **Password** *string* Account password.<br>
2169: * &emsp; **Language** *string* New value of language for user.<br>
2170: * &emsp; **SignMe** *bool* Indicates if it is necessary to remember user between sessions. *optional*<br>
2171: * }
2172: *
2173: * @apiParamExample {json} Request-Example:
2174: * {
2175: * Module: 'Core',
2176: * Method: 'Login',
2177: * Parameters: '{ Login: "login_value", Password: "password_value", SignMe: true }'
2178: * }
2179: *
2180: * @apiSuccess {object[]} Result Array of response objects.
2181: * @apiSuccess {string} Result.Module Module name.
2182: * @apiSuccess {string} Result.Method Method name.
2183: * @apiSuccess {mixed} Result.Result Object in case of success, otherwise **false**.
2184: * @apiSuccess {string} Result.Result.AuthToken Authentication token.
2185: * @apiSuccess {int} [Result.ErrorCode] Error code.
2186: *
2187: * @apiSuccessExample {json} Success response example:
2188: * {
2189: * Module: 'Core',
2190: * Method: 'Login',
2191: * Result: { AuthToken: 'token_value' }
2192: * }
2193: *
2194: * @apiSuccessExample {json} Error response example:
2195: * {
2196: * Module: 'Core',
2197: * Method: 'Login',
2198: * Result: false,
2199: * ErrorCode: 102
2200: * }
2201: */
2202: /**
2203: * Broadcasts event Login to other modules, gets responses from them and returns AuthToken.
2204: *
2205: * @param string $Login Account login.
2206: * @param string $Password Account password.
2207: * @param string $Language New value of language for user.
2208: * @param bool $SignMe Indicates if it is necessary to remember user between sessions.
2209: * @return array
2210: * @throws ApiException
2211: */
2212: public function Login($Login, $Password, $Language = '', $SignMe = false)
2213: {
2214: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
2215:
2216: $Login = str_replace(" ", "", $Login);
2217: $aAuthData = $this->Decorator()->Authenticate($Login, $Password, $SignMe);
2218:
2219: return $this->Decorator()->SetAuthDataAndGetAuthToken($aAuthData, $Language, $SignMe);
2220: }
2221:
2222: /**
2223: *
2224: * @param string $Login
2225: * @param string $Realm
2226: * @param string $Type
2227: * @return void
2228: */
2229: public function GetDigestHash($Login, $Realm, $Type)
2230: {
2231: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2232:
2233: $mResult = null;
2234:
2235: $aArgs = array(
2236: 'Login' => $Login,
2237: 'Realm' => $Realm,
2238: 'Type' => $Type
2239: );
2240:
2241: $this->broadcastEvent(
2242: 'GetDigestHash',
2243: $aArgs,
2244: $mResult
2245: );
2246:
2247: return $mResult;
2248: }
2249:
2250: public function GetAccountUsedToAuthorize($Login)
2251: {
2252: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2253:
2254: $mResult = null;
2255:
2256: $aArgs = array(
2257: 'Login' => $Login
2258: );
2259:
2260: $this->broadcastEvent(
2261: 'GetAccountUsedToAuthorize',
2262: $aArgs,
2263: $mResult
2264: );
2265:
2266: return $mResult;
2267: }
2268:
2269: /**
2270: * @param string $Password Account password.
2271: * @return bool
2272: * @throws ApiException
2273: */
2274: public function VerifyPassword($Password)
2275: {
2276: /** This method is restricted to be called by web API (see denyMethodsCallByWebApi method). **/
2277:
2278: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
2279: $mResult = false;
2280: $bResult = false;
2281:
2282: $oApiIntegrator = $this->getIntegratorManager();
2283: $aUserInfo = $oApiIntegrator->getAuthenticatedUserInfo(Api::getAuthToken());
2284: if (isset($aUserInfo['account']) && isset($aUserInfo['accountType'])) {
2285: $r = new \ReflectionClass($aUserInfo['accountType']);
2286: $oQuery = $r->getMethod('query')->invoke(null);
2287:
2288: $oAccount = $oQuery->find($aUserInfo['account']);
2289: if ($oAccount) {
2290: $aArgs = array(
2291: 'Login' => $oAccount->getLogin(),
2292: 'Password' => $Password,
2293: 'SignMe' => false
2294: );
2295: $this->broadcastEvent(
2296: 'Login',
2297: $aArgs,
2298: $mResult
2299: );
2300:
2301: if (is_array($mResult)
2302: && isset($mResult['token'])
2303: && $mResult['token'] === 'auth'
2304: && isset($mResult['id'])
2305: ) {
2306: $UserId = Api::getAuthenticatedUserId();
2307: if ($mResult['id'] === $UserId) {
2308: $bResult = true;
2309: }
2310: }
2311: }
2312: }
2313:
2314: return $bResult;
2315: }
2316:
2317: /**
2318: * @param $email
2319: * @param $resetOption
2320: * @return bool
2321: */
2322: public function ResetPassword($email, $resetOption)
2323: {
2324: $mResult = false;
2325:
2326: $aArgs = array(
2327: 'email' => $email,
2328: 'resetOption' => $resetOption
2329: );
2330: $this->broadcastEvent(
2331: 'ResetPassword',
2332: $aArgs,
2333: $mResult
2334: );
2335:
2336:
2337: if (!empty($mResult)) {
2338: Api::LogEvent('resetPassword-success: ' . $email, self::GetName());
2339: } else {
2340: Api::LogEvent('resetPassword-failed: ' . $email, self::GetName());
2341: }
2342:
2343: return $mResult;
2344: }
2345:
2346:
2347: /**
2348: *
2349: */
2350: public function ResetPasswordBySecurityQuestion($securityAnswer, $securityToken)
2351: {
2352: $mResult = false;
2353:
2354: $aArgs = array(
2355: 'securityAnswer' => $securityAnswer,
2356: 'securityToken' => $securityToken
2357: );
2358: $this->broadcastEvent(
2359: 'ResetPasswordBySecurityQuestion',
2360: $aArgs,
2361: $mResult
2362: );
2363:
2364:
2365: if (!empty($mResult)) {
2366: Api::LogEvent('ResetPasswordBySecurityQuestion-success: ' . $securityAnswer, self::GetName());
2367: return $mResult;
2368: }
2369:
2370: Api::LogEvent('ResetPasswordBySecurityQuestion-failed: ' . $securityAnswer, self::GetName());
2371: }
2372:
2373: /**
2374: *
2375: */
2376: public function UpdatePassword($Password, $ConfirmPassword, $Hash)
2377: {
2378: $mResult = false;
2379:
2380: $aArgs = array(
2381: 'Password' => $Password,
2382: 'ConfirmPassword' => $ConfirmPassword,
2383: 'Hash' => $Hash
2384: );
2385: $this->broadcastEvent(
2386: 'UpdatePassword',
2387: $aArgs,
2388: $mResult
2389: );
2390:
2391: if (!empty($mResult)) {
2392: Api::LogEvent('updatePassword-success: ' . $Hash, self::GetName());
2393: return $mResult;
2394: }
2395:
2396: Api::LogEvent('updatePassword-failed: ' . $Hash, self::GetName());
2397: }
2398:
2399: /**
2400: * @api {post} ?/Api/ Logout
2401: * @apiName Logout
2402: * @apiGroup Core
2403: * @apiDescription Logs out authenticated user. Clears session.
2404: *
2405: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2406: * @apiHeaderExample {json} Header-Example:
2407: * {
2408: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2409: * }
2410: *
2411: * @apiParam {string=Core} Module Module name.
2412: * @apiParam {string=Logout} Method Method name.
2413: *
2414: * @apiParamExample {json} Request-Example:
2415: * {
2416: * Module: 'Core',
2417: * Method: 'Logout'
2418: * }
2419: *
2420: * @apiSuccess {object[]} Result Array of response objects.
2421: * @apiSuccess {string} Result.Module Module name.
2422: * @apiSuccess {string} Result.Method Method name.
2423: * @apiSuccess {bool} Result.Result Indicates if logout was successful.
2424: * @apiSuccess {int} [Result.ErrorCode] Error code.
2425: *
2426: * @apiSuccessExample {json} Success response example:
2427: * {
2428: * Module: 'Core',
2429: * Method: 'Logout',
2430: * Result: true
2431: * }
2432: *
2433: * @apiSuccessExample {json} Error response example:
2434: * {
2435: * Module: 'Core',
2436: * Method: 'Logout',
2437: * Result: false,
2438: * ErrorCode: 102
2439: * }
2440: */
2441: /**
2442: * Logs out authenticated user. Clears session.
2443: *
2444: * @return bool
2445: * @throws ApiException
2446: */
2447: public function Logout()
2448: {
2449: Api::checkUserRoleIsAtLeast(UserRole::Anonymous);
2450:
2451: Api::LogEvent('logout', self::GetName());
2452:
2453: Api::UserSession()->Delete(
2454: Api::getAuthToken()
2455: );
2456:
2457: return true;
2458: }
2459:
2460: /**
2461: * Creates channel with specified login and description.
2462: *
2463: * @param string $Login New channel login.
2464: * @param string $Description New channel description.
2465: * @return int New channel identifier.
2466: * @throws ApiException
2467: */
2468: public function CreateChannel($Login, $Description = '')
2469: {
2470: $mResult = -1;
2471: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2472:
2473: $mResult = false;
2474:
2475: $Login = \trim($Login);
2476: if ($Login !== '') {
2477: $oChannel = new Models\Channel();
2478:
2479: $oChannel->Login = $Login;
2480:
2481: if ($Description !== '') {
2482: $oChannel->Description = $Description;
2483: }
2484:
2485: if ($this->getChannelsManager()->createChannel($oChannel)) {
2486: $mResult = $oChannel->Id;
2487: }
2488: } else {
2489: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
2490: }
2491:
2492: return $mResult;
2493: }
2494:
2495: /**
2496: * Updates channel.
2497: *
2498: * @param int $ChannelId Channel identifier.
2499: * @param string $Login New login for channel.
2500: * @param string $Description New description for channel.
2501: * @return bool
2502: * @throws ApiException
2503: */
2504: public function UpdateChannel($ChannelId, $Login = '', $Description = '')
2505: {
2506: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2507:
2508: if ($ChannelId > 0) {
2509: $oChannel = $this->getChannelsManager()->getChannelById($ChannelId);
2510:
2511: if ($oChannel) {
2512: $Login = \trim($Login);
2513: if (!empty($Login)) {
2514: $oChannel->Login = $Login;
2515: }
2516: if (!empty($Description)) {
2517: $oChannel->Description = $Description;
2518: }
2519:
2520: return $this->getChannelsManager()->updateChannel($oChannel);
2521: }
2522: } else {
2523: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
2524: }
2525:
2526: return false;
2527: }
2528:
2529: /**
2530: * Deletes channel.
2531: *
2532: * @param int $ChannelId Identifier of channel to delete.
2533: * @return bool
2534: * @throws ApiException
2535: */
2536: public function DeleteChannel($ChannelId)
2537: {
2538: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2539:
2540: if ($ChannelId > 0) {
2541: $oChannel = $this->getChannelsManager()->getChannelById($ChannelId);
2542:
2543: if ($oChannel) {
2544: return $this->getChannelsManager()->deleteChannel($oChannel);
2545: }
2546: } else {
2547: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
2548: }
2549:
2550: return false;
2551: }
2552:
2553: /**
2554: * @api {post} ?/Api/ GetTenants
2555: * @apiName GetTenants
2556: * @apiGroup Core
2557: * @apiDescription Obtains tenant list if super administrator is authenticated.
2558: *
2559: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2560: * @apiHeaderExample {json} Header-Example:
2561: * {
2562: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2563: * }
2564: *
2565: * @apiParam {string=Core} Module Module name.
2566: * @apiParam {string=GetTenants} Method Method name.
2567: * @apiParam {string} Parameters JSON.stringified object <br>
2568: * {<br>
2569: * &emsp; **Offset** *int* Offset of tenant list.<br>
2570: * &emsp; **Limit** *int* Limit of result tenant list.<br>
2571: * &emsp; **Search** *string* Search string.<br>
2572: * }
2573: *
2574: * @apiParamExample {json} Request-Example:
2575: * {
2576: * Module: 'Core',
2577: * Method: 'GetTenants'
2578: * }
2579: *
2580: * @apiSuccess {object[]} Result Array of response objects.
2581: * @apiSuccess {string} Result.Module Module name.
2582: * @apiSuccess {string} Result.Method Method name.
2583: * @apiSuccess {mixed} Result.Result Object with array of tenants and their count in case of success, otherwise **false**.
2584: * @apiSuccess {int} [Result.ErrorCode] Error code.
2585: *
2586: * @apiSuccessExample {json} Success response example:
2587: * {
2588: * Module: 'Core',
2589: * Method: 'GetTenants',
2590: * Result: {
2591: * Items: [
2592: * { Id: 123, Name: 'Default', SiteName: '' }
2593: * ],
2594: * Count: 1
2595: * }
2596: * }
2597: *
2598: * @apiSuccessExample {json} Error response example:
2599: * {
2600: * Module: 'Core',
2601: * Method: 'GetTenants',
2602: * Result: false,
2603: * ErrorCode: 102
2604: * }
2605: */
2606: /**
2607: * Obtains tenant list if super administrator is authenticated.
2608: * @param int $Offset Offset of the list.
2609: * @param int $Limit Limit of the list.
2610: * @param string $Search Search string.
2611: * @return array {
2612: * *array* **Items** Tenant list
2613: * *int* **Count** Tenant count
2614: * }
2615: * @throws ApiException
2616: */
2617: public function GetTenants($Offset = 0, $Limit = 0, $Search = '')
2618: {
2619: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
2620:
2621: $oAuthenticatedUser = Api::getAuthenticatedUser();
2622: $bSuperadmin = $oAuthenticatedUser->Role === UserRole::SuperAdmin;
2623:
2624: $aTenantsFromDb = $this->getTenantsManager()->getTenantList($Offset, $Limit, $Search);
2625: $oSettings = $this->oModuleSettings;
2626: $aTenants = [];
2627:
2628: foreach ($aTenantsFromDb as $oTenant) {
2629: if ($bSuperadmin || $oTenant->Id === $oAuthenticatedUser->IdTenant) {
2630: $aTenants[] = [
2631: 'Id' => $oTenant->Id,
2632: 'Name' => $oTenant->Name,
2633: 'SiteName' => $oSettings->GetTenantValue($oTenant->Name, 'SiteName', '')
2634: ];
2635: }
2636: }
2637:
2638: $iTenantsCount = $Limit > 0 ? $this->getTenantsManager()->getTenantsCount($Search) : count($aTenants);
2639: return array(
2640: 'Items' => $aTenants,
2641: 'Count' => $iTenantsCount,
2642: );
2643: }
2644:
2645: /**
2646: * @api {post} ?/Api/ GetTenant
2647: * @apiName GetTenant
2648: * @apiGroup Core
2649: * @apiDescription Returns tenant.
2650: *
2651: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2652: * @apiHeaderExample {json} Header-Example:
2653: * {
2654: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2655: * }
2656: *
2657: * @apiParam {string=Core} Module Module name.
2658: * @apiParam {string=GetTenant} Method Method name.
2659: * @apiParam {string} Parameters JSON.stringified object <br>
2660: * {<br>
2661: * &emsp; **Id** *int* Tenant identifier.<br>
2662: * }
2663: *
2664: * @apiParamExample {json} Request-Example:
2665: * {
2666: * Module: 'Core',
2667: * Method: 'GetTenant',
2668: * Parameters: '{ Id: 123 }'
2669: * }
2670: *
2671: * @apiSuccess {object[]} Result Array of response objects.
2672: * @apiSuccess {string} Result.Module Module name.
2673: * @apiSuccess {string} Result.Method Method name.
2674: * @apiSuccess {mixed} Result.Result Object in case of success, otherwise **false**.
2675: * @apiSuccess {int} [Result.ErrorCode] Error code.
2676: *
2677: * @apiSuccessExample {json} Success response example:
2678: * {
2679: * Module: 'Core',
2680: * Method: 'GetTenant',
2681: * Result: { Description: '', Name: 'Default', SiteName: '', WebDomain: '' }
2682: * }
2683: *
2684: * @apiSuccessExample {json} Error response example:
2685: * {
2686: * Module: 'Core',
2687: * Method: 'GetTenant',
2688: * Result: false,
2689: * ErrorCode: 102
2690: * }
2691: */
2692: /**
2693: * Returns tenant object by identifier.
2694: *
2695: * @param int $Id Tenant identifier.
2696: * @return Models\Tenant|null
2697: */
2698: public function GetTenant($Id)
2699: {
2700: $oAuthenticatedUser = Api::getAuthenticatedUser();
2701: if (($oAuthenticatedUser instanceof User) && $oAuthenticatedUser->IdTenant === $Id) {
2702: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
2703: } else {
2704: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2705: }
2706:
2707: return $this->GetTenantWithoutRoleCheck($Id);
2708: }
2709:
2710: /**
2711: * @api {post} ?/Api/ CreateTenant
2712: * @apiName CreateTenant
2713: * @apiGroup Core
2714: * @apiDescription Creates tenant.
2715: *
2716: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2717: * @apiHeaderExample {json} Header-Example:
2718: * {
2719: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2720: * }
2721: *
2722: * @apiParam {string=Core} Module Module name.
2723: * @apiParam {string=CreateTenant} Method Method name.
2724: * @apiParam {string} Parameters JSON.stringified object <br>
2725: * {<br>
2726: * &emsp; **ChannelId** *int* Identifier of channel new tenant belongs to.<br>
2727: * &emsp; **Name** *string* New tenant name.<br>
2728: * &emsp; **Description** *string* New tenant description.<br>
2729: * &emsp; **WebDomain** *string* New tenant web domain.<br>
2730: * &emsp; **SiteName** *string* New tenant site name.<br>
2731: * }
2732: *
2733: * @apiParamExample {json} Request-Example:
2734: * {
2735: * Module: 'Core',
2736: * Method: 'CreateTenant',
2737: * Parameters: '{ ChannelId: 123, Name: "name_value", Description: "description_value" }'
2738: * }
2739: *
2740: * @apiSuccess {object[]} Result Array of response objects.
2741: * @apiSuccess {string} Result.Module Module name.
2742: * @apiSuccess {string} Result.Method Method name.
2743: * @apiSuccess {bool} Result.Result Indicates if tenant was created successfully.
2744: * @apiSuccess {int} [Result.ErrorCode] Error code.
2745: *
2746: * @apiSuccessExample {json} Success response example:
2747: * {
2748: * Module: 'Core',
2749: * Method: 'CreateTenant',
2750: * Result: true
2751: * }
2752: *
2753: * @apiSuccessExample {json} Error response example:
2754: * {
2755: * Module: 'Core',
2756: * Method: 'CreateTenant',
2757: * Result: false,
2758: * ErrorCode: 102
2759: * }
2760: */
2761: /**
2762: * Creates tenant.
2763: *
2764: * @param int $ChannelId Identifier of channel new tenant belongs to.
2765: * @param string $Name New tenant name.
2766: * @param string $Description New tenant description.
2767: * @param string $WebDomain New tenant web domain.
2768: * @param string $SiteName Tenant site name.
2769: * @return bool
2770: * @throws ApiException
2771: */
2772: public function CreateTenant($ChannelId = 0, $Name = '', $Description = '', $WebDomain = '', $SiteName = null)
2773: {
2774: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2775:
2776: $oSettings = &Api::GetSettings();
2777: if (/*!$oSettings->EnableMultiChannel && */$ChannelId === 0) { // TODO: temporary ignore 'EnableMultiChannel' config
2778: $aChannels = $this->getChannelsManager()->getChannelList(0, 1);
2779: $ChannelId = count($aChannels) === 1 ? $aChannels[0]->Id : 0;
2780: }
2781: $Name = \trim(\Aurora\System\Utils::getSanitizedFilename($Name));
2782:
2783: if ($Name !== '' && $ChannelId > 0) {
2784: $iTenantsCount = $this->getTenantsManager()->getTenantsByChannelIdCount($ChannelId);
2785: if ($oSettings->EnableMultiTenant || $iTenantsCount === 0) {
2786: $oTenant = new Models\Tenant();
2787:
2788: $oTenant->Name = $Name;
2789: $oTenant->Description = $Description;
2790: $oTenant->WebDomain = $WebDomain;
2791: $oTenant->IdChannel = $ChannelId;
2792:
2793: if ($this->getTenantsManager()->createTenant($oTenant)) {
2794: if ($SiteName !== null) {
2795: $oSettings = $this->oModuleSettings;
2796: $oSettings->SaveTenantSettings($oTenant->Name, [
2797: 'SiteName' => $SiteName
2798: ]);
2799: }
2800: return $oTenant->Id;
2801: }
2802: }
2803: } else {
2804: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
2805: }
2806:
2807: return false;
2808: }
2809:
2810: /**
2811: * @api {post} ?/Api/ UpdateTenant
2812: * @apiName UpdateTenant
2813: * @apiGroup Core
2814: * @apiDescription Updates tenant.
2815: *
2816: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2817: * @apiHeaderExample {json} Header-Example:
2818: * {
2819: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2820: * }
2821: *
2822: * @apiParam {string=Core} Module Module name.
2823: * @apiParam {string=UpdateTenant} Method Method name.
2824: * @apiParam {string} Parameters JSON.stringified object <br>
2825: * {<br>
2826: * &emsp; **TenantId** *int* Identifier of tenant to update.<br>
2827: * &emsp; **Description** *string* Tenant description.<br>
2828: * &emsp; **WebDomain** *string* Tenant web domain.<br>
2829: * &emsp; **SiteName** *string* Tenant site name.<br>
2830: * &emsp; **ChannelId** *int* Identifier of the new tenant channel.<br>
2831: * }
2832: *
2833: * @apiParamExample {json} Request-Example:
2834: * {
2835: * Module: 'Core',
2836: * Method: 'UpdateTenant',
2837: * Parameters: '{ TenantId: 123, Description: "description_value", ChannelId: 123 }'
2838: * }
2839: *
2840: * @apiSuccess {object[]} Result Array of response objects.
2841: * @apiSuccess {string} Result.Module Module name.
2842: * @apiSuccess {string} Result.Method Method name.
2843: * @apiSuccess {bool} Result.Result Indicates if tenant was updated successfully.
2844: * @apiSuccess {int} [Result.ErrorCode] Error code.
2845: *
2846: * @apiSuccessExample {json} Success response example:
2847: * {
2848: * Module: 'Core',
2849: * Method: 'UpdateTenant',
2850: * Result: true
2851: * }
2852: *
2853: * @apiSuccessExample {json} Error response example:
2854: * {
2855: * Module: 'Core',
2856: * Method: 'UpdateTenant',
2857: * Result: false,
2858: * ErrorCode: 102
2859: * }
2860: */
2861: /**
2862: * Updates tenant.
2863: *
2864: * @param int $TenantId Identifier of tenant to update.
2865: * @param string $Description Tenant description.
2866: * @param string $WebDomain Tenant web domain.
2867: * @param string $SiteName Tenant site name.
2868: * @param int $ChannelId Identifier of the tenant channel.
2869: * @return bool
2870: * @throws ApiException
2871: */
2872: public function UpdateTenant($TenantId, $Description = null, $WebDomain = null, $SiteName = null, $ChannelId = 0)
2873: {
2874: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
2875: $oAuthenticatedUser = Api::getAuthenticatedUser();
2876: if ($oAuthenticatedUser->Role === UserRole::TenantAdmin && $oAuthenticatedUser->IdTenant !== $TenantId) {
2877: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
2878: } else {
2879: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2880: }
2881:
2882: if (!empty($TenantId)) {
2883: $oTenant = $this->getTenantsManager()->getTenantById($TenantId);
2884: if ($oTenant) {
2885: if ($SiteName !== null) {
2886: $oSettings = $this->oModuleSettings;
2887: $oSettings->SaveTenantSettings($oTenant->Name, [
2888: 'SiteName' => $SiteName
2889: ]);
2890: }
2891: if ($Description !== null) {
2892: $oTenant->Description = $Description;
2893: }
2894: if ($WebDomain !== null && $oAuthenticatedUser->Role === UserRole::SuperAdmin) {
2895: $oTenant->WebDomain = $WebDomain;
2896: }
2897: if (!empty($ChannelId) && $oAuthenticatedUser->Role === UserRole::SuperAdmin) {
2898: $oTenant->IdChannel = $ChannelId;
2899: }
2900:
2901: return $this->getTenantsManager()->updateTenant($oTenant);
2902: }
2903: } else {
2904: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
2905: }
2906:
2907: return false;
2908: }
2909:
2910: /**
2911: * @api {post} ?/Api/ DeleteTenants
2912: * @apiName DeleteTenants
2913: * @apiGroup Core
2914: * @apiDescription Deletes tenants specified by a list of identifiers.
2915: *
2916: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2917: * @apiHeaderExample {json} Header-Example:
2918: * {
2919: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2920: * }
2921: *
2922: * @apiParam {string=Core} Module Module name.
2923: * @apiParam {string=DeleteTenants} Method Method name.
2924: * @apiParam {string} Parameters JSON.stringified object <br>
2925: * {<br>
2926: * &emsp; **IdList** *array* List of tenants identifiers.<br>
2927: * }
2928: *
2929: * @apiParamExample {json} Request-Example:
2930: * {
2931: * Module: 'Core',
2932: * Method: 'DeleteTenants',
2933: * Parameters: '{ IdList: [123, 456] }'
2934: * }
2935: *
2936: * @apiSuccess {object[]} Result Array of response objects.
2937: * @apiSuccess {string} Result.Module Module name.
2938: * @apiSuccess {string} Result.Method Method name.
2939: * @apiSuccess {bool} Result.Result Indicates if tenants were deleted successfully.
2940: * @apiSuccess {int} [Result.ErrorCode] Error code.
2941: *
2942: * @apiSuccessExample {json} Success response example:
2943: * {
2944: * Module: 'Core',
2945: * Method: 'DeleteTenants',
2946: * Result: true
2947: * }
2948: *
2949: * @apiSuccessExample {json} Error response example:
2950: * {
2951: * Module: 'Core',
2952: * Method: 'DeleteTenants',
2953: * Result: false,
2954: * ErrorCode: 102
2955: * }
2956: */
2957: /**
2958: * Deletes tenants specified by a list of identifiers.
2959: * @param array $IdList List of tenants identifiers.
2960: * @return bool
2961: */
2962: public function DeleteTenants($IdList)
2963: {
2964: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
2965:
2966: $bResult = true;
2967:
2968: foreach ($IdList as $sId) {
2969: $bResult = $bResult && self::Decorator()->DeleteTenant($sId);
2970: }
2971:
2972: return $bResult;
2973: }
2974:
2975: /**
2976: * @api {post} ?/Api/ DeleteTenant
2977: * @apiName DeleteTenant
2978: * @apiGroup Core
2979: * @apiDescription Deletes tenant.
2980: *
2981: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
2982: * @apiHeaderExample {json} Header-Example:
2983: * {
2984: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
2985: * }
2986: *
2987: * @apiParam {string=Core} Module Module name.
2988: * @apiParam {string=DeleteTenant} Method Method name.
2989: * @apiParam {string} Parameters JSON.stringified object <br>
2990: * {<br>
2991: * &emsp; **TenantId** *int* Identifier of tenant to delete.<br>
2992: * }
2993: *
2994: * @apiParamExample {json} Request-Example:
2995: * {
2996: * Module: 'Core',
2997: * Method: 'DeleteTenant',
2998: * Parameters: '{ TenantId: 123 }'
2999: * }
3000: *
3001: * @apiSuccess {object[]} Result Array of response objects.
3002: * @apiSuccess {string} Result.Module Module name.
3003: * @apiSuccess {string} Result.Method Method name.
3004: * @apiSuccess {bool} Result.Result Indicates if tenant was deleted successfully.
3005: * @apiSuccess {int} [Result.ErrorCode] Error code.
3006: *
3007: * @apiSuccessExample {json} Success response example:
3008: * {
3009: * Module: 'Core',
3010: * Method: 'DeleteTenant',
3011: * Result: true
3012: * }
3013: *
3014: * @apiSuccessExample {json} Error response example:
3015: * {
3016: * Module: 'Core',
3017: * Method: 'DeleteTenant',
3018: * Result: false,
3019: * ErrorCode: 102
3020: * }
3021: */
3022: /**
3023: * Deletes tenant.
3024: *
3025: * @param int $TenantId Identifier of tenant to delete.
3026: * @return bool
3027: * @throws ApiException
3028: */
3029: public function DeleteTenant($TenantId)
3030: {
3031: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3032:
3033: if (!empty($TenantId)) {
3034: $oTenant = $this->getTenantsManager()->getTenantById($TenantId);
3035:
3036: if ($oTenant) {
3037: // Delete tenant config files.
3038: $sTenantSpacePath = Api::GetModuleManager()->GetModulesSettingsPath() . 'tenants/' . $oTenant->Name;
3039: if (@is_dir($sTenantSpacePath)) {
3040: $this->deleteTree($sTenantSpacePath);
3041: }
3042:
3043: // Delete group
3044: Group::where('TenantId', $oTenant->Id)->delete();
3045:
3046: // Delete users
3047: $userIds = User::where('IdTenant', $oTenant->Id)->select('Id')->pluck('Id')->toArray();
3048: self::Decorator()->DeleteUsers($userIds);
3049:
3050: // Delete tenant itself.
3051: return $this->getTenantsManager()->deleteTenant($oTenant);
3052: }
3053: } else {
3054: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3055: }
3056:
3057: return false;
3058: }
3059:
3060: /**
3061: * @api {post} ?/Api/ GetUsers
3062: * @apiName GetUsers
3063: * @apiGroup Core
3064: * @apiDescription Returns user list.
3065: *
3066: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3067: * @apiHeaderExample {json} Header-Example:
3068: * {
3069: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3070: * }
3071: *
3072: * @apiParam {string=Core} Module Module name.
3073: * @apiParam {string=GetUsers} Method Method name.
3074: * @apiParam {string} Parameters JSON.stringified object <br>
3075: * {<br>
3076: * &emsp; **Offset** *int* Offset of user list.<br>
3077: * &emsp; **Limit** *int* Limit of result user list.<br>
3078: * &emsp; **OrderBy** *string* Name of field order by.<br>
3079: * &emsp; **OrderType** *int* Order type.<br>
3080: * &emsp; **Search** *string* Search string.<br>
3081: * }
3082: *
3083: * @apiParamExample {json} Request-Example:
3084: * {
3085: * Module: 'Core',
3086: * Method: 'GetUsers',
3087: * Parameters: '{ Offset: 0, Limit: 0, OrderBy: "", OrderType: 0, Search: 0 }'
3088: * }
3089: *
3090: * @apiSuccess {object[]} Result Array of response objects.
3091: * @apiSuccess {string} Result.Module Module name.
3092: * @apiSuccess {string} Result.Method Method name.
3093: * @apiSuccess {mixed} Result.Result List of users in case of success, otherwise **false**.
3094: * @apiSuccess {int} [Result.ErrorCode] Error code.
3095: *
3096: * @apiSuccessExample {json} Success response example:
3097: * {
3098: * Module: 'Core',
3099: * Method: 'GetUsers',
3100: * Result: {
3101: * Items: [
3102: * { Id: 123, PublicId: 'user123_PublicId' },
3103: * { Id: 124, PublicId: 'user124_PublicId' }
3104: * ],
3105: * Count: 2
3106: * }
3107: * }
3108: *
3109: * @apiSuccessExample {json} Error response example:
3110: * {
3111: * Module: 'Core',
3112: * Method: 'GetUsers',
3113: * Result: false,
3114: * ErrorCode: 102
3115: * }
3116: */
3117: /**
3118: * Returns user list.
3119: *
3120: * @param int $TenantId Tenant identifier.
3121: * @param int $Offset Offset of user list.
3122: * @param int $Limit Limit of result user list.
3123: * @param string $OrderBy Name of field order by.
3124: * @param int $OrderType Order type.
3125: * @param string $Search Search string.
3126: * @param array $Filters Filters.
3127: * @return array {
3128: * *array* **Items** User list.
3129: * *int* **Count** Users count.
3130: * }
3131: */
3132: public function GetUsers($TenantId = 0, $Offset = 0, $Limit = 0, $OrderBy = 'PublicId', $OrderType = \Aurora\System\Enums\SortOrder::ASC, $Search = '', $Filters = null, $GroupId = -1)
3133: {
3134: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3135:
3136: $oAuthenticatedUser = Api::getAuthenticatedUser();
3137: if ($oAuthenticatedUser->Role === UserRole::TenantAdmin) {
3138: if ($oAuthenticatedUser->IdTenant !== $TenantId) {
3139: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
3140: }
3141: } else {
3142: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3143: }
3144:
3145: $aResult = [
3146: 'Items' => [],
3147: 'Count' => 0,
3148: ];
3149:
3150: $Filters = ($Filters instanceof Builder) ? $Filters : Models\User::query();
3151: if ($TenantId !== 0) {
3152: $Filters = $Filters->where('IdTenant', $TenantId);
3153: }
3154:
3155: $aResult['Count'] = $this->getUsersManager()->getUsersCount($Search, $Filters, $GroupId);
3156: $aUsers = $this->getUsersManager()->getUserList($Offset, $Limit, $OrderBy, $OrderType, $Search, $Filters, $GroupId);
3157: foreach ($aUsers as $oUser) {
3158: $aGroups = [];
3159: if ($this->oModuleSettings->AllowGroups) {
3160: foreach ($oUser->Groups as $oGroup) {
3161: if (!$oGroup->IsAll) {
3162: $aGroups[] = [
3163: 'Id' => $oGroup->Id,
3164: 'TenantId' => $oGroup->TenantId,
3165: 'Name' => $oGroup->Name
3166: ];
3167: }
3168: }
3169: }
3170: $aResult['Items'][] = [
3171: 'Id' => $oUser->Id,
3172: 'UUID' => $oUser->UUID,
3173: 'Name' => $oUser->Name,
3174: 'PublicId' => $oUser->PublicId,
3175: 'Role' => $oUser->Role,
3176: 'IsDisabled' => $oUser->IsDisabled,
3177: 'Groups' => $aGroups,
3178: ];
3179: }
3180:
3181: return $aResult;
3182: }
3183:
3184: /**
3185: *
3186: */
3187: public function GetTotalUsersCount()
3188: {
3189: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3190:
3191: return $this->getUsersManager()->getTotalUsersCount();
3192: }
3193:
3194: /**
3195: * @api {post} ?/Api/ GetUser
3196: * @apiName GetUser
3197: * @apiGroup Core
3198: * @apiDescription Returns user data.
3199: *
3200: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3201: * @apiHeaderExample {json} Header-Example:
3202: * {
3203: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3204: * }
3205: *
3206: * @apiParam {string=Core} Module Module name.
3207: * @apiParam {string=GetUser} Method Method name.
3208: * @apiParam {string} Parameters JSON.stringified object <br>
3209: * {<br>
3210: * &emsp; **UserId** *string* User identifier.<br>
3211: * }
3212: *
3213: * @apiParamExample {json} Request-Example:
3214: * {
3215: * Module: 'Core',
3216: * Method: 'GetUser',
3217: * Parameters: '{ "Id": 17 }'
3218: * }
3219: *
3220: * @apiSuccess {object[]} Result Array of response objects.
3221: * @apiSuccess {string} Result.Module Module name.
3222: * @apiSuccess {string} Result.Method Method name.
3223: * @apiSuccess {bool} Result.Result Indicates if test of database connection was successful.
3224: * @apiSuccess {int} [Result.ErrorCode] Error code.
3225: *
3226: * @apiSuccessExample {json} Success response example:
3227: * {
3228: * Module: 'Core',
3229: * Method: 'GetUser',
3230: * Result: {
3231: * 'Name': '',
3232: * 'PublicId': 'mail@domain.com',
3233: * 'Role': 2,
3234: * 'WriteSeparateLog': false
3235: * }
3236: * }
3237: *
3238: * @apiSuccessExample {json} Error response example:
3239: * {
3240: * Module: 'Core',
3241: * Method: 'GetUser',
3242: * Result: false,
3243: * ErrorCode: 102
3244: * }
3245: */
3246: /**
3247: * Returns user object.
3248: *
3249: * @param int|string $Id User identifier or UUID.
3250: * @return Models\User
3251: */
3252: public function GetUser($Id = '')
3253: {
3254: $oUser = $this->getUsersManager()->getUser($Id);
3255: $oAuthenticatedUser = Api::getAuthenticatedUser();
3256:
3257: if ($oUser) { // User may be needed for anonymous on reset password or register screens. It can be obtained after using skipCheckUserRole method.
3258: if (($oAuthenticatedUser instanceof User) && $oAuthenticatedUser->Role === UserRole::NormalUser && $oAuthenticatedUser->Id === $oUser->Id) {
3259: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3260: } elseif (($oAuthenticatedUser instanceof User) && $oAuthenticatedUser->Role === UserRole::TenantAdmin && $oAuthenticatedUser->IdTenant === $oUser->IdTenant) {
3261: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3262: } else {
3263: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3264: }
3265:
3266: return $oUser;
3267: }
3268:
3269: return null;
3270: }
3271:
3272: /**
3273: *
3274: */
3275: public function TurnOffSeparateLogs()
3276: {
3277: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3278:
3279: $Filters = Models\User::query();
3280: $oAuthenticatedUser = Api::getAuthenticatedUser();
3281: if ($oAuthenticatedUser->Role === UserRole::TenantAdmin) {
3282: $Filters = $Filters->where('IdTenant', $oAuthenticatedUser->IdTenant);
3283: }
3284:
3285: $aResults = $this->getUsersManager()->getUserList(0, 0, 'PublicId', \Aurora\System\Enums\SortOrder::ASC, '', $Filters->where('WriteSeparateLog', true));
3286: foreach ($aResults as $aUser) {
3287: $oUser = self::Decorator()->GetUser($aUser['EntityId']);
3288: if ($oUser) {
3289: $oUser->WriteSeparateLog = false;
3290: $this->UpdateUserObject($oUser);
3291: }
3292: }
3293:
3294: return true;
3295: }
3296:
3297: /**
3298: *
3299: */
3300: public function ClearSeparateLogs()
3301: {
3302: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3303:
3304: Api::RemoveSeparateLogs();
3305:
3306: return true;
3307: }
3308:
3309: /**
3310: *
3311: */
3312: public function GetUsersWithSeparateLog()
3313: {
3314: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3315:
3316: $Filters = Models\User::query();
3317: $oAuthenticatedUser = Api::getAuthenticatedUser();
3318: if ($oAuthenticatedUser->Role === UserRole::TenantAdmin) {
3319: $Filters = $Filters->where('IdTenant', $oAuthenticatedUser->IdTenant);
3320: }
3321:
3322: $aResults = $this->getUsersManager()->getUserList(0, 0, 'PublicId', \Aurora\System\Enums\SortOrder::ASC, '', $Filters->where('WriteSeparateLog', true));
3323: $aUsers = [];
3324: foreach ($aResults as $aUser) {
3325: $aUsers[] = $aUser['PublicId'];
3326: }
3327: return $aUsers;
3328: }
3329:
3330: /**
3331: * @api {post} ?/Api/ CreateUser
3332: * @apiName CreateUser
3333: * @apiGroup Core
3334: * @apiDescription Creates user.
3335: *
3336: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3337: * @apiHeaderExample {json} Header-Example:
3338: * {
3339: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3340: * }
3341: *
3342: * @apiParam {string=Core} Module Module name.
3343: * @apiParam {string=CreateUser} Method Method name.
3344: * @apiParam {string} Parameters JSON.stringified object <br>
3345: * {<br>
3346: * &emsp; **TenantId** *int* Identifier of tenant that will contain new user.<br>
3347: * &emsp; **PublicId** *string* New user name.<br>
3348: * &emsp; **Role** *int* New user role.<br>
3349: * }
3350: *
3351: * @apiParamExample {json} Request-Example:
3352: * {
3353: * Module: 'Core',
3354: * Method: 'CreateUser',
3355: * Parameters: '{ TenantId: 123, PublicId: "PublicId_value", Role: 2 }'
3356: * }
3357: *
3358: * @apiSuccess {object[]} Result Array of response objects.
3359: * @apiSuccess {string} Result.Module Module name.
3360: * @apiSuccess {string} Result.Method Method name.
3361: * @apiSuccess {mixed} Result.Result User identifier in case of success, otherwise **false**.
3362: * @apiSuccess {int} [Result.ErrorCode] Error code.
3363: *
3364: * @apiSuccessExample {json} Success response example:
3365: * {
3366: * Module: 'Core',
3367: * Method: 'CreateUser',
3368: * Result: 123
3369: * }
3370: *
3371: * @apiSuccessExample {json} Error response example:
3372: * {
3373: * Module: 'Core',
3374: * Method: 'CreateUser',
3375: * Result: false,
3376: * ErrorCode: 102
3377: * }
3378: */
3379: /**
3380: * Creates user.
3381: *
3382: * @param int $TenantId Identifier of tenant that will contain new user.
3383: * @param string $PublicId New user name.
3384: * @param int $Role New user role.
3385: * @param bool $WriteSeparateLog Indicates if log file should be written separate for this user.
3386: * @return int|false
3387: * @throws ApiException
3388: */
3389: public function CreateUser($TenantId = 0, $PublicId = '', $Role = UserRole::NormalUser, $WriteSeparateLog = false, $IsDisabled = false, $Note = null)
3390: {
3391: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3392:
3393: if (!UserRole::validateValue($Role)) {
3394: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3395: }
3396:
3397: $oTenant = null;
3398:
3399: // if $TenantId === 0 we need to get default tenant
3400: if ($TenantId === 0) {
3401: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3402: $oTenant = $this->getTenantsManager()->getDefaultGlobalTenant();
3403: $TenantId = $oTenant ? $oTenant->Id : null;
3404: }
3405:
3406: $oAuthenticatedUser = Api::getAuthenticatedUser();
3407: if (!($oAuthenticatedUser instanceof User && $oAuthenticatedUser->Role === UserRole::TenantAdmin && $oAuthenticatedUser->IdTenant === $TenantId)) {
3408: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3409: }
3410:
3411: if (!$oTenant) {
3412: $oTenant = $this->getTenantsManager()->getTenantById($TenantId);
3413: if (!$oTenant) {
3414: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3415: }
3416: }
3417:
3418: $PublicId = \trim($PublicId);
3419: if (substr_count($PublicId, '@') > 1) {
3420: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3421: }
3422:
3423: if (!empty($TenantId) && !empty($PublicId)) {
3424: $oUser = $this->getUsersManager()->getUserByPublicId($PublicId);
3425: if ($oUser instanceof Models\User) {
3426: throw new ApiException(Notifications::UserAlreadyExists, null, 'UserAlreadyExists');
3427: } else {
3428: if (class_exists('\Aurora\Modules\Licensing\Module')) {
3429: $oLicense = \Aurora\Modules\Licensing\Module::Decorator();
3430: if (!$oLicense->ValidateUsersCount($this->GetTotalUsersCount()) || !$oLicense->ValidatePeriod()) {
3431: Api::Log("Error: License limit");
3432: throw new ApiException(Notifications::LicenseLimit, null, 'LicenseLimit');
3433: }
3434: }
3435: }
3436:
3437: $oUser = new Models\User();
3438:
3439: $oUser->PublicId = $PublicId;
3440: $oUser->IdTenant = $TenantId;
3441: $oUser->Role = $Role;
3442: $oUser->WriteSeparateLog = $WriteSeparateLog;
3443:
3444: $oUser->Language = Api::GetLanguage(true);
3445: $oUser->TimeFormat = $this->oModuleSettings->TimeFormat;
3446: $oUser->DateFormat = $this->oModuleSettings->DateFormat;
3447: $oUser->DefaultTimeZone = '';
3448:
3449:
3450: $oUser->IsDisabled = $IsDisabled;
3451: $oUser->Note = $Note;
3452:
3453: if ($this->getUsersManager()->createUser($oUser)) {
3454: return $oUser->Id;
3455: }
3456: } else {
3457: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3458: }
3459:
3460: return false;
3461: }
3462:
3463: /**
3464: * @api {post} ?/Api/ UpdateUser
3465: * @apiName UpdateUser
3466: * @apiGroup Core
3467: * @apiDescription Updates user.
3468: *
3469: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3470: * @apiHeaderExample {json} Header-Example:
3471: * {
3472: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3473: * }
3474: *
3475: * @apiParam {string=Core} Module Module name.
3476: * @apiParam {string=UpdateUser} Method Method name.
3477: * @apiParam {string} Parameters JSON.stringified object <br>
3478: * {<br>
3479: * &emsp; **UserId** *int* Identifier of user to update.<br>
3480: * &emsp; **UserName** *string* New user name.<br>
3481: * &emsp; **TenantId** *int* Identifier of tenant that will contain the user.<br>
3482: * &emsp; **Role** *int* New user role.<br>
3483: * }
3484: *
3485: * @apiParamExample {json} Request-Example:
3486: * {
3487: * Module: 'Core',
3488: * Method: 'UpdateUser',
3489: * Parameters: '{ UserId: 123, UserName: "name_value", TenantId: 123, Role: 2 }'
3490: * }
3491: *
3492: * @apiSuccess {object[]} Result Array of response objects.
3493: * @apiSuccess {string} Result.Module Module name.
3494: * @apiSuccess {string} Result.Method Method name.
3495: * @apiSuccess {bool} Result.Result Indicates if user was updated successfully.
3496: * @apiSuccess {int} [Result.ErrorCode] Error code.
3497: *
3498: * @apiSuccessExample {json} Success response example:
3499: * {
3500: * Module: 'Core',
3501: * Method: 'UpdateUser',
3502: * Result: true
3503: * }
3504: *
3505: * @apiSuccessExample {json} Error response example:
3506: * {
3507: * Module: 'Core',
3508: * Method: 'UpdateUser',
3509: * Result: false,
3510: * ErrorCode: 102
3511: * }
3512: */
3513: /**
3514: * Updates user.
3515: *
3516: * @param int $UserId Identifier of user to update.
3517: * @param string $PublicId New user name.
3518: * @param int $TenantId Identifier of tenant that will contain the user.
3519: * @param int $Role New user role.
3520: * @param bool|null $IsDisabled Disbles the user
3521: * @param bool $WriteSeparateLog New value of indicator if user's logs should be in a separate file.
3522: * @param array $GroupIds List of system group ids user belogs to.
3523: * @param string $Note User text note.
3524: * @return bool
3525: * @throws ApiException
3526: */
3527: public function UpdateUser($UserId, $PublicId = '', $TenantId = 0, $Role = -1, $IsDisabled = null, $WriteSeparateLog = null, $GroupIds = null, $Note = null)
3528: {
3529: $PublicId = \trim($PublicId);
3530:
3531: $oUser = null;
3532: if ($UserId > 0) {
3533: $oUser = self::Decorator()->GetUserWithoutRoleCheck($UserId);
3534: }
3535: if ($oUser) {
3536: if ((!empty($TenantId) && $oUser->IdTenant != $TenantId) || (!empty($PublicId) && $oUser->PublicId != $PublicId)) {
3537: // Only super administrator can edit users TenantId and PublicId
3538: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3539: } elseif ($Role !== -1 || $IsDisabled !== null || $WriteSeparateLog !== null || $GroupIds !== null || $Note !== null) {
3540: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3541: } elseif ($UserId === Api::getAuthenticatedUserId()) {
3542: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3543: } else {
3544: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3545: }
3546:
3547: Api::checkUserAccess($oUser);
3548:
3549: if (!empty($PublicId)) {
3550: $oUser->PublicId = $PublicId;
3551: }
3552: if (!empty($TenantId)) {
3553: $oUser->IdTenant = $TenantId;
3554: }
3555: if (UserRole::validateValue($Role)) {
3556: $oUser->Role = $Role;
3557: }
3558: if ($IsDisabled !== null) {
3559: $oUser->IsDisabled = (bool) $IsDisabled;
3560: }
3561: if ($WriteSeparateLog !== null) {
3562: $oUser->WriteSeparateLog = $WriteSeparateLog;
3563: }
3564: if ($Note !== null) {
3565: $oUser->Note = (string) $Note;
3566: }
3567:
3568: $mResult = $this->getUsersManager()->updateUser($oUser);
3569: if ($mResult && $this->oModuleSettings->AllowGroups && $GroupIds !== null) {
3570: self::Decorator()->UpdateUserGroups($UserId, $GroupIds);
3571: }
3572:
3573: return $mResult;
3574: } else {
3575: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3576: }
3577: }
3578:
3579: /**
3580: * @api {post} ?/Api/ DeleteUsers
3581: * @apiName DeleteUsers
3582: * @apiGroup Core
3583: * @apiDescription Deletes users specified by a list of identifiers.
3584: *
3585: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3586: * @apiHeaderExample {json} Header-Example:
3587: * {
3588: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3589: * }
3590: *
3591: * @apiParam {string=Core} Module Module name.
3592: * @apiParam {string=DeleteUsers} Method Method name.
3593: * @apiParam {string} Parameters JSON.stringified object <br>
3594: * {<br>
3595: * &emsp; **IdList** *int* List of users identifiers.<br>
3596: * }
3597: *
3598: * @apiParamExample {json} Request-Example:
3599: * {
3600: * Module: 'Core',
3601: * Method: 'DeleteUsers',
3602: * Parameters: '{ IdList: [125, 457] }'
3603: * }
3604: *
3605: * @apiSuccess {object[]} Result Array of response objects.
3606: * @apiSuccess {string} Result.Module Module name.
3607: * @apiSuccess {string} Result.Method Method name.
3608: * @apiSuccess {bool} Result.Result Indicates if users were deleted successfully.
3609: * @apiSuccess {int} [Result.ErrorCode] Error code.
3610: *
3611: * @apiSuccessExample {json} Success response example:
3612: * {
3613: * Module: 'Core',
3614: * Method: 'DeleteUsers',
3615: * Result: true
3616: * }
3617: *
3618: * @apiSuccessExample {json} Error response example:
3619: * {
3620: * Module: 'Core',
3621: * Method: 'DeleteUsers',
3622: * Result: false,
3623: * ErrorCode: 102
3624: * }
3625: */
3626: /**
3627: * Deletes users specified by a list of identifiers.
3628: * @param array $IdList List of users identifiers.
3629: * @return bool
3630: */
3631: public function DeleteUsers($IdList)
3632: {
3633: $bResult = true;
3634:
3635: foreach ($IdList as $sId) {
3636: $bResult = $bResult && self::Decorator()->DeleteUser($sId);
3637: }
3638:
3639: return $bResult;
3640: }
3641:
3642: /**
3643: * @api {post} ?/Api/ DeleteUser
3644: * @apiName DeleteUser
3645: * @apiGroup Core
3646: * @apiDescription Deletes user.
3647: *
3648: * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method.
3649: * @apiHeaderExample {json} Header-Example:
3650: * {
3651: * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8"
3652: * }
3653: *
3654: * @apiParam {string=Core} Module Module name.
3655: * @apiParam {string=DeleteUser} Method Method name.
3656: * @apiParam {string} Parameters JSON.stringified object <br>
3657: * {<br>
3658: * &emsp; **UserId** *int* User identifier.<br>
3659: * }
3660: *
3661: * @apiParamExample {json} Request-Example:
3662: * {
3663: * Module: 'Core',
3664: * Method: 'DeleteUser',
3665: * Parameters: '{ UserId: 123 }'
3666: * }
3667: *
3668: * @apiSuccess {object[]} Result Array of response objects.
3669: * @apiSuccess {string} Result.Module Module name.
3670: * @apiSuccess {string} Result.Method Method name.
3671: * @apiSuccess {bool} Result.Result Indicates if user was deleted successfully.
3672: * @apiSuccess {int} [Result.ErrorCode] Error code.
3673: *
3674: * @apiSuccessExample {json} Success response example:
3675: * {
3676: * Module: 'Core',
3677: * Method: 'DeleteUser',
3678: * Result: true
3679: * }
3680: *
3681: * @apiSuccessExample {json} Error response example:
3682: * {
3683: * Module: 'Core',
3684: * Method: 'DeleteUser',
3685: * Result: false,
3686: * ErrorCode: 102
3687: * }
3688: */
3689: /**
3690: * Deletes user.
3691: *
3692: * @param int $UserId User identifier.
3693: * @return bool
3694: * @throws ApiException
3695: */
3696: public function DeleteUser($UserId = 0)
3697: {
3698: $oAuthenticatedUser = Api::getAuthenticatedUser();
3699:
3700: $oUser = self::Decorator()->GetUserWithoutRoleCheck($UserId);
3701:
3702: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3703:
3704: if ($oUser instanceof Models\User && $oAuthenticatedUser->Role === UserRole::TenantAdmin &&
3705: $oUser->IdTenant !== $oAuthenticatedUser->IdTenant) {
3706: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
3707: } else {
3708: if ($oUser->IdTenant === $oAuthenticatedUser->IdTenant) {
3709: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3710: } else {
3711: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3712: }
3713: }
3714:
3715: $bResult = false;
3716:
3717: if (!empty($UserId) && is_int($UserId)) {
3718: $bResult = $this->getUsersManager()->deleteUserById($UserId);
3719: if ($bResult) {
3720: UserBlock::where('UserId', $UserId)->delete();
3721: }
3722: } else {
3723: throw new ApiException(Notifications::InvalidInputParameter, null, 'InvalidInputParameter');
3724: }
3725:
3726: return $bResult;
3727: }
3728:
3729: /**
3730: *
3731: */
3732: public function GetLogFilesData()
3733: {
3734: $aData = [];
3735:
3736: $sFileName = Api::GetLogFileName();
3737: $sFilePath = Api::GetLogFileDir() . $sFileName;
3738: $aData['LogFileName'] = $sFileName;
3739: $aData['LogSizeBytes'] = file_exists($sFilePath) ? filesize($sFilePath) : 0;
3740:
3741: $sEventFileName = Api::GetLogFileName(Logger::$sEventLogPrefix);
3742: $sEventFilePath = Api::GetLogFileDir() . $sEventFileName;
3743: $aData['EventLogFileName'] = $sEventFileName;
3744: $aData['EventLogSizeBytes'] = file_exists($sEventFilePath) ? filesize($sEventFilePath) : 0;
3745:
3746: $sErrorFileName = Api::GetLogFileName(Logger::$sErrorLogPrefix);
3747: $sErrorFilePath = Api::GetLogFileDir() . $sErrorFileName;
3748: $aData['ErrorLogFileName'] = $sErrorFileName;
3749: $aData['ErrorLogSizeBytes'] = file_exists($sErrorFilePath) ? filesize($sErrorFilePath) : 0;
3750:
3751: return $aData;
3752: }
3753:
3754: public function GetLogFile($FilePrefix = '', $PublicId = '')
3755: {
3756: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3757:
3758: if ($PublicId !== '') {
3759: $FilePrefix = $PublicId . '-';
3760: }
3761: $sFileName = Api::GetLogFileDir() . Api::GetLogFileName($FilePrefix);
3762:
3763: if (file_exists($sFileName)) {
3764: $mResult = fopen($sFileName, "r");
3765:
3766: if (false !== $mResult && is_resource($mResult)) {
3767: $sContentType = \MailSo\Base\Utils::MimeContentType($sFileName);
3768: \Aurora\System\Managers\Response::OutputHeaders(true, $sContentType, $sFileName);
3769:
3770: if ($sContentType === 'text/plain') {
3771: $sLogData = stream_get_contents($mResult);
3772: echo(\MailSo\Base\HtmlUtils::ClearTags($sLogData));
3773: } else {
3774: \MailSo\Base\Utils::FpassthruWithTimeLimitReset($mResult, 8192, function ($sData) {
3775: return \MailSo\Base\HtmlUtils::ClearTags($sData);
3776: });
3777: }
3778:
3779: @fclose($mResult);
3780: }
3781: }
3782: }
3783:
3784: /**
3785: *
3786: */
3787: public function GetLog($FilePrefix = '', $PartSize = 10240)
3788: {
3789: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3790:
3791: $sFileName = Api::GetLogFileDir() . Api::GetLogFileName($FilePrefix);
3792:
3793: $logData = '';
3794:
3795: if (file_exists($sFileName)) {
3796: $iOffset = filesize($sFileName) - $PartSize;
3797: $iOffset = $iOffset < 0 ? 0 : $iOffset;
3798: $logData = \MailSo\Base\HtmlUtils::ClearTags(file_get_contents($sFileName, false, null, $iOffset, $PartSize));
3799: }
3800:
3801: return $logData;
3802: }
3803:
3804: /**
3805: *
3806: * @param string $FilePrefix
3807: * @return bool
3808: */
3809: public function ClearLog($FilePrefix = '')
3810: {
3811: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
3812:
3813: $sFileName = Api::GetLogFileDir() . Api::GetLogFileName($FilePrefix);
3814:
3815: return Api::ClearLog($sFileName);
3816: }
3817:
3818: /**
3819: * Updates user Timezone.
3820: *
3821: * @param string $Timezone New Timezone.
3822: *
3823: */
3824: public function UpdateUserTimezone($Timezone)
3825: {
3826: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3827:
3828: $oUser = Api::getAuthenticatedUser();
3829:
3830: if ($oUser && $Timezone) {
3831: if ($oUser && $oUser->DefaultTimeZone !== $Timezone) {
3832: $oUser->DefaultTimeZone = $Timezone;
3833: $this->UpdateUserObject($oUser);
3834: }
3835: } else {
3836: return false;
3837: }
3838: return true;
3839: }
3840:
3841: /**
3842: *
3843: */
3844: public function GetCompatibilities()
3845: {
3846: return [];
3847: }
3848:
3849: /**
3850: *
3851: */
3852: public function IsModuleDisabledForObject($oObject, $sModuleName)
3853: {
3854: return ($oObject instanceof \Aurora\System\Classes\Model) ? $oObject->isModuleDisabled($sModuleName) : false;
3855: }
3856:
3857: /**
3858: *
3859: */
3860: public function GetUserSessions()
3861: {
3862: $aResult = [];
3863: if (\Aurora\Api::GetSettings()->StoreAuthTokenInDB) {
3864: $oUser = Api::getAuthenticatedUser();
3865: if ($oUser) {
3866: $aUserSessions = Api::UserSession()->GetUserSessionsFromDB($oUser->Id);
3867: foreach ($aUserSessions as $oUserSession) {
3868: $aTokenInfo = Api::DecodeKeyValues($oUserSession->Token);
3869:
3870: if ($aTokenInfo !== false && isset($aTokenInfo['id'])) {
3871: $aResult[] = [
3872: 'LastUsageDateTime' => $oUserSession->LastUsageDateTime,
3873: 'ExpireDateTime' => (int) isset($aTokenInfo['@expire']) ? $aTokenInfo['@expire'] : 0,
3874: ];
3875: }
3876: }
3877: }
3878: }
3879: return $aResult;
3880: }
3881:
3882: /**
3883: *
3884: */
3885: public function CreateGroup($TenantId, $Name)
3886: {
3887: if (!$this->oModuleSettings->AllowGroups) {
3888: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
3889: }
3890:
3891: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
3892:
3893: $oUser = Api::getAuthenticatedUser();
3894: if ($oUser->Role === UserRole::TenantAdmin && $oUser->IdTenant !== $TenantId) {
3895: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
3896: }
3897:
3898: $oGroup = Group::firstWhere([
3899: 'TenantId' => $TenantId,
3900: 'Name' => $Name
3901: ]);
3902:
3903: if ($oGroup) {
3904: throw new \Aurora\Modules\Core\Exceptions\Exception(Enums\ErrorCodes::GroupAlreadyExists);
3905: } else {
3906: $oGroup = new Models\Group();
3907: $oGroup->Name = $Name;
3908: $oGroup->TenantId = $TenantId;
3909: if ($oGroup->save()) {
3910: return $oGroup->Id;
3911: } else {
3912: return false;
3913: }
3914: }
3915: }
3916:
3917: /**
3918: * Returns a user group
3919: * @param int $GroupId
3920: *
3921: * @return Group|false
3922: */
3923: public function GetGroup($GroupId)
3924: {
3925: if (!$this->oModuleSettings->AllowGroups) {
3926: return false;
3927: }
3928:
3929: $mResult = false;
3930:
3931: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3932:
3933: $oUser = Api::getAuthenticatedUser();
3934: $oGroup = Group::firstWhere([ 'Id' => $GroupId ]);
3935: if ($oUser && $oGroup && ($oUser->Role === UserRole::TenantAdmin || $oUser->Role === UserRole::NormalUser) && $oUser->IdTenant !== $oGroup->TenantId) {
3936: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
3937: }
3938:
3939: $mResult = $oGroup;
3940:
3941: return $mResult;
3942: }
3943:
3944: /**
3945: *
3946: */
3947: public function GetAllGroup($TenantId)
3948: {
3949: if (!$this->oModuleSettings->AllowGroups) {
3950: return false;
3951: }
3952:
3953: $mResult = false;
3954:
3955: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3956:
3957: $oUser = Api::getAuthenticatedUser();
3958: if ($oUser && ($oUser->Role === UserRole::TenantAdmin || $oUser->Role === UserRole::NormalUser) && $oUser->IdTenant !== $TenantId) {
3959: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
3960: }
3961:
3962: $oGroup = Group::firstWhere([
3963: 'TenantId' => $TenantId,
3964: 'IsAll' => true
3965: ]);
3966:
3967: if (!$oGroup) {
3968: $oGroup = new Models\Group();
3969: $oGroup->Name = 'All';
3970: $oGroup->TenantId = $TenantId;
3971: $oGroup->IsAll = true;
3972:
3973: if ($oGroup->save()) {
3974: $mResult = $oGroup;
3975: } else {
3976: $mResult = false;
3977: }
3978: } else {
3979: $mResult = $oGroup;
3980: }
3981:
3982: return $mResult;
3983: }
3984:
3985: /**
3986: *
3987: */
3988: public function GetGroups($TenantId, $Search = '')
3989: {
3990: if (!$this->oModuleSettings->AllowGroups) {
3991: return [
3992: 'Items' => [],
3993: 'Count' => 0
3994: ];
3995: }
3996:
3997: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
3998:
3999: $oUser = Api::getAuthenticatedUser();
4000: if ($oUser && ($oUser->Role === UserRole::TenantAdmin || $oUser->Role === UserRole::NormalUser) && $oUser->IdTenant !== $TenantId) {
4001: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4002: }
4003:
4004: $query = Group::where('TenantId', $TenantId);
4005: if (!empty($Search)) {
4006: $query = $query->where(function ($q) use ($Search) {
4007: $q->where('Name', 'LIKE', '%' . $Search . '%');
4008: $q->orWhere('IsAll', true);
4009: });
4010: }
4011:
4012: $aGroups = $query->get()->map(function ($oGroup) use ($oUser) {
4013:
4014: $aArgs = [
4015: 'User' => $oUser,
4016: 'Group' => $oGroup
4017: ];
4018: $mResult = false;
4019:
4020: try {
4021: $this->broadcastEvent('GetGroupContactsEmails', $aArgs, $mResult);
4022: } catch (\Exception $oException) {
4023: }
4024:
4025: $aEmails = [];
4026: if (is_array($mResult)) {
4027: $aEmails = $mResult;
4028: }
4029:
4030: return [
4031: 'Id' => $oGroup->Id,
4032: 'Name' => $oGroup->getName(),
4033: 'Emails' => implode(', ', $aEmails),
4034: 'IsAll' => !!$oGroup->IsAll
4035: ];
4036: })->toArray();
4037:
4038: if (!empty($Search)) {
4039: $aGroups = array_filter($aGroups, function ($aGroup) use ($Search) {
4040: return (stripos($aGroup['Name'], $Search) !== false);
4041: });
4042: }
4043:
4044: return [
4045: 'Items' => $aGroups,
4046: 'Count' => count($aGroups)
4047: ];
4048: }
4049:
4050: /**
4051: *
4052: */
4053: public function UpdateGroup($GroupId, $Name)
4054: {
4055: if (!$this->oModuleSettings->AllowGroups) {
4056: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4057: }
4058:
4059: $mResult = false;
4060:
4061: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
4062:
4063: $oGroup = Group::find($GroupId);
4064: if ($oGroup && !$oGroup->IsAll) {
4065: $oUser = Api::getAuthenticatedUser();
4066: if ($oUser && $oUser->Role === UserRole::TenantAdmin && $oGroup->TenantId !== $oUser->IdTenant) {
4067: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4068: }
4069:
4070: if ($oGroup->Name !== $Name && Group::where(['TenantId' => $oGroup->TenantId, 'Name' => $Name])->count() > 0) {
4071: throw new ApiException(ErrorCodes::GroupAlreadyExists, null, 'GroupAlreadyExists');
4072: } else {
4073: $oGroup->Name = $Name;
4074: $mResult = !!$oGroup->save();
4075: }
4076: }
4077:
4078: return $mResult;
4079: }
4080:
4081: /**
4082: * Deletes groups specified by a list of identifiers.
4083: * @param array $IdList List of groups identifiers.
4084: * @return bool
4085: */
4086: public function DeleteGroups($IdList)
4087: {
4088: if (!$this->oModuleSettings->AllowGroups) {
4089: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4090: }
4091:
4092: Api::checkUserRoleIsAtLeast(UserRole::SuperAdmin);
4093:
4094: $bResult = true;
4095:
4096: foreach ($IdList as $iId) {
4097: $bResult = $bResult && self::Decorator()->DeleteGroup($iId);
4098: }
4099:
4100: return $bResult;
4101: }
4102:
4103: /**
4104: *
4105: */
4106: public function DeleteGroup($GroupId)
4107: {
4108: if (!$this->oModuleSettings->AllowGroups) {
4109: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4110: }
4111:
4112: $mResult = false;
4113:
4114: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
4115:
4116: $oGroup = Group::find($GroupId);
4117: if ($oGroup && !$oGroup->IsAll) {
4118: $oUser = Api::getAuthenticatedUser();
4119: if ($oUser && $oUser->Role === UserRole::TenantAdmin && $oGroup->TenantId !== $oUser->IdTenant) {
4120: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4121: }
4122:
4123: $mResult = $oGroup->delete();
4124: }
4125:
4126: return $mResult;
4127: }
4128:
4129: /**
4130: *
4131: */
4132: public function GetGroupUsers($TenantId, $GroupId)
4133: {
4134: if (!$this->oModuleSettings->AllowGroups) {
4135: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4136: }
4137:
4138: $mResult = [];
4139:
4140: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
4141:
4142: $oGroup = Group::where('TenantId', $TenantId)->where('Id', $GroupId)->first();
4143: if ($oGroup) {
4144: $oUser = Api::getAuthenticatedUser();
4145: if ($oUser && ($oUser->Role === UserRole::NormalUser || $oUser->Role === UserRole::TenantAdmin) && $oGroup->TenantId !== $oUser->IdTenant) {
4146: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4147: }
4148:
4149: if ($oGroup->IsAll) {
4150: $teamContacts = ContactsModule::Decorator()->GetContacts($oUser->Id, StorageType::Team, 0, 0);
4151: if (isset($teamContacts['List'])) {
4152: $mResult = array_map(function ($item) {
4153: return [
4154: 'UserId' => $item['UserId'],
4155: 'Name' => $item['FullName'],
4156: 'PublicId' => $item['ViewEmail']
4157: ];
4158: }, $teamContacts['List']);
4159: }
4160: } else {
4161: $mResult = $oGroup->Users()->get()->map(function ($oUser) {
4162: return [
4163: 'UserId' => $oUser->Id,
4164: 'Name' => $oUser->Name,
4165: 'PublicId' => $oUser->PublicId
4166: ];
4167: })->toArray();
4168: }
4169: }
4170:
4171: return $mResult;
4172: }
4173:
4174: /**
4175: *
4176: */
4177: public function AddUsersToGroup($GroupId, $UserIds)
4178: {
4179: if (!$this->oModuleSettings->AllowGroups) {
4180: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4181: }
4182:
4183: $mResult = false;
4184:
4185: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
4186:
4187: $oGroup = Group::find($GroupId);
4188: if ($oGroup && !$oGroup->IsAll) {
4189: $oUser = Api::getAuthenticatedUser();
4190: if ($oUser && $oUser->Role === UserRole::TenantAdmin && $oGroup->TenantId !== $oUser->IdTenant) {
4191: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4192: }
4193:
4194: $oGroup->Users()->syncWithoutDetaching($UserIds);
4195: $mResult = true;
4196: }
4197:
4198: return $mResult;
4199: }
4200:
4201: /**
4202: *
4203: */
4204: public function RemoveUsersFromGroup($GroupId, $UserIds)
4205: {
4206: if (!$this->oModuleSettings->AllowGroups) {
4207: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4208: }
4209:
4210: $mResult = false;
4211:
4212: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
4213:
4214: $oGroup = Group::find($GroupId);
4215: if ($oGroup) {
4216: $oUser = Api::getAuthenticatedUser();
4217: if ($oUser && $oUser->Role === UserRole::TenantAdmin && $oGroup->TenantId !== $oUser->IdTenant) {
4218: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4219: }
4220:
4221: $oGroup->Users()->detach($UserIds);
4222: $mResult = true;
4223: }
4224:
4225: return $mResult;
4226: }
4227:
4228: /**
4229: *
4230: */
4231: public function UpdateUserGroups($UserId, $GroupIds)
4232: {
4233: if (!$this->oModuleSettings->AllowGroups) {
4234: throw new ApiException(Notifications::MethodAccessDenied, null, 'MethodAccessDenied');
4235: }
4236:
4237: $mResult = false;
4238:
4239: Api::checkUserRoleIsAtLeast(UserRole::TenantAdmin);
4240: $oAuthUser = Api::getAuthenticatedUser();
4241: $oUser = User::find($UserId);
4242:
4243: if ($oAuthUser && $oAuthUser->Role === UserRole::TenantAdmin && $oAuthUser->IdTenant !== $oUser->IdTenant) {
4244: throw new ApiException(Notifications::AccessDenied, null, 'AccessDenied');
4245: }
4246: if ($oUser) {
4247: $aGroupIds = Group::where('IsAll', false)->whereIn('Id', $GroupIds)->get(['Id'])->map(function ($oGroup) {
4248: return $oGroup->Id;
4249: });
4250: $oUser->Groups()->sync($aGroupIds);
4251: $mResult = true;
4252: }
4253:
4254: return $mResult;
4255: }
4256: /***** public functions might be called with web API *****/
4257: }
4258: