From daff6a6a954a7ffd094358676666ba6065ddce4f Mon Sep 17 00:00:00 2001 From: varjolintu Date: Tue, 15 Aug 2023 16:33:05 +0300 Subject: [PATCH] Changes 2023-08-15 --- keepassxc-browser/background/event.js | 7 +++ keepassxc-browser/background/page.js | 14 ++++++ keepassxc-browser/background/protocol.js | 14 +++--- keepassxc-browser/content/fill.js | 38 +++++++++++--- .../content/keepassxc-browser.js | 49 +++++++++---------- keepassxc-browser/popups/popup.js | 2 +- 6 files changed, 85 insertions(+), 39 deletions(-) diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 9c31102..9a654d6 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -137,6 +137,10 @@ kpxcEvent.initLoginPopup = async function(tab, logins) { browserAction.show(tab, popupData); }; +kpxcEvent.isProtocolV2 = async function(tab) { + return keepass.protocolV2; +}; + kpxcEvent.loadKeyRing = async function() { const item = await browser.storage.local.get({ 'keyRing': {} }).catch((err) => { logError('kpxcEvent.loadKeyRing error: ' + err); @@ -294,12 +298,15 @@ kpxcEvent.messageHandlers = { 'hide_troubleshooting_guide_alert': kpxcEvent.hideTroubleshootingGuideAlert, 'init_http_auth': kpxcEvent.initHttpAuth, 'is_connected': kpxcEvent.getIsKeePassXCAvailable, + 'is_protocol_v2': kpxcEvent.isProtocolV2, 'load_keyring': kpxcEvent.loadKeyRing, 'load_settings': kpxcEvent.loadSettings, 'lock_database': kpxcEvent.lockDatabase, + 'page_clear_auto_lock_requested': page.clearAutoLockRequested, 'page_clear_logins': kpxcEvent.pageClearLogins, 'page_clear_submitted': page.clearSubmittedCredentials, 'page_get_autosubmit_performed': page.getAutoSubmitPerformed, + 'page_get_auto_lock_requested': page.getAutoLockRequested, 'page_get_login_id': page.getLoginId, 'page_get_manual_fill': page.getManualFill, 'page_get_redirect_count': kpxcEvent.pageGetRedirectCount, diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 945c9e2..4b8ba27 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -34,6 +34,7 @@ const defaultSettings = { const AUTO_SUBMIT_TIMEOUT = 5000; var page = {}; +page.autoLockRequested = false; page.autoSubmitPerformed = false; page.attributeMenuItemIds = []; page.blockedTabs = []; @@ -212,6 +213,11 @@ page.retrieveCredentials = async function(tab, args = []) { // TODO: Make keepass.js to handle protocol/protocolClient and legacyProtocol/legacyProcotolClient const credentials = await keepass.getCredentials(tab, args); page.tabs[tab.id].credentials = credentials; + + if (credentials.autoLockRequested) { + page.autoLockRequested = true; + } + return credentials; }; @@ -267,6 +273,14 @@ page.setAutoSubmitPerformed = async function(tab) { } }; +page.clearAutoLockRequested = async function() { + page.autoLockRequested = false; +}; + +page.getAutoLockRequested = async function() { + return page.autoLockRequested; +}; + page.getLoginList = async function(tab) { return page.tabs[tab.id] ? page.tabs[tab.id].loginList : []; }; diff --git a/keepassxc-browser/background/protocol.js b/keepassxc-browser/background/protocol.js index 0811a34..5f1f953 100644 --- a/keepassxc-browser/background/protocol.js +++ b/keepassxc-browser/background/protocol.js @@ -190,7 +190,7 @@ protocol.getCredentials = async function(tab, args = []) { } if (httpAuth) { - messageData.httpAuth = 'true'; + messageData.httpAuth = true; } try { @@ -302,19 +302,21 @@ protocol.getDatabaseStatuses = async function(tab, args = []) { } }; +// TODO: Finish this protocol.getTotp = async function(tab, args = []) { if (!keepass.isConnected) { return []; } - const [ uuid, oldTotp ] = args; + /*const [ uuids, oldTotp ] = args; + // KeePassXC 2.6.1 and older does not support retrieving if (!keepass.compareVersion('2.6.1', keepass.currentKeePassXC, true)) { return oldTotp; - } + }*/ const messageData = { action: kpActions.GET_TOTP, - uuid: uuid, + uuids: args, keys: protocol.getKeys(), // Added hash: keepass.databaseHash // Added }; @@ -323,7 +325,7 @@ protocol.getTotp = async function(tab, args = []) { const response = await protocolClient.sendMessage(tab, messageData); if (response) { keepass.updateLastUsed(keepass.databaseHash); - return response.totp; + return response.totpList; } return; @@ -444,7 +446,7 @@ protocol.updateCredentials = async function(tab, args = []) { } const [ entryId, username, password, url, group, groupUuid ] = args; - const [ dbid ] = keepass.getCryptoKey(); + //const [ dbid ] = keepass.getCryptoKey(); const messageData = { action: kpActions.CREATE_CREDENTIALS, diff --git a/keepassxc-browser/content/fill.js b/keepassxc-browser/content/fill.js index 5c8db8c..62b84b5 100644 --- a/keepassxc-browser/content/fill.js +++ b/keepassxc-browser/content/fill.js @@ -140,24 +140,42 @@ kpxcFill.fillTOTPFromUuid = async function(el, uuid) { return; } + let totpFound = false; if (user.totp?.length > 0) { - // Retrieve a new TOTP value - const totp = await sendMessage('get_totp', [ user.uuid, user.totp ]); - if (!totp) { - kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); - return; - } + const protocolV2 = await sendMessage('is_protocol_v2'); + if (protocolV2) { + const totpList = await sendMessage('get_totp', [ user.uuid ]); + if (!totpList) { + kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); + return; + } - kpxcFill.setTOTPValue(el, totp); + const result = totpList.find(t => t.uuid === uuid); + kpxcFill.setTOTPValue(el, result?.totp); + } else { + const totp = await sendMessage('get_totp', [ user.uuid, user.totp ]); + if (!totp) { + kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); + return; + } + + kpxcFill.setTOTPValue(el, totp); + } + return; } else if (user.stringFields?.length > 0) { const stringFields = user.stringFields; for (const s of stringFields) { const val = s['KPH: {TOTP}']; if (val) { kpxcFill.setTOTPValue(el, val); + totpFound = true; } } } + + if (!totpFound) { + kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); + } }; // Set normal or segmented TOTP value @@ -272,6 +290,12 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui await sendMessage('page_set_manual_fill', ManualFill.NONE); await kpxcFill.performAutoSubmit(combination, skipAutoSubmit); + + // Auto-lock database when requested + if (await sendMessage('page_get_auto_lock_requested')) { + await sendMessage('page_clear_auto_lock_requested'); + sendMessage('lock_database'); + } }; // Fills StringFields defined in Custom Fields diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index a89e465..eb234f7 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -748,35 +748,34 @@ kpxc.updateDatabaseState = async function() { kpxc.databaseState = res.databaseClosed ? DatabaseState.LOCKED : DatabaseState.UNLOCKED; }; -// Updates the TOTP Autocomplete Menu +// Updates the TOTP Autocomplete Menu. All TOTP's will be listed +// TODO: Check that the latest TOTP is actually updated with Protocol V1 kpxc.updateTOTPList = async function() { - let uuid = await sendMessage('page_get_login_id'); - if (uuid === undefined || kpxc.credentials.length === 0) { - // Credential haven't been selected - logDebug('Error: No credentials selected for TOTP.'); - return; + const hasTotp = function(cred) { + return cred.totp || (cred.stringFields && cred.stringFields.some(s => s['KPH: {TOTP}'])); + }; + + // Filter credentials with TOTP set + const credentialList = kpxc.credentials.filter(c => hasTotp(c)); + + // Check for duplicate entries (TOTP might be in another database with identical username and title) + const duplicates = credentialList.filter(c => + kpxc.credentials.some(l => l.login === c.login && l.name === c.name && l.uuid !== c.uuid) + ); + + // Use the UUID from the duplicate (if it contains the TOTP) for Autocomplete menu instead of the original + for (const e of kpxcUserAutocomplete.elements) { + const duplicate = duplicates.find(d => d.login === e.value); + if (duplicate) { + e.uuid = duplicate.uuid; + } } - // Use the first credential available if not set - if (uuid === '') { - uuid = kpxc.credentials[0].uuid; - } + // Filter the User Autocomplete menu items to those which contains TOTP + kpxcTOTPAutocomplete.elements = kpxcUserAutocomplete.elements.filter(e => credentialList.find(u => u.uuid === e.uuid)); - const credentials = kpxc.credentials.find(c => c.uuid === uuid); - if (credentials) { - const username = credentials.login; - const password = credentials.password; - - // If no username is set, compare with a password - const credentialList = kpxc.credentials.filter(c => (c.totp || (c.stringFields && c.stringFields.some(s => s['KPH: {TOTP}']))) - && (c.login === username || (!username && c.password === password))); - - // Filter TOTP Autocomplete Menu with matching 2FA credentials - kpxcTOTPAutocomplete.elements = kpxcUserAutocomplete.elements.filter(e => credentialList.some(u => u.uuid === e.uuid)); - return credentialList; - } - - return []; + // Return filtered list of kpxc.credentials with TOTP set + return credentialList; }; diff --git a/keepassxc-browser/popups/popup.js b/keepassxc-browser/popups/popup.js index c176386..5b556e6 100644 --- a/keepassxc-browser/popups/popup.js +++ b/keepassxc-browser/popups/popup.js @@ -38,7 +38,7 @@ function statusResponse(status) { } // Only supported with Protocol V2 - if (status.databaseAssociationStatuses) { + if (status.databaseAssociationStatuses && Object.keys(status.databaseAssociationStatuses).length > 0) { // This can be also shown when isAnyAssociated is true? if (status.databaseAssociationStatuses.associationNeeded) { $('#not-configured').show();