Select group when adding new credentials

This commit is contained in:
varjolintu 2019-02-05 10:22:00 +02:00
parent c3a36027b5
commit 34362ff08e
12 changed files with 623 additions and 146 deletions

View file

@ -307,6 +307,26 @@
"message": "Please choose the credentials you want to update.",
"description": "A popup message shown choosing what credentials user wants to update."
},
"popupRememberChooseGroup": {
"message": "Please choose the group you want to add the new credentials.",
"description": "A popup message shown choosing what group user wants to use for new credentials."
},
"popupRememberErrorDefaultGroupNotFound": {
"message": "Error: Specified default group not found. Creating all necessary groups.",
"description": "Error message shown when default group set cannot be found."
},
"popupRememberErrorCreatingNewGroup": {
"message": "New group(s) cannot be created.",
"description": "Error message shown when new groups cannot be created."
},
"popupRememberErrorPasswordNotChanged": {
"message": "Error: Credentials not updated. The password has not been changed.",
"description": "Error message shown when credential password is not changed."
},
"popupRememberErrorCannotSaveCredentials": {
"message": "Error: Credentials cannot be saved or updated.",
"description": "Error message shown when credentials cannot be saved or updated."
},
"popupLoginText": {
"message": "Select the login information you would like to get entered into the page.",
"description": "A popup message shown when one or multiple credentials are present."
@ -447,6 +467,22 @@
"message": "Maximum (Number of) Redirects:",
"description": "RMaximum (Number of) Redirects options text."
},
"optionsLabelDefaultGroup": {
"message": "Default group for saving new passwords:",
"description": "Default group options text."
},
"optionsDefaultGroupHelpText": {
"message": "Separate the group with slashes, for example: Group/ChildGroup.",
"description": "Default group help text."
},
"optionsLabelDefaultGroupCheckboxText": {
"message": "Always ask where to save new credentials",
"description": "Default group checkbox help text."
},
"optionsLabelDefaultGroupCheckboxHelpText": {
"message": "When enabled, the popup shows a group listing.",
"description": "Default checkbox explanation help text."
},
"optionsCheckboxUsePasswordGenerator": {
"message": "Activate password generator.",
"description": "Activate password generator checkbox text."

View file

@ -167,13 +167,13 @@ browserAction.stackPop = function(tabId) {
browserAction.stackPush = function(data, tabId) {
const id = tabId || page.currentTabId;
browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true);
browserAction.removeLevelFromStack(null, { 'id': id }, data.level, '<=', true);
page.tabs[id].stack.push(data);
};
browserAction.stackUnshift = function(data, tabId) {
const id = tabId || page.currentTabId;
browserAction.removeLevelFromStack(null, {'id': id}, data.level, '<=', true);
browserAction.removeLevelFromStack(null, { 'id': id }, data.level, '<=', true);
page.tabs[id].stack.unshift(data);
};
@ -193,7 +193,7 @@ browserAction.removeRememberPopup = function(callback, tab, removeImmediately) {
const currentMS = Date.now();
if (removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) {
browserAction.stackPop(tab.id);
browserAction.show(null, {"id": tab.id});
browserAction.show(null, { 'id': tab.id });
page.clearCredentials(tab.id);
} else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) {
data.visibleForPageUpdates -= 1;
@ -202,7 +202,7 @@ browserAction.removeRememberPopup = function(callback, tab, removeImmediately) {
};
browserAction.setRememberPopup = function(tabId, username, password, url, usernameExists, credentialsList) {
browser.storage.local.get({'settings': {}}).then(function(item) {
browser.storage.local.get({ 'settings': {} }).then(function(item) {
const settings = item.settings;
// Don't show anything if the site is in the ignore
@ -233,7 +233,7 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna
index: 0,
counter: 0,
max: 2,
icons: ['icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png']
icons: [ 'icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png' ]
},
icon: 'icon_remember_red_background_19x19.png',
popup: 'popup_remember.html'

View file

@ -284,8 +284,10 @@ kpxcEvent.messageHandlers = {
'add_credentials': keepass.addCredentials,
'associate': keepass.associate,
'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC,
'create_new_group': keepass.createNewGroup,
'generate_password': keepass.generatePassword,
'get_connected_database': kpxcEvent.onGetConnectedDatabase,
'get_database_groups': keepass.getDatabaseGroups,
'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions,
'get_status': kpxcEvent.onGetStatus,
'get_tab_information': kpxcEvent.onGetTabInformation,

View file

@ -33,7 +33,9 @@ const kpActions = {
CHANGE_PUBLIC_KEYS: 'change-public-keys',
LOCK_DATABASE: 'lock-database',
DATABASE_LOCKED: 'database-locked',
DATABASE_UNLOCKED: 'database-unlocked'
DATABASE_UNLOCKED: 'database-unlocked',
GET_DATABASE_GROUPS: 'get-database-groups',
CREATE_NEW_GROUP: 'create-new-group'
};
const kpErrors = {
@ -127,11 +129,11 @@ keepass.sendNativeMessage = function(request, enableTimeout = false) {
});
};
keepass.addCredentials = function(callback, tab, username, password, url) {
keepass.updateCredentials(callback, tab, null, username, password, url);
keepass.addCredentials = function(callback, tab, username, password, url, group, groupUuid) {
keepass.updateCredentials(callback, tab, null, username, password, url, group, groupUuid);
};
keepass.updateCredentials = function(callback, tab, entryId, username, password, url) {
keepass.updateCredentials = function(callback, tab, entryId, username, password, url, group, groupUuid) {
page.debug('keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})', tab.id, entryId, username, url);
if (tab && page.tabs[tab.id]) {
page.tabs[tab.id].errorMessage = null;
@ -162,6 +164,11 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
messageData.uuid = entryId;
}
if (group && groupUuid) {
messageData.group = group;
messageData.groupUuid = groupUuid;
}
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
@ -678,6 +685,135 @@ keepass.lockDatabase = function(tab) {
});
};
keepass.getDatabaseGroups = function(callback, tab) {
keepass.testAssociation((taResponse) => {
if (!taResponse) {
browserAction.showDefault(null, tab);
callback([]);
return;
}
if (tab && page.tabs[tab.id]) {
page.tabs[tab.id].errorMessage = null;
}
if (!keepass.isConnected) {
callback([]);
return;
}
let groups = [];
const kpAction = kpActions.GET_DATABASE_GROUPS;
const nonce = keepass.getNonce();
const incrementedNonce = keepass.incrementedNonce(nonce);
const messageData = {
action: kpAction
};
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
nonce: nonce,
clientID: keepass.clientID
};
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
if (!res) {
keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
callback([]);
return;
}
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
if (keepass.verifyResponse(parsed, incrementedNonce)) {
groups = parsed.groups;
groups.defaultGroup = page.settings.defaultGroup;
groups.defaultGroupAlwaysAsk = page.settings.defaultGroupAlwaysAsk;
keepass.updateLastUsed(keepass.databaseHash);
callback(groups);
} else {
console.log('getDatabaseGroups rejected');
callback([]);
}
} else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
callback([]);
} else {
browserAction.showDefault(null, tab);
callback([]);
}
});
}, tab, false);
};
keepass.createNewGroup = function(callback, tab, groupName) {
keepass.testAssociation((taResponse) => {
if (!taResponse) {
browserAction.showDefault(null, tab);
callback([]);
return;
}
if (tab && page.tabs[tab.id]) {
page.tabs[tab.id].errorMessage = null;
}
if (!keepass.isConnected) {
callback([]);
return;
}
const kpAction = kpActions.CREATE_NEW_GROUP;
const nonce = keepass.getNonce();
const incrementedNonce = keepass.incrementedNonce(nonce);
const messageData = {
action: kpAction,
groupName: groupName
};
const request = {
action: kpAction,
message: keepass.encrypt(messageData, nonce),
nonce: nonce,
clientID: keepass.clientID
};
keepass.sendNativeMessage(request).then((response) => {
if (response.message && response.nonce) {
const res = keepass.decrypt(response.message, response.nonce);
if (!res) {
keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
callback([]);
return;
}
const message = nacl.util.encodeUTF8(res);
const parsed = JSON.parse(message);
if (keepass.verifyResponse(parsed, incrementedNonce)) {
keepass.updateLastUsed(keepass.databaseHash);
callback(parsed);
} else {
console.log('getDatabaseGroups rejected');
callback([]);
}
} else if (response.error && response.errorCode) {
keepass.handleError(tab, response.errorCode, response.error);
callback([]);
} else {
browserAction.showDefault(null, tab);
callback([]);
}
});
}, tab, false);
};
keepass.generateNewKeyPair = function() {
keepass.keyPair = nacl.box.keyPair();
//console.log(nacl.util.encodeBase64(keepass.keyPair.publicKey) + ' ' + nacl.util.encodeBase64(keepass.keyPair.secretKey));

View file

@ -9,7 +9,9 @@ const defaultSettings = {
autoRetrieveCredentials: true,
showNotifications: true,
showLoginNotifications: true,
saveDomainOnly: true
saveDomainOnly: true,
defaultGroup: '',
defaultGroupAlwaysAsk: false
};
var page = {};
@ -49,7 +51,13 @@ page.initSettings = function() {
if (!('saveDomainOnly' in page.settings)) {
page.settings.saveDomainOnly = defaultSettings.saveDomainOnly;
}
browser.storage.local.set({'settings': page.settings});
if (!('defaultGroup' in page.settings)) {
page.settings.defaultGroup = defaultSettings.defaultGroup;
}
if (!('defaultGroupAlwaysAsk' in page.settings)) {
page.settings.defaultGroupAlwaysAsk = defaultSettings.defaultGroupAlwaysAsk;
}
browser.storage.local.set({ 'settings': page.settings });
resolve(page.settings);
});
});

