From e6aef00193b9cdb1809d6f5f22541469c4e598ab Mon Sep 17 00:00:00 2001 From: brantje Date: Wed, 5 Oct 2016 16:58:19 +0200 Subject: [PATCH] Downloading shared files works --- appinfo/routes.php | 1 + controller/filecontroller.php | 8 +++---- controller/sharecontroller.php | 40 +++++++++++++++++++++++++++++++- js/app/controllers/credential.js | 36 ++++++++++++++++++++++++---- js/app/controllers/share.js | 28 ++++++++++++++++++---- js/app/services/fileservice.js | 4 ++-- js/app/services/shareservice.js | 8 +++++++ js/templates.js | 2 +- lib/AppInfo/Application.php | 5 +++- lib/Db/FileMapper.php | 19 ++++++++++++++- lib/Service/FileService.php | 4 ++++ templates/views/show_vault.html | 5 ++-- 12 files changed, 139 insertions(+), 21 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 782d0e03..0922549e 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -62,6 +62,7 @@ return [ ['name' => 'share#unshareCredential', 'url' => '/api/v2/sharing/credential/{item_guid}', 'verb' => 'DELETE'], ['name' => 'share#getRevisions', 'url' => '/api/v2/sharing/credential/{item_guid}/revisions', 'verb' => 'GET'], ['name' => 'share#getItemAcl', 'url' => '/api/v2/sharing/credential/{item_guid}/acl', 'verb' => 'GET'], + ['name' => 'share#getFile', 'url' => '/api/v2/sharing/credential/{item_guid}/file/{file_guid}', 'verb' => 'GET'], ['name' => 'share#updateSharedCredentialACL', 'url' => '/api/v2/sharing/credential/{item_guid}/acl', 'verb' => 'PATCH'], //Internal API diff --git a/controller/filecontroller.php b/controller/filecontroller.php index dc9f6a07..eef277ce 100644 --- a/controller/filecontroller.php +++ b/controller/filecontroller.php @@ -56,20 +56,20 @@ class FileController extends ApiController { return $this->fileService->deleteFile($file_id, $this->userId); } - public function updateFile($file_id, $data, $filename, $mimetype, $size){ + public function updateFile($file_id, $file_data, $filename, $mimetype, $size){ try{ $file = $this->fileService->getFile($file_id, $this->userId); } catch (DoesNotExistException $doesNotExistException){ } if($file){ - if($data) { - $file->setFileData($data); + if($file_data) { + $file->setFileData($file_data); } if($filename) { $file->setFilename($filename); } - if($filename || $data){ + if($filename || $file_data){ $this->fileService->updateFile($file); } } diff --git a/controller/sharecontroller.php b/controller/sharecontroller.php index 839bffc8..b5123109 100644 --- a/controller/sharecontroller.php +++ b/controller/sharecontroller.php @@ -16,6 +16,7 @@ use OCA\Passman\Db\ShareRequest; use OCA\Passman\Db\SharingACL; use OCA\Passman\Db\Vault; use OCA\Passman\Service\CredentialService; +use OCA\Passman\Service\FileService; use OCA\Passman\Service\NotificationService; use OCA\Passman\Service\ShareService; use OCP\AppFramework\Db\DoesNotExistException; @@ -23,6 +24,8 @@ use OCP\AppFramework\Http\NotFoundResponse; use OCP\IRequest; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\ApiController; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; use OCP\IGroup; use OCP\IGroupManager; @@ -43,6 +46,7 @@ class ShareController extends ApiController { private $shareService; private $credentialService; private $notificationService; + private $fileService; private $limit = 50; private $offset = 0; @@ -56,7 +60,8 @@ class ShareController extends ApiController { VaultService $vaultService, ShareService $shareService, CredentialService $credentialService, - NotificationService $notificationService + NotificationService $notificationService, + FileService $fileService ) { parent::__construct($AppName, $request); @@ -68,6 +73,7 @@ class ShareController extends ApiController { $this->shareService = $shareService; $this->credentialService = $credentialService; $this->notificationService = $notificationService; + $this->fileService = $fileService; } /** @@ -385,6 +391,11 @@ class ShareController extends ApiController { } } + /** + * @param $item_guid + * @return JSONResponse + * @NoAdminRequired + */ public function getItemAcl($item_guid) { $acl = $this->shareService->getCredentialAclList($item_guid); $pending = $this->shareService->getCredentialPendingAclList($item_guid); @@ -404,6 +415,33 @@ class ShareController extends ApiController { } } + /** + * @param $credential_guid + * @param $file_guid + * @NoAdminRequired + */ + public function getFile($item_guid, $file_guid){ + try { + $credential = $this->credentialService->getCredentialByGUID($item_guid); + } catch (DoesNotExistException $e){ + return new JSONResponse(array()); + } + + $acl = $this->shareService->getACL($this->userId->getUID(), $credential->getGuid()); + if (!$acl->hasPermission(SharingACL::FILES)){ + return new NotFoundResponse(); + } else { + return $this->fileService->getFileByGuid($file_guid); + } + } + + /** + * @param $item_guid + * @param $user_id + * @param $permission + * @return JSONResponse + * @NoAdminRequired + */ public function updateSharedCredentialACL($item_guid, $user_id, $permission) { try { $credential = $this->credentialService->getCredentialByGUID($item_guid); diff --git a/js/app/controllers/credential.js b/js/app/controllers/credential.js index 514d2494..4b9eb156 100644 --- a/js/app/controllers/credential.js +++ b/js/app/controllers/credential.js @@ -316,17 +316,43 @@ angular.module('passmanApp') }); - $scope.downloadFile = function (file) { - FileService.getFile(file).then(function (result) { - var file_data = EncryptService.decryptString(result.file_data); + $scope.downloadFile = function (credential, file) { + console.log(credential, file); + var callback = function(result){ + var key = null; + if(!result.hasOwnProperty('file_data')){ + NotificationService.showNotification('Error downloading file, you probably don\'t have enough permissions', 5000); + return; + + } + if(!credential.hasOwnProperty('acl') && credential.hasOwnProperty('shared_key')){ + key = EncryptService.decryptString(angular.copy(credential.shared_key)); + } + if(credential.hasOwnProperty('acl')){ + key = EncryptService.decryptString(angular.copy(credential.acl.shared_key)); + } + + var file_data = EncryptService.decryptString(result.file_data, key); var uriContent = FileService.dataURItoBlob(file_data, file.mimetype), a = document.createElement("a"); a.style = "display: none"; + a.id= 'downloadLink'; a.href = uriContent; a.download = escapeHTML(file.filename); - document.body.appendChild(a); + jQuery('.detailsView').append(a); a.click(); window.URL.revokeObjectURL(uriContent); - }); + jQuery('#downloadLink').remove(); + setTimeout(function(){ + $scope.selectedCredential = credential; + }, 200) + }; + + if(!credential.hasOwnProperty('acl')){ + FileService.getFile(file).then(callback); + } else { + ShareService.downloadSharedFile(credential, file).then(callback); + } + }; }]); diff --git a/js/app/controllers/share.js b/js/app/controllers/share.js index 8257c088..3515ac57 100644 --- a/js/app/controllers/share.js +++ b/js/app/controllers/share.js @@ -10,7 +10,7 @@ */ angular.module('passmanApp') .controller('ShareCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', 'ShareService', 'NotificationService', 'SharingACL','EncryptService', 'FileService', - function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, ShareService, NotificationService, SharingACL, EncryptService) { + function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, ShareService, NotificationService, SharingACL, EncryptService, FileService) { $scope.active_vault = VaultService.getActiveVault(); $scope.tabs = [{ @@ -183,7 +183,18 @@ angular.module('passmanApp') _credential.shared_key = null; CredentialService.updateCredential(_credential).then(function () { NotificationService.showNotification('Credential unshared', 4000) - }) + }); + + for(var f = 0; f < $scope.storedCredential.files.length; f++){ + var _file = $scope.storedCredential.files[f]; + FileService.getFile(_file).then(function (fileData) { + console.log(fileData); + //Decrypt with old key + fileData.filename = EncryptService.decryptString(fileData.filename); + fileData.file_data = EncryptService.decryptString(fileData.file_data); + FileService.updateFile(fileData, $scope.active_vault.vaultKey); + }) + } }; /** @@ -251,7 +262,6 @@ angular.module('passmanApp') for (var i = 0; i < list.length; i++) { var iterator = i; var target_user = list[i]; - console.log(target_user) if(target_user.hasOwnProperty('created')){ console.log('Updating permissions') @@ -273,7 +283,6 @@ angular.module('passmanApp') CredentialService.updateCredential(encryptedSharedCredential, true).then(function(sharedCredential){ $scope.storedCredential = ShareService.decryptSharedCredential(sharedCredential, key); }); - console.log($scope.storedCredential); //@TODO Update files with new key (async) // Files are stored in $scope.storedCredential.files @@ -281,6 +290,17 @@ angular.module('passmanApp') // Then decrypt the data obtained with var EncryptService.decryptString(result.file_data); // To update a file you can use the FileService.updateFile + for(var f = 0; f < $scope.storedCredential.files.length; f++){ + var _file = $scope.storedCredential.files[f]; + FileService.getFile(_file).then(function (fileData) { + //Decrypt with old key + fileData.filename = EncryptService.decryptString(fileData.filename); + fileData.file_data = EncryptService.decryptString(fileData.file_data); + FileService.updateFile(fileData, key); + }) + } + + //@TODO Update revisions with new key (async) // With CredentialService.getRevisions we can get the revisions. // Then we can update them using CredentialService.updateRevision diff --git a/js/app/services/fileservice.js b/js/app/services/fileservice.js index 022051c3..8e958bdf 100644 --- a/js/app/services/fileservice.js +++ b/js/app/services/fileservice.js @@ -60,8 +60,8 @@ angular.module('passmanApp') var queryUrl = OC.generateUrl('apps/passman/api/v2/file/'+ file.file_id); var _file = angular.copy(file); _file.filename = EncryptService.encryptString(_file.filename, key); - var data = EncryptService.encryptString(angular.copy(file.data), key); - _file.data = data; + var data = EncryptService.encryptString(angular.copy(file.file_data), key); + _file.file_data = data; return $http.patch(queryUrl, _file).then(function (response) { if (response.data) { return response.data; diff --git a/js/app/services/shareservice.js b/js/app/services/shareservice.js index fb066678..d3bc68a0 100644 --- a/js/app/services/shareservice.js +++ b/js/app/services/shareservice.js @@ -129,6 +129,14 @@ angular.module('passmanApp') } }); }, + downloadSharedFile: function (credential, file) { + var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/'+ credential.guid +'/file/'+ file.guid); + return $http.get(queryUrl).then(function (response) { + if (response.data) { + return response.data; + } + }); + }, encryptSharedCredential: function (credential, sharedKey) { var _credential = angular.copy(credential); _credential.shared_key = EncryptService.encryptString(sharedKey); diff --git a/js/templates.js b/js/templates.js index 1e62c13f..1eb56672 100644 --- a/js/templates.js +++ b/js/templates.js @@ -113,7 +113,7 @@ angular.module('views/share_credential.html', []).run(['$templateCache', functio angular.module('views/show_vault.html', []).run(['$templateCache', function($templateCache) { 'use strict'; $templateCache.put('views/show_vault.html', - '
Showing deleted since: All time {{delete_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}} Showing {{filtered_credentials.length}} of {{active_vault.credentials.length}} credentials
+
{{filtered_credentials}}
{{ ::credential.label}} {{ ::tag.text}}
  • {{ ::credential.label}}
    {{ ::tag.text}}
Label{{selectedCredential.label}}
Account
Password
OTP
E-mail
URL
Files
{{field.label}}
Expire time{{selectedCredential.expire_time * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
Changed{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
Created{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{tag.text}}
'); + '
Showing deleted since: All time {{delete_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}} Showing {{filtered_credentials.length}} of {{active_vault.credentials.length}} credentials
+
{{filtered_credentials}}
{{ ::credential.label}} {{ ::tag.text}}
  • {{ ::credential.label}}
    {{ ::tag.text}}
Label{{selectedCredential.label}}
Account
Password
OTP
E-mail
URL
Files
{{field.label}}
Expire time{{selectedCredential.expire_time * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
Changed{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
Created{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{tag.text}}
'); }]); angular.module('views/vaults.html', []).run(['$templateCache', function($templateCache) { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 76cf20b6..d2468d79 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -20,6 +20,7 @@ use OCA\Passman\Service\ActivityService; use OCA\Passman\Service\CronService; use OCA\Passman\Service\CredentialService; use OCA\Passman\Service\ShareService; +use OCA\Passman\Service\FileService; use OCA\Passman\Service\VaultService; use OCA\Passman\Utility\Utils; use OCA\Passman\Service\NotificationService; @@ -56,7 +57,8 @@ class Application extends App { $c->query('VaultService'), $c->query('ShareService'), $c->query('CredentialService'), - $c->query('NotificationService') + $c->query('NotificationService'), + $c->query('FileService') ); }); @@ -91,6 +93,7 @@ class Application extends App { $container->registerAlias('NotificationService', NotificationService::class); $container->registerAlias('ActivityService', ActivityService::class); $container->registerAlias('VaultService', VaultService::class); + $container->registerAlias('FileService', FileService::class); $container->registerAlias('ShareService', ShareService::class); $container->registerAlias('Utils', Utils::class); } diff --git a/lib/Db/FileMapper.php b/lib/Db/FileMapper.php index f9021bd3..1a72ca37 100644 --- a/lib/Db/FileMapper.php +++ b/lib/Db/FileMapper.php @@ -38,7 +38,24 @@ class FileMapper extends Mapper { $sql .= ' and `user_id` = ? '; array_push($params, $user_id); } - return $this->findEntities($sql, $params); + return $this->findEntity($sql, $params); + } + /** + * @param $file_id + * @param null $user_id + * @return File + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result + */ + public function getFileByGuid($file_guid, $user_id = null) { + $sql = 'SELECT * FROM `*PREFIX*passman_files` ' . + 'WHERE `guid` = ?'; + $params = [$file_guid]; + if ($user_id !== null) { + $sql .= ' and `user_id` = ? '; + array_push($params, $user_id); + } + return $this->findEntity($sql, $params); } public function create($file_raw, $userId) { diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 16ea1599..cf3afacd 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -29,6 +29,10 @@ class FileService { return $this->fileMapper->getFile($fileId, $userId); } + public function getFileByGuid($file_guid, $userId = null) { + return $this->fileMapper->getFileByGuid($file_guid, $userId); + } + public function createFile($file, $userId) { return $this->fileMapper->create($file, $userId); } diff --git a/templates/views/show_vault.html b/templates/views/show_vault.html index e682bd96..fafb2495 100644 --- a/templates/views/show_vault.html +++ b/templates/views/show_vault.html @@ -58,7 +58,7 @@ {{filtered_credentials}} -
+
@@ -164,7 +164,7 @@ @@ -248,6 +248,7 @@
+