From 16839713e5d89d602efc2b57212a97af35d85bb4 Mon Sep 17 00:00:00 2001 From: binsky Date: Fri, 12 Mar 2021 16:26:34 +0100 Subject: [PATCH] implement global nextcloud search credential handling --- js/app/controllers/credential.js | 19 ++++++++ js/templates.js | 74 ++++++++++++++++---------------- lib/Search/Provider.php | 50 +++++++++++++++++---- 3 files changed, 97 insertions(+), 46 deletions(-) diff --git a/js/app/controllers/credential.js b/js/app/controllers/credential.js index 3c70736a..9d84f33c 100644 --- a/js/app/controllers/credential.js +++ b/js/app/controllers/credential.js @@ -118,6 +118,7 @@ VaultService.updateSharingKeys($scope.active_vault); }); } + $scope.checkURLAction(); }); }); }; @@ -544,6 +545,24 @@ VaultService.clearVaultService(); }); + $scope.$watch(function(){ return $location.search(); }, function(params){ + $scope.checkURLAction(); + }); + + $scope.checkURLAction = function () { + var search = $location.search(); + if (search.show !== undefined && $scope.active_vault.credentials !== undefined && + $scope.active_vault.credentials.length > 0) { + $scope.closeSelected(); + $scope.active_vault.credentials.forEach(function(credential, index, myArray) { + if (credential.guid === search.show) { + $scope.selectCredential(credential); + return true; + } + }); + } + }; + $scope.clearState = function () { $scope.delete_time = 0; }; diff --git a/js/templates.js b/js/templates.js index 0cf3ef7c..07eed1ea 100644 --- a/js/templates.js +++ b/js/templates.js @@ -1,96 +1,96 @@ angular.module('templates-main', ['views/credential_revisions.html', 'views/edit_credential.html', 'views/partials/credential_template.html', 'views/partials/forms/edit_credential/basics.html', 'views/partials/forms/edit_credential/custom_fields.html', 'views/partials/forms/edit_credential/files.html', 'views/partials/forms/edit_credential/otp.html', 'views/partials/forms/edit_credential/password.html', 'views/partials/forms/settings/export.html', 'views/partials/forms/settings/general_settings.html', 'views/partials/forms/settings/generic_csv_import.html', 'views/partials/forms/settings/import.html', 'views/partials/forms/settings/password_settings.html', 'views/partials/forms/settings/sharing.html', 'views/partials/forms/settings/tool.html', 'views/partials/forms/share_credential/basics.html', 'views/partials/forms/share_credential/link_sharing.html', 'views/partials/icon-picker.html', 'views/partials/password-meter.html', 'views/settings.html', 'views/share_credential.html', 'views/show_vault.html', 'views/vault_req_deletion.html', 'views/vaults.html']); -angular.module('views/credential_revisions.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/credential_revisions.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/credential_revisions.html', '
{{ \'revision.of\' | translate}} {{revision.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} ({{revision.credential_data.label}}) {{ \'revision.edited.by\' | translate}} {{revision.edited_by}}
{{ \'no.revisions\' | translate}}
{{ \'revision.of\' | translate}} {{selectedRevision.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'label\' | translate }}
{{ \'account\' | translate }}
{{ \'password\' | translate }}
{{\'otp\' | translate}}
{{\'email\' | translate}}
{{ \'url\' | translate}}
{{\'notes\' | translate}}
{{ \'files\' | translate}}
{{field.label}}
{{field.value.filename}} ({{field.value.size | bytes}})
{{ \'expire.time\' | translate }}
{{selectedRevision.credential_data.expire_time * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'changed\' | translate}}
{{selectedRevision.credential_data.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'created\' | translate}}
{{selectedRevision.credential_data.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{tag.text}}
'); }]); -angular.module('views/edit_credential.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/edit_credential.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/edit_credential.html', '
'); }]); -angular.module('views/partials/credential_template.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/credential_template.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/credential_template.html', '
{{ \'label\' | translate }}
{{ \'compromised.warning\' | translate }}
{{ \'account\' | translate }}
{{ \'password\' | translate }}
{{\'otp\' | translate}}
{{\'email\' | translate}}
{{ \'url\' | translate}}
{{\'notes\' | translate}}
{{field.label}}
{{field.value.filename}} ({{field.value.size | bytes}})
{{ \'expire.time\' | translate }}
{{credential.expire_time * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'changed\' | translate}}
{{credential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'created\' | translate}}
{{credential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{tag.text}}
'); }]); -angular.module('views/partials/forms/edit_credential/basics.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/edit_credential/basics.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/basics.html', - '
{{ \'compromised.warning\' | translate }}
'); + '
{{ \'compromised.warning\' | translate }}
'); }]); -angular.module('views/partials/forms/edit_credential/custom_fields.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/edit_credential/custom_fields.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/custom_fields.html', - '
{{ \'label\' | translate}}{{ \'value\' | translate}}{{ \'type\' | translate}}{{ \'actions\' | translate}}
{{ field.label || "empty" }}{{ field.value || \'empty\' | translate }} * {{field.value.filename}} ({{field.value.size | bytes}}){{ field.field_type }}
'); + '
{{ \'label\' | translate}}{{ \'value\' | translate}}{{ \'type\' | translate}}{{ \'actions\' | translate}}
{{ field.label || "empty" }}{{ field.value || \'empty\' | translate }} * {{field.value.filename}} ({{field.value.size | bytes}}){{ field.field_type }}
'); }]); -angular.module('views/partials/forms/edit_credential/files.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/edit_credential/files.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/files.html', - '
{{ \'file.name\' | translate }}{{ \'upload.date\' | translate}}{{ \'size\' | translate}}{{ \'actions\' | translate}}
{{ file.filename || "empty" }}{{file.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}{{file.size | bytes}}
'); + '
{{ \'file.name\' | translate }}{{ \'upload.date\' | translate}}{{ \'size\' | translate}}{{ \'actions\' | translate}}
{{ file.filename || "empty" }}{{file.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}{{file.size | bytes}}
'); }]); -angular.module('views/partials/forms/edit_credential/otp.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/edit_credential/otp.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/otp.html', - '
{{ \'upload.qr\' | translate}}
{{ \'current.qr\' | translate}}
{{ \'type\' | translate}}:{{storedCredential.otp.type}}
{{ \'label\' | translate}}:{{storedCredential.otp.label}}
{{ \'issuer\' | translate}}:{{storedCredential.otp.issuer}}
{{ \'secret\' | translate}}:{{storedCredential.otp.secret}}
{{ \'otp\' | translate}}:
'); + '
{{ \'upload.qr\' | translate}}
{{ \'current.qr\' | translate}}
{{ \'type\' | translate}}:{{storedCredential.otp.type}}
{{ \'label\' | translate}}:{{storedCredential.otp.label}}
{{ \'issuer\' | translate}}:{{storedCredential.otp.issuer}}
{{ \'secret\' | translate}}:{{storedCredential.otp.secret}}
{{ \'otp\' | translate}}:
'); }]); -angular.module('views/partials/forms/edit_credential/password.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/edit_credential/password.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/password.html', - '
{{\'no.expire.date\' | translate}} {{ storedCredential.expire_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'generation.settings\' | translate}}
'); + '
{{\'no.expire.date\' | translate}} {{ storedCredential.expire_time | date:\'dd-MM-yyyy @ HH:mm:ss\'}}
{{ \'generation.settings\' | translate}}
'); }]); -angular.module('views/partials/forms/settings/export.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/export.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/export.html', - '
{{selectedExporter.description}}

{{error}}
'); + '
{{selectedExporter.description}}

{{error}}
'); }]); -angular.module('views/partials/forms/settings/general_settings.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/general_settings.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/general_settings.html', - '

{{ \'rename.vault\' | translate}}

{{ \'change.vault.key\' | translate}}

  • {{error}}
  • {{\'min.vault.key.strength\' | translate:required_score}}
{{\'warning.leave\' | translate}}
{{ \'processing\' | translate}} {{cur_state.process}}
{{ \'total.progress\' | translate}}

{{\'delete.vault\' | translate}}

{{ \'vault.remove.notice\' | translate }} {{\'delete.vault.checkbox\' | translate}}
{{\'deleting.pw\' | translate:translationData}}

{{ \'about.passman\' | translate}}

{{ \'version\' | translate}}: {{passman_version}}

{{ \'donate.support\' | translate}}

{{ \'bookmarklet\' | translate}}

{{ \'bookmarklet.info1\' | translate}}
{{ \'bookmarklet.info2\' | translate}}

'); + '

{{ \'rename.vault\' | translate}}

{{ \'change.vault.key\' | translate}}

  • {{error}}
  • {{\'min.vault.key.strength\' | translate:required_score}}
{{\'warning.leave\' | translate}}
{{ \'processing\' | translate}} {{cur_state.process}}
{{ \'total.progress\' | translate}}

{{\'delete.vault\' | translate}}

{{ \'vault.remove.notice\' | translate }} {{\'delete.vault.checkbox\' | translate}}
{{\'deleting.pw\' | translate:translationData}}

{{ \'about.passman\' | translate}}

{{ \'version\' | translate}}: {{passman_version}}

{{ \'donate.support\' | translate}}

{{ \'bookmarklet\' | translate}}

{{ \'bookmarklet.info1\' | translate}}
{{ \'bookmarklet.info2\' | translate}}

'); }]); -angular.module('views/partials/forms/settings/generic_csv_import.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/generic_csv_import.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/generic_csv_import.html', '
{{ \'select.csv\' | translate}}
{{ \'skip.first.row\' | translate}}
{{ \'import.csv.label.req\' | translate}}
{{ \'upload.progress\' | translate}}
{{ \'first.five.lines\' | translate }}
{{ \'assign.column\' | translate }}
{{line[$index]}}
{{ \'example.credential\' | translate}}
'); }]); -angular.module('views/partials/forms/settings/import.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/import.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/import.html', - '
{{ \'import.steps\' | translate }}
  • {{step}}

{{ \'read.progress\' | translate}}
{{ \'upload.progress\' | translate}}
'); + '
{{ \'import.steps\' | translate }}
  • {{step}}

{{ \'read.progress\' | translate}}
{{ \'upload.progress\' | translate}}
'); }]); -angular.module('views/partials/forms/settings/password_settings.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/password_settings.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/password_settings.html', - '
'); + '
'); }]); -angular.module('views/partials/forms/settings/sharing.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/sharing.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/sharing.html', - '
'); + '
'); }]); -angular.module('views/partials/forms/settings/tool.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/settings/tool.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/settings/tool.html', '

{{ \'tool.intro\' | translate}}

{{ \'min.strength\' | translate}}

{{ \'scan.result.msg\' | translate}}

{{ \'label\' | translate}}{{ \'score\' | translate}}{{ \'password\' | translate}}{{ \'action\' | translate}}
{{result.label}}
'); }]); -angular.module('views/partials/forms/share_credential/basics.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/share_credential/basics.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/share_credential/basics.html', '
{{\'cyphering\' | translate}}...
{{ \'uploading\' | translate}}...
{{ \'user\' | translate}}{{ \'crypto.time\' | translate}}
{{user.user}}{{user.time}} s
{{ \'crypto.total.time\' | translate}}: {{ calculate_total_time() }}
{{\'user\' | translate}}{{ \'perm.read\' | translate}}{{ \'perm.write\' | translate}}{{ \'perm.files\' | translate}}{{ \'perm.revisions\' | translate}}
{{user.userId}} {{ \'pending\' | translate}}
'); }]); -angular.module('views/partials/forms/share_credential/link_sharing.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/forms/share_credential/link_sharing.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/share_credential/link_sharing.html', '

{{ \'share.until.date\' | translate}} {{ share_settings.linkSharing.settings.expire_time | date:\'dd-MM-yyyy @ HH:mm:ss\' }}
{{ \'expire.views\' | translate}}
{{ \'show.files\' | translate}}
'); }]); -angular.module('views/partials/icon-picker.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/icon-picker.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/icon-picker.html', - '
'); + '
'); }]); -angular.module('views/partials/password-meter.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/partials/password-meter.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/password-meter.html', '
{{ \'details\' | translate }} {{ \'hide.details\' | translate}}
{{message}}
{{ \'password.score\' | translate}}:
{{score.score}}
{{ \'cracking.times\' | translate}}
{{ \'cracking.time.100h\' | translate}}
{{ \'cracking.time.100h.desc\' | translate}}
{{score.crack_times_display.online_throttling_100_per_hour}}
{{ \'cracking.time.10s\' | translate}}
{{ \'cracking.time.10s.desc\' | translate}}
{{score.crack_times_display.online_no_throttling_10_per_second}}
{{ \'cracking.time.10ks\' | translate}}
{{ \'cracking.time.10ks.desc\' | translate}}
{{score.crack_times_display.offline_slow_hashing_1e4_per_second}}
{{ \'cracking.time.10Bs\' | translate}}
{{ \'cracking.time.10Bs.desc\' | translate}}
{{score.crack_times_display.offline_fast_hashing_1e10_per_second}}
{{ \'match.sequence\' | translate}}:
{{ \'match.sequence.link\' | translate}}
{{sequence.token}}
{{ \'pattern\' | translate}}{{sequence.pattern}}
{{ \'matched.word\' | translate}}{{sequence.matched_word}}
{{ \'dictionary.name\' | translate}}{{sequence.dictionary_name}}
{{ \'rank\' | translate}}{{sequence.rank}}
{{ \'reversed\' | translate}}{{sequence.reversed}}
{{ \'guesses\' | translate}}{{sequence.guesses}}
{{ \'base.guesses\' | translate}}{{sequence.base_guesses}}
{{ \'uppercase.variations\' | translate}}{{sequence.uppercase_variations}}
{{ \'leet.variations\' | translate}}{{sequence.l33t_variations}}
'); }]); -angular.module('views/settings.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/settings.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/settings.html', '
'); }]); -angular.module('views/share_credential.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/share_credential.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/share_credential.html', '
'); }]); -angular.module('views/show_vault.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/show_vault.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/show_vault.html', '
{{ \'use.regex\' | translate }}
{{ ::tag.text}} {{ ::credential.label}} {{ \'compromised.warning.list\' | translate}}
  • {{ ::credential.label}}
    {{ ::tag.text}}
{{\'vault.hint.hello\' | translate}}
{{\'vault.hint.hello.add\' | translate}}
{{ \'vault.hint.list.notags\' | translate}}
{{ \'vault.hint.list.nosearch\' | translate}} \'{{filterOptions.filterText}}\'
{{ \'vault.hint.list.nogood\' | translate}}
{{ \'vault.hint.list.nomedium\' | translate}}
{{ \'vault.hint.list.nobad\' | translate}}
{{ \'vault.hint.list.noexpired\' | translate}}
{{ \'vault.hint.list.nodeleted\' | translate}}
'); }]); -angular.module('views/vault_req_deletion.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/vault_req_deletion.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/vault_req_deletion.html', - '
{{ \'req.intro1\' | translate }}
{{ \'req.intro2\' | translate }}
{{ \'req.intro3\' | translate }}

{{ \'request.deletion.warning\' | translate}} {{ \'cancel\' | translate}}
Cancel
'); + '
{{ \'req.intro1\' | translate }}
{{ \'req.intro2\' | translate }}
{{ \'req.intro3\' | translate }}

{{ \'request.deletion.warning\' | translate}} {{ \'cancel\' | translate}}
Cancel
'); }]); -angular.module('views/vaults.html', []).run(['$templateCache', function($templateCache) { +angular.module('views/vaults.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/vaults.html', - '
  • + Create a new vault
  • {{vault.name}} {{ \'delete.request.pending\' | translate}}
    {{ \'created\' | translate}}: {{vault.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} | {{ \'last.access\' | translate}}: {{vault.last_access * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} {{\'never\' | translate}}
  • {{ \'no.vaults\' | translate}}
  • {{ \'go.back.vaults\' | translate }}
'); + '
  • + Create a new vault
  • {{vault.name}} {{ \'delete.request.pending\' | translate}}
    {{ \'created\' | translate}}: {{vault.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} | {{ \'last.access\' | translate}}: {{vault.last_access * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} {{\'never\' | translate}}
  • {{ \'no.vaults\' | translate}}
  • {{ \'go.back.vaults\' | translate }}
'); }]); diff --git a/lib/Search/Provider.php b/lib/Search/Provider.php index 0a7292aa..d4b0da97 100644 --- a/lib/Search/Provider.php +++ b/lib/Search/Provider.php @@ -24,6 +24,13 @@ namespace OCA\Passman\Search; use OCA\Passman\AppInfo\Application; +use OCA\Passman\Db\CredentialMapper; +use OCA\Passman\Db\VaultMapper; +use OCA\Passman\Service\VaultService; +use OCA\Passman\Utility\Utils; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\IDBConnection; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; @@ -31,6 +38,7 @@ use OCP\Search\IProvider; use OCP\Search\ISearchQuery; use OCP\Search\SearchResult; use OCP\Search\SearchResultEntry; +use Safe\Exceptions\StringsException; class Provider implements IProvider { @@ -40,9 +48,12 @@ class Provider implements IProvider { /** @var IURLGenerator */ private IURLGenerator $urlGenerator; - public function __construct(IL10N $l10n, IURLGenerator $urlGenerator) { + private IDBConnection $db; + + public function __construct(IL10N $l10n, IURLGenerator $urlGenerator, IDBConnection $db) { $this->l10n = $l10n; $this->urlGenerator = $urlGenerator; + $this->db = $db; } public function getId(): string { @@ -63,16 +74,37 @@ class Provider implements IProvider { } public function search(IUser $user, ISearchQuery $query): SearchResult { + $VaultService = new VaultService(new VaultMapper($this->db, new Utils())); + $Vaults = $VaultService->getByUser($user->getUID()); + $CredentialMapper = new CredentialMapper($this->db, new Utils()); + + $searchResultEntries = []; + + foreach ($Vaults as $Vault) { + try { + $Credentials = $CredentialMapper->getCredentialsByVaultId($Vault->getId(), $Vault->getUserId()); + + foreach ($Credentials as $Credential) { + if (strpos($Credential->getLabel(), $query->getTerm()) !== false) { + try { + $searchResultEntries[] = new SearchResultEntry( + $this->urlGenerator->imagePath(Application::APP_ID, 'app.svg'), + $Credential->getLabel(), + \Safe\sprintf("Part of Passman vault %s", $Vault->getName()), + $this->urlGenerator->linkToRoute('passman.page.index') . "#/vault/" . $Vault->getGuid() . "?show=" . $Credential->getGuid() + ); + } catch (StringsException $e) { + } + } + } + } catch (DoesNotExistException $e) { + } catch (MultipleObjectsReturnedException $e) { + } + } + return SearchResult::complete( $this->l10n->t(Application::APP_ID), - [ - new SearchResultEntry( - $this->urlGenerator->imagePath(Application::APP_ID, 'app.svg'), - $this->l10n->t('Search in current page'), - $this->l10n->t('This requires an already unlocked Passman vault'), - '#?search=' . $query->getTerm() - ) - ] + $searchResultEntries ); } }