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\StandardAuth; |
9: | |
10: | use Aurora\Modules\StandardAuth\Models\Account; |
11: | |
12: | /** |
13: | * This module provides API for authentication by login/password that relies on database. |
14: | * |
15: | * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0 |
16: | * @license https://afterlogic.com/products/common-licensing Afterlogic Software License |
17: | * @copyright Copyright (c) 2023, Afterlogic Corp. |
18: | * |
19: | * @package Modules |
20: | */ |
21: | class Module extends \Aurora\System\Module\AbstractModule |
22: | { |
23: | public $oApiAccountsManager = null; |
24: | |
25: | public function getAccountsManager() |
26: | { |
27: | if ($this->oApiAccountsManager === null) { |
28: | $this->oApiAccountsManager = new Managers\Accounts\Manager($this); |
29: | } |
30: | |
31: | return $this->oApiAccountsManager; |
32: | } |
33: | |
34: | /***** private functions *****/ |
35: | /** |
36: | * Initializes module. |
37: | * |
38: | * @ignore |
39: | */ |
40: | public function init() |
41: | { |
42: | $this->subscribeEvent('Login', array($this, 'onLogin'), 90); |
43: | $this->subscribeEvent('Register', array($this, 'onRegister')); |
44: | $this->subscribeEvent('CheckAccountExists', array($this, 'onCheckAccountExists')); |
45: | $this->subscribeEvent('Core::DeleteUser::after', array($this, 'onAfterDeleteUser')); |
46: | $this->subscribeEvent('Core::GetAccounts', array($this, 'onGetAccounts')); |
47: | $this->subscribeEvent('Core::GetAccountUsedToAuthorize', array($this, 'onGetAccountUsedToAuthorize'), 200); |
48: | |
49: | $this->denyMethodCallByWebApi('CreateAccount'); |
50: | $this->denyMethodCallByWebApi('SaveAccount'); |
51: | } |
52: | |
53: | /** |
54: | * Tries to log in with specified credentials via StandardAuth module. Writes to $mResult array with auth token data if logging in was successfull. |
55: | * @ignore |
56: | * @param array $aArgs Credentials for logging in. |
57: | * @param mixed $mResult Is passed by reference. |
58: | */ |
59: | public function onLogin($aArgs, &$mResult) |
60: | { |
61: | $oAccount = $this->getAccountsManager()->getAccountByCredentials( |
62: | $aArgs['Login'], |
63: | $aArgs['Password'] |
64: | ); |
65: | |
66: | if ($oAccount) { |
67: | $mResult = \Aurora\System\UserSession::getTokenData($oAccount, $aArgs['SignMe']); |
68: | return true; |
69: | } |
70: | } |
71: | |
72: | /** |
73: | * Creates account with specified credentials. |
74: | * @ignore |
75: | * @param array $aArgs New account credentials. |
76: | * @param type $mResult Is passed by reference. |
77: | */ |
78: | public function onRegister($aArgs, &$mResult) |
79: | { |
80: | $mResult = $this->CreateAccount( |
81: | 0, |
82: | $aArgs['UserId'], |
83: | $aArgs['Login'], |
84: | $aArgs['Password'] |
85: | ); |
86: | } |
87: | |
88: | /** |
89: | * Checks if module has account with specified login. |
90: | * @ignore |
91: | * @param array $aArgs |
92: | * @throws \Aurora\System\Exceptions\ApiException |
93: | */ |
94: | public function onCheckAccountExists($aArgs) |
95: | { |
96: | $oAccount = new Models\Account(); |
97: | $oAccount->Login = $aArgs['Login']; |
98: | if ($this->getAccountsManager()->isExists($oAccount)) { |
99: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::AccountExists); |
100: | } |
101: | } |
102: | |
103: | /** |
104: | * Deletes all basic accounts which are owned by the specified user. |
105: | * @ignore |
106: | * @param array $aArgs |
107: | * @param mixed $mResult. |
108: | */ |
109: | public function onAfterDeleteUser($aArgs, $mResult) |
110: | { |
111: | if ($mResult) { |
112: | Account::where('IdUser', $aArgs['UserId'])->delete(); |
113: | } |
114: | } |
115: | |
116: | /** |
117: | * |
118: | * @param array $aArgs |
119: | * @param array $aResult |
120: | */ |
121: | public function onGetAccounts($aArgs, &$aResult) |
122: | { |
123: | $aUserInfo = \Aurora\System\Api::getAuthenticatedUserInfo($aArgs['AuthToken']); |
124: | if (isset($aUserInfo['userId'])) { |
125: | $mResult = $this->getAccountsManager()->getUserAccounts($aUserInfo['userId']); |
126: | foreach ($mResult as $oItem) { |
127: | $aResult[] = [ |
128: | 'Type' => $oItem->getName(), |
129: | 'Module' => $oItem->getModule(), |
130: | 'Id' => $oItem->Id, |
131: | 'UUID' => '', //$oItem->UUID, TODO: |
132: | 'Login' => $oItem->Login |
133: | ]; |
134: | } |
135: | } |
136: | } |
137: | |
138: | public function onGetAccountUsedToAuthorize($aArgs, &$mResult) |
139: | { |
140: | $oAccount = $this->getAccountsManager()->getAccountUsedToAuthorize($aArgs['Login']); |
141: | if ($oAccount) { |
142: | $mResult = $oAccount; |
143: | return true; |
144: | } |
145: | } |
146: | |
147: | /***** private functions *****/ |
148: | |
149: | /***** public functions *****/ |
150: | /** |
151: | * Creates account with credentials. |
152: | * Denied for web API call |
153: | * |
154: | * @param int $iTenantId Tenant identifier. |
155: | * @param int $iUserId User identifier. |
156: | * @param string $sLogin New account login. |
157: | * @param string $sPassword New account password. |
158: | * @return bool|array |
159: | * @throws \Aurora\System\Exceptions\ApiException |
160: | */ |
161: | public function CreateAccount($iTenantId = 0, $iUserId = 0, $sLogin = '', $sPassword = '') |
162: | { |
163: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
164: | |
165: | $aArgs = array( |
166: | 'Login' => $sLogin |
167: | ); |
168: | $this->broadcastEvent( |
169: | 'CheckAccountExists', |
170: | $aArgs |
171: | ); |
172: | |
173: | if ($iUserId > 0) { |
174: | $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId); |
175: | } else { |
176: | $sPublicId = (string)$sLogin; |
177: | $bPrevState = \Aurora\System\Api::skipCheckUserRole(true); |
178: | $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserByPublicId($sPublicId); |
179: | |
180: | if (empty($oUser)) { |
181: | $iUserId = \Aurora\Modules\Core\Module::Decorator()->CreateUser($iTenantId, $sPublicId); |
182: | $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId); |
183: | } |
184: | \Aurora\System\Api::skipCheckUserRole($bPrevState); |
185: | } |
186: | |
187: | // $mResult = null; |
188: | // $aArgs = array( |
189: | // 'TenantId' => $iTenantId, |
190: | // 'UserId' => $iUserId, |
191: | // 'login' => $sLogin, |
192: | // 'password' => $sPassword |
193: | // ); |
194: | // $this->broadcastEvent( |
195: | // 'CreateAccount', |
196: | // $aArgs, |
197: | // $mResult |
198: | // ); |
199: | |
200: | if ($oUser instanceof \Aurora\Modules\Core\Models\User) { |
201: | $oAccount = new Models\Account(); |
202: | |
203: | $oAccount->IdUser = $oUser->Id; |
204: | $oAccount->Login = $sLogin; |
205: | $oAccount->setPassword($sPassword); |
206: | |
207: | if ($this->getAccountsManager()->isExists($oAccount)) { |
208: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::AccountExists); |
209: | } |
210: | |
211: | $this->getAccountsManager()->createAccount($oAccount); |
212: | return $oAccount ? array( |
213: | 'EntityId' => $oAccount->Id |
214: | ) : false; |
215: | } else { |
216: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::CanNotCreateAccount); |
217: | } |
218: | |
219: | return false; |
220: | } |
221: | /** |
222: | * Updates account. |
223: | * |
224: | * @param \Aurora\Modules\StandardAuth\Models\Account $oAccount |
225: | * @return bool |
226: | * @throws \Aurora\System\Exceptions\ApiException |
227: | */ |
228: | public function SaveAccount($oAccount) |
229: | { |
230: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
231: | |
232: | if ($oAccount instanceof Models\Account) { |
233: | $this->getAccountsManager()->createAccount($oAccount); |
234: | |
235: | return $oAccount ? array( |
236: | 'EntityId' => $oAccount->Id |
237: | ) : false; |
238: | } else { |
239: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter); |
240: | } |
241: | |
242: | return false; |
243: | } |
244: | /***** public functions *****/ |
245: | |
246: | /***** public functions might be called with web API *****/ |
247: | /** |
248: | * @apiDefine StandardAuth Standard Auth Module |
249: | * This module provides API for authentication by login/password that relies on database. |
250: | */ |
251: | |
252: | /** |
253: | * @api {post} ?/Api/ CreateAuthenticatedUserAccount |
254: | * @apiName CreateAuthenticatedUserAccount |
255: | * @apiGroup StandardAuth |
256: | * @apiDescription Creates basic account for specified user. |
257: | * |
258: | * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method. |
259: | * @apiHeaderExample {json} Header-Example: |
260: | * { |
261: | * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8" |
262: | * } |
263: | * |
264: | * @apiParam {string=StandardAuth} Module Module name. |
265: | * @apiParam {string=CreateAuthenticatedUserAccount} Method Method name. |
266: | * @apiParam {string} Parameters JSON.stringified object <br> |
267: | * {<br> |
268: | *   **Login** *string* New account login.<br> |
269: | *   **Password** *string* Password New account password.<br> |
270: | * } |
271: | * |
272: | * @apiParamExample {json} Request-Example: |
273: | * { |
274: | * Module: 'StandardAuth', |
275: | * Method: 'CreateAuthenticatedUserAccount', |
276: | * Parameters: '{ Login: "login_value", Password: "password_value" }' |
277: | * } |
278: | * |
279: | * @apiSuccess {object[]} Result Array of response objects. |
280: | * @apiSuccess {string} Result.Module Module name. |
281: | * @apiSuccess {string} Result.Method Method name. |
282: | * @apiSuccess {bool} Result.Result Indicates if account was created successfully. |
283: | * @apiSuccess {int} [Result.ErrorCode] Error code. |
284: | * |
285: | * @apiSuccessExample {json} Success response example: |
286: | * { |
287: | * Module: 'StandardAuth', |
288: | * Method: 'CreateAuthenticatedUserAccount', |
289: | * Result: true |
290: | * } |
291: | * |
292: | * @apiSuccessExample {json} Error response example: |
293: | * { |
294: | * Module: 'StandardAuth', |
295: | * Method: 'CreateAuthenticatedUserAccount', |
296: | * Result: false, |
297: | * ErrorCode: 102 |
298: | * } |
299: | */ |
300: | /** |
301: | * Creates basic account for specified user. |
302: | * |
303: | * @param string $Login New account login. |
304: | * @param string $Password New account password. |
305: | * @return bool |
306: | */ |
307: | public function CreateAuthenticatedUserAccount($TenantId, $Login, $Password) |
308: | { |
309: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin); |
310: | |
311: | $UserId = \Aurora\System\Api::getAuthenticatedUserId(); |
312: | $result = false; |
313: | |
314: | if ($UserId) { |
315: | $result = $this->CreateAccount($TenantId, $UserId, $Login, $Password); |
316: | } |
317: | |
318: | return $result; |
319: | } |
320: | |
321: | /** |
322: | * @api {post} ?/Api/ UpdateAccount |
323: | * @apiName UpdateAccount |
324: | * @apiGroup StandardAuth |
325: | * @apiDescription Updates existing basic account. |
326: | * |
327: | * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method. |
328: | * @apiHeaderExample {json} Header-Example: |
329: | * { |
330: | * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8" |
331: | * } |
332: | * |
333: | * @apiParam {string=StandardAuth} Module Module name. |
334: | * @apiParam {string=UpdateAccount} Method Method name. |
335: | * @apiParam {string} Parameters JSON.stringified object <br> |
336: | * {<br> |
337: | *   **AccountId** *int* AccountId Identifier of account to update.<br> |
338: | *   **Login** *string* New value of account login. *optional*<br> |
339: | *   **Password** *string* New value of account password. *optional*<br> |
340: | * } |
341: | * |
342: | * @apiParamExample {json} Request-Example: |
343: | * { |
344: | * Module: 'StandardAuth', |
345: | * Method: 'UpdateAccount', |
346: | * Parameters: '{ AccountId: 123, Login: "login_value", Password: "password_value" }' |
347: | * } |
348: | * |
349: | * @apiSuccess {object[]} Result Array of response objects. |
350: | * @apiSuccess {string} Result.Module Module name. |
351: | * @apiSuccess {string} Result.Method Method name. |
352: | * @apiSuccess {mixed} Result.Result Object in case of success, otherwise **false**. |
353: | * @apiSuccess {string} Result.Result.EntityId Identifier of updated account. |
354: | * @apiSuccess {int} [Result.ErrorCode] Error code. |
355: | * |
356: | * @apiSuccessExample {json} Success response example: |
357: | * { |
358: | * Module: 'StandardAuth', |
359: | * Method: 'UpdateAccount', |
360: | * Result: true |
361: | * } |
362: | * |
363: | * @apiSuccessExample {json} Error response example: |
364: | * { |
365: | * Module: 'StandardAuth', |
366: | * Method: 'UpdateAccount', |
367: | * Result: false, |
368: | * ErrorCode: 102 |
369: | * } |
370: | */ |
371: | /** |
372: | * Updates existing basic account. |
373: | * |
374: | * @param int $AccountId Identifier of account to update. |
375: | * @param string $Login New value of account login. |
376: | * @param string $Password New value of account password. |
377: | * @return array|bool |
378: | * @throws \Aurora\System\Exceptions\ApiException |
379: | */ |
380: | public function UpdateAccount($AccountId = 0, $Password = '') |
381: | { |
382: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
383: | |
384: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
385: | |
386: | if ($AccountId > 0) { |
387: | $oAccount = $this->getAccountsManager()->getAccountById($AccountId); |
388: | |
389: | if (!empty($oAccount)) { |
390: | if ($oAccount->IdUser !== $oUser->Id) { |
391: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin); |
392: | } |
393: | if ($Password) { |
394: | $oAccount->setPassword($Password); |
395: | } |
396: | $this->getAccountsManager()->updateAccount($oAccount); |
397: | } |
398: | |
399: | return $oAccount ? array( |
400: | 'EntityId' => $oAccount->Id |
401: | ) : false; |
402: | } else { |
403: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter); |
404: | } |
405: | |
406: | return false; |
407: | } |
408: | |
409: | /** |
410: | * @api {post} ?/Api/ DeleteAccount |
411: | * @apiName DeleteAccount |
412: | * @apiGroup StandardAuth |
413: | * @apiDescription Deletes basic account. |
414: | * |
415: | * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method. |
416: | * @apiHeaderExample {json} Header-Example: |
417: | * { |
418: | * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8" |
419: | * } |
420: | * |
421: | * @apiParam {string=StandardAuth} Module Module name. |
422: | * @apiParam {string=DeleteAccount} Method Method name. |
423: | * @apiParam {string} Parameters JSON.stringified object <br> |
424: | * {<br> |
425: | *   **AccountId** *int* Identifier of account to delete.<br> |
426: | * } |
427: | * |
428: | * @apiParamExample {json} Request-Example: |
429: | * { |
430: | * Module: 'StandardAuth', |
431: | * Method: 'DeleteAccount', |
432: | * Parameters: '{ AccountId: 123 }' |
433: | * } |
434: | * |
435: | * @apiSuccess {object[]} Result Array of response objects. |
436: | * @apiSuccess {string} Result.Module Module name. |
437: | * @apiSuccess {string} Result.Method Method name. |
438: | * @apiSuccess {bool} Result.Result Indicates if account was deleted successfully. |
439: | * @apiSuccess {int} [Result.ErrorCode] Error code. |
440: | * |
441: | * @apiSuccessExample {json} Success response example: |
442: | * { |
443: | * Module: 'StandardAuth', |
444: | * Method: 'DeleteAccount', |
445: | * Result: true |
446: | * } |
447: | * |
448: | * @apiSuccessExample {json} Error response example: |
449: | * { |
450: | * Module: 'StandardAuth', |
451: | * Method: 'DeleteAccount', |
452: | * Result: false, |
453: | * ErrorCode: 102 |
454: | * } |
455: | */ |
456: | /** |
457: | * Deletes basic account. |
458: | * |
459: | * @param int $AccountId Identifier of account to delete. |
460: | * @return bool |
461: | * @throws \Aurora\System\Exceptions\ApiException |
462: | */ |
463: | public function DeleteAccount($AccountId = 0) |
464: | { |
465: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
466: | |
467: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
468: | |
469: | $bResult = false; |
470: | |
471: | if ($AccountId > 0) { |
472: | $oAccount = $this->getAccountsManager()->getAccountById($AccountId); |
473: | |
474: | if (!empty($oAccount) && ($oAccount->IdUser === $oUser->Id || |
475: | $oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin || |
476: | $oUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) { |
477: | $bResult = $this->getAccountsManager()->deleteAccount($oAccount); |
478: | } |
479: | |
480: | return $bResult; |
481: | } else { |
482: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter); |
483: | } |
484: | } |
485: | |
486: | /** |
487: | * @api {post} ?/Api/ GetUserAccounts |
488: | * @apiName GetUserAccounts |
489: | * @apiGroup StandardAuth |
490: | * @apiDescription Obtains basic account for specified user. |
491: | * |
492: | * @apiHeader {string} Authorization "Bearer " + Authentication token which was received as the result of Core.Login method. |
493: | * @apiHeaderExample {json} Header-Example: |
494: | * { |
495: | * "Authorization": "Bearer 32b2ecd4a4016fedc4abee880425b6b8" |
496: | * } |
497: | * |
498: | * @apiParam {string=StandardAuth} Module Module name. |
499: | * @apiParam {string=GetUserAccounts} Method Method name. |
500: | * @apiParam {string} Parameters JSON.stringified object <br> |
501: | * {<br> |
502: | *   **UserId** *int* User identifier.<br> |
503: | * } |
504: | * |
505: | * @apiParamExample {json} Request-Example: |
506: | * { |
507: | * Module: 'StandardAuth', |
508: | * Method: 'GetUserAccounts', |
509: | * Parameters: '{ UserId: 123 }' |
510: | * } |
511: | * |
512: | * @apiSuccess {object[]} Result Array of response objects. |
513: | * @apiSuccess {string} Result.Module Module name. |
514: | * @apiSuccess {string} Result.Method Method name. |
515: | * @apiSuccess {mixed} Result.Result List of account objects in case of success, otherwise **false**. Account object is like {id: 234, login: 'account_login'}. |
516: | * @apiSuccess {int} [Result.ErrorCode] Error code. |
517: | * |
518: | * @apiSuccessExample {json} Success response example: |
519: | * { |
520: | * Module: 'StandardAuth', |
521: | * Method: 'GetUserAccounts', |
522: | * Result: [{id: 234, login: 'account_login234'}, {id: 235, login: 'account_login235'}] |
523: | * } |
524: | * |
525: | * @apiSuccessExample {json} Error response example: |
526: | * { |
527: | * Module: 'StandardAuth', |
528: | * Method: 'GetUserAccounts', |
529: | * Result: false, |
530: | * ErrorCode: 102 |
531: | * } |
532: | */ |
533: | /** |
534: | * Obtains basic account for specified user. |
535: | * |
536: | * @param int $UserId User identifier. |
537: | * @return array|bool |
538: | */ |
539: | public function GetUserAccounts($UserId) |
540: | { |
541: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
542: | |
543: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
544: | if ($oUser->isNormalOrTenant() && $oUser->Id != $UserId) { |
545: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::AccessDenied); |
546: | } |
547: | |
548: | $aAccounts = array(); |
549: | $mResult = $this->getAccountsManager()->getUserAccounts($UserId); |
550: | |
551: | foreach ($mResult as $aItem) { |
552: | $aAccounts[] = array( |
553: | 'id' => $aItem['Id'], |
554: | 'login' => $aItem['Login'] |
555: | ); |
556: | } |
557: | |
558: | return $aAccounts; |
559: | } |
560: | /***** public functions might be called with web API *****/ |
561: | } |
562: |