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\InvitationLinkWebclient;
9:
10: use Aurora\System\Api;
11: use Aurora\System\Application;
12: use PHPMailer\PHPMailer\PHPMailer;
13:
14: /**
15: * Creates invitation link upon creating user in admin panel, and allows registering new user account with this link.
16: *
17: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
18: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
19: * @copyright Copyright (c) 2023, Afterlogic Corp.
20: *
21: * @package Modules
22: */
23: class Module extends \Aurora\System\Module\AbstractWebclientModule
24: {
25: protected $oMinModuleDecorator;
26:
27: protected $aRequireModules = array(
28: 'Min'
29: );
30:
31: /***** private functions *****/
32: /**
33: * Initializes module.
34: *
35: * @ignore
36: */
37: public function init()
38: {
39: $this->subscribeEvent('Register::before', array($this, 'onBeforeRegister'));
40: $this->subscribeEvent('Register::after', array($this, 'onAfterRegister'));
41:
42: $this->subscribeEvent('Core::CreateUser::after', array($this, 'onAfterCreateUser'));
43:
44: $this->subscribeEvent('StandardAuth::CreateAuthenticatedUserAccount::after', array($this, 'onAfterCreateUserAccount'));
45:
46: $this->subscribeEvent('CreateOAuthAccount', array($this, 'onCreateOAuthAccount'));
47: $this->subscribeEvent('Core::DeleteUser::after', array($this, 'onAfterDeleteUser'));
48: }
49:
50: /**
51: * Returns Min module decorator.
52: *
53: * @return \CApiModuleDecorator
54: */
55: private function getMinModuleDecorator()
56: {
57: if ($this->oMinModuleDecorator === null) {
58: $this->oMinModuleDecorator = \Aurora\Modules\Min\Module::Decorator();
59: }
60:
61: return $this->oMinModuleDecorator;
62: }
63:
64: /**
65: * Returns register module hash.
66: *
67: * @return string
68: */
69: protected function getRegisterModuleHash()
70: {
71: $sResult = null;
72: $oRegisterModuleDecorator = \Aurora\System\Api::GetModuleDecorator($this->getConfig('RegisterModuleName'));
73: if ($oRegisterModuleDecorator) {
74: $oRegisterModuleSettings = $oRegisterModuleDecorator->GetSettings();
75: $sResult = $oRegisterModuleSettings['HashModuleName'];
76: }
77:
78: return $sResult;
79: }
80:
81: /**
82: * Returns login module hash.
83: *
84: * @return string
85: */
86: protected function getLoginModuleHash()
87: {
88: $sResult = null;
89: $oLoginModuleDecorator = \Aurora\System\Api::GetModuleDecorator($this->getConfig('LoginModuleName'));
90: if ($oLoginModuleDecorator) {
91: $oLoginModuleSettings = $oLoginModuleDecorator->GetSettings();
92: $sResult = $oLoginModuleSettings['HashModuleName'];
93: }
94:
95: return $sResult;
96: }
97:
98: /**
99: * Returns id for Min Module
100: *
101: * @return string
102: */
103: protected function generateMinId($iUserId)
104: {
105: return \implode('|', array(self::GetName(), $iUserId, \md5($iUserId)));
106: }
107:
108: /**
109: * Returns user with identifier obtained from the Invitation link hash.
110: *
111: * @param string $InvitationLinkHash Invitation link hash.
112: * @return \Aurora\Modules\Core\Classes\User
113: */
114: protected function getUserByInvitationLinkHash($InvitationLinkHash)
115: {
116: $oUser = null;
117: $oMin = $this->getMinModuleDecorator();
118: if ($oMin) {
119: $mHash = $oMin->GetMinByHash($InvitationLinkHash);
120: if (isset($mHash['__hash__'], $mHash['UserId']) && !isset($mHash['Registered'])) {
121: $iUserId = $mHash['UserId'];
122: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId);
123: }
124: }
125: return $oUser;
126: }
127:
128: /**
129: * Writes to $aArgs['UserId'] user identifier obtained from Invitation link hash.
130: *
131: * @ignore
132: * @param array $aArgs
133: * @param mixed $mResult
134: */
135: public function onBeforeRegister(&$aArgs, &$mResult)
136: {
137: if (empty($aArgs['InvitationLinkHash'])) {
138: return true; // break other subscriptions and Register method itself to prevent creation of a new user
139: } else {
140: $oUser = $this->getUserByInvitationLinkHash($aArgs['InvitationLinkHash']);
141: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
142: $aArgs['UserId'] = $oUser->Id;
143: } else {
144: return true; // break other subscriptions and Register method itself to prevent creation of a new user
145: }
146: }
147: }
148:
149: /**
150: * Updates Invitation link hash in Min module.
151: *
152: * @ignore
153: * @param array $aArgs
154: * @param mixed $mResult
155: */
156: public function onAfterRegister($aArgs, &$mResult)
157: {
158: if ($mResult && !empty($aArgs['InvitationLinkHash'])) {
159: $oMin = $this->getMinModuleDecorator();
160: if ($oMin) {
161: $mHash = $oMin->GetMinByHash($aArgs['InvitationLinkHash']);
162: if (isset($mHash['__hash__'], $mHash['UserId']) && !isset($mHash['Registered'])) {
163: $oMin->DeleteMinByHash($mHash['__hash__']);
164: }
165: }
166: }
167: }
168:
169: /**
170: * Updates Invitation link hash in Min module for user with $aArgs['UserId'] identifier.
171: *
172: * @ignore
173: * @param array $aArgs
174: * @param mixed $mResult
175: */
176: public function onAfterCreateUserAccount($aArgs, &$mResult)
177: {
178: $userId = Api::getUserIdByPublicId($aArgs['Login']);
179: $oMin = $this->getMinModuleDecorator();
180: if ($oMin) {
181: $mHash = $oMin->GetMinById(
182: $this->generateMinId($userId)
183: );
184:
185: if (isset($mHash['__hash__'], $userId) && !isset($mHash['Registered'])) {
186: $oMin->DeleteMinByHash($mHash['__hash__']);
187: }
188: }
189: }
190:
191: /**
192: * Writes to $oUser variable user object for Invitation link hash from cookie.
193: *
194: * @ignore
195: * @param \Aurora\Modules\Core\Classes\User $oUser
196: */
197: public function onCreateOAuthAccount($aArgs, &$oUser)
198: {
199: if (isset($_COOKIE['InvitationLinkHash'])) {
200: $InvitationLinkHash = $_COOKIE['InvitationLinkHash'];
201:
202: $oFoundUser = $this->getUserByInvitationLinkHash($InvitationLinkHash);
203: if (!empty($oFoundUser)) {
204: unset($_COOKIE['InvitationLinkHash']);
205: $oUser = $oFoundUser;
206:
207: $oMin = $this->getMinModuleDecorator();
208: if ($oMin) {
209: $mHash = $oMin->GetMinByHash($InvitationLinkHash);
210: if (isset($mHash['__hash__'], $mHash['UserId']) && !isset($mHash['Registered'])) {
211: $oMin->DeleteMinByHash($mHash['__hash__']);
212: }
213: }
214: }
215: }
216: }
217:
218: /**
219: * Updates Invitation link hash in Min module for user with $aArgs['UserId'] identifier.
220: *
221: * @ignore
222: * @param array $aArgs
223: * @param mixed $mResult
224: */
225: public function onAfterCreateUser($aArgs, &$mResult)
226: {
227: $iUserId = isset($mResult) && (int) $mResult > 0 ? $mResult : 0;
228: if (0 < $iUserId) {
229: self::Decorator()->CreateInvitationLinkHash($iUserId);
230: }
231: }
232:
233: /**
234: * Deletes hash which are owened by the specified user.
235: *
236: * @ignore
237: * @param int $iUserId User Identifier.
238: */
239: public function onAfterDeleteUser($aArgs, $mResult)
240: {
241: if ($mResult) {
242: $this->getMinModuleDecorator()->DeleteMinByID(
243: $this->generateMinId($aArgs['UserId'])
244: );
245: }
246: }
247: /***** private functions *****/
248:
249: /***** public functions might be called with web API *****/
250: /**
251: * Obtains list of module settings for authenticated user.
252: *
253: * @return array
254: */
255: public function GetSettings()
256: {
257: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
258:
259: return array(
260: 'RegisterModuleHash' => $this->getRegisterModuleHash(),
261: 'RegisterModuleName' => $this->getConfig('RegisterModuleName'),
262: 'LoginModuleHash' => $this->getLoginModuleHash(),
263: 'EnableSendInvitationLinkViaMail' => $this->getConfig('EnableSendInvitationLinkViaMail'),
264: );
265: }
266:
267: /**
268: * Create Invitation link hash for specified user.
269: *
270: * @param int $UserId User identifier.
271: * @return string
272: */
273: public function CreateInvitationLinkHash($UserId)
274: {
275: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
276:
277: $mHash = '';
278: $oMin = $this->getMinModuleDecorator();
279: if ($oMin) {
280: $sMinId = $this->generateMinId($UserId);
281: $aHashData = $oMin->GetMinById($sMinId);
282: if (!$aHashData) {
283: $mHash = $oMin->CreateMin(
284: $sMinId,
285: array(
286: 'UserId' => $UserId
287: ),
288: $UserId
289: );
290: } else {
291: $mHash = $this->GetInvitationLinkHash($UserId);
292: }
293: }
294:
295: return $mHash;
296: }
297:
298: /**
299: *
300: * @param string $Email
301: * @param string $Hash
302: */
303: public function SendNotification($Email, $Hash)
304: {
305: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
306:
307: $oModuleManager = \Aurora\System\Api::GetModuleManager();
308: $sSiteName = $oModuleManager->getModuleConfigValue('Core', 'SiteName');
309: $sBody = \file_get_contents($this->GetPath().'/templates/InvitationMail.html');
310: if (\is_string($sBody)) {
311: $sBody = \strtr($sBody, array(
312: '{{INVITATION_URL}}' => \rtrim(Application::getBaseUrl(), '\\/ ') . "/index.php#register/" . $Hash,
313: '{{SITE_NAME}}' => $sSiteName
314: ));
315: }
316: $sSubject = "You're invited to join " . $sSiteName;
317: $sFrom = $this->getConfig('NotificationEmail', '');
318:
319: $oMail = new PHPMailer();
320:
321: $sType = $this->getConfig('NotificationType', 'mail');
322: if (\strtolower($sType) === 'mail') {
323: $oMail->isMail();
324: } elseif (\strtolower($sType) === 'smtp') {
325: $oMail->isSMTP();
326: $oMail->Host = $this->getConfig('NotificationHost', '');
327: $oMail->Port = (int) $this->getConfig('NotificationPort', 25);
328: ;
329: $oMail->SMTPAuth = (bool) $this->getConfig('NotificationUseAuth', false);
330: if ($oMail->SMTPAuth) {
331: $oMail->Username = $this->getConfig('NotificationLogin', '');
332: $oMail->Password = $this->getConfig('NotificationPassword', '');
333: }
334: $oMail->SMTPOptions = array(
335: 'ssl' => array(
336: 'verify_peer' => false,
337: 'verify_peer_name' => false,
338: 'allow_self_signed' => true
339: )
340: );
341: $sSmtpSecure = $this->getConfig('SMTPSecure', '');
342: if (!empty($sSmtpSecure)) {
343: $oMail->SMTPSecure = $sSmtpSecure;
344: }
345: }
346:
347: $oMail->setFrom($sFrom);
348: $oMail->addAddress($Email);
349: $oMail->addReplyTo($sFrom, $sSiteName);
350:
351: $oMail->isHTML(true); // Set email format to HTML
352:
353: $oMail->Subject = $sSubject;
354: $oMail->Body = $sBody;
355:
356: return $oMail->send();
357: }
358:
359: /**
360: * Returns Invitation link hash for specified user.
361: *
362: * @param int $UserId User identifier.
363: * @return string
364: */
365: public function GetInvitationLinkHash($UserId)
366: {
367: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
368:
369: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($UserId);
370: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
371: $bAllowHash = false;
372: if (!empty($oAuthenticatedUser) && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin && !empty($oUser) && $oUser->IdTenant === $oAuthenticatedUser->IdTenant) {
373: $bAllowHash = true;
374: } elseif (!empty($oAuthenticatedUser) && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
375: $bAllowHash = true;
376: }
377:
378: if (!$bAllowHash) {
379: return '';
380: }
381:
382: $mHash = '';
383: $oMin = $this->getMinModuleDecorator();
384: if ($oMin) {
385: $sMinId = $this->generateMinId($UserId);
386: $mHash = $oMin->GetMinById($sMinId);
387:
388: if ($mHash) {
389: if (isset($mHash['__hash__']) && !isset($mHash['Registered'])) {
390: $mHash = $mHash['__hash__'];
391: } else {
392: $mHash = '';
393: }
394: }
395: }
396:
397: return $mHash;
398: }
399:
400: /**
401: * Returns public id of user obtained from Invitation link hash.
402: *
403: * @param string $InvitationLinkHash Invitation link hash with information about user and its registration status.
404: * @return string
405: */
406: public function GetUserPublicId($InvitationLinkHash)
407: {
408: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
409:
410: $oUser = $this->getUserByInvitationLinkHash($InvitationLinkHash);
411: if ($oUser) {
412: return $oUser->PublicId;
413: }
414: return '';
415: }
416: /***** public functions might be called with web API *****/
417: }
418: