From 25975d1558df53f9eba3f7e94437392808ee8117 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Mon, 21 Aug 2023 19:08:55 +0300 Subject: [PATCH] Remove internal password generator --- .eslintrc | 2 +- keepassxc-browser/_locales/en/messages.json | 37 +-- .../content/keepassxc-browser.js | 2 +- keepassxc-browser/content/pwgen.js | 282 +----------------- keepassxc-browser/content/totp-field.js | 6 +- keepassxc-browser/content/ui.js | 25 +- keepassxc-browser/content/username-field.js | 3 +- keepassxc-browser/css/pwgen.css | 95 ------ 8 files changed, 21 insertions(+), 431 deletions(-) diff --git a/.eslintrc b/.eslintrc index 9ace490..04d1a71 100644 --- a/.eslintrc +++ b/.eslintrc @@ -133,7 +133,7 @@ "kpxcForm": true, "kpxcIcons": true, "kpxcObserverHelper": true, - "kpxcPasswordDialog": true, + "kpxcPasswordGenerator": true, "kpxcPasswordIcons": true, "kpxcSites": true, "kpxcTOTPAutocomplete": true, diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index fd251de..1901743 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -159,34 +159,6 @@ "message": "Password generator icon", "description": "Alt attribute text of the password generator icon." }, - "passwordGeneratorPlaceholder": { - "message": "Generated password", - "description": "Input field placeholder for generated password." - }, - "passwordGeneratorLabel": { - "message": "Also fill in the next password-field", - "description": "Checkbox text below the password generator input field." - }, - "passwordGeneratorTitle": { - "message": "Password Generator", - "description": "Password generator dialog title." - }, - "passwordGeneratorGenerate": { - "message": "Generate", - "description": "Generate button text in password generator." - }, - "passwordGeneratorTryAgain": { - "message": "Try again", - "description": "Generate button text in password generator when not connected." - }, - "passwordGeneratorCopy": { - "message": "Copy", - "description": "Copy button text in password generator." - }, - "passwordGeneratorFill": { - "message": "Fill password", - "description": "Fill a password in password generator." - }, "passwordGeneratorErrorTooLong": { "message": "Error: The generated password is longer than the allowed length!", "description": "A warning text shown in the password generator." @@ -203,13 +175,8 @@ "message": "Generate password", "description": "Password icon title text." }, - "passwordGeneratorError": { - "message": "Cannot receive generated password.", - "description": "Password generator error text when KeePassXC is closed." - }, - "passwordGeneratorErrorIsRunning": { - "message": "Is KeePassXC running and connected?", - "description": "Password generator error text when KeePassXC is closed." + "passwordGeneratorNotSupported": { + "message": "Launching the password generator is not supported in this KeePassXC version. Please update KeePassXC to a newer version." }, "usernameFieldText": { "message": "Fill credentials from KeePassXC", diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index 4c121af..d885b80 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -961,7 +961,7 @@ browser.runtime.onMessage.addListener(async function(req, sender) { } else if (req.action === 'retrive_credentials_forced') { await kpxc.retrieveCredentials(true); } else if (req.action === 'show_password_generator') { - kpxcPasswordDialog.trigger(); + kpxcPasswordGenerator.showPasswordGenerator(); } else if (req.action === 'request_autotype') { sendMessage('request_autotype', [ window.location.hostname ]); } diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index 1970325..9d6f462 100644 --- a/keepassxc-browser/content/pwgen.js +++ b/keepassxc-browser/content/pwgen.js @@ -12,7 +12,7 @@ kpxcPasswordIcons.switchIcon = function(state) { }; kpxcPasswordIcons.deleteHiddenIcons = function() { - kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons, 'kpxc-password-field'); + kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons); }; kpxcPasswordIcons.isValid = function(field) { @@ -59,7 +59,7 @@ PasswordIcon.prototype.createIcon = function(field) { 'alt': tr('passwordGeneratorIcon'), 'size': size, 'offset': offset, - 'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') + 'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') // Needed? }); icon.style.zIndex = '10000000'; @@ -76,13 +76,7 @@ PasswordIcon.prototype.createIcon = function(field) { } e.stopPropagation(); - - if (await useKeePassXCPasswordGenerator()) { - kpxcPasswordDialog.generate(null, field); - return; - } - - kpxcPasswordDialog.showDialog(field, icon); + kpxcPasswordGenerator.showPasswordGenerator(field); }); icon.addEventListener('mousedown', ev => ev.stopPropagation()); @@ -100,226 +94,23 @@ PasswordIcon.prototype.createIcon = function(field) { document.body.append(wrapper); }; -/** - * @Object kpxcPasswordDialog - * Provides a password dialog for content scripts. - * TODO: To be removed when KeePassXC 2.8.0 is released. 2.7.0 already uses KeePassXC's own password generator instead. - */ -const kpxcPasswordDialog = {}; -kpxcPasswordDialog.created = false; -kpxcPasswordDialog.icon = null; -kpxcPasswordDialog.input = null; -kpxcPasswordDialog.nextField = null; -kpxcPasswordDialog.selected = null; -kpxcPasswordDialog.startPosX = 0; -kpxcPasswordDialog.startPosY = 0; -kpxcPasswordDialog.diffX = 0; -kpxcPasswordDialog.diffY = 0; -kpxcPasswordDialog.dialog = null; -kpxcPasswordDialog.titleBar = null; -kpxcPasswordDialog.createDialog = function() { - if (kpxcPasswordDialog.created) { - // If database is open again, generate a new password right away - const input = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input'); - if (input.style.display === 'none') { - kpxcPasswordDialog.generate(); - } - return; - } - kpxcPasswordDialog.created = true; +const kpxcPasswordGenerator = {}; - const wrapper = kpxcUI.createElement('div'); - kpxcPasswordDialog.shadowRoot = wrapper.attachShadow({ mode: 'closed' }); - - const dialog = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-dialog'); - const titleBar = kpxcUI.createElement('div', 'kpxc-pwgen-titlebar', {}, tr('passwordGeneratorTitle')); - const closeButton = kpxcUI.createElement('div', 'kpxc-pwgen-close', {}, '×'); - closeButton.addEventListener('click', function(e) { - if (!e.isTrusted) { - return; - } - - kpxcPasswordDialog.openDialog(); - }); - titleBar.append(closeButton); - - const passwordRow = kpxcUI.createElement('div', 'kpxc-pwgen-password-row'); - const input = kpxcUI.createElement('input', 'kpxc-pwgen-input', { 'placeholder': tr('passwordGeneratorPlaceholder'), 'type': 'text', 'tabindex': '-1' }); - passwordRow.appendMultiple(input); - - // Buttons - const buttonsRow = kpxcUI.createElement('div', 'kpxc-pwgen-buttons'); - const generateButton = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxc-pwgen-btn-generate' }, tr('passwordGeneratorGenerate')); - const copyButton = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxc-pwgen-btn-copy' }, tr('passwordGeneratorCopy')); - const fillButton = kpxcUI.createElement('button', 'kpxc-button kpxc-green-button', { 'id': 'kpxc-pwgen-btn-fill' }, tr('passwordGeneratorFill')); - - generateButton.addEventListener('click', function(e) { - kpxcPasswordDialog.generate(e); - }); - - fillButton.addEventListener('click', function(e) { - kpxcPasswordDialog.fill(e); - kpxcPasswordDialog.openDialog(); - }); - - copyButton.addEventListener('click', function(e) { - kpxcPasswordDialog.copy(e); - }); - - buttonsRow.appendMultiple(generateButton, copyButton, fillButton); - dialog.appendMultiple(titleBar, passwordRow, buttonsRow); - - const styleSheet = createStylesheet('css/pwgen.css'); - const buttonStyle = createStylesheet('css/button.css'); - const colorStyleSheet = createStylesheet('css/colors.css'); - - kpxcPasswordDialog.shadowRoot.append(colorStyleSheet); - kpxcPasswordDialog.shadowRoot.append(styleSheet); - kpxcPasswordDialog.shadowRoot.append(buttonStyle); - kpxcPasswordDialog.shadowRoot.append(dialog); - - const icon = $('.kpxc-pwgen-icon'); - if (icon) { - dialog.style.top = Pixels(icon.offsetTop + icon.offsetHeight); - dialog.style.left = icon.style.left; - } else { - const rect = document.activeElement.getBoundingClientRect(); - dialog.style.top = Pixels(rect.top + rect.height); - dialog.style.left = Pixels(rect.left); - } - - document.body.append(wrapper); - - kpxcPasswordDialog.dialog = dialog; - kpxcPasswordDialog.titleBar = titleBar; - kpxcPasswordDialog.titleBar.addEventListener('mousedown', function(e) { - kpxcPasswordDialog.mouseDown(e); - }); - - kpxcPasswordDialog.generate(); +kpxcPasswordGenerator.showPasswordGenerator = async function(field) { + kpxcPasswordGenerator.generate(field ?? document.activeElement); }; -kpxcPasswordDialog.mouseDown = function(e) { - kpxcPasswordDialog.selected = kpxcPasswordDialog.titleBar; - kpxcPasswordDialog.startPosX = e.clientX; - kpxcPasswordDialog.startPosY = e.clientY; - kpxcPasswordDialog.diffX = kpxcPasswordDialog.startPosX - kpxcPasswordDialog.dialog.offsetLeft; - kpxcPasswordDialog.diffY = kpxcPasswordDialog.startPosY - kpxcPasswordDialog.dialog.offsetTop; - return false; -}; - -kpxcPasswordDialog.openDialog = function() { - if (kpxcPasswordDialog.dialog.style.display === '' || kpxcPasswordDialog.dialog.style.display === 'none') { - kpxcPasswordDialog.dialog.style.display = 'block'; - } else { - kpxcPasswordDialog.dialog.style.display = 'none'; - } -}; - -kpxcPasswordDialog.trigger = async function() { - if (await useKeePassXCPasswordGenerator()) { - kpxcPasswordDialog.generate(null, document.activeElement); +kpxcPasswordGenerator.generate = async function(field) { + if (!await isPasswordGeneratorSupported()) { + kpxcUI.createNotification('error', tr('passwordGeneratorNotSupported')); return; } - kpxcPasswordDialog.showDialog(document.activeElement, kpxcPasswordDialog.icon); + kpxcPasswordGenerator.fill(field, await sendMessage('generate_password')); }; -kpxcPasswordDialog.showDialog = function(field, icon) { - if (!kpxcFields.isVisible(field)) { - icon.parentNode.removeChild(icon); - field.removeAttribute('kpxc-password-field'); - return; - } - - kpxcPasswordDialog.input = field; - - // Save next password field if found - if (kpxc.inputs.length > 0) { - const index = kpxc.inputs.indexOf(field); - const nextField = kpxc.inputs[index + 1]; - kpxcPasswordDialog.nextField = (nextField && nextField.getLowerCaseAttribute('type') === 'password') ? nextField : undefined; - } - - kpxcPasswordDialog.createDialog(); - initColorTheme(kpxcPasswordDialog.dialog); - kpxcPasswordDialog.openDialog(); - - // Adjust the dialog location - if (kpxcPasswordDialog.dialog) { - if (icon) { - kpxcPasswordDialog.dialog.style.top = Pixels(icon.offsetTop + icon.offsetHeight); - kpxcPasswordDialog.dialog.style.left = icon.style.left; - } else { - const rect = document.activeElement.getBoundingClientRect(); - kpxcPasswordDialog.dialog.style.top = Pixels(rect.top + rect.height); - kpxcPasswordDialog.dialog.style.left = Pixels(rect.left); - } - } -}; - -kpxcPasswordDialog.generate = async function(e, field) { - // This function can be also called from non-events - if (e) { - if (!e.isTrusted) { - return; - } - e.preventDefault(); - } - - if (await useKeePassXCPasswordGenerator()) { - kpxcPasswordDialog.newFill(field, await sendMessage('generate_password')); - return; - } - - callbackGeneratedPassword(await sendMessage('generate_password')); -}; - -kpxcPasswordDialog.copy = function(e) { - if (!e.isTrusted) { - return; - } - - e.preventDefault(); - kpxcPasswordDialog.copyPasswordToClipboard(); -}; - -kpxcPasswordDialog.fill = function(e) { - if (!e.isTrusted || !kpxcPasswordDialog.input) { - return; - } - - e.preventDefault(); - - const password = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input'); - if (kpxcPasswordDialog.input.getAttribute('maxlength')) { - if (password.value.length > kpxcPasswordDialog.input.getAttribute('maxlength')) { - const message = tr('passwordGeneratorErrorTooLong') + '\r\n' - + tr('passwordGeneratorErrorTooLongCut') + '\r\n' + tr('passwordGeneratorErrorTooLongRemember'); - message.style.whiteSpace = 'pre'; - kpxcUI.createNotification('error', message); - return; - } - } - - kpxcPasswordDialog.input.value = password.value; - kpxcPasswordDialog.input.dispatchEvent(new Event('keydown', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('keyup', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('input', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('change', { bubbles: true })); - - if (kpxcPasswordDialog.nextField) { - kpxcPasswordDialog.nextField.value = password.value; - kpxcPasswordDialog.nextField.dispatchEvent(new Event('keydown', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('keyup', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('input', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('change', { bubbles: true })); - } -}; - -// New way to fill the password -kpxcPasswordDialog.newFill = function(elem, password) { +kpxcPasswordGenerator.fill = function(elem, password) { if (!elem || !password) { return; } @@ -358,56 +149,7 @@ kpxcPasswordDialog.newFill = function(elem, password) { } }; -kpxcPasswordDialog.copyPasswordToClipboard = function() { - kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input').select(); - try { - return document.execCommand('copy'); - } catch (err) { - console.log('Could not copy password to clipboard: ' + err); - } - return false; -}; - -const callbackGeneratedPassword = function(passwords) { - if (passwords && passwords.length >= 1) { - const errorMessage = kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-error'); - if (errorMessage) { - enableButtons(); - - const input = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input'); - input.style.display = 'block'; - errorMessage.remove(); - } - - kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input').value = passwords[0].password; - } else { - if (kpxcPasswordDialog.shadowSelectorAll('div#kpxc-pwgen-error').length === 0) { - const input = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input'); - input.style.display = 'none'; - - const errorMessage = kpxcUI.createElement('div', '', { 'id': 'kpxc-pwgen-error' }, - tr('passwordGeneratorError') + '\r\n' + tr('passwordGeneratorErrorIsRunning')); - errorMessage.style.whiteSpace = 'pre'; - input.parentElement.append(errorMessage); - - disableButtons(); - } - } -}; - -const enableButtons = function() { - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorGenerate'); - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-copy').style.display = 'inline-block'; - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-fill').style.display = 'inline-block'; -}; - -const disableButtons = function() { - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorTryAgain'); - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-copy').style.display = 'none'; - kpxcPasswordDialog.shadowSelector('#kpxc-pwgen-btn-fill').style.display = 'none'; -}; - -const useKeePassXCPasswordGenerator = async function() { +const isPasswordGeneratorSupported = async function() { const response = await browser.runtime.sendMessage({ action: 'get_keepassxc_versions' }); diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index 9d5b9f5..f9b30e9 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -36,7 +36,7 @@ kpxcTOTPIcons.switchIcon = function(state, uuid) { }; kpxcTOTPIcons.deleteHiddenIcons = function() { - kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons, 'kpxc-totp-field'); + kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons); }; kpxcTOTPIcons.autoCompleteIsOneTimeCode = function(field) { @@ -90,10 +90,6 @@ kpxcTOTPIcons.isValid = function(field, forced) { logDebug('Error: TOTP field found but it is not valid:', field); return false; } - } else { - if (field.getAttribute('kpxc-totp-field') === 'true') { - return false; - } } return true; diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js index 23f48dd..7da9ff2 100644 --- a/keepassxc-browser/content/ui.js +++ b/keepassxc-browser/content/ui.js @@ -62,8 +62,7 @@ class Icon { } } - removeIcon(attr) { - this.inputField.removeAttribute(attr); + removeIcon() { this.shadowRoot.removeChild(this.icon); document.body.removeChild(this.shadowRoot.host); } @@ -173,12 +172,12 @@ kpxcUI.getRelativeTopPosition = function(rect) { return kpxcUI.bodyStyle.position.toLowerCase() === 'relative' ? rect.top - kpxcUI.bodyRect.top : rect.top; }; -kpxcUI.deleteHiddenIcons = function(iconList, attr) { +kpxcUI.deleteHiddenIcons = function(iconList) { const deletedIcons = []; for (const icon of iconList) { if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) { const index = iconList.indexOf(icon); - icon.removeIcon(attr); + icon.removeIcon(); iconList.splice(index, 1); deletedIcons.push(icon.inputField); @@ -345,23 +344,6 @@ const logDebug = function(message, extra) { } }; -// Enables dragging -document.addEventListener('mousemove', function(e) { - if (!kpxcUI.mouseDown) { - return; - } - - if (kpxcPasswordDialog.selected === kpxcPasswordDialog.titleBar) { - const xPos = e.clientX - kpxcPasswordDialog.diffX; - const yPos = e.clientY - kpxcPasswordDialog.diffY; - - if (kpxcPasswordDialog.selected !== null) { - kpxcPasswordDialog.dialog.style.left = Pixels(xPos); - kpxcPasswordDialog.dialog.style.top = Pixels(yPos); - } - } -}); - document.addEventListener('mousedown', function(e) { if (!e.isTrusted) { return; @@ -375,7 +357,6 @@ document.addEventListener('mouseup', function(e) { return; } - kpxcPasswordDialog.selected = null; kpxcUI.mouseDown = false; }); diff --git a/keepassxc-browser/content/username-field.js b/keepassxc-browser/content/username-field.js index 50e442c..99d69cf 100644 --- a/keepassxc-browser/content/username-field.js +++ b/keepassxc-browser/content/username-field.js @@ -13,7 +13,7 @@ kpxcUsernameIcons.switchIcon = function(state) { }; kpxcUsernameIcons.deleteHiddenIcons = function() { - kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons, 'kpxc-username-field'); + kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons); }; kpxcUsernameIcons.isValid = function(field) { @@ -122,7 +122,6 @@ UsernameFieldIcon.prototype.createIcon = function(field) { const iconClicked = async function(field, icon) { if (!kpxcFields.isCustomLoginFieldsUsed() && !kpxcFields.isVisible(field)) { icon.parentNode.removeChild(icon); - field.removeAttribute('kpxc-username-field'); return; } diff --git a/keepassxc-browser/css/pwgen.css b/keepassxc-browser/css/pwgen.css index 86cf3e2..64b574d 100644 --- a/keepassxc-browser/css/pwgen.css +++ b/keepassxc-browser/css/pwgen.css @@ -1,80 +1,3 @@ -.kpxc-pwgen-dialog { - background-color: var(--kpxc-background-color); - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2); - color: var(--kpxc-text-color); - font-family: 'Lato', sans-serif !important; - min-height: 80px; - padding: .1em .2em .1em .2em; - position: absolute !important; - width: 350px; - z-index: 10000000 !important; -} - -.kpxc-pwgen-titlebar { - background-color: #6cac4d; - color: #fff; - cursor: move; - font-size: 12px !important; - margin: .2em 0 .2em 0; - padding: .4em .2em .4em .8em; - position: relative; - user-select: none; -} - -.kpxc-pwgen-password-row { - color: #fff; - display: flex; - margin: .2em 0 .2em 0; - padding: .4em 1em; -} - -.kpxc-pwgen-buttons { - align-items: center; - border-top: 1px solid #ccc; - color: #fff; - display: flex; - justify-content: left; - padding: .4em 1em; - margin: .2em 0 .2em 0; -} - -.kpxc-pwgen-close { - color: #fff; - float: right; - font-size: inherit; - font-weight: bold; - text-align: center; - min-width: 20px; -} - -.kpxc-pwgen-close:hover { - background-color: #6cac4d !important; - color: #fff; - cursor: pointer; -} - -.kpxc-pwgen-input { - background: none !important; - border-radius: 4px !important; - color: var(--kpxc-text-color) !important; - font-size: 12px !important; - font-weight: normal !important; - height: auto !important; - padding: 4px !important; - width: 100% !important; -} - -.kpxc #kpxc-pwgen-error { - color: var(--kpxc-text-color); - font-size: 12px !important; - font-style: normal !important; - font-weight: normal !important; - text-align: center; - width: 100%; -} - .kpxc-pwgen-icon { cursor: pointer; position: absolute; @@ -89,21 +12,3 @@ background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; background-size: contain; } - -@media (prefers-color-scheme: dark) { - .kpxc-pwgen-dialog { - background: var(--kpxc-background-color) !important; - color: var(--kpxc-text-color) !important; - } - - .kpxc-pwgen-input { - background-color: var(--kpxc-input-background-color) !important; - border: var(--kpxc-input-border); - border-color: var(--kpxc-input-border-color); - color: var(--kpxc-text-color) !important; - } - - .kpxc #kpxc-pwgen-error { - color: var(--kpxc-text-color) !important; - } -}