diff --git a/Gruntfile.js b/Gruntfile.js index 8c405841..06ca2eb8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -75,6 +75,13 @@ module.exports = function (grunt) { src: ["**/public-page.scss"], dest: "css", ext: ".css" + }, + { + expand: true, + cwd: "sass", + src: ["**/admin.scss"], + dest: "css", + ext: ".css" } ] } @@ -132,6 +139,7 @@ module.exports = function (grunt) { '!css/app.*', 'css/bookmarklet.css', 'css/public-page.css', + 'css/admin.css', '!dist/*', '!dist/*/**', '!dist', diff --git a/appinfo/routes.php b/appinfo/routes.php index 79afa309..2886d210 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -84,5 +84,9 @@ return [ ['name' => 'internal#read', 'url' => '/api/internal/notifications/read/{credential_id}', 'verb' => 'DELETE'], ['name' => 'internal#getAppVersion', 'url' => '/api/internal/version', 'verb' => 'GET'], ['name' => 'internal#generatePerson', 'url' => '/api/internal/generate_person', 'verb' => 'GET'], + + //Admin routes + ['name' => 'admin#searchUser', 'url' => '/admin/search', 'verb' => 'GET'], + ['name' => 'admin#moveCredentials', 'url' => '/admin/move', 'verb' => 'POST'], ] ]; \ No newline at end of file diff --git a/controller/admincontroller.php b/controller/admincontroller.php new file mode 100644 index 00000000..4830f040 --- /dev/null +++ b/controller/admincontroller.php @@ -0,0 +1,103 @@ + + * @copyright Sander Brand 2016 + */ + +namespace OCA\Passman\Controller; + +use OCA\Passman\Db\CredentialRevision; +use OCA\Passman\Service\CredentialRevisionService; +use OCA\Passman\Service\FileService; +use OCA\Passman\Service\VaultService; +use OCP\IConfig; +use OCP\IRequest; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\ApiController; +use OCA\Passman\Service\CredentialService; +use \OCP\App; + +class AdminController extends ApiController { + private $userId; + private $vaultService; + private $credentialService; + private $fileService; + private $revisionService; + private $config; + + public function __construct($AppName, + IRequest $request, + $UserId, + VaultService $vaultService, + CredentialService $credentialService, + FileService $fileService, + CredentialRevisionService $revisionService, + IConfig $config + ) { + parent::__construct( + $AppName, + $request, + 'GET, POST, DELETE, PUT, PATCH, OPTIONS', + 'Authorization, Content-Type, Accept', + 86400); + $this->userId = $UserId; + $this->vaultService = $vaultService; + $this->credentialService = $credentialService; + $this->fileService = $fileService; + $this->revisionService = $revisionService; + $this->config = $config; + } + + + public function searchUser($term){ + $um = \OC::$server->getUserManager(); + $results = array(); + $searchResult = $um->search($term); + foreach ($searchResult as $user){ + array_push($results, array( + "value" => $user->getUID(), + "label" => $user->getDisplayName() . ' ('. $user->getBackendClassName() .')', + )); + } + return new JSONResponse($results); + } + + public function moveCredentials($source_account, $destination_account){ + $vaults = $this->vaultService->getByUser($source_account); + foreach ($vaults as $vault) { + $credentials = $this->credentialService->getCredentialsByVaultId($vault->getId(), $source_account); + foreach($credentials as $credential){ + $revisions = $this->revisionService->getRevisions($credential->getId()); + foreach ($revisions as $revision){ + $r = new CredentialRevision(); + $r->setId($revision['revision_id']); + $r->setGuid($revision['guid']); + $r->setCredentialId($credential->getId()); + $r->setUserId($destination_account); + $r->setCreated($revision['created']); + $r->setCredentialData(base64_encode(json_encode($revision['credential_data']))); + $r->setEditedBy($revision['edited_by']); + $this->revisionService->updateRevision($r); + } + + $c = $credential->jsonSerialize(); + $c['user_id'] = $destination_account; + $this->credentialService->updateCredential($c, true); + } + $vault->setUserId($destination_account); + $this->vaultService->updateVault($vault); + } + + $files = $this->fileService->getFilesFromUser($source_account); + foreach($files as $file){ + $file->setUserId($destination_account); + $this->fileService->updateFile($file); + } + return new JSONResponse(array('success'=> true)); + } +} \ No newline at end of file diff --git a/css/admin.css b/css/admin.css new file mode 100644 index 00000000..8bcecfbb --- /dev/null +++ b/css/admin.css @@ -0,0 +1,6 @@ +#passwordSharingSettings #mover table td { + padding: 5px; } +#passwordSharingSettings #mover input[type="text"] { + width: 350px; } + +/*# sourceMappingURL=admin.css.map */ diff --git a/css/admin.css.map b/css/admin.css.map new file mode 100644 index 00000000..da775fe5 --- /dev/null +++ b/css/admin.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAGM,wCAAE;EACA,OAAO,EAAE,GAAG;AAGhB,kDAAkB;EAChB,KAAK,EAAE,KAAK", +"sources": ["../sass/admin.scss"], +"names": [], +"file": "admin.css" +} diff --git a/js/settings-admin.js b/js/settings-admin.js index f91f4719..dcb945e9 100644 --- a/js/settings-admin.js +++ b/js/settings-admin.js @@ -21,7 +21,6 @@ */ $(document).ready(function () { - var Settings = function (baseUrl) { this._baseUrl = baseUrl; this._settings = []; @@ -86,7 +85,6 @@ $(document).ready(function () { var settings = new Settings(OC.generateUrl('apps/passman/api/v2/settings')); settings.load(); - // ADMIN SETTINGS // fill the boxes @@ -130,4 +128,36 @@ $(document).ready(function () { $('form[name="passman_settings"]')[1].remove(); } + var accountMover = { + 'source_account': '', + 'destination_account': '' + }; + $( ".username-autocomplete" ).autocomplete({ + source: OC.generateUrl('apps/passman/admin/search'), + minLength: 1, + select: function( event, ui ) { + accountMover[$(this).attr('id')] = ui.item.value; + } + }); + + $('#move_credentials').click(function () { + var self = this; + $('#moveStatus').hide(); + $(self).attr('disabled', 'disabled'); + $(self).html(' Moving...'); + if(accountMover.source_account && accountMover.destination_account){ + $.post(OC.generateUrl('apps/passman/admin/move'), accountMover, function (data) { + if(data.success){ + $(self).removeAttr('disabled'); + $(self).html('Move'); + $('#moveStatus').fadeIn(); + setTimeout(function () { + $('#moveStatus').fadeOut(); + }, 3500) + } + }); + } + }); + + $('#passman-tabs').tabs(); }); diff --git a/lib/Db/CredentialMapper.php b/lib/Db/CredentialMapper.php index 02d49ca8..dfd58f75 100644 --- a/lib/Db/CredentialMapper.php +++ b/lib/Db/CredentialMapper.php @@ -148,15 +148,18 @@ class CredentialMapper extends Mapper { * Update a credential * * @param $raw_credential array An array containing all the credential fields + * @param $useRawUser bool * @return Credential The updated credential */ - public function updateCredential($raw_credential) { + public function updateCredential($raw_credential, $useRawUser) { $original = $this->getCredentialByGUID($raw_credential['guid']); + $uid = ($useRawUser) ? $raw_credential['user_id'] : $original->getUserId(); + $credential = new Credential(); $credential->setId($original->getId()); $credential->setGuid($original->getGuid()); $credential->setVaultId($original->getVaultId()); - $credential->setUserId($original->getUserId()); + $credential->setUserId($uid); $credential->setLabel($raw_credential['label']); $credential->setDescription($raw_credential['description']); $credential->setCreated($original->getCreated()); @@ -174,6 +177,7 @@ class CredentialMapper extends Mapper { $credential->setOtp($raw_credential['otp']); $credential->setHidden($raw_credential['hidden']); $credential->setDeleteTime($raw_credential['delete_time']); + if (isset($raw_credential['shared_key'])) { $credential->setSharedKey($raw_credential['shared_key']); } diff --git a/lib/Db/FileMapper.php b/lib/Db/FileMapper.php index d0dd80d9..b1e044d3 100644 --- a/lib/Db/FileMapper.php +++ b/lib/Db/FileMapper.php @@ -112,4 +112,19 @@ class FileMapper extends Mapper { public function updateFile(File $file) { return $this->update($file); } + + + /** + * @param $user_id + * @return File[] + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result + */ + public function getFilesFromUser($user_id) { + $sql = 'SELECT * FROM `*PREFIX*passman_files` ' . + 'WHERE `user_id` = ?'; + $params = [$user_id]; + + return $this->findEntities($sql, $params); + } } \ No newline at end of file diff --git a/lib/Service/CredentialService.php b/lib/Service/CredentialService.php index 0bfa5c8e..17b2fd0f 100644 --- a/lib/Service/CredentialService.php +++ b/lib/Service/CredentialService.php @@ -61,12 +61,13 @@ class CredentialService { /** * Update credential * - * @param $credential array + * @param $credential array | Credential + * @param $useRawUser bool * @return Credential */ - public function updateCredential($credential) { + public function updateCredential($credential, $useRawUser = false) { $credential = $this->encryptService->encryptCredential($credential); - return $this->credentialMapper->updateCredential($credential); + return $this->credentialMapper->updateCredential($credential, $useRawUser); } /** diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index d6257085..829b7927 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -101,4 +101,18 @@ class FileService { return $this->fileMapper->updateFile($file); } + /** + * Update file + * + * @param string $userId + * @return File[] + */ + public function getFilesFromUser($userId){ + $files = $this->fileMapper->getFilesFromUser($userId); + $results = array(); + foreach ($files as $file){ + array_push($results, $this->encryptService->decryptFile($file)); + } + return $results; + } } \ No newline at end of file diff --git a/sass/admin.scss b/sass/admin.scss new file mode 100644 index 00000000..259ad564 --- /dev/null +++ b/sass/admin.scss @@ -0,0 +1,12 @@ +#passwordSharingSettings{ + #mover{ + table{ + td{ + padding: 5px; + } + } + input[type="text"]{ + width: 350px; + } + } +} \ No newline at end of file diff --git a/templates/part.admin.php b/templates/part.admin.php index 10b7d98d..9673d540 100644 --- a/templates/part.admin.php +++ b/templates/part.admin.php @@ -5,6 +5,9 @@ use \OCP\App; script('passman', 'settings-admin'); +style('passman', 'admin'); +style('passman', 'vendor/font-awesome/font-awesome.min'); + $checkVersion = OC::$server->getConfig()->getAppValue('passman', 'check_version', '1') === '1'; $AppInstance = new App(); $localVersion = $AppInstance->getAppInfo("passman")["version"]; @@ -41,86 +44,126 @@ $ciphers = openssl_get_cipher_methods(); ?>
-
-

t('Passman Settings')); ?>

- t('Github version:') . ' ' . $githubVersion); - print '
'; - } ?> - Local version:
- t('A newer version of passman is available')); - } - ?> -

t('Sharing')); ?>

-

- - -

+

t('Passman Settings')); ?>

+ t('Github version:') . ' ' . $githubVersion); + print '
'; + } ?> + Local version:
+ t('A newer version of passman is available')); + } + ?> +
+ +
+ +

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + +

+ +
+
+

+ + +

-

- - -

-

t('General')); ?>

-

- - -

-

- - -

-

- - -

-

- - -

-

- - -

- +

+ + +

+
+
+

t('Move credentials from one account to another')); ?>

+
+ + + + + + + + + +
t('Source account')); ?>
t('Destination account')); ?>
+ + + +
+
+ Requests to delete vault +
+ +