Add support for locking single or all databases

This commit is contained in:
varjolintu 2023-08-29 10:26:34 +03:00
parent d3184cc42c
commit 1f86cc3d5a
14 changed files with 176 additions and 44 deletions

View file

@ -1202,5 +1202,13 @@
"lockDatabase": { "lockDatabase": {
"message": "Lock database", "message": "Lock database",
"description": "Lock database button title text." "description": "Lock database button title text."
},
"lockAllDatabases": {
"message": "Lock all databases",
"description": "Lock all databases button title text."
},
"noAction": {
"message": "No action",
"description": "No action text."
} }
} }

View file

@ -38,7 +38,7 @@ browserAction.showDefault = async function(tab) {
// Get the current tab if no tab given // Get the current tab if no tab given
tab ??= await getCurrentTab(); tab ??= await getCurrentTab();
if (!tab) { if (!tab || tab.id < 0) {
return; return;
} }

View file

@ -166,10 +166,10 @@ kpxcEvent.loadSettings = async function() {
}); });
}; };
kpxcEvent.lockDatabase = async function(tab) { kpxcEvent.lockDatabase = async function(tab, args) {
try { try {
await keepass.lockDatabase(tab); await keepass.lockDatabase(tab, args);
return kpxcEvent.showStatus(tab, false); return kpxcEvent.getStatus(tab);
} catch (err) { } catch (err) {
logError('kpxcEvent.lockDatabase error: ' + err); logError('kpxcEvent.lockDatabase error: ' + err);
return false; return false;
@ -258,6 +258,7 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) {
error: errorMessage || null, error: errorMessage || null,
identifier: keyId, identifier: keyId,
keePassXCAvailable: keepass.isKeePassXCAvailable, keePassXCAvailable: keepass.isKeePassXCAvailable,
protocolV2: keepass.protocolV2,
showGettingStartedGuideAlert: page.settings.showGettingStartedGuideAlert, showGettingStartedGuideAlert: page.settings.showGettingStartedGuideAlert,
showTroubleshootingGuideAlert: page.settings.showTroubleshootingGuideAlert, showTroubleshootingGuideAlert: page.settings.showTroubleshootingGuideAlert,
usernameFieldDetected: usernameFieldDetected usernameFieldDetected: usernameFieldDetected

View file

@ -48,7 +48,7 @@ httpAuth.handleRequestCallback = function(details, callback) {
}; };
httpAuth.retrieveCredentials = async function(tabId, url, submitUrl) { httpAuth.retrieveCredentials = async function(tabId, url, submitUrl) {
return await keepass.retrieveCredentials(tabId, [ url, submitUrl, false, true ]).catch((err) => { return await keepass.getCredentials(tabId, [ url, submitUrl, false, true ]).catch((err) => {
logError('httpAuth.retrieveCredentials error: ' + err); logError('httpAuth.retrieveCredentials error: ' + err);
return Promise.reject(); return Promise.reject();
}); });

View file

@ -386,14 +386,14 @@ keepass.checkForNewKeePassXCVersion = function() {
keepass.latestKeePassXC.lastChecked = new Date().valueOf(); keepass.latestKeePassXC.lastChecked = new Date().valueOf();
}; };
keepass.updatePopup = function() { keepass.updatePopup = function(tab) {
if (page && page.tabs.length > 0) { if (page && page.tabs.length > 0) {
browserAction.showDefault(); browserAction.showDefault(tab);
} }
}; };
// Updates the database hashes to content script // Updates the database hashes to content script
keepass.updateDatabase = async function() { keepass.updateDatabase = async function(tab) {
if (!keepass.isKeePassXCAvailable) { if (!keepass.isKeePassXCAvailable) {
return; return;
} }
@ -406,14 +406,14 @@ keepass.updateDatabase = async function() {
// TODO: Only show "Connect" if the active database is not connected? // TODO: Only show "Connect" if the active database is not connected?
// TODO: What if there are credentials from another database but the selected one is not connected? // TODO: What if there are credentials from another database but the selected one is not connected?
const result = await protocol.testAssociationFromDatabaseStatuses(); const result = await protocol.testAssociationFromDatabaseStatuses();
keepass.updatePopup(); keepass.updatePopup(tab);
keepass.updateDatabaseHashToContent(result); keepass.updateDatabaseHashToContent(result);
return; return;
} }
// Legacy protocol // Legacy protocol
await keepassProtocol.testAssociation(null, [ true ]); await keepassProtocol.testAssociation(null, [ true ]);
keepass.updatePopup(); keepass.updatePopup(tab);
keepass.updateDatabaseHashToContent(); keepass.updateDatabaseHashToContent();
}; };

View file

@ -322,23 +322,21 @@ protocol.lockDatabase = async function(tab, args = []) {
return false; return false;
} }
const [ lockSingle ] = args;
const messageData = { const messageData = {
action: kpActions.LOCK_DATABASE action: kpActions.LOCK_DATABASE,
lockSingle: lockSingle
}; };
try { try {
const response = await protocolClient.sendMessage(tab, messageData); const response = await protocolClient.sendMessage(tab, messageData);
if (response) { if (response) {
//keepass.isDatabaseClosed = true; // ? keepass.updateDatabase(tab);
keepass.updateDatabase();
// Display error message in the popup
keepass.handleError(tab, kpErrors.DATABASE_NOT_OPENED);
return true; return true;
} else {
//keepass.isDatabaseClosed = true; // ?
} }
keepass.isDatabaseClosed = true;
return false; return false;
} catch (err) { } catch (err) {
logError(`lockDatabase failed: ${err}`); logError(`lockDatabase failed: ${err}`);

View file

@ -97,6 +97,37 @@ code {
justify-content: flex-end; justify-content: flex-end;
} }
.dropdown-toggle {
display: none;
height: 31px;
width: 10px;
}
.kpxc-dropdown-menu {
border: var(--kpxc-card-border-color);
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2) !important;
overflow: hidden;;
position: absolute;
right: 12px;
top: 40px;
z-index: 2147483646;
}
.kpxc-dropdown-item {
background: var(--kpxc-background-color);
color: var(--kpxc-text-color);
cursor: pointer;
padding: 5px;
}
.kpxc-dropdown-item:hover {
background-color: #28a745 !important;
color: #ffffff !important;
}
#list { #list {
padding-left: 0px; padding-left: 0px;
} }
@ -185,6 +216,15 @@ code {
background-color: var(--kpxc-table-hover-color) !important; background-color: var(--kpxc-table-hover-color) !important;
} }
.kpxc-dropdown-menu {
border: var(--kpxc-card-border-color);
}
.kpxc-dropdown-item {
background: var(--kpxc-background-color);
color: var(--kpxc-text-color);
}
span.bg-success { span.bg-success {
background-color: var(--kpxc-table-hover-color) !important; background-color: var(--kpxc-table-hover-color) !important;
color: var(--kpxc-text-color) !important; color: var(--kpxc-text-color) !important;

View file

@ -24,8 +24,14 @@
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button> <button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button>
<button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button> <button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button>
</div> </div>
<div class="lock-button-area"> <div class="btn-group lock-button-area">
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button> <button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button>
<select id="dropdown-button" class="btn btn-sm btn-danger dropdown-toggle" style="display: none"></select>
</div>
<div class="kpxc-dropdown-menu" style="display: none">
<div class="kpxc-dropdown-item">
<span id="kpxc-dropdown-item" data-i18n="lockAllDatabases"></span>
</div>
</div> </div>
</div> </div>

View file

@ -19,6 +19,7 @@ function statusResponse(status) {
$('#configured-not-associated').hide(); $('#configured-not-associated').hide();
$('#lock-database-button').hide(); $('#lock-database-button').hide();
$('#getting-started-guide').hide(); $('#getting-started-guide').hide();
hideDropdownButton();
if (!status.keePassXCAvailable) { if (!status.keePassXCAvailable) {
$('#error-message').textContent = status.error; $('#error-message').textContent = status.error;
@ -38,7 +39,9 @@ function statusResponse(status) {
} }
// Only supported with Protocol V2 // Only supported with Protocol V2
if (status.databaseAssociationStatuses && Object.keys(status.databaseAssociationStatuses).length > 0) { if (status.protocolV2
&& status.databaseAssociationStatuses
&& Object.keys(status.databaseAssociationStatuses).length > 0) {
// This can be also shown when isAnyAssociated is true? // This can be also shown when isAnyAssociated is true?
if (status.databaseAssociationStatuses.associationNeeded) { if (status.databaseAssociationStatuses.associationNeeded) {
$('#not-configured').show(); $('#not-configured').show();
@ -53,6 +56,7 @@ function statusResponse(status) {
$('#configured-and-associated').show(); $('#configured-and-associated').show();
$('#associated-identifier').textContent = status.identifier; $('#associated-identifier').textContent = status.identifier;
$('#lock-database-button').show(); $('#lock-database-button').show();
showDropdownButton(status.protocolV2);
if (status.usernameFieldDetected) { if (status.usernameFieldDetected) {
$('#username-field-detected').show(); $('#username-field-detected').show();
@ -156,9 +160,11 @@ const sendMessageToTab = async function(message) {
}); });
$('#lock-database-button').addEventListener('click', async () => { $('#lock-database-button').addEventListener('click', async () => {
statusResponse(await browser.runtime.sendMessage({ statusResponse(await lockDatabase());
action: 'lock_database' });
}));
$('.kpxc-dropdown-item').addEventListener('click', async () => {
statusResponse(await lockDatabase(false));
}); });
$('#username-only-button').addEventListener('click', async () => { $('#username-only-button').addEventListener('click', async () => {

View file

@ -60,6 +60,39 @@ async function getLoginData() {
return logins; return logins;
} }
async function lockDatabase(lockSingle = true) {
return await browser.runtime.sendMessage({
action: 'lock_database',
args: [ lockSingle ]
});
}
// Show the dropdown button if protocol is supported
async function showDropdownButton(isV2) {
const isProtocolV2 = isV2 || await browser.runtime.sendMessage({ action: 'is_protocol_v2' });
if (isProtocolV2) {
$('.lock-button-area')?.classList.add('btn-group');
$('#dropdown-button')?.show();
} else {
$('.lock-button-area')?.classList.remove('btn-group');
$('#dropdown-button')?.hide();
}
}
function hideDropdownButton() {
$('.lock-button-area')?.classList.remove('btn-group');
$('#dropdown-button')?.hide();
}
function hideElementsOnDatabaseLock() {
$('.credentials').hide();
$('#database-not-opened').show();
$('#lock-database-button').hide();
$('#dropdown-button').hide();
$('#btn-dismiss')?.hide();
$('#database-error-message').textContent = tr('errorMessageDatabaseNotOpened');
}
// Sets default popup size for Chromium based browsers to prevent flash on popup open // Sets default popup size for Chromium based browsers to prevent flash on popup open
function setDefaultPopupSize() { function setDefaultPopupSize() {
if (!isFirefox()) { if (!isFirefox()) {
@ -83,7 +116,36 @@ function resizePopup() {
document.addEventListener('DOMContentLoaded', initSettings); document.addEventListener('DOMContentLoaded', initSettings);
} }
document.addEventListener('mouseup', function(e) {
if (!e.isTrusted) {
return;
}
if (e.target.id !== 'kpxc-dropdown-item') {
$('.kpxc-dropdown-menu')?.hide();
}
});
updateAvailableResponse(await browser.runtime.sendMessage({ updateAvailableResponse(await browser.runtime.sendMessage({
action: 'update_available_keepassxc' action: 'update_available_keepassxc'
})); }));
$('#dropdown-button').addEventListener('click', (e) => {
const dropdownMenu = $('.kpxc-dropdown-menu');
if (!dropdownMenu) {
return;
}
if (dropdownMenu.style.display === 'none') {
dropdownMenu.show();
} else {
dropdownMenu.hide();
}
e.target.blur();
});
$('.kpxc-dropdown-item').addEventListener('click', () => {
$('.kpxc-dropdown-menu')?.hide();
});
})(); })();

View file

@ -24,8 +24,14 @@
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button> <button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button>
<button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button> <button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button>
</div> </div>
<div class="lock-button-area"> <div class="btn-group lock-button-area">
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button> <button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button>
<select id="dropdown-button" class="btn btn-sm btn-danger dropdown-toggle" style="display: none"></select>
</div>
<div class="kpxc-dropdown-menu" style="display: none">
<div class="kpxc-dropdown-item">
<span id="dropdown-item" data-i18n="lockAllDatabases"></span>
</div>
</div> </div>
</div> </div>

View file

@ -5,6 +5,7 @@
await initColorTheme(); await initColorTheme();
$('#lock-database-button').show(); $('#lock-database-button').show();
await showDropdownButton();
const data = await getLoginData(); const data = await getLoginData();
const ll = document.getElementById('login-list'); const ll = document.getElementById('login-list');
@ -31,16 +32,14 @@
ll.appendChild(a); ll.appendChild(a);
} }
$('#lock-database-button').addEventListener('click', function() { $('#lock-database-button').addEventListener('click', () => {
browser.runtime.sendMessage({ lockDatabase();
action: 'lock_database' hideElementsOnDatabaseLock();
}); });
$('.credentials').hide(); $('.kpxc-dropdown-item').addEventListener('click', () => {
$('#btn-dismiss').hide(); lockDatabase(false);
$('#database-not-opened').show(); hideElementsOnDatabaseLock();
$('#lock-database-button').hide();
$('#database-error-message').textContent = tr('errorMessageDatabaseNotOpened');
}); });
$('#btn-dismiss').addEventListener('click', async () => { $('#btn-dismiss').addEventListener('click', async () => {

View file

@ -24,8 +24,14 @@
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button> <button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText"><i class="fa fa-cog" aria-hidden="true"></i></button>
<button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button> <button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button>
</div> </div>
<div class="lock-button-area"> <div class="btn-group lock-button-area">
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button> <button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button>
<select id="dropdown-button" class="btn btn-sm btn-danger dropdown-toggle" style="display: none"></select>
</div>
<div class="kpxc-dropdown-menu" style="display: none">
<div class="kpxc-dropdown-item">
<span id="dropdown-item" data-i18n="lockAllDatabases"></span>
</div>
</div> </div>
</div> </div>

View file

@ -4,13 +4,14 @@
resizePopup(); resizePopup();
await initColorTheme(); await initColorTheme();
$('#lock-database-button').show();
const tab = await getCurrentTab(); const tab = await getCurrentTab();
if (!tab) { if (!tab) {
return []; return [];
} }
$('#lock-database-button').show();
showDropdownButton();
const logins = await getLoginData(); const logins = await getLoginData();
const ll = document.getElementById('login-list'); const ll = document.getElementById('login-list');
@ -61,15 +62,14 @@
filter.focus(); filter.focus();
} }
$('#lock-database-button').addEventListener('click', (e) => { $('#lock-database-button').addEventListener('click', () => {
browser.runtime.sendMessage({ lockDatabase();
action: 'lock_database' hideElementsOnDatabaseLock();
}); });
$('#credentialsList').hide(); $('.kpxc-dropdown-item').addEventListener('click', () => {
$('#database-not-opened').show(); lockDatabase(false);
$('#lock-database-button').hide(); hideElementsOnDatabaseLock();
$('#database-error-message').textContent = tr('errorMessageDatabaseNotOpened');
}); });
$('#reopen-database-button').addEventListener('click', (e) => { $('#reopen-database-button').addEventListener('click', (e) => {