|   1:  | <?php | 
|   2:  |  | 
|   3:  |  | 
|   4:  |  | 
|   5:  |  | 
|   6:  |  | 
|   7:  |  | 
|   8:  | namespace Aurora\Modules\CoreParanoidEncryptionWebclientPlugin; | 
|   9:  |  | 
|  10:  | use Aurora\Api; | 
|  11:  | use Aurora\Modules\Files\Classes\FileItem; | 
|  12:  | use Aurora\System\Exceptions\ApiException; | 
|  13:  |  | 
|  14:  |  | 
|  15:  |  | 
|  16:  |  | 
|  17:  |  | 
|  18:  |  | 
|  19:  |  | 
|  20:  |  | 
|  21:  |  | 
|  22:  |  | 
|  23:  | class Module extends \Aurora\System\Module\AbstractWebclientModule | 
|  24:  | { | 
|  25:  |     public static $sStorageType = 'encrypted'; | 
|  26:  |     public static $iStorageOrder = 10; | 
|  27:  |     public static $sPersonalStorageType = 'personal'; | 
|  28:  |     public static $sSharedStorageType = 'shared'; | 
|  29:  |     public static $sEncryptedFolder = '.encrypted'; | 
|  30:  |     protected $aRequireModules = ['PersonalFiles','S3Filestorage']; | 
|  31:  |  | 
|  32:  |     public function init() | 
|  33:  |     { | 
|  34:  |         $this->subscribeEvent('Files::GetStorages::after', [$this, 'onAfterGetStorages'], 1); | 
|  35:  |         $this->subscribeEvent('Files::FileItemtoResponseArray', [$this, 'onFileItemToResponseArray']); | 
|  36:  |  | 
|  37:  |         $this->subscribeEvent('Files::GetFile', [$this, 'onGetFile']); | 
|  38:  |         $this->subscribeEvent('Files::CreateFile', [$this, 'onCreateFile']); | 
|  39:  |  | 
|  40:  |         $this->subscribeEvent('Files::GetItems::before', [$this, 'onBeforeGetItems']); | 
|  41:  |         $this->subscribeEvent('Files::GetItems', [$this, 'onGetItems'], 10001); | 
|  42:  |         $this->subscribeEvent('Files::Copy::before', [$this, 'onBeforeCopyOrMove']); | 
|  43:  |         $this->subscribeEvent('Files::Move::before', [$this, 'onBeforeCopyOrMove']); | 
|  44:  |         $this->subscribeEvent('Files::Delete::before', [$this, 'onBeforeDelete']); | 
|  45:  |  | 
|  46:  |         $this->subscribeEvent('Files::GetFileInfo::before', [$this, 'onBeforeMethod']); | 
|  47:  |         $this->subscribeEvent('Files::CreateFolder::before', [$this, 'onBeforeMethod']); | 
|  48:  |         $this->subscribeEvent('Files::Rename::before', [$this, 'onBeforeMethod']); | 
|  49:  |         $this->subscribeEvent('Files::GetQuota::before', [$this, 'onBeforeMethod']); | 
|  50:  |         $this->subscribeEvent('Files::CreateLink::before', [$this, 'onBeforeMethod']); | 
|  51:  |         $this->subscribeEvent('Files::GetFileContent::before', [$this, 'onBeforeMethod']); | 
|  52:  |         $this->subscribeEvent('Files::IsFileExists::before', [$this, 'onBeforeMethod']); | 
|  53:  |         $this->subscribeEvent('Files::CheckQuota::before', [$this, 'onBeforeMethod']); | 
|  54:  |         $this->subscribeEvent('Files::CreatePublicLink::before', [$this, 'onBeforeMethod']); | 
|  55:  |         $this->subscribeEvent('Files::DeletePublicLink::before', [$this, 'onBeforeMethod']); | 
|  56:  |         $this->subscribeEvent('Files::GetPublicFiles::after', [$this, 'onAfterGetPublicFiles']); | 
|  57:  |         $this->subscribeEvent('Files::SaveFilesAsTempFiles::after', [$this, 'onAfterSaveFilesAsTempFiles']); | 
|  58:  |         $this->subscribeEvent('Files::UpdateExtendedProps::before', [$this, 'onBeforeMethod']); | 
|  59:  |         $this->subscribeEvent('OpenPgpFilesWebclient::CreatePublicLink::before', [$this, 'onBeforeMethod']); | 
|  60:  |  | 
|  61:  |         $this->subscribeEvent('SharedFiles::UpdateShare::before', [$this, 'onBeforeUpdateShare']); | 
|  62:  |         $this->subscribeEvent('SharedFiles::CreateSharedFile', [$this, 'onCreateOrUpdateSharedFile']); | 
|  63:  |         $this->subscribeEvent('SharedFiles::UpdateSharedFile', [$this, 'onCreateOrUpdateSharedFile']); | 
|  64:  |  | 
|  65:  |         $this->subscribeEvent('Files::GetExtendedProps::before', [$this, 'onBeforeGetExtendedProps']); | 
|  66:  |     } | 
|  67:  |  | 
|  68:  |     protected function getEncryptedPath($sPath) | 
|  69:  |     { | 
|  70:  |         return '/' . self::$sEncryptedFolder . \ltrim($sPath); | 
|  71:  |     } | 
|  72:  |  | 
|  73:  |     protected function startsWith($haystack, $needle) | 
|  74:  |     { | 
|  75:  |         return (substr($haystack, 0, strlen($needle)) === $needle); | 
|  76:  |     } | 
|  77:  |  | 
|  78:  |     public function onAfterGetStorages($aArgs, &$mResult) | 
|  79:  |     { | 
|  80:  |         $oUser = \Aurora\System\Api::getAuthenticatedUser(); | 
|  81:  |         if ($oUser->{$this->GetName() . '::EnableModule'}) { | 
|  82:  |             array_unshift($mResult, [ | 
|  83:  |                 'Type' => static::$sStorageType, | 
|  84:  |                 'DisplayName' => $this->i18N('LABEL_STORAGE'), | 
|  85:  |                 'IsExternal' => false, | 
|  86:  |                 'Order' => static::$iStorageOrder, | 
|  87:  |                 'IsDroppable' => false | 
|  88:  |             ]); | 
|  89:  |         } | 
|  90:  |     } | 
|  91:  |  | 
|  92:  |     public function onGetFile($aArgs, &$mResult) | 
|  93:  |     { | 
|  94:  |         if ($aArgs['Type'] === self::$sStorageType) { | 
|  95:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
|  96:  |             $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
|  97:  |  | 
|  98:  |             $this->GetModuleManager()->broadcastEvent( | 
|  99:  |                 'Files', | 
| 100:  |                 'GetFile', | 
| 101:  |                 $aArgs, | 
| 102:  |                 $mResult | 
| 103:  |             ); | 
| 104:  |         } | 
| 105:  |     } | 
| 106:  |  | 
| 107:  |     public function onCreateFile($aArgs, &$mResult) | 
| 108:  |     { | 
| 109:  |         if ($aArgs['Type'] === self::$sStorageType) { | 
| 110:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
| 111:  |             $aArgs['Path'] =  $this->getEncryptedPath($aArgs['Path']); | 
| 112:  |  | 
| 113:  |             $this->GetModuleManager()->broadcastEvent( | 
| 114:  |                 'Files', | 
| 115:  |                 'CreateFile', | 
| 116:  |                 $aArgs, | 
| 117:  |                 $mResult | 
| 118:  |             ); | 
| 119:  |         } | 
| 120:  |     } | 
| 121:  |  | 
| 122:  |      | 
| 123:  |  | 
| 124:  |  | 
| 125:  |  | 
| 126:  |  | 
| 127:  |     public function onBeforeGetItems(&$aArgs, &$mResult) | 
| 128:  |     { | 
| 129:  |         if ($aArgs['Type'] === self::$sStorageType) { | 
| 130:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
| 131:  |             $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
| 132:  |  | 
| 133:  |             if (!\Aurora\Modules\Files\Module::Decorator()->IsFileExists($aArgs['UserId'], $aArgs['Type'], '', self::$sEncryptedFolder)) { | 
| 134:  |                 \Aurora\Modules\Files\Module::Decorator()->CreateFolder($aArgs['UserId'], $aArgs['Type'], '', self::$sEncryptedFolder); | 
| 135:  |             } | 
| 136:  |         } | 
| 137:  |     } | 
| 138:  |  | 
| 139:  |      | 
| 140:  |  | 
| 141:  |  | 
| 142:  |  | 
| 143:  |  | 
| 144:  |     public function onGetItems(&$aArgs, &$mResult) | 
| 145:  |     { | 
| 146:  |         if ($aArgs['Type'] === self::$sPersonalStorageType && $aArgs['Path'] === '' && is_array($mResult)) { | 
| 147:  |             foreach ($mResult as $iKey => $oFileItem) { | 
| 148:  |                 if ($oFileItem instanceof FileItem && $oFileItem->IsFolder && $oFileItem->Name === self::$sEncryptedFolder) { | 
| 149:  |                     unset($mResult[$iKey]); | 
| 150:  |                 } | 
| 151:  |                 if ($oFileItem->Shared) { | 
| 152:  |                      | 
| 153:  |                 } | 
| 154:  |             } | 
| 155:  |         } | 
| 156:  |          | 
| 157:  |         if ( | 
| 158:  |             $this->oHttp->GetHeader('x-client') !== 'WebClient' | 
| 159:  |             && $aArgs['Type'] === self::$sPersonalStorageType | 
| 160:  |             && substr($aArgs['Path'], 1, 11) === self::$sEncryptedFolder | 
| 161:  |             && is_array($mResult) | 
| 162:  |         ) { | 
| 163:  |             foreach ($mResult as $iKey => $oFileItem) { | 
| 164:  |                 if (isset($oFileItem->ExtendedProps) && isset($oFileItem->ExtendedProps['ParanoidKey']) && empty($oFileItem->ExtendedProps['ParanoidKey'])) { | 
| 165:  |                     unset($mResult[$iKey]); | 
| 166:  |                 } | 
| 167:  |             } | 
| 168:  |         } | 
| 169:  |     } | 
| 170:  |  | 
| 171:  |      | 
| 172:  |  | 
| 173:  |  | 
| 174:  |  | 
| 175:  |  | 
| 176:  |     public function onBeforeCopyOrMove(&$aArgs, &$mResult) | 
| 177:  |     { | 
| 178:  |         if ($aArgs['FromType'] === self::$sStorageType || $aArgs['ToType'] === self::$sStorageType) { | 
| 179:  |             if ($aArgs['FromType'] === self::$sStorageType) { | 
| 180:  |                 $aArgs['FromType'] = self::$sPersonalStorageType; | 
| 181:  |                 $aArgs['FromPath'] = $this->getEncryptedPath($aArgs['FromPath']); | 
| 182:  |             } | 
| 183:  |             if ($aArgs['ToType'] === self::$sStorageType) { | 
| 184:  |                 $aArgs['ToType'] = self::$sPersonalStorageType; | 
| 185:  |                 $aArgs['ToPath'] = $this->getEncryptedPath($aArgs['ToPath']); | 
| 186:  |             } | 
| 187:  |  | 
| 188:  |             foreach ($aArgs['Files'] as $iKey => $aItem) { | 
| 189:  |                 if ($aItem['FromType'] === self::$sStorageType) { | 
| 190:  |                     $aArgs['Files'][$iKey]['FromType'] = self::$sPersonalStorageType; | 
| 191:  |                     $aArgs['Files'][$iKey]['FromPath'] = $this->getEncryptedPath($aItem['FromPath']); | 
| 192:  |                 } | 
| 193:  |             } | 
| 194:  |         } | 
| 195:  |     } | 
| 196:  |  | 
| 197:  |      | 
| 198:  |  | 
| 199:  |  | 
| 200:  |  | 
| 201:  |  | 
| 202:  |     public function onBeforeDelete(&$aArgs, &$mResult) | 
| 203:  |     { | 
| 204:  |         if ($aArgs['Type'] === self::$sStorageType) { | 
| 205:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
| 206:  |             $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
| 207:  |  | 
| 208:  |             foreach ($aArgs['Items'] as $iKey => $aItem) { | 
| 209:  |                 $aArgs['Items'][$iKey]['Path'] = $this->getEncryptedPath($aItem['Path']); | 
| 210:  |             } | 
| 211:  |         } | 
| 212:  |     } | 
| 213:  |  | 
| 214:  |      | 
| 215:  |  | 
| 216:  |  | 
| 217:  |  | 
| 218:  |  | 
| 219:  |     public function onBeforeMethod(&$aArgs, &$mResult) | 
| 220:  |     { | 
| 221:  |         if ($aArgs['Type'] === self::$sStorageType) { | 
| 222:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
| 223:  |             if (isset($aArgs['Path'])) { | 
| 224:  |                 $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
| 225:  |             } | 
| 226:  |         } | 
| 227:  |     } | 
| 228:  |  | 
| 229:  |     public function onBeforeUpdateShare(&$aArgs, &$mResult) | 
| 230:  |     { | 
| 231:  |         if ($aArgs['Storage'] === self::$sStorageType) { | 
| 232:  |             if ($aArgs['IsDir']) { | 
| 233:  |                 $iErrorCode = 0; | 
| 234:  |                 if (class_exists('\Aurora\Modules\SharedFiles\Enums\ErrorCodes')) { | 
| 235:  |                     $iErrorCode = \Aurora\Modules\SharedFiles\Enums\ErrorCodes::NotPossibleToShareDirectoryInEcryptedStorage; | 
| 236:  |                 } | 
| 237:  |                 throw new ApiException($iErrorCode); | 
| 238:  |             } | 
| 239:  |             $aArgs['Storage'] = self::$sPersonalStorageType; | 
| 240:  |             $aArgs['Type'] = self::$sPersonalStorageType; | 
| 241:  |             $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
| 242:  |         } | 
| 243:  |     } | 
| 244:  |  | 
| 245:  |     public function onCreateOrUpdateSharedFile(&$aArgs, &$mResult) | 
| 246:  |     { | 
| 247:  |         extract($aArgs); | 
| 248:  |         if (!empty($Share['ParanoidKeyShared']) && class_exists('\Aurora\Modules\SharedFiles\Models\SharedFile')) { | 
| 249:  |             $oSharedFile = \Aurora\Modules\SharedFiles\Models\SharedFile::where('owner', $UserPrincipalUri) | 
| 250:  |                 ->where('storage', $Storage) | 
| 251:  |                 ->where('path', $FullPath) | 
| 252:  |                 ->where('principaluri', 'principals/' . $Share['PublicId'])->first(); | 
| 253:  |             $oSharedFile->setExtendedProp('ParanoidKeyShared', $Share['ParanoidKeyShared']); | 
| 254:  |             $oSharedFile->save(); | 
| 255:  |         } | 
| 256:  |     } | 
| 257:  |  | 
| 258:  |      | 
| 259:  |  | 
| 260:  |  | 
| 261:  |  | 
| 262:  |     public function onFileItemToResponseArray(&$aArgs) | 
| 263:  |     { | 
| 264:  |         if (isset($aArgs[0]) && $aArgs[0] instanceof \Aurora\Modules\Files\Classes\FileItem) { | 
| 265:  |             if ($this->startsWith($aArgs[0]->Path, '/.encrypted')) { | 
| 266:  |                 $aArgs[0]->Path = str_replace('/.encrypted', '', $aArgs[0]->Path); | 
| 267:  |                 $aArgs[0]->FullPath = str_replace('/.encrypted', '', $aArgs[0]->FullPath); | 
| 268:  |                 $aArgs[0]->TypeStr = self::$sStorageType; | 
| 269:  |             } | 
| 270:  |         } | 
| 271:  |     } | 
| 272:  |  | 
| 273:  |    public function onAfterSaveFilesAsTempFiles(&$aArgs, &$mResult) | 
| 274:  |    { | 
| 275:  |        $aResult = []; | 
| 276:  |        foreach ($mResult as $oFileData) { | 
| 277:  |            foreach ($aArgs['Files'] as $oFileOrigData) { | 
| 278:  |                if ($oFileOrigData['Name'] === $oFileData['Name']) { | 
| 279:  |                    if (isset($oFileOrigData['IsEncrypted']) && $oFileOrigData['IsEncrypted']) { | 
| 280:  |                        $oFileData['Actions'] = []; | 
| 281:  |                        $oFileData['ThumbnailUrl'] = ''; | 
| 282:  |                    } | 
| 283:  |                } | 
| 284:  |            } | 
| 285:  |            $aResult[] = $oFileData; | 
| 286:  |        } | 
| 287:  |        $mResult = $aResult; | 
| 288:  |    } | 
| 289:  |  | 
| 290:  |      | 
| 291:  |  | 
| 292:  |  | 
| 293:  |  | 
| 294:  |    public function onAfterGetPublicFiles(&$aArgs, &$mResult) | 
| 295:  |    { | 
| 296:  |        if (is_array($mResult) && isset($mResult['Items']) && is_array($mResult['Items'])) {  | 
| 297:  |            $mResult['Items'] = array_filter( | 
| 298:  |                $mResult['Items'], | 
| 299:  |                function ($FileItem) { | 
| 300:  |                    return !isset($FileItem->ExtendedProps) | 
| 301:  |                        || !isset($FileItem->ExtendedProps['InitializationVector']); | 
| 302:  |                } | 
| 303:  |            ); | 
| 304:  |        } | 
| 305:  |    } | 
| 306:  |  | 
| 307:  |    public function onBeforeGetExtendedProps(&$aArgs, &$mResult) | 
| 308:  |    { | 
| 309:  |        if ($aArgs['Type'] === self::$sStorageType) { | 
| 310:  |            $aArgs['Type'] = self::$sPersonalStorageType; | 
| 311:  |            $aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']); | 
| 312:  |        } | 
| 313:  |    } | 
| 314:  |  | 
| 315:  |      | 
| 316:  |  | 
| 317:  |  | 
| 318:  |  | 
| 319:  |  | 
| 320:  |     public function GetSettings() | 
| 321:  |     { | 
| 322:  |         \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); | 
| 323:  |         $aSettings = null; | 
| 324:  |         $oUser = \Aurora\System\Api::getAuthenticatedUser(); | 
| 325:  |         if (!empty($oUser) && $oUser->isNormalOrTenant()) { | 
| 326:  |             $aSettings = [ | 
| 327:  |                 'EnableModule'			=> $oUser->{self::GetName().'::EnableModule'}, | 
| 328:  |                 'DontRemindMe'			=> $oUser->{self::GetName().'::DontRemindMe'}, | 
| 329:  |                 'EnableInPersonalStorage' => $oUser->{self::GetName().'::EnableInPersonalStorage'}, | 
| 330:  |                 'ChunkSizeMb'			=> $this->getConfig('ChunkSizeMb', 5), | 
| 331:  |                 'AllowMultiChunkUpload'	=> $this->getConfig('AllowMultiChunkUpload', true), | 
| 332:  |                 'AllowChangeSettings' 	=> $this->getConfig('AllowChangeSettings', true), | 
| 333:  |                 'EncryptionMode' 		=> 3  | 
| 334:  |             ]; | 
| 335:  |         } | 
| 336:  |  | 
| 337:  |         return $aSettings; | 
| 338:  |     } | 
| 339:  |  | 
| 340:  |      | 
| 341:  |  | 
| 342:  |  | 
| 343:  |  | 
| 344:  |  | 
| 345:  |  | 
| 346:  |  | 
| 347:  |     public function UpdateSettings($EnableModule, $EnableInPersonalStorage) | 
| 348:  |     { | 
| 349:  |         \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); | 
| 350:  |  | 
| 351:  |         $iUserId = \Aurora\System\Api::getAuthenticatedUserId(); | 
| 352:  |         if (0 < $iUserId) { | 
| 353:  |             $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId); | 
| 354:  |             $oUser->setExtendedProp(self::GetName().'::EnableModule', $EnableModule); | 
| 355:  |             $oUser->setExtendedProp(self::GetName().'::EnableInPersonalStorage', $EnableInPersonalStorage); | 
| 356:  |             \Aurora\Modules\Core\Module::Decorator()->UpdateUserObject($oUser); | 
| 357:  |         } | 
| 358:  |         return true; | 
| 359:  |     } | 
| 360:  |  | 
| 361:  |      | 
| 362:  |  | 
| 363:  |  | 
| 364:  |  | 
| 365:  |  | 
| 366:  |     public function DontRemindMe() | 
| 367:  |     { | 
| 368:  |         \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); | 
| 369:  |  | 
| 370:  |         $bResult = false; | 
| 371:  |         $iUserId = \Aurora\System\Api::getAuthenticatedUserId(); | 
| 372:  |         if (0 < $iUserId) { | 
| 373:  |             $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId); | 
| 374:  |             $oUser->setExtendedProp(self::GetName().'::DontRemindMe', true); | 
| 375:  |             $bResult = \Aurora\Modules\Core\Module::Decorator()->UpdateUserObject($oUser); | 
| 376:  |         } | 
| 377:  |  | 
| 378:  |         return $bResult; | 
| 379:  |     } | 
| 380:  | } | 
| 381:  |  |