/home/ivoiecob/email.hirewise-va.com/modules/CoreParanoidEncryptionWebclientPlugin/Module.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\Modules\CoreParanoidEncryptionWebclientPlugin;
use Aurora\Api;
use Aurora\Modules\Files\Classes\FileItem;
use Aurora\System\Exceptions\ApiException;
/**
* Paranoid Encryption module allows you to encrypt files in File module using client-based functionality only.
*
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2023, Afterlogic Corp.
*
* @property Settings $oModuleSettings
*
* @package Modules
*/
class Module extends \Aurora\System\Module\AbstractWebclientModule
{
public static $sStorageType = 'encrypted';
public static $iStorageOrder = 10;
public static $sPersonalStorageType = 'personal';
public static $sSharedStorageType = 'shared';
public static $sEncryptedFolder = '.encrypted';
protected $aRequireModules = ['PersonalFiles','S3Filestorage'];
public function init()
{
$this->subscribeEvent('Files::GetStorages::after', [$this, 'onAfterGetStorages'], 1);
$this->subscribeEvent('Files::FileItemtoResponseArray', [$this, 'onFileItemToResponseArray']);
$this->subscribeEvent('Files::GetFile', [$this, 'onGetFile'], 20);
$this->subscribeEvent('Files::CreateFile', [$this, 'onCreateFile']);
$this->subscribeEvent('Files::GetItems::before', [$this, 'onBeforeGetItems']);
$this->subscribeEvent('Files::GetItems', [$this, 'onGetItems'], 10001);
$this->subscribeEvent('Files::Copy::before', [$this, 'onBeforeCopyOrMove']);
$this->subscribeEvent('Files::Move::before', [$this, 'onBeforeCopyOrMove']);
$this->subscribeEvent('Files::Delete::before', [$this, 'onBeforeDelete']);
$this->subscribeEvent('Files::GetFileInfo::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::CreateFolder::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::Rename::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::GetQuota::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::CreateLink::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::GetFileContent::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::IsFileExists::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::CheckQuota::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::CreatePublicLink::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::DeletePublicLink::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('Files::GetPublicFiles::after', [$this, 'onAfterGetPublicFiles']);
$this->subscribeEvent('Files::SaveFilesAsTempFiles::after', [$this, 'onAfterSaveFilesAsTempFiles']);
$this->subscribeEvent('Files::UpdateExtendedProps::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('OpenPgpFilesWebclient::CreatePublicLink::before', [$this, 'onBeforeMethod']);
$this->subscribeEvent('SharedFiles::UpdateShare::before', [$this, 'onBeforeUpdateShare']);
$this->subscribeEvent('SharedFiles::CreateSharedFile', [$this, 'onCreateOrUpdateSharedFile']);
$this->subscribeEvent('SharedFiles::UpdateSharedFile', [$this, 'onCreateOrUpdateSharedFile']);
$this->subscribeEvent('Files::GetExtendedProps::before', [$this, 'onBeforeGetExtendedProps']);
}
/**
* @return Module
*/
public static function getInstance()
{
return parent::getInstance();
}
/**
* @return Module
*/
public static function Decorator()
{
return parent::Decorator();
}
/**
* @return Settings
*/
public function getModuleSettings()
{
return $this->oModuleSettings;
}
protected function getEncryptedPath($sPath)
{
return '/' . self::$sEncryptedFolder . \ltrim($sPath);
}
protected function startsWith($haystack, $needle)
{
return (substr($haystack, 0, strlen($needle)) === $needle);
}
public function onAfterGetStorages($aArgs, &$mResult)
{
$oUser = \Aurora\System\Api::getAuthenticatedUser();
if ($oUser->getExtendedProp($this->GetName() . '::EnableModule')) {
array_unshift($mResult, [
'Type' => static::$sStorageType,
'DisplayName' => $this->i18N('LABEL_STORAGE'),
'IsExternal' => false,
'Order' => static::$iStorageOrder,
'IsDroppable' => false
]);
}
}
public function onGetFile(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
}
$aExtendedProps = \Aurora\Modules\Files\Module::Decorator()->GetExtendedProps($aArgs['UserId'], $aArgs['Type'], $aArgs['Path'], $aArgs['Name']);
if (isset($aExtendedProps['InitializationVector'])) {
$aArgs['NoRedirect'] = true;
}
}
public function onCreateFile($aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
$this->GetModuleManager()->broadcastEvent(
'Files',
'CreateFile',
$aArgs,
$mResult
);
}
}
/**
* @ignore
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onBeforeGetItems(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
if (!\Aurora\Modules\Files\Module::Decorator()->IsFileExists($aArgs['UserId'], $aArgs['Type'], '', self::$sEncryptedFolder)) {
\Aurora\Modules\Files\Module::Decorator()->CreateFolder($aArgs['UserId'], $aArgs['Type'], '', self::$sEncryptedFolder);
}
}
}
/**
* @ignore
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onGetItems(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sPersonalStorageType && $aArgs['Path'] === '' && is_array($mResult)) {
foreach ($mResult as $iKey => $oFileItem) {
if ($oFileItem instanceof FileItem && $oFileItem->IsFolder && $oFileItem->Name === self::$sEncryptedFolder) {
unset($mResult[$iKey]);
}
if ($oFileItem->Shared) {
// \Aurora\Modules\SharedFiles\Models\SharedFile::where();
}
}
}
//Encrypted files excluded from shared folders
if (
$this->oHttp->GetHeader('x-client') !== 'WebClient'
&& $aArgs['Type'] === self::$sPersonalStorageType
&& substr($aArgs['Path'], 1, 11) === self::$sEncryptedFolder
&& is_array($mResult)
) {
foreach ($mResult as $iKey => $oFileItem) {
if (isset($oFileItem->ExtendedProps) && isset($oFileItem->ExtendedProps['ParanoidKey']) && empty($oFileItem->ExtendedProps['ParanoidKey'])) {
unset($mResult[$iKey]);
}
}
}
}
/**
* @ignore
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onBeforeCopyOrMove(&$aArgs, &$mResult)
{
if ($aArgs['FromType'] === self::$sStorageType || $aArgs['ToType'] === self::$sStorageType) {
if ($aArgs['FromType'] === self::$sStorageType) {
$aArgs['FromType'] = self::$sPersonalStorageType;
$aArgs['FromPath'] = $this->getEncryptedPath($aArgs['FromPath']);
}
if ($aArgs['ToType'] === self::$sStorageType) {
$aArgs['ToType'] = self::$sPersonalStorageType;
$aArgs['ToPath'] = $this->getEncryptedPath($aArgs['ToPath']);
}
foreach ($aArgs['Files'] as $iKey => $aItem) {
if ($aItem['FromType'] === self::$sStorageType) {
$aArgs['Files'][$iKey]['FromType'] = self::$sPersonalStorageType;
$aArgs['Files'][$iKey]['FromPath'] = $this->getEncryptedPath($aItem['FromPath']);
}
}
}
}
/**
* @ignore
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onBeforeDelete(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
foreach ($aArgs['Items'] as $iKey => $aItem) {
$aArgs['Items'][$iKey]['Path'] = $this->getEncryptedPath($aItem['Path']);
}
}
}
/**
* @ignore
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onBeforeMethod(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
if (isset($aArgs['Path'])) {
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
}
}
}
public function onBeforeUpdateShare(&$aArgs, &$mResult)
{
if ($aArgs['Storage'] === self::$sStorageType) {
if ($aArgs['IsDir']) {
$iErrorCode = 0;
if (class_exists('\Aurora\Modules\SharedFiles\Enums\ErrorCodes')) {
$iErrorCode = \Aurora\Modules\SharedFiles\Enums\ErrorCodes::NotPossibleToShareDirectoryInEcryptedStorage;
}
throw new ApiException($iErrorCode);
}
$aArgs['Storage'] = self::$sPersonalStorageType;
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
}
}
public function onCreateOrUpdateSharedFile(&$aArgs, &$mResult)
{
if (!empty($aArgs['Share']['ParanoidKeyShared']) && class_exists('\Aurora\Modules\SharedFiles\Models\SharedFile')) {
$oSharedFile = \Aurora\Modules\SharedFiles\Models\SharedFile::where('owner', $aArgs['UserPrincipalUri'])
->where('storage', $aArgs['Storage'])
->where('path', $aArgs['FullPath'])
->where('principaluri', 'principals/' . $aArgs['Share']['PublicId'])->first();
$oSharedFile->setExtendedProp('ParanoidKeyShared', $aArgs['Share']['ParanoidKeyShared']);
$oSharedFile->save();
}
}
/**
* @param array $aArgs
* @return void
*/
public function onFileItemToResponseArray(&$aArgs)
{
if (isset($aArgs[0]) && $aArgs[0] instanceof \Aurora\Modules\Files\Classes\FileItem) {
if ($this->startsWith($aArgs[0]->Path, '/.encrypted')) {
$aArgs[0]->Path = str_replace('/.encrypted', '', $aArgs[0]->Path);
$aArgs[0]->FullPath = str_replace('/.encrypted', '', $aArgs[0]->FullPath);
$aArgs[0]->TypeStr = self::$sStorageType;
}
}
}
public function onAfterSaveFilesAsTempFiles(&$aArgs, &$mResult)
{
$aResult = [];
foreach ($mResult as $oFileData) {
foreach ($aArgs['Files'] as $oFileOrigData) {
if ($oFileOrigData['Name'] === $oFileData['Name']) {
if (isset($oFileOrigData['IsEncrypted']) && $oFileOrigData['IsEncrypted']) {
$oFileData['Actions'] = [];
$oFileData['ThumbnailUrl'] = '';
}
}
}
$aResult[] = $oFileData;
}
$mResult = $aResult;
}
/**
* @param array $aArgs Arguments of event.
* @param mixed $mResult Is passed by reference.
*/
public function onAfterGetPublicFiles(&$aArgs, &$mResult)
{
if (is_array($mResult) && isset($mResult['Items']) && is_array($mResult['Items'])) { //remove from result all encrypted files
$mResult['Items'] = array_filter(
$mResult['Items'],
function ($FileItem) {
return !isset($FileItem->ExtendedProps)
|| !isset($FileItem->ExtendedProps['InitializationVector']);
}
);
}
}
public function onBeforeGetExtendedProps(&$aArgs, &$mResult)
{
if ($aArgs['Type'] === self::$sStorageType) {
$aArgs['Type'] = self::$sPersonalStorageType;
$aArgs['Path'] = $this->getEncryptedPath($aArgs['Path']);
}
}
/**
* Obtains list of module settings for authenticated user.
*
* @return array
*/
public function GetSettings()
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
$aSettings = null;
$oUser = \Aurora\System\Api::getAuthenticatedUser();
if ($oUser && $oUser->isNormalOrTenant()) {
$aSettings = [
'EnableModule' => $oUser->getExtendedProp(self::GetName() . '::EnableModule'),
'DontRemindMe' => $oUser->getExtendedProp(self::GetName() . '::DontRemindMe'),
'EnableInPersonalStorage' => $oUser->getExtendedProp(self::GetName() . '::EnableInPersonalStorage'),
'ChunkSizeMb' => $this->oModuleSettings->ChunkSizeMb,
'AllowMultiChunkUpload' => $this->oModuleSettings->AllowMultiChunkUpload,
'AllowChangeSettings' => $this->oModuleSettings->AllowChangeSettings,
'EncryptionMode' => 3, //temporary brought back this setting for compatibility with current versions of mobile apps
'AllowBackwardCompatibility' => $this->oModuleSettings->AllowBackwardCompatibility
];
}
return $aSettings;
}
/**
* Updates settings of the Paranoid Encryption Module.
*
* @param boolean $EnableModule indicates if user turned on Paranoid Encryption Module.
* @param boolean $EnableInPersonalStorage
* @return boolean
*/
public function UpdateSettings($EnableModule, $EnableInPersonalStorage)
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$iUserId = \Aurora\System\Api::getAuthenticatedUserId();
if (0 < $iUserId) {
$oUser = \Aurora\Api::getUserById($iUserId);
$oUser->setExtendedProp(self::GetName() . '::EnableModule', $EnableModule);
$oUser->setExtendedProp(self::GetName() . '::EnableInPersonalStorage', $EnableInPersonalStorage);
\Aurora\Modules\Core\Module::Decorator()->UpdateUserObject($oUser);
}
return true;
}
/**
* Updates DontRemindMe setting of the Paranoid Encryption Module.
*
* @return boolean
*/
public function DontRemindMe()
{
\Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$bResult = false;
$iUserId = \Aurora\System\Api::getAuthenticatedUserId();
if (0 < $iUserId) {
$oUser = \Aurora\Api::getUserById($iUserId);
$oUser->setExtendedProp(self::GetName() . '::DontRemindMe', true);
$bResult = \Aurora\Modules\Core\Module::Decorator()->UpdateUserObject($oUser);
}
return $bResult;
}
}