View file

@ -119,6 +119,10 @@ h2+hr {
width: 75%;
}
#defaultGroup {
width: 50%;
}
tr.clone {
display: none;
}

View file

@ -87,6 +87,30 @@
</div>
</p>
<hr />
<p>
<div class="form-group">
<label for="defaultGroup" data-i18n="optionsLabelDefaultGroup"></label>
<div class="control-group">
<div class="input-append">
<input type="text" id="defaultGroup" placeholder="KeePassXC-Browser Passwords">
<button class="btn btn-sm btn-primary" id="defaultGroupButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-danger" id="defaultGroupButtonReset"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonReset"></span></button>
</div>
</div>
<span class="help-block">
<span data-i18n="optionsDefaultGroupHelpText"></span>
<br />
<span data-i18n="optionsDefault"></span> KeePassXC-Browser Passwords
</span>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="defaultGroupAlwaysAsk" value="true" /><span data-i18n="optionsLabelDefaultGroupCheckboxText"></span>
</label>
<span class="help-block" data-i18n="optionsLabelDefaultGroupCheckboxHelpText"></span>
</div>
</div>
</p>
<hr />
<p>
<div class="checkbox">
<label class="checkbox">

View file

@ -54,7 +54,9 @@ options.saveSettingsPromise = function() {
options.saveSetting = function(name) {
const id = '#' + name;
$(id).closest('.control-group').removeClass('error').addClass('success');
setTimeout(() => { $(id).closest('.control-group').removeClass('success'); }, 2500);
setTimeout(() => {
$(id).closest('.control-group').removeClass('success');
}, 2500);
browser.storage.local.set({ 'settings': options.settings });
browser.runtime.sendMessage({
@ -79,6 +81,11 @@ options.saveKeyRing = function() {
options.initGeneralSettings = function() {
$('#tab-general-settings input[type=checkbox]').each(function() {
$(this).attr('checked', options.settings[$(this).attr('name')]);
if ($(this).attr('name') === 'defaultGroupAlwaysAsk' && $(this).attr('checked')) {
$('#defaultGroup').prop('disabled', true);
$('#defaultGroupButton').prop('disabled', true);
$('#defaultGroupButtonReset').prop('disabled', true);
}
});
$('#tab-general-settings input[type=checkbox]').change(function() {
@ -87,10 +94,22 @@ options.initGeneralSettings = function() {
options.saveSettingsPromise().then((x) => {
if (name === 'autoFillAndSend') {
browser.runtime.sendMessage({ action: 'init_http_auth' });
} else if (name === 'defaultGroupAlwaysAsk') {
if ($(this).is(':checked')) {
$('#defaultGroup').prop('disabled', true);
$('#defaultGroupButton').prop('disabled', true);
$('#defaultGroupButtonReset').prop('disabled', true);
} else {
$('#defaultGroup').prop('disabled', false);
$('#defaultGroupButton').prop('disabled', false);
$('#defaultGroupButtonReset').prop('disabled', false);
}
}
});
});
$('#tab-general-settings input#defaultGroup').val(options.settings['defaultGroup']);
$('#tab-general-settings input[type=radio]').each(function() {
if ($(this).val() === options.settings[$(this).attr('name')]) {
$(this).attr('checked', options.settings[$(this).attr('name')]);
@ -155,6 +174,20 @@ options.initGeneralSettings = function() {
options.settings['allowedRedirect'] = allowedRedirectval;
options.saveSetting('allowedRedirect');
});
$('#defaultGroupButton').click(function() {
const value = $('#defaultGroup').val();
if (value.length > 0) {
options.settings['defaultGroup'] = value;
options.saveSettings();
}
});
$('#defaultGroupButtonReset').click(function() {
$('#defaultGroup').val('');
options.settings['defaultGroup'] = '';
options.saveSettings();
});
};
options.showKeePassXCVersions = function(response) {

View file

@ -46,6 +46,12 @@ body {
font-size: 80%;
color: #787878;
}
#list {
padding-left: 0px;
}
#child {
border-top: 0px;
}
#update-available {
padding-top: 10px;
margin-top: 10px;
@ -64,9 +70,24 @@ body {
}
#login-filter {
outline:none;
border-radius: 4px 0 0 4px;
border-radius: 4px;
border: 1px solid #ccc;
margin-bottom: 5px;
padding: 2px 10px;
width: 100%;
}
}
.credentials .groups {
display: none;
border-top: 1px solid #333;
}
.connected-database {
display: none;
font-size: 85%;
color: #787878;
}
.credentials .username-new {
display: none;
}
.credentials .username-exists {
display: none;
}

View file

@ -10,15 +10,6 @@
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
<script type="text/javascript" src="popup_functions.js"></script>
<script type="text/javascript" src="popup_remember.js"></script>
<style type="text/css">
.credentials {display: none; border-top: 1px solid #333;}
.connected-database {display: none; font-size: 85%; color: #787878;}
.credentials .username-new {display: none;}
.credentials .username-exists {display: none;}
.small { font-weight: bold; }
.small .normal { font-weight: normal; }
.info { font-weight: normal; }
</style>
</head>
<body>
<div class="buttons">
@ -41,11 +32,16 @@
<p data-i18n="popupRememberSaving" i18n-placeholder="<span></span><em></em>"></p>
</div>
<div class="credentials">
<div class="credentials" style="display: none;">
<p class="username-new"><span data-i18n="popupRememberNewUsername" i18n-placeholder="<strong></strong>"></span></p>
<p class="username-exists"><span data-i18n="popupRememberUsernameExists" i18n-placeholder="<strong></strong>"></span></p>
<p data-i18n="popupRememberChooseCredentials"></p>
<ul id="list"></ul>
<ul id="list" class="list-group"></ul>
</div>
<div class="groups" style="display: none;">
<p data-i18n="popupRememberChooseGroup"></p>
<ul id="list" class="list-group"></ul>
</div>
<script type="text/javascript" src="../translate.js"></script>
</body>

View file

@ -1,5 +1,7 @@
'use strict';
const DEFAULT_BROWSER_GROUP = 'KeePassXC-Browser Passwords';
var _tab;
function _initialize(tab) {
@ -27,14 +29,108 @@ function _initialize(tab) {
$('.information-username:first').text(_tab.credentials.username);
$('#btn-new').click(function(e) {
e.preventDefault();
$('.credentials').hide();
$('ul#list').empty();
// Get group listing from KeePassXC
browser.runtime.sendMessage({
action: 'add_credentials',
args: [_tab.credentials.username, _tab.credentials.password, _tab.credentials.url]
}).then(_verifyResult);
action: 'get_database_groups'
}).then((result) => {
// Only the Root group and no KeePassXC-Browser passwords -> save to default
// Or when default group is not set and defaultGroupAskAlways is disabled -> save to default
if ((result.groups === undefined || (result.groups.length > 0 && result.groups[0].children.length === 0)) ||
(!result.defaultGroupAlwaysAsk && (result.defaultGroup === '' || result.defaultGroup === DEFAULT_BROWSER_GROUP))) {
browser.runtime.sendMessage({
action: 'add_credentials',
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url ]
}).then(_verifyResult);
return;
} else if (!result.defaultGroupAlwaysAsk && (result.defaultGroup !== '' || result.defaultGroup !== DEFAULT_BROWSER_GROUP)) {
// Another group name has been specified
const [ gname, guuid ] = getDefaultGroup(result.groups[0].children, result.defaultGroup);
if (gname === '' && guuid === '') {
showNotification(tr('popupRememberErrorDefaultGroupNotFound'));
// Create a new group
browser.runtime.sendMessage({
action: 'create_new_group',
args: [ result.defaultGroup ]
}).then((newGroup) => {
if (newGroup.name && newGroup.uuid) {
browser.runtime.sendMessage({
action: 'add_credentials',
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, newGroup.name, newGroup.uuid ]
}).then(_verifyResult);
} else {
showNotification(tr('popupRememberErrorCreatingNewGroup'));
}
return;
});
}
browser.runtime.sendMessage({
action: 'add_credentials',
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, gname, guuid ]
}).then(_verifyResult);
return;
}
const addChildren = function(group, parentElement, depth) {
++depth;
const padding = depth * 20;
for (const child of group.children) {
const a = createLink(child.name, child.uuid, child.children.length > 0);
a.attr('id', 'child');
a.css('cssText', 'padding-left: ' + String(padding) + 'px !important;');
if (parentElement.attr('id') === 'root') {
a.attr('id', 'root-child');
}
$('ul#list').append(a);
addChildren(child, a, depth);
}
};
const createLink = function(group, groupUuid, hasChildren) {
const a = $('<a>')
.attr('href', '#')
.attr('class', 'list-group-item')
.text(group)
.click(function(ev) {
ev.preventDefault();
browser.runtime.sendMessage({
action: 'add_credentials',
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, group, groupUuid ]
}).then(_verifyResult);
});
if (hasChildren) {
a.text('\u25BE ' + group);
}
return a;
};
// Create the link list for group selection
let depth = 0;
for (const g of result.groups) {
const a = createLink(g.name, g.uuid, g.children.length > 0);
a.attr('id', 'root');
$('ul#list').append(a);
addChildren(g, a, depth);
}
$('.groups').show();
});
});
$('#btn-update').click(function(e) {
e.preventDefault();
$('.groups').hide();
$('ul#list').empty();
// Only one entry which could be updated
if (_tab.credentials.list.length === 1) {
@ -45,7 +141,7 @@ function _initialize(tab) {
browser.runtime.sendMessage({
action: 'update_credentials',
args: [_tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url]
args: [ _tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url ]
}).then(_verifyResult);
} else {
$('.credentials:first .username-new:first strong:first').text(_tab.credentials.username);
@ -62,6 +158,7 @@ function _initialize(tab) {
for (let i = 0; i < _tab.credentials.list.length; i++) {
const $a = $('<a>')
.attr('href', '#')
.attr('class', 'list-group-item')
.text(_tab.credentials.list[i].login + ' (' + _tab.credentials.list[i].name + ')')
.data('entryId', i)
.click(function(e) {
@ -85,7 +182,7 @@ function _initialize(tab) {
// Show a notification if the user tries to update credentials using the old password
if (credentials[entryId].password === _tab.credentials.password) {
showNotification('Error: Credentials not updated. The password has not been changed.');
showNotification(tr('popupRememberErrorPasswordNotChanged'));
_close();
return;
}
@ -101,8 +198,7 @@ function _initialize(tab) {
$a.css('font-weight', 'bold');
}
const $li = $('<li class=\"list-group-item\">').append($a);
$('ul#list').append($li);
$('ul#list').append($a);
}
$('.credentials').show();
@ -117,10 +213,10 @@ function _initialize(tab) {
$('#btn-ignore').click(function(e) {
browser.windows.getCurrent().then((win) => {
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
const tab = tabs[0];
const currentTab = tabs[0];
browser.runtime.getBackgroundPage().then((global) => {
browser.tabs.sendMessage(tab.id, {
action: 'ignore_site',
browser.tabs.sendMessage(currentTab.id, {
action: 'ignore-site',
args: [ _tab.credentials.url ]
});
_close();
@ -141,7 +237,7 @@ function _connectedDatabase(db) {
function _verifyResult(code) {
if (code === 'error') {
showNotification('Error: Credentials cannot be saved or updated.');
showNotification(tr('popupRememberErrorCannotSaveCredentials'));
}
_close();
}
@ -158,6 +254,26 @@ function _close() {
close();
}
// Traverse the groups and ensure all paths are found
const getDefaultGroup = function(groups, defaultGroup) {
const getGroup = function(group, splitted, depth) {
++depth;
for (const g of group) {
if (g.name === splitted[depth]) {
if (splitted.length === (depth + 1)) {
return [ g.name, g.uuid ];
}
return getGroup(g.children, splitted, depth);
}
}
return [ '', '' ];
};
let depth = -1;
const splitted = defaultGroup.split('/');
return getGroup(groups, splitted, depth);
};
$(function() {
browser.runtime.sendMessage({
action: 'stack_add',

View file

@ -32,20 +32,20 @@ Currently these messages are implemented:
Request:
```javascript
{
"action": "change-public-keys",
"publicKey": "<current public key>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "change-public-keys",
"publicKey": "<current public key>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response (success):
```javascript
{
"action": "change-public-keys",
"version": "2.2.0",
"publicKey": "<host public key>",
"success": "true"
"action": "change-public-keys",
"version": "2.2.0",
"publicKey": "<host public key>",
"success": "true"
}
```
@ -53,26 +53,26 @@ Response (success):
Unencrypted message:
```javascript
{
"action": "get-databasehash"
"action": "get-databasehash"
}
```
Request:
```javascript
{
"action": "get-databasehash",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "get-databasehash",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"action": "hash",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
"action": "hash",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
}
```
@ -80,30 +80,30 @@ Response message data (success, decrypted):
Unencrypted message:
```javascript
{
"action": "associate",
"key": "<current public key>",
"idKey": "<a new identification key>"
"action": "associate",
"key": "<current public key>",
"idKey": "<a new identification key>"
}
```
Request:
```javascript
{
"action": "associate",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "associate",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0",
"success": "true",
"id": "testclient",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0",
"success": "true",
"id": "testclient",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
}
```
@ -111,30 +111,30 @@ Response message data (success, decrypted):
Unencrypted message:
```javascript
{
"action": "test-associate",
"id": "<saved database identifier>",
"key": "<saved database public key>"
"action": "test-associate",
"id": "<saved database identifier>",
"key": "<saved database public key>"
}
```
Request:
```javascript
{
"action": "test-associate",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "test-associate",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"version": "2.2.0",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"hash": "29234e32274a32276e25666a42",
"id": "testclient",
"success": "true"
"version": "2.2.0",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"hash": "29234e32274a32276e25666a42",
"id": "testclient",
"success": "true"
}
```
@ -142,24 +142,24 @@ Response message data (success, decrypted):
Request (no unencrypted message is needed):
```javascript
{
"action": "generate-password",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "generate-password",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"version": "2.2.0",
"entries": [
{
"login": 144,
"password": "testclientpassword"
}
],
"success": "true",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
"version": "2.2.0",
"entries": [
{
"login": 144,
"password": "testclientpassword"
}
],
"success": "true",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
}
```
@ -167,49 +167,49 @@ Response message data (success, decrypted):
Unencrypted message:
```javascript
{
"action": "get-logins",
"url": "<snip>",
"submitUrl": optional,
"httpAuth": optional,
"keys": [
{
"id": <connected_id>,
"key": <connected_key>
},
...
]
"action": "get-logins",
"url": "<snip>",
"submitUrl": optional,
"httpAuth": optional,
"keys": [
{
"id": <connected_id>,
"key": <connected_key>
},
...
]
}
```
Request:
```javascript
{
"action": "get-logins",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "get-logins",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"count": "2",
"entries" : [
{
"login": "user1",
"name": "user1",
"password": "passwd1"
},
{
"login": "user2",
"name": "user2",
"password": "passwd2"
}],
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"success": "true",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
"count": "2",
"entries" : [
{
"login": "user1",
"name": "user1",
"password": "passwd1"
},
{
"login": "user2",
"name": "user2",
"password": "passwd2"
}],
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"success": "true",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
}
```
@ -217,36 +217,39 @@ Response message data (success, decrypted):
Unencrypted message:
```javascript
{
"action": "set-login",
"url": "<snip>",
"submitUrl": "<snip>",
"id": "testclient",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"login": "user1",
"password": "passwd1"
"action": "set-login",
"url": "<snip>",
"submitUrl": "<snip>",
"id": "testclient",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"login": "user1",
"password": "passwd1",
"group": "<group name>",
"groupUuid": "<group UUID>",
"uuid": "<entry UUID>"
}
```
Request:
```javascript
{
"action": "set-login",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "set-login",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```javascript
{
"count": null,
"entries" : null,
"error": "",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"success": "true",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
"count": null,
"entries" : null,
"error": "",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"success": "true",
"hash": "29234e32274a32276e25666a42",
"version": "2.2.0"
}
```
@ -254,26 +257,124 @@ Response message data (success, decrypted):
Unencrypted message:
```javascript
{
"action": "lock-database"
"action": "lock-database"
}
```
Request:
```javascript
{
"action": "lock-database",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
"action": "lock-database",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success always returns an error, decrypted):
```javascript
{
"action": "lock-database",
"errorCode": 1,
"error": "Database not opened",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
"action": "lock-database",
"errorCode": 1,
"error": "Database not opened",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q"
}
```
### get-database-groups
Unencrypted message:
```javascript
{
"action": "get-database-groups"
}
```
Request:
```javascript
{
"action": "get-database-groups",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```json
{
"defaultGroup": "<default group name>",
"defaultGroupAlwaysAllow": false,
"groups": [
{
"name": "Root",
"uuid": "<group UUID>",
"children": [
{
"name": "KeePassXC-Browser Passwords",
"uuid": "<group UUID>",
"children": []
},
{
"name": "SecondRoot",
"uuid": "<group UUID>",
"children": [
{
"name": "Child",
"uuid": "<group UUID>",
"children": [
{
"name": "GrandChild",
"uuid": "<group UUID>",
"children": []
}
]
}
]
},
{
"name": "ThirdRoot",
"uuid": "<group UUID>",
"children": [
{
"name": "Child2",
"uuid": "<group UUID>",
"children": []
}
]
},
{
"name": "Child2",
"uuid": "<group UUID>",
"children": []
}
]
}
]
}
```
### create-new-group
Unencrypted message:
```javascript
{
"action": "create-new-group",
"groupName": "<group name or path>"
}
```
Request:
```javascript
{
"action": "create-new-group",
"message": "<encrypted message>",
"nonce": "tZvLrBzkQ9GxXq9PvKJj4iAnfPT0VZ3Q",
"clientID": "<clientID>"
}
```
Response message data (success, decrypted):
```json
{
"name": "<group name>",
"uuid": "<group UUID>"
}
```