From 08380827c218e3b3dc56ba5ce0ffb33a868712fa Mon Sep 17 00:00:00 2001 From: Bart Nagel Date: Sun, 27 Sep 2020 12:14:08 +0300 Subject: Allow filling KPH attributes from context menu Co-authored-by: varjolintu --- keepassxc-browser/_locales/en/messages.json | 4 ++ keepassxc-browser/background/event.js | 3 +- keepassxc-browser/background/init.js | 3 ++ keepassxc-browser/background/page.js | 61 +++++++++++++++++++++++++- keepassxc-browser/content/keepassxc-browser.js | 24 ++++++++++ 5 files changed, 93 insertions(+), 2 deletions(-) (limited to 'keepassxc-browser') diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index d876115..855e003 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -27,6 +27,10 @@ "message": "Show Password Generator", "description": "Show password generator text." }, + "contextMenuFillAttribute": { + "message": "Fill attributeā€¦", + "description": "Context menu parent item for filling a custom attribute." + }, "multipleCredentialsDetected": { "message": "HTTP authentication with multiple credentials detected. Click on the extension icon to choose the correct one.", "description": "Notification when HTTP Authentication has multiple credentials detected." diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 862f0d3..986935a 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -236,5 +236,6 @@ kpxcEvent.messageHandlers = { 'update_credentials': keepass.updateCredentials, 'username_field_detected': kpxcEvent.onUsernameFieldDetected, 'save_settings': kpxcEvent.onSaveSettings, - 'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC + 'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC, + 'update_context_menu': page.updateContextMenu }; diff --git a/keepassxc-browser/background/init.js b/keepassxc-browser/background/init.js index 3cea8ab..0e7840e 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -107,6 +107,7 @@ const contextMenuItems = [ { title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_username_password' }, { title: tr('contextMenuFillPassword'), action: 'fill_password' }, { title: tr('contextMenuFillTOTP'), action: 'fill_totp' }, + { title: tr('contextMenuFillAttribute'), id: 'fill_attribute', visible: false }, { title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' }, { title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' } ]; @@ -122,6 +123,8 @@ for (const item of contextMenuItems) { browser.contextMenus.create({ title: item.title, contexts: menuContexts, + visible: item.visible, + id: item.id, onclick: (info, tab) => { browser.tabs.sendMessage(tab.id, { action: item.action diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 0d0df3c..7efac4d 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -24,6 +24,7 @@ const defaultSettings = { }; var page = {}; +page.attributeMenuItemIds = []; page.blockedTabs = []; page.currentRequest = {}; page.currentTabId = -1; @@ -170,7 +171,14 @@ page.initSitePreferences = async function() { await browser.storage.local.set({ 'settings': page.settings }); }; -page.switchTab = function(tab) { +page.switchTab = async function(tab) { + browser.contextMenus.update('fill_attribute', { visible: false }); + if (page.tabs[tab.id] + && page.tabs[tab.id].credentials.length > 0 + && page.tabs[tab.id].credentials.some(e => e.stringFields && e.stringFields.length > 0)) { + await page.updateContextMenu(tab, page.tabs[tab.id].credentials); + } + browserAction.showDefault(tab); browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => { console.log('Cannot send activated_tab message: ', e); @@ -203,6 +211,8 @@ page.clearLogins = function(tabId) { page.tabs[tabId].loginList = []; page.currentRequest = {}; page.passwordFilled = false; + + browser.contextMenus.update('fill_attribute', { visible: false }); }; // Clear all logins from all pages and update the content scripts @@ -234,6 +244,7 @@ page.createTabEntry = function(tabId) { }; page.clearSubmittedCredentials(); + browser.contextMenus.update('fill_attribute', { visible: false }); }; page.removePageInformationFromNotExistingTabs = async function() { @@ -312,3 +323,51 @@ page.setSubmitted = async function(tab, args = []) { const [ submitted, username, password, url, oldCredentials ] = args; page.setSubmittedCredentials(submitted, username, password, url, oldCredentials, tab.id); }; + +// Update context menu for attribute filling +page.updateContextMenu = async function(tab, credentials) { + // Remove any old attribute items + while (page.attributeMenuItemIds.length) { + browser.contextMenus.remove(page.attributeMenuItemIds.pop()); + } + + // Set parent item visibility + browser.contextMenus.update('fill_attribute', { visible: true }); + + // Add any new attribute items + for (const cred of credentials) { + if (!cred.stringFields) { + continue; + } + + for (const attribute of cred.stringFields) { + // Show username inside [] if there are KPH attributes inside multiple credentials + const attributeName = Object.keys(attribute)[0].slice(5); + const finalName = credentials.length > 1 + ? `[${cred.login}] ${attributeName}` + : attributeName; + + page.attributeMenuItemIds.push(createContextMenuItem({ + action: 'fill_attribute', + args: attribute, + parentId: 'fill_attribute', + title: finalName + })); + } + } +}; + +const createContextMenuItem = function({action, args, ...options}) { + return browser.contextMenus.create({ + contexts: menuContexts, + onclick: (info, tab) => { + browser.tabs.sendMessage(tab.id, { + action: action, + args: args + }).catch((err) => { + console.log(err); + }); + }, + ...options + }); +}; diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index ee38c4f..0201028 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -720,6 +720,21 @@ kpxc.detectDatabaseChange = async function(response) { } }; +// Fill selected attribute from the context menu +kpxc.fillAttributeToActiveElementWith = async function(attr) { + const el = document.activeElement; + if (el.nodeName !== 'INPUT' || kpxc.credentials.length === 0) { + return; + } + + const value = Object.values(attr); + if (!value || value.length === 0) { + return; + } + + kpxc.setValue(el, value[0]); +}; + // Fill requested from the context menu. Active element is used for combination detection kpxc.fillInFromActiveElement = async function(passOnly = false) { if (kpxc.credentials.length === 0) { @@ -1292,6 +1307,12 @@ kpxc.retrieveCredentialsCallback = async function(credentials) { await kpxc.prepareCredentials(); } + // Update fill_attribute context menu if String Fields are available + const stringFieldsFound = credentials.some(e => e.stringFields && e.stringFields.length > 0); + if (stringFieldsFound) { + await sendMessage('update_context_menu', credentials); + } + // Retrieve submitted credentials if available const creds = await sendMessage('page_get_submitted'); if (creds && creds.submitted) { @@ -1761,6 +1782,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) { } else if (req.action === 'fill_totp') { await kpxc.receiveCredentialsIfNecessary(); kpxc.fillFromTOTP(); + } else if (req.action === 'fill_attribute' && req.args) { + await kpxc.receiveCredentialsIfNecessary(); + kpxc.fillAttributeToActiveElementWith(req.args); } else if (req.action === 'ignore_site') { kpxc.ignoreSite(req.args); } else if (req.action === 'redetect_fields') { -- cgit v1.2.3