mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Add support for custom password change forms (#2383)
Add support for custom password change forms
This commit is contained in:
parent
9066f67e53
commit
ef2225f6b6
3 changed files with 94 additions and 52 deletions
|
|
@ -149,11 +149,11 @@ kpxcSites.expectedTOTPMaxLength = function() {
|
|||
|
||||
/**
|
||||
* Handles a few exceptions for certain sites where form submit button is not regognized properly.
|
||||
* @param {object} form Form element
|
||||
* @param {object} form Form element (optional)
|
||||
* @returns {object} Button element
|
||||
*/
|
||||
kpxcSites.formSubmitButtonExceptionFound = function(form) {
|
||||
if (form.action.startsWith(googleUrl)) {
|
||||
if (form?.action?.startsWith(googleUrl)) {
|
||||
const findDiv = $('#identifierNext, #passwordNext');
|
||||
if (!findDiv) {
|
||||
return undefined;
|
||||
|
|
@ -162,14 +162,14 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) {
|
|||
const buttons = findDiv.getElementsByTagName('button');
|
||||
kpxcSites.savedForm = form;
|
||||
return buttons.length > 0 ? buttons[0] : undefined;
|
||||
} else if (form.action.startsWith('https://www.ebay.')) {
|
||||
} else if (form?.action?.startsWith('https://www.ebay.')) {
|
||||
// For eBay we must return the first button.
|
||||
for (const i of form.elements) {
|
||||
if (i.type === 'button') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else if (form.action.includes('signin.aws.amazon.com')) {
|
||||
} else if (form?.action?.includes('signin.aws.amazon.com')) {
|
||||
// For Amazon AWS the button is outside the form.
|
||||
const button = $('#signin_button');
|
||||
if (button) {
|
||||
|
|
@ -182,14 +182,17 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) {
|
|||
'odc.officeapps.live.com',
|
||||
'login.microsoftonline.com',
|
||||
'login.microsoftonline.us',
|
||||
].some(u => form.action.includes(u))) {
|
||||
].some(u => form?.action?.includes(u))) {
|
||||
const buttons = Array.from(form.querySelectorAll(kpxcForm.formButtonQuery));
|
||||
if (buttons?.length > 1) {
|
||||
return buttons[1];
|
||||
}
|
||||
} else if (form.action.startsWith('https://barmerid.id.bconnect.barmer.de')) {
|
||||
} else if (form?.action?.startsWith('https://barmerid.id.bconnect.barmer.de')) {
|
||||
const loginButton = $('#btn-login');
|
||||
return loginButton?.shadowRoot?.children?.[0];
|
||||
} else if (!form && document.location.href.includes('reddit.com/settings')) {
|
||||
// Reddit change password popup
|
||||
return $('.button[slot=primary-button]');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,39 @@
|
|||
*/
|
||||
const kpxcForm = {};
|
||||
kpxcForm.formButtonQuery = 'button[type=button], button[type=submit], input[type=button], input[type=submit], button:not([type]), div[role=button]';
|
||||
kpxcForm.savedCustomInputs = [];
|
||||
kpxcForm.savedForms = [];
|
||||
kpxcForm.submitTriggered = false;
|
||||
|
||||
// Activate the Credential Banner if existing credentials are not found
|
||||
kpxcForm.activateCredentialBanner = async function(usernameValue, passwordInputs, passwordField) {
|
||||
let passwordValue = '';
|
||||
// Check if the form has three password fields -> a possible password change form
|
||||
if (passwordInputs && passwordInputs.length >= 2) {
|
||||
passwordValue = kpxcForm.getNewPassword(passwordInputs);
|
||||
} else if (passwordField) {
|
||||
// Use the combination password field instead
|
||||
passwordValue = passwordField.value;
|
||||
}
|
||||
|
||||
// Return if credentials are already found
|
||||
if (kpxc.credentials.some(c => c.login === usernameValue && c.password === passwordValue)) {
|
||||
kpxcForm.submitTriggered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordField) {
|
||||
await kpxc.setPasswordFilled(true);
|
||||
}
|
||||
|
||||
const url = trimURL(kpxc.settings.saveDomainOnlyNewCreds ? window.top.location.origin : window.top.location.href);
|
||||
await sendMessage('page_set_submitted', [ true, usernameValue, passwordValue, url, kpxc.credentials ]);
|
||||
|
||||
// Show the banner if the page does not reload
|
||||
kpxc.rememberCredentials(usernameValue, passwordValue);
|
||||
kpxcForm.submitTriggered = false;
|
||||
};
|
||||
|
||||
// Returns true if form has been already saved
|
||||
kpxcForm.formIdentified = function(form) {
|
||||
return kpxcForm.savedForms.some(f => f.form === form);
|
||||
|
|
@ -94,8 +124,29 @@ kpxcForm.getNewPassword = function(passwordInputs = []) {
|
|||
return '';
|
||||
};
|
||||
|
||||
// Returns the username value from an input field or selected login
|
||||
kpxcForm.getUsernameValue = async function(usernameField) {
|
||||
if (usernameField) {
|
||||
return usernameField.value || usernameField.placeholder;
|
||||
} else if (kpxc.credentials.length === 1) {
|
||||
// Single entry found for the page, use the username of it instead of an empty one
|
||||
return kpxc.credentials[0].login;
|
||||
} else {
|
||||
// Multiple entries found for the page, try to find out which one might have been used
|
||||
const pageUuid = await sendMessage('page_get_login_id');
|
||||
if (pageUuid) {
|
||||
const credential = kpxc.credentials.find(c => c.uuid === pageUuid);
|
||||
if (credential) {
|
||||
return credential.login;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
// Initializes form and attaches the submit button to our own callback
|
||||
kpxcForm.init = function(form, credentialFields) {
|
||||
kpxcForm.initForm = function(form, credentialFields) {
|
||||
if (!form.action || typeof form.action !== 'string') {
|
||||
logDebug('Error: Form action is not found.');
|
||||
return;
|
||||
|
|
@ -113,6 +164,30 @@ kpxcForm.init = function(form, credentialFields) {
|
|||
}
|
||||
};
|
||||
|
||||
// Initialize a "form" where three different password input fields are in a combination
|
||||
kpxcForm.initCustomForm = function(combinations) {
|
||||
if (combinations?.length >= 2 && combinations?.every(c => !c?.form && !c.username && c.password)) {
|
||||
kpxcForm.savedCustomInputs = [];
|
||||
const submitButton = kpxcSites.formSubmitButtonExceptionFound();
|
||||
if (submitButton) {
|
||||
kpxcForm.savedCustomInputs = combinations?.map(c => c.password);
|
||||
submitButton.addEventListener('click', kpxcForm.onCustomFormSubmit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Triggers when a custom form has been identified with a specific form submit button
|
||||
kpxcForm.onCustomFormSubmit = async function(e) {
|
||||
if (!e.isTrusted || kpxcForm.savedCustomInputs?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
kpxcForm.submitTriggered = true;
|
||||
|
||||
const usernameValue = await kpxcForm.getUsernameValue();
|
||||
await kpxcForm.activateCredentialBanner(usernameValue, kpxcForm.savedCustomInputs);
|
||||
};
|
||||
|
||||
// Triggers when form is submitted. Shows the credential banner
|
||||
kpxcForm.onSubmit = async function(e) {
|
||||
if (!e.isTrusted) {
|
||||
|
|
@ -154,49 +229,8 @@ kpxcForm.onSubmit = async function(e) {
|
|||
}
|
||||
|
||||
const [ usernameField, passwordField, passwordInputs ] = kpxcForm.getCredentialFieldsFromForm(form);
|
||||
let usernameValue = '';
|
||||
let passwordValue = '';
|
||||
|
||||
if (usernameField) {
|
||||
usernameValue = usernameField.value || usernameField.placeholder;
|
||||
} else if (kpxc.credentials.length === 1) {
|
||||
// Single entry found for the page, use the username of it instead of an empty one
|
||||
usernameValue = kpxc.credentials[0].login;
|
||||
} else {
|
||||
// Multiple entries found for the page, try to find out which one might have been used
|
||||
const pageUuid = await sendMessage('page_get_login_id');
|
||||
if (pageUuid) {
|
||||
const credential = kpxc.credentials.find(c => c.uuid === pageUuid);
|
||||
if (credential) {
|
||||
usernameValue = credential.login;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the form has three password fields -> a possible password change form
|
||||
if (passwordInputs && passwordInputs.length >= 2) {
|
||||
passwordValue = kpxcForm.getNewPassword(passwordInputs);
|
||||
} else if (passwordField) {
|
||||
// Use the combination password field instead
|
||||
passwordValue = passwordField.value;
|
||||
}
|
||||
|
||||
// Return if credentials are already found
|
||||
if (kpxc.credentials.some(c => c.login === usernameValue && c.password === passwordValue)) {
|
||||
kpxcForm.submitTriggered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordField) {
|
||||
await kpxc.setPasswordFilled(true);
|
||||
}
|
||||
|
||||
const url = trimURL(kpxc.settings.saveDomainOnlyNewCreds ? window.top.location.origin : window.top.location.href);
|
||||
await sendMessage('page_set_submitted', [ true, usernameValue, passwordValue, url, kpxc.credentials ]);
|
||||
|
||||
// Show the banner if the page does not reload
|
||||
kpxc.rememberCredentials(usernameValue, passwordValue);
|
||||
kpxcForm.submitTriggered = false;
|
||||
const usernameValue = await kpxcForm.getUsernameValue(usernameField);
|
||||
await kpxcForm.activateCredentialBanner(usernameValue, passwordInputs, passwordField);
|
||||
};
|
||||
|
||||
// Save form to Object array
|
||||
|
|
|
|||
|
|
@ -327,15 +327,20 @@ kpxc.initCombinations = async function(inputs = []) {
|
|||
const field = c.username || c.password;
|
||||
if (field && c.form) {
|
||||
// Initialize form-submit for remembering credentials
|
||||
kpxcForm.init(c.form, c);
|
||||
kpxcForm.initForm(c.form, c);
|
||||
}
|
||||
|
||||
// Don't allow duplicates
|
||||
if (!kpxc.combinations.some(f => f.username === c.username && f.password === c.password && f.totp === c.totp && f.form === c.form)) {
|
||||
if (!kpxc.combinations.some(f =>
|
||||
f.username === c.username && f.password === c.password && f.totp === c.totp && f.form === c.form
|
||||
)) {
|
||||
kpxc.combinations.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Identify a custom password change form
|
||||
kpxcForm.initCustomForm(combinations);
|
||||
|
||||
// Update the fields in Custom Login Fields banner if it's open
|
||||
if (kpxcCustomLoginFieldsBanner.created) {
|
||||
kpxcCustomLoginFieldsBanner.updateFieldSelections();
|
||||
|
|
|
|||
Loading…
Reference in a new issue