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\IPAllowList;
9:
10: use Aurora\Modules\Core\Models\User;
11: use Aurora\System\Exceptions\ApiException;
12:
13: /**
14: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
15: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
16: * @copyright Copyright (c) 2023, Afterlogic Corp.
17: *
18: * @property Settings $oModuleSettings
19: *
20: * @package Modules
21: */
22: class Module extends \Aurora\System\Module\AbstractModule
23: {
24: public function init()
25: {
26: $this->aErrors = [
27: Enums\ErrorCodes::IpIsNotAllowed => $this->i18N('ERROR_IP_IS_NOT_ALLOWED'),
28: ];
29:
30: $this->subscribeEvent('Core::Login::before', array($this, 'onBeforeLogin'));
31: $this->subscribeEvent('System::RunEntry::before', [$this, 'onBeforeRunEntry'], 100);
32: }
33:
34: /**
35: * @return Module
36: */
37: public static function getInstance()
38: {
39: return parent::getInstance();
40: }
41:
42: /**
43: * @return Module
44: */
45: public static function Decorator()
46: {
47: return parent::Decorator();
48: }
49:
50: /**
51: * @return Settings
52: */
53: public function getModuleSettings()
54: {
55: return $this->oModuleSettings;
56: }
57:
58: public function GetSettings()
59: {
60: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
61:
62: return [
63: 'CurrentIP' => $this->_getCurrentIp()
64: ];
65: }
66:
67: /**
68: * Obtains user settings. Method is allowed for superadmin only.
69: *
70: * @param int $UserId
71: * @return array|null
72: */
73: public function GetUserSettings($UserId)
74: {
75: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
76:
77: $oUser = \Aurora\System\Api::getUserById($UserId);
78: if ($oUser instanceof User && $oUser->isNormalOrTenant()) {
79: $aList = $this->GetIpAllowlist($oUser);
80: $bIpAllowlistEnabled = (count($aList) > 0);
81: return [
82: 'IpAllowlistEnabled' => $bIpAllowlistEnabled
83: ];
84: }
85:
86: return null;
87: }
88:
89: public function DisableUserIpAllowlist($UserId)
90: {
91: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
92:
93: $mResult = false;
94: $oUser = \Aurora\System\Api::getUserById($UserId);
95: if ($oUser instanceof User && $oUser->isNormalOrTenant()) {
96: $oUser->setExtendedProp(self::GetName() . '::IPAllowList', \json_encode([]));
97: $mResult = $oUser->save();
98: }
99: return $mResult;
100: }
101:
102: public function GetIpAllowlist($User = null)
103: {
104: if ($User === null) {
105: $User = \Aurora\System\Api::getAuthenticatedUser();
106: } else {
107: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
108: }
109: $aList = [];
110: if ($User instanceof User) {
111: if (null !== $User->getExtendedProp(self::GetName() . '::IPAllowList')) {
112: $aList = \json_decode($User->getExtendedProp(self::GetName() . '::IPAllowList'), true);
113: }
114: }
115:
116: return $aList;
117: }
118:
119: public function AddIpToAllowlist($IP, $Comment)
120: {
121: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
122:
123: $mResult = false;
124: $oUser = \Aurora\System\Api::getAuthenticatedUser();
125: if ($oUser instanceof User) {
126: $aList = $this->GetIpAllowlist();
127: $aList[$IP] = ['Comment' => $Comment];
128: $oUser->setExtendedProp(self::GetName() . '::IPAllowList', \json_encode($aList));
129: $mResult = $oUser->save();
130: }
131:
132: return $mResult;
133: }
134:
135: public function RemoveIpFromAllowlist($IP)
136: {
137: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
138:
139: $mResult = false;
140: $oUser = \Aurora\System\Api::getAuthenticatedUser();
141: if ($oUser instanceof User) {
142: $aList = $this->GetIpAllowlist();
143: if (isset($aList[$IP])) {
144: unset($aList[$IP]);
145: $oUser->setExtendedProp(self::GetName() . '::IPAllowList', \json_encode($aList));
146: $mResult = $oUser->save();
147: }
148: }
149: return $mResult;
150: }
151:
152: protected function _getCurrentIp()
153: {
154: if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
155: return $_SERVER['HTTP_CLIENT_IP'];
156: } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
157: return $_SERVER['HTTP_X_FORWARDED_FOR'];
158: } else {
159: return $_SERVER['REMOTE_ADDR'];
160: }
161: }
162:
163: protected function checkIpAddress($oUser = null)
164: {
165: $sIpAddress = $this->_getCurrentIp();
166: $aList = $this->GetIpAllowlist($oUser);
167: if (is_array($aList) && count($aList) > 0) {
168: if (!in_array($sIpAddress, array_keys($aList))) {
169: \Aurora\System\Api::LogEvent('access-denied: ' . $oUser->PublicId, self::GetName());
170: throw new ApiException(Enums\ErrorCodes::IpIsNotAllowed, null, '', [], $this);
171: }
172: }
173: }
174:
175: public function onBeforeRunEntry($aArgs, &$mResult)
176: {
177: $aEntries = ['api', 'download'];
178: if (isset($aArgs['EntryName']) && in_array(strtolower($aArgs['EntryName']), $aEntries)) {
179: $this->checkIpAddress();
180: }
181: }
182:
183: public function onBeforeLogin($aArgs, &$mResult)
184: {
185: if (isset($aArgs['Login']) && isset($aArgs['Password'])) {
186: $aAuthData = \Aurora\Modules\Core\Module::Decorator()->Authenticate($aArgs['Login'], $aArgs['Password']);
187: if (is_array($aAuthData) && isset($aAuthData['id'])) {
188: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserWithoutRoleCheck($aAuthData['id']);
189: if ($oUser) {
190: \Aurora\Api::skipCheckUserRole(true);
191: $this->checkIpAddress($oUser);
192: \Aurora\Api::skipCheckUserRole(false);
193: }
194: }
195: }
196: }
197: }
198: