diff options
author | Matthias <ilovemilk@wusa.io> | 2020-04-11 12:57:03 +0300 |
---|---|---|
committer | Matthias <ilovemilk@wusa.io> | 2020-04-11 12:57:03 +0300 |
commit | 5e9cebdcad048e166f9392e3aefeea1529669bc0 (patch) | |
tree | 4ff37e522cba5762369b320f0919c115393f29a8 | |
parent | 133a820b88a78a4281e017d88d11bed169b5fdbf (diff) |
fix frontend and controller to work without services
-rw-r--r-- | appinfo/info.xml | 2 | ||||
-rw-r--r-- | appinfo/routes.php | 28 | ||||
-rw-r--r-- | lib/AppInfo/Application.php | 26 | ||||
-rw-r--r-- | lib/Controller/DetectionController.php | 80 | ||||
-rw-r--r-- | lib/Controller/FileOperationController.php | 263 | ||||
-rw-r--r-- | lib/Controller/MonitoringController.php | 324 | ||||
-rw-r--r-- | lib/Controller/PageController.php (renamed from lib/Controller/RecoverController.php) | 22 | ||||
-rw-r--r-- | lib/Controller/ScanController.php | 447 | ||||
-rw-r--r-- | lib/Controller/SettingsController.php (renamed from lib/Controller/BasicController.php) | 43 | ||||
-rw-r--r-- | lib/Model/Detection.php | 56 | ||||
-rw-r--r-- | lib/Model/DetectionDeserializer.php | 47 | ||||
-rw-r--r-- | lib/Model/Settings.php | 59 | ||||
-rw-r--r-- | lib/Service/DetectionService.php | 68 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/components/FileOperationsTable.vue | 1 | ||||
-rw-r--r-- | src/components/ProtectionStatus.vue | 25 | ||||
-rw-r--r-- | src/components/ServiceStatus.vue | 92 | ||||
-rw-r--r-- | src/views/Protection.vue | 33 |
18 files changed, 642 insertions, 975 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index 5e47879..5095179 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -37,7 +37,7 @@ <navigations> <navigation> <name>Ransomware recovery</name> - <route>ransomware_detection.recover.index</route> + <route>ransomware_detection.page.index</route> </navigation> </navigations> </info> diff --git a/appinfo/routes.php b/appinfo/routes.php index 8aa2fce..e103b07 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,22 +10,16 @@ */ return [ 'routes' => [ - ['name' => 'recover#index', 'url' => '/', 'verb' => 'GET'], - ['name' => 'recover#scan', 'url' => '/scan', 'verb' => 'GET'], - ], - 'ocs' => [ - // Basic controller - ['name' => 'basic#changeColorMode', 'url' => '/api/{apiVersion}/change-color-mode/{colorMode}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'basic#getColorMode', 'url' => '/api/{apiVersion}/get-color-mode', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'basic#getDebugMode', 'url' => '/api/{apiVersion}/get-debug-mode', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - // Monitoring controller - ['name' => 'monitoring#listFileOperations', 'url' => '/api/{apiVersion}/list', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'monitoring#export', 'url' => '/api/{apiVersion}/export', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'monitoring#deleteSequence', 'url' => '/api/{apiVersion}/delete-sequence/{sequence}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'monitoring#recover', 'url' => '/api/{apiVersion}/recover', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v1']], - // Scan controller - ['name' => 'scan#recover', 'url' => '/api/{apiVersion}/scan-recover', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'scan#filesToScan', 'url' => '/api/{apiVersion}/files-to-scan', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'scan#scanSequence', 'url' => '/api/{apiVersion}/scan-sequence', 'verb' => 'POST', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], + // File operation controller + ['name' => 'fileOperation#findAll', 'url' => '/api/{apiVersion}/file-operation', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'fileOperation#find', 'url' => '/api/{apiVersion}/file-operation/{id}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'fileOperation#recover', 'url' => '/api/{apiVersion}/file-operation/{id}/recover', 'verb' => 'PUT', 'requirements' => ['apiVersion' => 'v1']], + // Settings controller + ['name' => 'settings#update', 'url' => '/api/{apiVersion}/settings', 'verb' => 'PUT', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'settings#findAll', 'url' => '/api/{apiVersion}/settings', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], + // Detection controller + ['name' => 'detection#findAll', 'url' => '/api/{apiVersion}/detection', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'detection#find', 'url' => '/api/{apiVersion}/detection/{id}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], ], ]; diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 9172b33..f90b0f5 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -34,10 +34,12 @@ use OCA\RansomwareDetection\Analyzer\EntropyFunnellingAnalyzer; use OCA\RansomwareDetection\Analyzer\FileCorruptionAnalyzer; use OCA\RansomwareDetection\Analyzer\FileExtensionAnalyzer; use OCA\RansomwareDetection\Entropy\Entropy; +use OCA\RansomwareDetection\Controller\DetectionController; use OCA\RansomwareDetection\Notification\Notifier; use OCA\RansomwareDetection\StorageWrapper; use OCA\RansomwareDetection\Connector\Sabre\RequestPlugin; use OCA\RansomwareDetection\Service\FileOperationService; +use OCA\RansomwareDetection\Service\DetectionService; use OCA\RansomwareDetection\Mapper\FileOperationMapper; use OCP\AppFramework\App; use OCP\App\IAppManager; @@ -70,6 +72,12 @@ class Application extends App ); }); + $container->registerService('DetectionDeserializer', function ($c) { + return new DetectionDeserializer( + $c->query('FileOperationMapper') + ); + }); + // services $container->registerService('FileOperationService', function ($c) { return new FileOperationService( @@ -78,6 +86,15 @@ class Application extends App ); }); + $container->registerService('DetectionService', function ($c) { + return new DetectionService( + $c->query('FileOperationService'), + $c->query('DetectionDeserializer'), + $c->query('OCP\IConfig'), + $c->query('ServerContainer')->getUserSession()->getUser()->getUID() + ); + }); + // classifier $container->registerService('Classifier', function ($c) { return new Classifier( @@ -87,6 +104,15 @@ class Application extends App ); }); + // controller + $container->registerService('DetectionController', function ($c) { + return new DetectionController( + $c->query('AppName'), + $c->query('Request'), + $c->query('DetectionService') + ); + }); + // entropy $container->registerService('Entropy', function ($c) { return new Entropy( diff --git a/lib/Controller/DetectionController.php b/lib/Controller/DetectionController.php new file mode 100644 index 0000000..dc0a527 --- /dev/null +++ b/lib/Controller/DetectionController.php @@ -0,0 +1,80 @@ +<?php + +/** + * @copyright Copyright (c) 2018 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Controller; + +use OCA\RansomwareDetection\AppInfo\Application; +use OCA\RansomwareDetection\Service\DetectionService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Controller; +use OCP\IConfig; +use OCP\IUserSession; +use OCP\IRequest; + +class DetectionController extends Controller +{ + /** @var DetectionService */ + protected $detectionService; + + /** + * @param string $appName + * @param IRequest $request + * @param DetectionService $detectionService + */ + public function __construct( + $appName, + IRequest $request, + DetectionService $detectionService + ) { + parent::__construct($appName, $request); + + $this->detectionService = $detectionService; + } + + /** + * List detections. + * + * @NoCSRFRequired + * @NoAdminRequired + * + * @return JSONResponse + */ + public function findAll() { + $detections = $this->detectionService->getDetections(); + + return new JSONResponse($detections, Http::STATUS_OK); + } + + /** + * Find detection with $id. + * + * @NoCSRFRequired + * @NoAdminRequired + * + * @return JSONResponse + */ + public function find($id) { + $detection = $this->detectionService->getDetection($id); + + return new JSONResponse($detection, Http::STATUS_OK); + } +}
\ No newline at end of file diff --git a/lib/Controller/FileOperationController.php b/lib/Controller/FileOperationController.php new file mode 100644 index 0000000..d38ff29 --- /dev/null +++ b/lib/Controller/FileOperationController.php @@ -0,0 +1,263 @@ +<?php + +/** + * @copyright Copyright (c) 2018 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Controller; + +use OCA\RansomwareDetection\Monitor; +use OCA\RansomwareDetection\AppInfo\Application; +use OCA\RansomwareDetection\Db\FileOperation; +use OCA\RansomwareDetection\Service\FileOperationService; +use OCA\Files_Trashbin\Trashbin; +use OCA\Files_Trashbin\Helper; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Controller; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\IConfig; +use OCP\IUserSession; +use OCP\IRequest; +use OCP\ILogger; + +class FileOperationController extends Controller +{ + /** @var IConfig */ + protected $config; + + /** @var IUserSession */ + protected $userSession; + + /** @var ILogger */ + protected $logger; + + /** @var Folder */ + protected $userFolder; + + /** @var FileOperationService */ + protected $service; + + /** @var string */ + protected $userId; + + /** + * @param string $appName + * @param IRequest $request + * @param IUserSession $userSession + * @param IConfig $config + * @param ILogger $logger + * @param Folder $userFolder + * @param FileOperationService $service + * @param string $userId + */ + public function __construct( + $appName, + IRequest $request, + IUserSession $userSession, + IConfig $config, + ILogger $logger, + Folder $userFolder, + FileOperationService $service, + $userId + ) { + parent::__construct($appName, $request); + + $this->config = $config; + $this->userSession = $userSession; + $this->userFolder = $userFolder; + $this->logger = $logger; + $this->service = $service; + $this->userId = $userId; + } + + /** + * Lists the files. + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function findAll() + { + $files = $this->service->findAll(); + + return new JSONResponse($files, Http::STATUS_OK); + } + + /** + * Find file with id. + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @return JSONResponse + */ + public function find($id) + { + $file = $this->service->find($id); + + return new JSONResponse($file, Http::STATUS_OK); + } + + /** + * Recover files from trashbin or remove them from normal storage. + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $id file operation id + * + * @return JSONResponse + */ + public function recover($id) + { + try { + $file = $this->service->find($id); + switch ($file->getCommand()) { + case Monitor::WRITE: + // Recover new created files by deleting them + $filePath = $file->getPath().'/'.$file->getOriginalName(); + if ($this->deleteFromStorage($filePath)) { + $this->service->deleteById($id); + + return new JSONResponse(null, Http::STATUS_NO_CONTENT); + } else { + // File cannot be deleted + return new JSONResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); + } + break; + case Monitor::DELETE: + // Recover deleted files by restoring them from the trashbin + // It's not necessary to use the real path + $dir = '/'; + $candidate = $this->findCandidateToRestore($dir, $file->getOriginalName()); + if ($candidate !== null) { + $path = $dir.'/'.$candidate['name'].'.d'.$candidate['mtime']; + if (Trashbin::restore($path, $candidate['name'], $candidate['mtime']) !== false) { + $this->service->deleteById($id); + + return new JSONResponse(null, Http::STATUS_NO_CONTENT); + } + // File does not exist + return new JSONResponse(null, Http::STATUS_BAD_REQUEST); + } else { + // No candidate found + return new JSONResponse(null, Http::STATUS_BAD_REQUEST); + } + break; + case Monitor::RENAME: + $this->service->deleteById($id); + + return new JSONResponse(null, Http::STATUS_NO_CONTENT); + break; + case Monitor::CREATE: + // Recover new created folders + $filePath = $file->getPath().'/'.$file->getOriginalName(); + if ($this->deleteFromStorage($filePath)) { + $this->service->deleteById($id); + + return new JSONResponse(null, Http::STATUS_NO_CONTENT); + } else { + // File cannot be deleted + return new JSONResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); + } + break; + default: + // All other commands need no recovery + $this->service->deleteById($id); + + return new JSONResponse(null, Http::STATUS_NO_CONTENT); + break; + } + } catch (\OCP\AppFramework\Db\MultipleObjectsReturnedException $exception) { + // Found more than one with the same file name + $this->logger->debug('recover: Found more than one with the same file name.', array('app' => Application::APP_ID)); + + return new JSONResponse(null, Http::STATUS_BAD_REQUEST); + } catch (\OCP\AppFramework\Db\DoesNotExistException $exception) { + // Nothing found + $this->logger->debug('recover: Files does not exist.', array('app' => Application::APP_ID)); + + return new JSONResponse(null, Http::STATUS_BAD_REQUEST); + } + } + + /** + * Deletes a file from the storage. + * + * @param string $path + * + * @return bool + */ + private function deleteFromStorage($path) + { + try { + $node = $this->userFolder->get($path); + if ($node->isDeletable()) { + $node->delete(); + } else { + return false; + } + + return true; + } catch (\OCP\Files\NotFoundException $exception) { + // Nothing found + $this->logger->debug('deleteFromStorage: Not found exception.', array('app' => Application::APP_ID)); + + return true; + } + } + + /** + * Finds a candidate to restore if a file with the specific does not exist. + * + * @param string $dir + * @param string $fileName + * + * @return FileInfo + */ + private function findCandidateToRestore($dir, $fileName) + { + $files = array(); + $trashBinFiles = $this->getTrashFiles($dir); + + foreach ($trashBinFiles as $trashBinFile) { + if (strcmp($trashBinFile['name'], $fileName) === 0) { + $files[] = $trashBinFile; + } + } + + return array_pop($files); + } + + /** + * Workaround for testing. + * + * @param string $dir + * + * @return array + */ + private function getTrashFiles($dir) + { + return Helper::getTrashFiles($dir, $this->userId, 'mtime', false); + } + +}
\ No newline at end of file diff --git a/lib/Controller/MonitoringController.php b/lib/Controller/MonitoringController.php deleted file mode 100644 index b309361..0000000 --- a/lib/Controller/MonitoringController.php +++ /dev/null @@ -1,324 +0,0 @@ -<?php - -/** - * @copyright Copyright (c) 2018 Matthias Held <matthias.held@uni-konstanz.de> - * @author Matthias Held <matthias.held@uni-konstanz.de> - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -namespace OCA\RansomwareDetection\Controller; - -use OCA\RansomwareDetection\Monitor; -use OCA\RansomwareDetection\Classifier; -use OCA\RansomwareDetection\Analyzer\SequenceAnalyzer; -use OCA\RansomwareDetection\AppInfo\Application; -use OCA\RansomwareDetection\Db\FileOperation; -use OCA\RansomwareDetection\Service\FileOperationService; -use OCA\Files_Trashbin\Trashbin; -use OCA\Files_Trashbin\Helper; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\OCSController; -use OCP\Files\File; -use OCP\Files\Folder; -use OCP\IConfig; -use OCP\IUserSession; -use OCP\IRequest; -use OCP\ILogger; - -class MonitoringController extends OCSController -{ - /** @var IConfig */ - protected $config; - - /** @var IUserSession */ - protected $userSession; - - /** @var Classifier */ - protected $classifier; - - /** @var ILogger */ - protected $logger; - - /** @var Folder */ - protected $userFolder; - - /** @var FileOperationService */ - protected $service; - - /** @var SequenceAnalyzer */ - protected $sequenceAnalyzer; - - /** @var string */ - protected $userId; - - /** - * @param string $appName - * @param IRequest $request - * @param IUserSession $userSession - * @param IConfig $config - * @param Classifier $classifier - * @param ILogger $logger - * @param Folder $userFolder - * @param FileOperationService $service - * @param SequenceAnalyzer $sequenceAnalyzer - * @param string $userId - */ - public function __construct( - $appName, - IRequest $request, - IUserSession $userSession, - IConfig $config, - Classifier $classifier, - ILogger $logger, - Folder $userFolder, - FileOperationService $service, - SequenceAnalyzer $sequenceAnalyzer, - $userId - ) { - parent::__construct($appName, $request); - - $this->config = $config; - $this->userSession = $userSession; - $this->classifier = $classifier; - $this->userFolder = $userFolder; - $this->logger = $logger; - $this->service = $service; - $this->sequenceAnalyzer = $sequenceAnalyzer; - $this->userId = $userId; - } - - /** - * Lists the classified files and sequences. - * - * @NoAdminRequired - * - * @return JSONResponse - */ - public function listFileOperations() - { - $files = $this->service->findAll(); - - $sequences = []; - - // Classify files and put together the sequences. - foreach ($files as $file) { - $this->classifier->classifyFile($file); - $sequences[$file->getSequence()][] = $file; - } - - $result = []; - - foreach ($sequences as $sequenceId => $sequence) { - if (sizeof($sequence) >= $this->config->getAppValue(Application::APP_ID, 'minimum_sequence_length', 0)) { - usort($sequence, function ($a, $b) { - return $b->getId() - $a->getId(); - }); - $sequenceResult = $this->sequenceAnalyzer->analyze($sequenceId, $sequence); - $sequenceInformation = ['id' => $sequenceId, 'suspicionScore' => $sequenceResult->getSuspicionScore(), 'sequence' => $sequence]; - $result[] = $sequenceInformation; - } - } - - usort($result, function ($a, $b) { - return $b['id'] - $a['id']; - }); - - return new JSONResponse($result, Http::STATUS_ACCEPTED); - } - - /** - * Exports classification and analysis data. - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @param int $sequence - * - * @return JSONResponse - */ - public function export() - { - $files = $this->service->findAll(); - - $sequences = []; - - // Classify files and put together the sequences. - foreach ($files as $file) { - $this->classifier->classifyFile($file); - $sequences[$file->getSequence()][] = $file; - } - - $result = []; - - foreach ($sequences as $sequenceId => $sequence) { - if (sizeof($sequence) >= $this->config->getAppValue(Application::APP_ID, 'minimum_sequence_length', 0)) { - $result[] = $this->sequenceAnalyzer->analyze($sequenceId, $sequence); - } - } - - return new JSONResponse($result, Http::STATUS_ACCEPTED); - } - - /** - * Deletes a sequence from the database. - * - * @NoAdminRequired - * - * @param int $sequence - * - * @return JSONResponse - */ - public function deleteSequence($sequence) - { - $files = $this->service->deleteSequenceById($sequence); - - return new JSONResponse(['status' => 'success'], Http::STATUS_ACCEPTED); - } - - /** - * Recover files from trashbin or remove them from normal storage. - * - * @NoAdminRequired - * - * @param int $id file operation id - * - * @return JSONResponse - */ - public function recover($id) - { - try { - $file = $this->service->find($id); - if ($file->getCommand() === Monitor::WRITE) { - // Recover new created files by deleting them - $filePath = $file->getPath().'/'.$file->getOriginalName(); - if ($this->deleteFromStorage($filePath)) { - $this->service->deleteById($id); - - return new JSONResponse(['status' => 'success', 'id' => $id], Http::STATUS_OK); - } else { - return new JSONResponse(['status' => 'error', 'message' => 'File cannot be deleted.'], Http::STATUS_BAD_REQUEST); - } - } elseif ($file->getCommand() === Monitor::DELETE) { - // Recover deleted files by restoring them from the trashbin - // It's not necessary to use the real path - $dir = '/'; - $candidate = $this->findCandidateToRestore($dir, $file->getOriginalName()); - if ($candidate !== null) { - $path = $dir.'/'.$candidate['name'].'.d'.$candidate['mtime']; - if (Trashbin::restore($path, $candidate['name'], $candidate['mtime']) !== false) { - $this->service->deleteById($id); - - return new JSONResponse(['status' => 'success', 'id' => $id], Http::STATUS_OK); - } - - return new JSONResponse(['status' => 'error', 'message' => 'File does not exist.', 'path' => $path, 'name' => $candidate['name'], 'mtime' => $candidate['mtime']], Http::STATUS_BAD_REQUEST); - } else { - return new JSONResponse(['status' => 'error', 'message' => 'No candidate found.'], Http::STATUS_BAD_REQUEST); - } - } elseif ($file->getCommand() === Monitor::RENAME) { - $this->service->deleteById($id); - - return new JSONResponse(['status' => 'success', 'id' => $id], Http::STATUS_OK); - } elseif ($file->getCommand() === Monitor::CREATE) { - // Recover new created folders - $filePath = $file->getPath().'/'.$file->getOriginalName(); - if ($this->deleteFromStorage($filePath)) { - $this->service->deleteById($id); - - return new JSONResponse(['status' => 'success', 'id' => $id], Http::STATUS_OK); - } else { - return new JSONResponse(['status' => 'error', 'message' => 'File cannot be deleted.'], Http::STATUS_BAD_REQUEST); - } - } else { - // All other commands need no recovery - $this->service->deleteById($id); - - return new JSONResponse(['id' => $id], Http::STATUS_OK); - } - } catch (\OCP\AppFramework\Db\MultipleObjectsReturnedException $exception) { - // Found more than one with the same file name - $this->logger->debug('recover: Found more than one with the same file name.', array('app' => Application::APP_ID)); - - return new JSONResponse(['status' => 'error', 'message' => 'Found more than one with the same file name.'], Http::STATUS_BAD_REQUEST); - } catch (\OCP\AppFramework\Db\DoesNotExistException $exception) { - // Nothing found - $this->logger->debug('recover: Files does not exist.', array('app' => Application::APP_ID)); - - return new JSONResponse(['status' => 'error', 'message' => 'Files does not exist.'], Http::STATUS_BAD_REQUEST); - } - } - - /** - * Deletes a file from the storage. - * - * @param string $path - * - * @return bool - */ - private function deleteFromStorage($path) - { - try { - $node = $this->userFolder->get($path); - if ($node->isDeletable()) { - $node->delete(); - } else { - return false; - } - - return true; - } catch (\OCP\Files\NotFoundException $exception) { - // Nothing found - $this->logger->debug('deleteFromStorage: Not found exception.', array('app' => Application::APP_ID)); - - return true; - } - } - - /** - * Finds a candidate to restore if a file with the specific does not exist. - * - * @param string $dir - * @param string $fileName - * - * @return FileInfo - */ - private function findCandidateToRestore($dir, $fileName) - { - $files = array(); - $trashBinFiles = $this->getTrashFiles($dir); - - foreach ($trashBinFiles as $trashBinFile) { - if (strcmp($trashBinFile['name'], $fileName) === 0) { - $files[] = $trashBinFile; - } - } - - return array_pop($files); - } - - /** - * Workaround for testing. - * - * @param string $dir - * - * @return array - */ - private function getTrashFiles($dir) - { - return Helper::getTrashFiles($dir, $this->userId, 'mtime', false); - } -} diff --git a/lib/Controller/RecoverController.php b/lib/Controller/PageController.php index 52497df..6c2fdc2 100644 --- a/lib/Controller/RecoverController.php +++ b/lib/Controller/PageController.php @@ -22,26 +22,33 @@ namespace OCA\RansomwareDetection\Controller; use OCA\RansomwareDetection\AppInfo\Application; +use OCA\RansomwareDetection\Service\FileOperationService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\IRequest; -class RecoverController extends Controller +class PageController extends Controller { + /** @var FileOperationService */ + protected $service; + /** @var int */ private $userId; /** * @param string $appName * @param IRequest $request + * @param FileOperationService $service * @param string $userId */ public function __construct( $appName, IRequest $request, + FileOperationService $service, $userId ) { parent::__construct($appName, $request); + $this->service = $service; $this->userId = $userId; } @@ -57,17 +64,4 @@ class RecoverController extends Controller { return new TemplateResponse(Application::APP_ID, 'index'); } - - /** - * Scan page. - * - * @NoAdminRequired - * @NoCSRFRequired - * - * @return TemplateResponse - */ - public function scan() - { - return new TemplateResponse(Application::APP_ID, 'scan'); - } } diff --git a/lib/Controller/ScanController.php b/lib/Controller/ScanController.php deleted file mode 100644 index a7eca58..0000000 --- a/lib/Controller/ScanController.php +++ /dev/null @@ -1,447 +0,0 @@ -<?php - -/** - * @copyright Copyright (c) 2018 Matthias Held <matthias.held@uni-konstanz.de> - * @author Matthias Held <matthias.held@uni-konstanz.de> - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -namespace OCA\RansomwareDetection\Controller; - -use OCA\RansomwareDetection\Monitor; -use OCA\RansomwareDetection\Classifier; -use OCA\RansomwareDetection\Analyzer\SequenceAnalyzer; -use OCA\RansomwareDetection\Analyzer\EntropyAnalyzer; -use OCA\RansomwareDetection\Analyzer\FileCorruptionAnalyzer; -use OCA\RansomwareDetection\Analyzer\FileExtensionAnalyzer; -use OCA\RansomwareDetection\Analyzer\FileExtensionResult; -use OCA\RansomwareDetection\AppInfo\Application; -use OCA\RansomwareDetection\Db\FileOperation; -use OCA\RansomwareDetection\Exception\NotAFileException; -use OCA\RansomwareDetection\Service\FileOperationService; -use OCA\RansomwareDetection\Scanner\StorageStructure; -use OCP\Files\NotFoundException; -use OCA\Files_Trashbin\Trashbin; -use OCA\Files_Trashbin\Helper; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\OCSController; -use OCP\Files\File; -use OCP\Files\Folder; -use OCP\IConfig; -use OCP\IUserSession; -use OCP\IRequest; -use OCP\IDBConnection; -use OCP\ILogger; - -class ScanController extends OCSController -{ - /** @var IConfig */ - protected $config; - - /** @var IUserSession */ - protected $userSession; - - /** @var Classifier */ - protected $classifier; - - /** @var ILogger */ - protected $logger; - - /** @var Folder */ - protected $userFolder; - - /** @var FileOperationService */ - protected $service; - - /** @var SequenceAnalyzer */ - protected $sequenceAnalyzer; - - /** @var EntropyAnalyzer */ - protected $entropyAnalyzer; - - /** @var FileCorruptionAnalyzer */ - protected $fileCorruptionAnalyzer; - - /** @var FileExtensionAnalyzer */ - protected $fileExtensionAnalyzer; - - /** @var IDBConnection */ - protected $connection; - - /** @var string */ - protected $userId; - - /** - * @param string $appName - * @param IRequest $request - * @param IUserSession $userSession - * @param IConfig $config - * @param Classifier $classifier - * @param ILogger $logger - * @param Folder $userFolder - * @param FileOperationService $service - * @param SequenceAnalyzer $sequenceAnalyzer - * @param EntropyAnalyzer $entropyAnalyzer - * @param FileCorruptionAnalyzer $fileCorruptionAnalyzer - * @param FileExtensionAnalyzer $fileExtensionAnalyzer - * @param IDBConnection $connection - * @param string $userId - */ - public function __construct( - $appName, - IRequest $request, - IUserSession $userSession, - IConfig $config, - Classifier $classifier, - ILogger $logger, - Folder $userFolder, - FileOperationService $service, - SequenceAnalyzer $sequenceAnalyzer, - EntropyAnalyzer $entropyAnalyzer, - FileCorruptionAnalyzer $fileCorruptionAnalyzer, - FileExtensionAnalyzer $fileExtensionAnalyzer, - IDBConnection $connection, - $userId - ) { - parent::__construct($appName, $request); - - $this->config = $config; - $this->userSession = $userSession; - $this->classifier = $classifier; - $this->userFolder = $userFolder; - $this->logger = $logger; - $this->service = $service; - $this->sequenceAnalyzer = $sequenceAnalyzer; - $this->entropyAnalyzer = $entropyAnalyzer; - $this->fileCorruptionAnalyzer = $fileCorruptionAnalyzer; - $this->fileExtensionAnalyzer = $fileExtensionAnalyzer; - $this->connection = $connection; - $this->userId = $userId; - } - - /** - * Post scan recovery. - * - * @NoAdminRequired - * - * @param integer $id - * @param integer $sequence - * @param integer $command - * @param string $path - * @param string $name - * @param integer $timestamp - * - * @return JSONResponse - */ - public function recover($id, $sequence, $command, $path, $name, $timestamp) - { - if ($command === Monitor::WRITE) { - // Delete file - if ($this->deleteFromStorage($path . '/' . $name)) { - return new JSONResponse(['status' => 'success', 'id' => $id, 'sequence' => $sequence], Http::STATUS_OK); - } else { - return new JSONResponse(['status' => 'error', 'message' => 'File cannot be deleted.'], Http::STATUS_OK); - } - } else if ($command === Monitor::DELETE) { - // Restore file - $trashPath = '/'.$name.'.d'.$timestamp;; - if ($this->restoreFromTrashbin($trashPath, $name, $timestamp) !== false) { - return new JSONResponse(['status' => 'success', 'id' => $id, 'sequence' => $sequence], Http::STATUS_OK); - } - - return new JSONResponse(['status' => 'error', 'message' => 'File does not exist.', 'path' => $trashPath, 'name' => $name, 'mtime' => $timestamp], Http::STATUS_OK); - } else { - // Scan can only detect WRITE and DELETE this should never happen. - $this->logger->error('postRecover: RENAME or CREATE operation.', array('app' => Application::APP_ID)); - return new JSONResponse(['status' => 'error', 'message' => 'Wrong command.'], Http::STATUS_BAD_REQUEST); - } - - } - - /** - * The files to scan. - * - * @NoAdminRequired - * - * @return JSONResponse - */ - public function filesToScan() - { - $start = time(); - $storageStructure = $this->getStorageStructure($this->userFolder); - $trashStorageStructure = $this->getTrashStorageStructure(); - - $allFiles = array(); - - // convert file to json and merge into one array - $files = $storageStructure->getFiles(); - for ($i = 0; $i < count($files); $i++) { - $allFiles[] = ['id' => $files[$i]->getId(), 'path' => $files[$i]->getInternalPath(), 'timestamp' => $this->getLastActivity($files[$i]->getId())['timestamp']]; - } - $trashFiles = $trashStorageStructure->getFiles(); - for ($i = 0; $i < count($trashFiles); $i++) { - $allFiles[] = ['id' => $trashFiles[$i]->getId(), 'path' => $trashFiles[$i]->getInternalPath(), 'timestamp' => $trashFiles[$i]->getMtime()]; - } - - // sort ASC for timestamp - usort($allFiles, function ($a, $b) { - if ($a['timestamp'] === $b['timestamp']) { - return 0; - } - return $a['timestamp'] - $b['timestamp']; - }); - - // build sequences - $sequencesArray = array(); - $sequence = array(); - for ($i = 0; $i < count($allFiles); $i++) { - if ($i === 0) { - $sequence = array(); - } else { - if ($allFiles[$i]['timestamp'] - $allFiles[$i - 1]['timestamp'] > 180) { - $sequencesArray[] = $sequence; - $sequence = array(); - } - } - $sequence[] = $allFiles[$i]; - } - $sequencesArray[] = $sequence; - $end = time(); - - return new JSONResponse(['status' => 'success', 'sequences' => $sequencesArray, 'numberOfFiles' => $storageStructure->getNumberOfFiles(), 'scanDuration' => $end - $start], Http::STATUS_OK); - } - - /** - * Scan sequence. - * - * @NoAdminRequired - * - * @param string $sequence - * @return JSONResponse - */ - public function scanSequence($sequence) { - if (sizeof($sequence) > $this->config->getAppValue(Application::APP_ID, 'minimum_sequence_length', 0)) { - $sequenceResults = array(); - foreach ($sequence as $file) { - try { - $fileOperation = $this->buildFileOperation($file); - } catch (NotAFileException $exception) { - $this->logger->debug('scanSequence: Path to file doesn\'t lead to file object.', array('app' => Application::APP_ID)); - continue; - } catch (NotFoundException $exception) { - $this->logger->debug('scanSequence: Not found.', array('app' => Application::APP_ID)); - continue; - } - - $this->classifier->classifyFile($fileOperation); - $jsonSequence[] = ['userId' => $fileOperation->getUserId(), 'path' => $fileOperation->getPath(), 'originalName' => preg_replace('/.d[0-9]{10}/', '', $fileOperation->getOriginalName()), - 'type' => $fileOperation->getType(), 'mimeType' => $fileOperation->getMimeType(), 'size' => $fileOperation->getSize(), 'corrupted' => $fileOperation->getCorrupted(), 'timestamp' => $fileOperation->getTimestamp(), 'entropy' => $fileOperation->getEntropy(), - 'standardDeviation' => $fileOperation->getStandardDeviation(), 'command' => $fileOperation->getCommand(), 'fileClass' => $fileOperation->getFileClass(), 'fileExtensionClass' => $fileOperation->getFileExtensionClass(), 'suspicionClass' => $fileOperation->getSuspicionClass()]; - $fileOperationSequence[] = $fileOperation; - } - if (sizeof($fileOperationSequence) > 0) { - $sequenceResult = $this->sequenceAnalyzer->analyze(0, $fileOperationSequence); - return new JSONResponse(['status' => 'success', 'suspicionScore' => $sequenceResult->getSuspicionScore(), 'sequence' => $jsonSequence], Http::STATUS_OK); - } else { - return new JSONResponse(['status' => 'error', 'message' => 'The file(s) requested do(es) not exist.']); - } - } else { - return new JSONResponse(['status' => 'error', 'message' => 'Sequence is to short.'], Http::STATUS_OK); - } - } - - /** - * Just for testing purpose to mock the external static method. - * - * @return array - */ - protected function getTrashFiles() { - return Helper::getTrashFiles("/", $this->userId, 'mtime', false); - } - - /** - * Just for testing purpose to mock the external static method. - * - * @param string $trashPath - * @param array $pathInfo - * @param integer $timestamp - * @return boolean - */ - protected function restoreFromTrashbin($trashPath, $name, $timestamp) - { - return Trashbin::restore($trashPath, $name, $timestamp); - } - - /** - * Get last activity. - * Visibility 'protected' is that it's possible to mock the database access. - * - * @param $objectId - */ - protected function getLastActivity($objectId) - { - $query = $this->connection->getQueryBuilder(); - $query->select('*')->from('activity'); - $query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($this->userId))) - ->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId))); - $result = $query->execute(); - while ($row = $result->fetch()) { - $rows[] = $row; - } - $result->closeCursor(); - if (isset($rows) && is_array($rows)) { - return array_pop($rows); - } else { - $this->logger->debug('getLastActivity: No activity found.', array('app' => Application::APP_ID)); - return 0; - } - } - - /** - * Builds a file operations from a file info array. - * - * @param array $file - * @return FileOperation - */ - private function buildFileOperation($file) - { - $fileOperation = new FileOperation(); - $fileOperation->setUserId($this->userId); - if (strpos($file['path'], 'files_trashbin') !== false) { - $node = $this->userFolder->getParent()->get($file['path'] . '.d' . $file['timestamp']); - $fileOperation->setCommand(Monitor::DELETE); - $fileOperation->setTimestamp($file['timestamp']); - $pathInfo = pathinfo($node->getInternalPath()); - $fileOperation->setPath($pathInfo['dirname']); - } else { - $node = $this->userFolder->getParent()->get($file['path']); - $lastActivity = $this->getLastActivity($file['id']); - $fileOperation->setCommand(Monitor::WRITE); - $fileOperation->setTimestamp($lastActivity['timestamp']); - $pathInfo = pathinfo($node->getInternalPath()); - $fileOperation->setPath(str_replace('files', '', $pathInfo['dirname'])); - } - if (!($node instanceof File)) { - throw new NotAFileException(); - } - $fileOperation->setOriginalName($node->getName()); - $fileOperation->setType('file'); - $fileOperation->setMimeType($node->getMimeType()); - $fileOperation->setSize($node->getSize()); - $fileOperation->setTimestamp($file['timestamp']); - - // file extension analysis - $fileExtensionResult = $this->fileExtensionAnalyzer->analyze($node->getInternalPath()); - $fileOperation->setFileExtensionClass($fileExtensionResult->getFileExtensionClass()); - - $fileCorruptionResult = $this->fileCorruptionAnalyzer->analyze($node); - $isCorrupted = $fileCorruptionResult->isCorrupted(); - $fileOperation->setCorrupted($isCorrupted); - if ($isCorrupted) { - $fileOperation->setFileExtensionClass(FileExtensionResult::SUSPICIOUS); - } - - // entropy analysis - $entropyResult = $this->entropyAnalyzer->analyze($node); - $fileOperation->setEntropy($entropyResult->getEntropy()); - $fileOperation->setStandardDeviation($entropyResult->getStandardDeviation()); - $fileOperation->setFileClass($entropyResult->getFileClass()); - - return $fileOperation; - } - - /** - * Get trash storage structure. - * - * @return StorageStructure - */ - private function getTrashStorageStructure() - { - $storageStructure = new StorageStructure(0, []); - $nodes = $this->getTrashFiles(); - foreach ($nodes as $node) { - $storageStructure->addFile($node); - $storageStructure->increaseNumberOfFiles(); - } - return $storageStructure; - } - - /** - * Get storage structure recursively. - * - * @param INode $node - * - * @return StorageStructure - */ - private function getStorageStructure($node) - { - // set count for node to 0 - $storageStructure = new StorageStructure(0, []); - if ($node instanceof Folder) { - // it's a folder - $nodes = $node->getDirectoryListing(); - if (count($nodes) === 0) { - // folder is empty so nothing to do - return $storageStructure; - } - foreach ($nodes as $tmpNode) { - // analyse files in subfolder - $tmpStorageStructure = $this->getStorageStructure($tmpNode); - $storageStructure->setFiles(array_merge($storageStructure->getFiles(), $tmpStorageStructure->getFiles())); - $storageStructure->setNumberOfFiles($storageStructure->getNumberOfFiles() + $tmpStorageStructure->getNumberOfFiles()); - } - return $storageStructure; - } - else if ($node instanceof File) { - // it's a file - $storageStructure->addFile($node); - $storageStructure->increaseNumberOfFiles(); - return $storageStructure; - } - else { - // it's me Mario. - // there is nothing else than file or folder - $this->logger->error('getStorageStructure: Neither file nor folder.', array('app' => Application::APP_ID)); - } - } - - /** - * Deletes a file from the storage. - * - * @param string $path - * - * @return bool - */ - private function deleteFromStorage($path) - { - try { - $node = $this->userFolder->get($path); - if ($node instanceof File && $node->isDeletable()) { - $node->delete(); - } else { - return false; - } - - return true; - } catch (\OCP\Files\NotFoundException $exception) { - // Nothing found - $this->logger->debug('deleteFromStorage: Not found exception.', array('app' => Application::APP_ID)); - - return true; - } - } -} diff --git a/lib/Controller/BasicController.php b/lib/Controller/SettingsController.php index 2d72f50..dd00909 100644 --- a/lib/Controller/BasicController.php +++ b/lib/Controller/SettingsController.php @@ -22,14 +22,15 @@ namespace OCA\RansomwareDetection\Controller; use OCA\RansomwareDetection\AppInfo\Application; +use OCA\RansomwareDetection\Model\Settings; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\OCSController; +use OCP\AppFramework\Controller; use OCP\IConfig; use OCP\IUserSession; use OCP\IRequest; -class BasicController extends OCSController +class SettingsController extends Controller { /** @var IConfig */ protected $config; @@ -62,46 +63,32 @@ class BasicController extends OCSController } /** - * Get debug mode. + * Get settings. * + * @NoCSRFRequired * @NoAdminRequired * * @return JSONResponse */ - public function getDebugMode() - { - $debugMode = $this->config->getAppValue(Application::APP_ID, 'debug', 0); + public function findAll() { + $debug = $this->config->getAppValue(Application::APP_ID, 'debug', 0); + $color = $this->config->getUserValue($this->userId, Application::APP_ID, 'color_mode', 0); + $settings = new Settings($debug, $color); - return new JSONResponse(['status' => 'success', 'message' => 'Get debug mode.', 'debugMode' => $debugMode], Http::STATUS_ACCEPTED); + return new JSONResponse($settings, Http::STATUS_OK); } /** - * Get color mode. + * Set settings. * + * @NoCSRFRequired * @NoAdminRequired * * @return JSONResponse */ - public function getColorMode() - { - $colorMode = $this->config->getUserValue($this->userId, Application::APP_ID, 'color_mode', 0); + public function update($color, $debug) { + $this->config->setUserValue($this->userId, Application::APP_ID, 'color_mode', $color); - return new JSONResponse(['status' => 'success', 'message' => 'Get color mode.', 'colorMode' => $colorMode], Http::STATUS_ACCEPTED); - } - - /** - * Changes color mode. - * - * @NoAdminRequired - * - * @param int $colorMode - * - * @return JSONResponse - */ - public function changeColorMode($colorMode) - { - $this->config->setUserValue($this->userId, Application::APP_ID, 'color_mode', $colorMode); - - return new JSONResponse(['status' => 'success', 'message' => 'Color mode changed.'], Http::STATUS_ACCEPTED); + return new JSONResponse(null, Http::STATUS_OK); } } diff --git a/lib/Model/Detection.php b/lib/Model/Detection.php new file mode 100644 index 0000000..86884bd --- /dev/null +++ b/lib/Model/Detection.php @@ -0,0 +1,56 @@ +<?php +/** + * @copyright Copyright (c) 2019 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Model; + +class Detection implements \JsonSerializable { + private $id; + private $fileOperations = array(); + + public function __construct($id, $fileOperations) { + $this->id = $id; + $this->fileOperations = $fileOperations; + } + + public function getId() { + return $this->id; + } + + public function setId($id) { + $this->id = $id; + } + + public function getFileOperations() { + return $this->fileOperations; + } + + public function setFileOperations($fileOperations) { + $this->fileOperations = $fileOperations; + } + + public function addFileOperation($fileOperation) { + array_push($this->fileOperations, $fileOperation); + } + + public function jsonSerialize() + { + return get_object_vars($this); + } +}
\ No newline at end of file diff --git a/lib/Model/DetectionDeserializer.php b/lib/Model/DetectionDeserializer.php new file mode 100644 index 0000000..5f70eff --- /dev/null +++ b/lib/Model/DetectionDeserializer.php @@ -0,0 +1,47 @@ +<?php +/** + * @copyright Copyright (c) 2019 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Model; + +use OCA\RansomwareDetection\Db\FileOperationMapper; + +class DetectionDeserializer { + + /** @var FileOperationMapper */ + protected $mapper; + + /** + * @param FileOperationMapper $mapper + */ + public function __construct( + FileOperationMapper $mapper + ) { + $this->mapper = $mapper; + } + + public function deserialize($json) { + $detection = new Detection(); + $detection->setId($json['id']); + foreach ($json['fileOperations'] as $fileOperation) { + $detection->addFileOperation($this->mapper->find($fileOperation->getId())); + } + return $detection; + } +}
\ No newline at end of file diff --git a/lib/Model/Settings.php b/lib/Model/Settings.php new file mode 100644 index 0000000..027d457 --- /dev/null +++ b/lib/Model/Settings.php @@ -0,0 +1,59 @@ +<?php +/** + * @copyright Copyright (c) 2019 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Model; + +class Settings implements \JsonSerializable { + /** + * The debug state of the app. + */ + private $debug; + + /** + * The color state of the app. + */ + private $color; + + public function __construct($debug, $color) { + $this->debug = $debug; + $this->color = $color; + } + + public function getDebug() { + return $this->debug; + } + + public function setDebug($debug) { + $this->debug = $debug; + } + + public function getColor() { + return $this->color; + } + + public function setColor() { + $this->color = $color; + } + + public function jsonSerialize() + { + return get_object_vars($this); + } +}
\ No newline at end of file diff --git a/lib/Service/DetectionService.php b/lib/Service/DetectionService.php new file mode 100644 index 0000000..ca69c31 --- /dev/null +++ b/lib/Service/DetectionService.php @@ -0,0 +1,68 @@ +<?php +/** + * @copyright Copyright (c) 2019 Matthias Held <matthias.held@uni-konstanz.de> + * @author Matthias Held <matthias.held@uni-konstanz.de> + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace OCA\RansomwareDetection\Service; + +use OCA\RansomwareDetection\AppInfo\Application; +use OCA\RansomwareDetection\Model\Detection; +use OCA\RansomwareDetection\Model\DetectionDeserializer; +use OCP\IConfig; + +class DetectionService { + + /** @var FileOperationService */ + protected $service; + + /** @var DetectionDeserializer */ + protected $deserializer; + + /** @var IConfig */ + protected $config; + + /** @var string */ + protected $userId; + + /** + * @param FileOperationService $service + * @param IConfig $config + * @param string $userId + */ + public function __construct( + FileOperationService $service, + DetectionDeserializer $deserializer, + IConfig $config, + $userId + ) + { + $this->service = $service; + $this->deserializer = $deserializer; + $this->config = $config; + $this->userId = $userId; + } + + public function getDetections() { + $detectionObjects = array(); + return $detectionObjects; + } + + public function getDetection($id) { + return DetectionSerializer::deserialize(json_decode(new Detection())); + } +}
\ No newline at end of file diff --git a/package.json b/package.json index 619ecf6..bd9602c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@polymer/paper-spinner": "^3.0.2", "@vaadin/vaadin-grid": "^5.4.3", "core-js": "^3.0.1", + "moment": "^2.24.0", "nextcloud-axios": "^0.2.0", "nextcloud-server": "^0.15.9", "nextcloud-vue": "^0.11.4", diff --git a/src/components/FileOperationsTable.vue b/src/components/FileOperationsTable.vue index c5cc5b4..a5f6715 100644 --- a/src/components/FileOperationsTable.vue +++ b/src/components/FileOperationsTable.vue @@ -16,6 +16,7 @@ import '@polymer/iron-icon/iron-icon.js'; import '@polymer/iron-icons/iron-icons.js'; import '../webcomponents/ransomware-icons' import 'time-elements/dist/time-elements'; +import moment from 'moment' export default { name: 'FileOperationsTable', diff --git a/src/components/ProtectionStatus.vue b/src/components/ProtectionStatus.vue index 62e4805..e60220e 100644 --- a/src/components/ProtectionStatus.vue +++ b/src/components/ProtectionStatus.vue @@ -19,11 +19,6 @@ import axios from 'nextcloud-axios' export default { name: 'ProtectionStatus', props: { - protectionLink: { - type: String, - default: '', - required: true - }, detectionLink: { type: String, default: '', @@ -31,32 +26,15 @@ export default { } }, created() { - this.fetchServicesStatus(); this.fetchDetectionStatus(); }, data() { return { detection: 0, - protection: 0 + protection: 1 }; }, methods: { - fetchServicesStatus() { - axios({ - method: 'GET', - url: this.protectionLink - }) - .then(json => { - this.protection = 1; - for (i = 0; i < json.data.length; i++) { - if (json.data[i].status == 0) { - this.protection = 0; - } - } - this.$emit('protection-state-changed'); - }) - .catch( error => { console.error(error); }); - }, fetchDetectionStatus() { axios({ method: 'GET', @@ -67,6 +45,7 @@ export default { if (json.data.length > 0) { this.detection = 1; } + this.$emit('protection-state-changed'); }) .catch( error => { console.error(error); }); } diff --git a/src/components/ServiceStatus.vue b/src/components/ServiceStatus.vue deleted file mode 100644 index 9c005b5..0000000 --- a/src/components/ServiceStatus.vue +++ /dev/null @@ -1,92 +0,0 @@ -<template> - <div> - <h2 class="container"> - <div class="item name">{{ serviceName }}</div> - <div v-if="serviceStatus" class="item status active">Active</div> - <div v-if="!serviceStatus" class="item status offline">Offline</div> - </h2> - <div v-if="!serviceStatus" class="description">{{description}}</div> - </div> -</template> - -<script> -import '@polymer/paper-card/paper-card.js'; -import '@polymer/paper-button/paper-button.js'; -import '@polymer/iron-icon/iron-icon.js'; -import '@polymer/iron-icons/iron-icons.js'; -import axios from 'nextcloud-axios' - -export default { - name: 'ServiceStatus', - props: { - link: { - type: String, - default: '', - required: true - }, - description: { - type: String, - default: '', - required: true - } - }, - data() { - return { - serviceName: "Not available.", - serviceStatus: 0 - }; - }, - created () { - this.fetchServiceName(); - this.fetchServiceStatus(); - }, - methods: { - fetchServiceName: function() { - axios({ - method: 'GET', - url: this.link - }) - .then(json => { - this.serviceName = json.data.name; - }) - .catch( error => { console.error(error); }); - }, - fetchServiceStatus() { - axios({ - method: 'GET', - url: this.link - }) - .then(json => { - this.serviceStatus = json.data.status; - this.$emit('service-state-changed'); - }) - .catch( error => { console.error(error); }); - } - } -} -</script> - -<style lang="scss" scoped> - .container { - display: flex; - width: 100%; - justify-content: space-between; - } - h2 { - margin: 0px; - } - .item { - padding: 5px 10px 5px 10px; - } - .description { - color: #9b9b9b; - padding: 0px 10px 0px 10px; - } - .active { - color: #18b977; - } - .offline { - color: #e2523d; - } - -</style>
\ No newline at end of file diff --git a/src/views/Protection.vue b/src/views/Protection.vue index 3ce0848..201919f 100644 --- a/src/views/Protection.vue +++ b/src/views/Protection.vue @@ -5,11 +5,7 @@ <paper-spinner active></paper-spinner> </div> <div> - <ProtectionStatus :detection-link="detectionUrl" :protection-link="servicesUrl" id="protection-status" v-on:protection-state-changed="protectionStateChanged"></ProtectionStatus> - <div id="services"> - <ServiceStatus :link="detectionServiceUrl" description="Your files currently cannot be analyzed for ransomware. To enable ransomware detection, contact your system administator." v-on:service-state-changed="detectionStateChanged" class="service"></ServiceStatus> - <ServiceStatus :link="monitorServiceUrl" description="There may be a problem with your Nextcloud installation. Please contact your system administator." v-on:service-state-changed="monitorStateChanged" class="service"></ServiceStatus> - </div> + <ProtectionStatus :detection-link="detectionUrl" id="protection-status" v-on:protection-state-changed="protectionStateChanged"></ProtectionStatus> </div> </iron-pages> </AppContent> @@ -20,51 +16,30 @@ import '@polymer/paper-spinner/paper-spinner.js'; import '@polymer/iron-pages/iron-pages.js'; import AppContent from 'nextcloud-vue/dist/Components/AppContent' import ProtectionStatus from '../components/ProtectionStatus' -import ServiceStatus from '../components/ServiceStatus' export default { name: 'Protection', components: { AppContent, - ProtectionStatus, - ServiceStatus + ProtectionStatus }, data() { return { protectionReady: false, - detectionReady: false, - monitorReady: false }; }, computed: { detectionUrl() { return OC.generateUrl('/apps/ransomware_detection/api/v1/detection'); - }, - servicesUrl() { - return OC.generateUrl('/apps/ransomware_detection/api/v1/service'); - }, - detectionServiceUrl() { - return OC.generateUrl('/apps/ransomware_detection/api/v1/service/0'); - }, - monitorServiceUrl() { - return OC.generateUrl('/apps/ransomware_detection/api/v1/service/1'); - } + } }, methods: { protectionStateChanged() { this.protectionReady = true; this.hideSpinner(); }, - monitorStateChanged() { - this.monitorReady = true; - this.hideSpinner(); - }, - detectionStateChanged() { - this.detectionReady = true; - this.hideSpinner(); - }, hideSpinner() { - if (this.protectionReady && this.monitorReady && this.detectionReady) { + if (this.protectionReady) { document.querySelector('iron-pages').selectIndex(1); } } |