diff options
author | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-02-28 12:57:33 +0300 |
---|---|---|
committer | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-02-28 12:57:33 +0300 |
commit | 445cf8d9abf34bdce1ec25669a9a0deb81c5febe (patch) | |
tree | f9043274853db6f11926e15ac4afff1f4ae81ad6 | |
parent | 2d04bf0f1f483b144e7e65a8ed8ef2b429916641 (diff) | |
parent | 94bf2d2c92b4bf73243ca33e2da21e264debe9d6 (diff) |
[#155] Merge PR
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
-rw-r--r-- | src/js/Controller/Clipboard/WriteClipboard.js | 21 | ||||
-rw-r--r-- | src/js/Controller/Setting/Get.js | 2 | ||||
-rw-r--r-- | src/js/Controller/Setting/Reset.js | 4 | ||||
-rw-r--r-- | src/js/Controller/Setting/Set.js | 15 | ||||
-rw-r--r-- | src/js/Manager/ClipboardManager.js | 124 | ||||
-rw-r--r-- | src/js/Manager/ControllerManager.js | 7 | ||||
-rw-r--r-- | src/js/Settings/MasterSettingsProvider.js | 12 | ||||
-rw-r--r-- | src/platform/chrome/manifest.json | 4 | ||||
-rw-r--r-- | src/platform/fenix/manifest.json | 3 | ||||
-rw-r--r-- | src/platform/firefox/manifest.json | 3 | ||||
-rw-r--r-- | src/platform/generic/_locales/de/messages.json | 24 | ||||
-rw-r--r-- | src/platform/generic/_locales/en/messages.json | 22 | ||||
-rw-r--r-- | src/vue/Components/List/Item/Password.vue | 8 | ||||
-rw-r--r-- | src/vue/Components/Options/Settings.vue | 38 | ||||
-rw-r--r-- | src/vue/Components/Tools/Generate.vue | 2 |
15 files changed, 278 insertions, 11 deletions
diff --git a/src/js/Controller/Clipboard/WriteClipboard.js b/src/js/Controller/Clipboard/WriteClipboard.js new file mode 100644 index 0000000..de8d14b --- /dev/null +++ b/src/js/Controller/Clipboard/WriteClipboard.js @@ -0,0 +1,21 @@ +import ClipboardManager from '@js/Manager/ClipboardManager'; +import AbstractController from '@js/Controller/AbstractController'; +import ErrorManager from "@js/Manager/ErrorManager"; + +export default class WriteClipboard extends AbstractController { + + /** + * + * @param {Message} message + * @param {Message} reply + */ + async execute(message, reply) { + try { + let result = ClipboardManager.write(message.getPayload().type, message.getPayload().value); + + if(result) reply.setPayload(true); + } catch(e) { + ErrorManager.logError(e) + } + } +} diff --git a/src/js/Controller/Setting/Get.js b/src/js/Controller/Setting/Get.js index cea64a7..269693e 100644 --- a/src/js/Controller/Setting/Get.js +++ b/src/js/Controller/Setting/Get.js @@ -21,6 +21,8 @@ export default class Get extends AbstractController { 'theme.current', 'theme.custom', 'debug.localisation.enabled', + 'clipboard.clear.delay', + 'clipboard.clear.passwords', 'search.recommendation.mode', 'search.recommendation.maxRows' ]; diff --git a/src/js/Controller/Setting/Reset.js b/src/js/Controller/Setting/Reset.js index 3d05c9c..ce66856 100644 --- a/src/js/Controller/Setting/Reset.js +++ b/src/js/Controller/Setting/Reset.js @@ -19,7 +19,9 @@ export default class Reset extends AbstractController { 'theme.custom', 'debug.localisation.enabled', 'search.recommendation.mode', - 'search.recommendation.maxRows' + 'search.recommendation.maxRows', + 'clipboard.clear.passwords', + 'clipboard.clear.delay' ]; } diff --git a/src/js/Controller/Setting/Set.js b/src/js/Controller/Setting/Set.js index 7522560..8afeeb3 100644 --- a/src/js/Controller/Setting/Set.js +++ b/src/js/Controller/Setting/Set.js @@ -16,7 +16,8 @@ export default class Set extends AbstractController { 'popup.related.search', 'notification.password.new', 'notification.password.update', - 'debug.localisation.enabled' + 'debug.localisation.enabled', + 'clipboard.clear.passwords' ]; } @@ -40,6 +41,8 @@ export default class Set extends AbstractController { await this._setSearchRecommendationMode(value); } else if(setting === 'search.recommendation.maxRows') { await this._setSearchRecommendationMaxRows(Number(value)); + } else if(setting === 'clipboard.clear.delay') { + await this._setClipboardClearDelay(Number(value)); } else if(this._booleanSettings.indexOf(setting) !== -1) { await this._setBoolean(setting, value); } else { @@ -115,4 +118,14 @@ export default class Set extends AbstractController { async _setBoolean(setting, value) { await SettingsService.set(setting, value === true); } + + /** + * + * @param {Number} value + * @return {Promise<void>} + * @private + */ + async _setClipboardClearDelay(value) { + await SettingsService.set('clipboard.clear.delay', value); + } }
\ No newline at end of file diff --git a/src/js/Manager/ClipboardManager.js b/src/js/Manager/ClipboardManager.js new file mode 100644 index 0000000..6fb8009 --- /dev/null +++ b/src/js/Manager/ClipboardManager.js @@ -0,0 +1,124 @@ +import ErrorManager from '@js/Manager/ErrorManager'; +import SettingsService from '@js/Services/SettingsService'; +import SystemService from '@js/Services/SystemService'; + +class ClipboardManager { + + /** + * + * @return {Boolean} + */ + async requestReadPermission() { + try { + return await SystemService.getBrowserApi().permissions.request({ + permissions: ["clipboardRead"] + }); + } catch (e) { + ErrorManager.logError(e, "ClipboardManager.getReadPermission()"); + } + } + + /** + * + * @return {Boolean} + */ + async getReadPermissions() { + try { + return await SystemService.getBrowserApi().permissions.contains({ + permissions: ["clipboardRead"] + }); + } catch (e) { + ErrorManager.logError(e, "ClipboardManager.getReadPermission()"); + } + } + + /** + * + * @return {String} + */ + async readText() { + try { + if(await SystemService.getBrowserApi().extension.getBackgroundPage() !== window) { + var permissions = await this.requestReadPermission(); + } + if(permissions === true || await this.getReadPermissions()) { + var element = this._createDOMElement(); + await document.execCommand('paste'); + var result = element.value; + this._removeDOMElement(element); + return result; + } + } catch (e) { + ErrorManager.logError(e, "ClipboardManager.readText()"); + } + } + + /** + * + * @param {String} type + * @param {String} value + */ + write(type, value) { + if(type === "password") { + this.writePassword(value); + } + else { + this.writeText(value); + } + } + + /** + * + * @param {String} value + * @param {String} type + */ + writeText(value) { + try { + var element = this._createDOMElement(value); + document.execCommand('copy', false, element.value); + this._removeDOMElement(element); + } catch (e) { + ErrorManager.logError(e); + } + } + + /** + * + * @param {String} value + */ + async writePassword(value) { + this.writeText(value); + if(await SettingsService.getValue('clipboard.clear.passwords') == true) { + setTimeout(async () => { + var current = await this.readText(); + if(current === undefined || current === "" || current === value) { + this.writeText(" "); + } + }, Number(await SettingsService.getValue('clipboard.clear.delay')) * 1000) + } + } + + /** + * + * @param {String} type + */ + _createDOMElement(value = "", type = "text") { + var element = document.createElement("INPUT"); + element.setAttribute("type", type); + element.setAttribute("value", value); + document.body.appendChild(element); + element.select(); + return element; + } + + /** + * + * @param {String} type + */ + _removeDOMElement(element) { + element.blur(); + document.body.removeChild(element); + } +} + +export default new ClipboardManager();
\ No newline at end of file diff --git a/src/js/Manager/ControllerManager.js b/src/js/Manager/ControllerManager.js index 3d68736..61e683a 100644 --- a/src/js/Manager/ControllerManager.js +++ b/src/js/Manager/ControllerManager.js @@ -298,6 +298,13 @@ class ControllerManager { await this._executeController(module, message, reply); } ); + MessageService.listen( + 'clipboard.write', + async (message, reply) => { + let module = await import(/* webpackChunkName: "WriteClipboard" */ '@js/Controller/Clipboard/WriteClipboard'); + await this._executeController(module, message, reply); + } + ); } /** diff --git a/src/js/Settings/MasterSettingsProvider.js b/src/js/Settings/MasterSettingsProvider.js index 05c9b02..e424864 100644 --- a/src/js/Settings/MasterSettingsProvider.js +++ b/src/js/Settings/MasterSettingsProvider.js @@ -85,6 +85,14 @@ class MasterSettingsProvider { 'sync.search.recommendation.mode', 'local.search.recommendation.mode' ], + 'clipboard.clear.passwords' : [ + 'sync.clipboard.clear.passwords', + 'local.clipboard.clear.passwords', + ], + 'clipboard.clear.delay' : [ + 'sync.clipboard.clear.delay', + 'local.clipboard.clear.delay', + ] }; this._defaults = { 'theme.custom' : null, @@ -101,7 +109,9 @@ class MasterSettingsProvider { 'notification.password.update' : true, 'debug.localisation.enabled' : true, 'search.recommendation.mode' : 'host', - 'search.recommendation.maxRows': 8 + 'search.recommendation.maxRows': 8, + 'clipboard.clear.passwords' : false, + 'clipboard.clear.delay' : 60 }; } diff --git a/src/platform/chrome/manifest.json b/src/platform/chrome/manifest.json index c15924b..c2ca77b 100644 --- a/src/platform/chrome/manifest.json +++ b/src/platform/chrome/manifest.json @@ -49,9 +49,13 @@ "*://*/*", "tabs", "storage", + "clipboardWrite", "contextMenus", "notifications", "webRequest", "webRequestBlocking" + ], + "optional_permissions" : [ + "clipboardRead" ] }
\ No newline at end of file diff --git a/src/platform/fenix/manifest.json b/src/platform/fenix/manifest.json index 21aac85..e1e299d 100644 --- a/src/platform/fenix/manifest.json +++ b/src/platform/fenix/manifest.json @@ -82,5 +82,8 @@ "clipboardWrite", "webRequest", "webRequestBlocking" + ], + "optional_permissions" : [ + "clipboardRead" ] }
\ No newline at end of file diff --git a/src/platform/firefox/manifest.json b/src/platform/firefox/manifest.json index 5beb17b..38df3c7 100644 --- a/src/platform/firefox/manifest.json +++ b/src/platform/firefox/manifest.json @@ -90,5 +90,8 @@ "clipboardWrite", "webRequest", "webRequestBlocking" + ], + "optional_permissions" : [ + "clipboardRead" ] }
\ No newline at end of file diff --git a/src/platform/generic/_locales/de/messages.json b/src/platform/generic/_locales/de/messages.json index 61c00be..ba8f7ef 100644 --- a/src/platform/generic/_locales/de/messages.json +++ b/src/platform/generic/_locales/de/messages.json @@ -103,6 +103,28 @@ "message" : "Antwortet auf HTTP Basic Authentication Anfragen mit dem ersten Eintrag der vorgeschlagenen Zugangsdaten. Dadurch können Zugangsdaten ungewollt an nicht vertrauenswürdige Server übermittelt werden.", "description": "Help text in the extension settings for the setting to automatically respond with the first recommended credential to any http authentication auth request" }, + "SettingsClearClipboardPasswords" : { + "message" : "Zwischenablage nach einer bestimmten Zeit automatisch löschen", + "description": "Label of the setting in the extension settings to automatically clear passwords from clipboard after a certain time." + }, + "HelpClearClipboardPasswords" : { + "message" : "Die Zwischenablage kann nur geleert werden, wenn noch mindestens ein Browserfenster offen ist.", + "description": "Help text in the extension settings for the setting to automatically clean passwords from clipboard after a certain time." + }, + "SettingsClearClipboardDelay" : { + "message" : "Zwischenablage nach Ablauf folgender Zeit leeren (Sekunden)", + "description": "Label of the setting in the extension settings to define the time when the clipboard content will be removed." + }, + "SettingsClipboardClearDelayOptions" : { + "message" : "$ROW$", + "description" : "Time in seconds until clipboard will get empty.", + "placeholders": { + "row": { + "content": "$1", + "example": "One of 15, 30, 45, 60, 90" + } + } + }, "NotificationSettings" : { "message" : "Benachrichtigungen", "description": "Headline above the notification section in the other settings tab in the extension settings" @@ -1219,4 +1241,4 @@ } } } -}
\ No newline at end of file +} diff --git a/src/platform/generic/_locales/en/messages.json b/src/platform/generic/_locales/en/messages.json index c855735..4844909 100644 --- a/src/platform/generic/_locales/en/messages.json +++ b/src/platform/generic/_locales/en/messages.json @@ -103,6 +103,28 @@ "message" : "Automatically respond to http basic authentication requests with the first suggested credential. Be aware that this may expose credentials to untrustworthy servers.", "description": "Help text in the extension settings for the setting to automatically respond with the first recommended credential to any http basic authentication request" }, + "SettingsClearClipboardPasswords" : { + "message" : "Automatically empty clipboard after a certain time", + "description": "Label of the setting in the extension settings to automatically clear passwords from clipboard after a certain time." + }, + "HelpClearClipboardPasswords" : { + "message" : "This feature will not clear the clipboard if you close all browser windows before the timeout is reached!", + "description": "Help text in the extension settings for the setting to automatically clean passwords from clipboard after a certain time." + }, + "SettingsClearClipboardDelay" : { + "message" : "Empty clipboard after the defined time (seconds)", + "description": "Label of the setting in the extension settings to define the time when the clipboard content will be removed." + }, + "SettingsClipboardClearDelayOptions" : { + "message" : "$ROW$", + "description" : "Time in seconds until clipboard will get empty.", + "placeholders": { + "row": { + "content": "$1", + "example": "One of 15, 30, 45, 60, 90" + } + } + }, "NotificationSettings" : { "message" : "Notifications", "description": "Headline above the notification section in the other settings tab in the extension settings" diff --git a/src/vue/Components/List/Item/Password.vue b/src/vue/Components/List/Item/Password.vue index 28d59ea..7e99be1 100644 --- a/src/vue/Components/List/Item/Password.vue +++ b/src/vue/Components/List/Item/Password.vue @@ -5,8 +5,8 @@ {{ password.getLabel() }} </div> <div class="options"> - <icon icon="user" hover-icon="clipboard" @click="copy('username')" draggable="true" @dragstart="drag($event, 'username')"/> - <icon icon="key" font="solid" hover-icon="clipboard" hover-font="regular" @click="copy('password')" draggable="true" @dragstart="drag($event, 'password')"/> + <icon icon="user" hover-icon="clipboard" @click="copy('username', 'text')" draggable="true" @dragstart="drag($event, 'username')"/> + <icon icon="key" font="solid" hover-icon="clipboard" hover-font="regular" @click="copy('password', 'password')" draggable="true" @dragstart="drag($event, 'password')"/> </div> <icon :class="securityClass" icon="shield-alt" font="solid"/> </li> @@ -92,9 +92,9 @@ ErrorManager.logError(e); } }, - copy(property) { + copy(property, type) { let data = this.password.getProperty(property); - navigator.clipboard.writeText(data); + MessageService.send({type: 'clipboard.write', payload: {type: type, value: data}}).catch(ErrorManager.catch); let label = property.capitalize(); if(['password', 'username', 'url'].indexOf(property) === -1) { diff --git a/src/vue/Components/Options/Settings.vue b/src/vue/Components/Options/Settings.vue index 1eade48..ad6162c 100644 --- a/src/vue/Components/Options/Settings.vue +++ b/src/vue/Components/Options/Settings.vue @@ -23,6 +23,15 @@ <translate tag="label" for="paste-basic-auth" say="SettingsPasteBasicAuth"/> <help-text type="warning" text="HelpPasteBasicAuth"/> </div> + <div class="setting"> + <slider-field id="clipboard-clear-passwords" v-model="clearClipboard"/> + <translate tag="label" for="clipboard-clear-passwords" say="SettingsClearClipboardPasswords"/> + <help-text type="warning" text="HelpClearClipboardPasswords"/> + </div> + <div class="setting"> + <translate tag="label" for="clipboard-clear-delay" say="SettingsClearClipboardDelay"/> + <select-field id="clipboard-clear-delay" :options="clearClipboardDelayOptions" v-model="clearClipboardDelay"/> + </div> <translate tag="h3" say="NotificationSettings"/> <div class="setting"> @@ -61,6 +70,8 @@ import SliderField from "@vue/Components/Form/SliderField"; import SelectField from "@vue/Components/Form/SelectField"; import HelpText from "@vue/Components/Options/Setting/HelpText"; + import ClipboardManager from '@js/Manager/ClipboardManager'; + export default { components: {HelpText, SliderField, SelectField, Translate}, @@ -75,7 +86,9 @@ relatedSearch : false, notifyPwUpdate : false, recSearchMode : 'host', - recSearchRows : 8 + recSearchRows : 8, + clearClipboard : false, + clearClipboardDelay: 60 }; }, @@ -110,7 +123,15 @@ for(i =1; i <= 20; i++) { result.push({id: i, label: ['SearchRecommendationMaxRowsNumber', i]}); } - return result; + return result; + }, + clearClipboardDelayOptions() { + var i = 1; + var result = []; + for(let i of [15, 30, 45, 60, 90]) { + result.push({id: i, label: ['SettingsClipboardClearDelayOptions', i]}); + } + return result; } }, @@ -126,6 +147,8 @@ this.getSetting('notification.password.update', 'notifyPwUpdate'); this.getSetting('search.recommendation.mode', 'recSearchMode'); this.getSetting('search.recommendation.maxRows', 'recSearchRows'); + this.getSetting('clipboard.clear.passwords', 'clearClipboard'); + this.getSetting('clipboard.clear.delay', 'clearClipboardDelay'); }, async getSetting(name, variable) { try { @@ -171,6 +194,17 @@ this.setSetting('paste.basic-auth', value); } }, + clearClipboard(value, oldValue) { + if(value === true) ClipboardManager.requestReadPermission(); + if(oldValue !== null && value !== oldValue) { + this.setSetting('clipboard.clear.passwords', value); + } + }, + clearClipboardDelay(value, oldValue) { + if(oldValue !== null && value !== oldValue) { + this.setSetting('clipboard.clear.delay', value); + } + }, relatedSearch(value, oldValue) { if(oldValue !== null && value !== oldValue) { this.setSetting('popup.related.search', value); diff --git a/src/vue/Components/Tools/Generate.vue b/src/vue/Components/Tools/Generate.vue index ba1c55a..61e6aef 100644 --- a/src/vue/Components/Tools/Generate.vue +++ b/src/vue/Components/Tools/Generate.vue @@ -101,7 +101,7 @@ copy() { let data = this.password, label = LocalisationService.translate('PropertyPassword'); - navigator.clipboard.writeText(data); + MessageService.send({type: 'clipboard.write', payload: {type: 'password', value: data}}).catch(ErrorManager.catch); ToastService .success(['PasswordPropertyCopied', label]) |