Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/undo-ransomware/ransomware_detection.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias <ilovemilk@wusa.io>2020-04-11 12:57:03 +0300
committerMatthias <ilovemilk@wusa.io>2020-04-11 12:57:03 +0300
commit5e9cebdcad048e166f9392e3aefeea1529669bc0 (patch)
tree4ff37e522cba5762369b320f0919c115393f29a8
parent133a820b88a78a4281e017d88d11bed169b5fdbf (diff)
fix frontend and controller to work without services
-rw-r--r--appinfo/info.xml2
-rw-r--r--appinfo/routes.php28
-rw-r--r--lib/AppInfo/Application.php26
-rw-r--r--lib/Controller/DetectionController.php80
-rw-r--r--lib/Controller/FileOperationController.php263
-rw-r--r--lib/Controller/MonitoringController.php324
-rw-r--r--lib/Controller/PageController.php (renamed from lib/Controller/RecoverController.php)22
-rw-r--r--lib/Controller/ScanController.php447
-rw-r--r--lib/Controller/SettingsController.php (renamed from lib/Controller/BasicController.php)43
-rw-r--r--lib/Model/Detection.php56
-rw-r--r--lib/Model/DetectionDeserializer.php47
-rw-r--r--lib/Model/Settings.php59
-rw-r--r--lib/Service/DetectionService.php68
-rw-r--r--package.json1
-rw-r--r--src/components/FileOperationsTable.vue1
-rw-r--r--src/components/ProtectionStatus.vue25
-rw-r--r--src/components/ServiceStatus.vue92
-rw-r--r--src/views/Protection.vue33
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);
}
}