keewebhttp: make it work

This commit is contained in:
antelle 2017-05-23 21:01:50 +02:00
parent ef1312bd24
commit 52ed78cb97
2 changed files with 295 additions and 265 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "0.0.1", "version": "0.0.2",
"manifestVersion": "0.1.0", "manifestVersion": "0.1.0",
"name": "keewebhttp", "name": "keewebhttp",
"description": "KeeWebHttp allows to use browser extensions with KeeWeb", "description": "KeeWebHttp allows to use browser extensions with KeeWeb",
@ -11,8 +11,9 @@
"licence": "MIT", "licence": "MIT",
"url": "https://plugins.keeweb.info/plugins/keewebhttp", "url": "https://plugins.keeweb.info/plugins/keewebhttp",
"resources": { "resources": {
"js": "dWXmIa4n78RRR6tiMNIqnejfj54lOsNJrn6mz1eQ3HYpCu4sVKv7E+9ABSGykyHJJVQDjTJAUF+7UcwCfDhIFfbFDttPmeylM3vtg+YAfaJFfU5e0l9/MtuUEuXuiUwjXi3jFg5yAYNq9ZCkHs2YCm4JyE8KXm1flmzIpmTiVi6TkX/ulmO74lLm2wXUD23eP7B8RqXk0wBoc7VcZ1x5uHomJIrQfS+J7vdXrMLBt/EJU4VNlyw8LLxcwP3/UbsBzBotpif3AH0hq+Dcwuh8v8H9Z+756T8EFEXz0JmCLhU0oyFcgOmUt0QwrIiFD18aHt0s80BmuqO600I/3M3+Sg==" "js": "NB8wqvULI7yTqf8FAkJYx2TbyW/BjDo7zpm15MHHHTcYYVIRhYFAeiKx5f9j9UtXLYr1deVc7STdvVLhNsD//tN4LRsSXmOiMt2NnhjIHLhVHwlKxSZTMeK5NrajxjdS0YtBKMg5oDt9vYa15qOvoX0fhmc9E1gD6j5z9sJAxT450JEDRZidXLnUnEnGIZunVH5OCe75eavvcEQ64sZ3pUouTN9iWU66sjC3hG/pk3o5I4+8W4v1MWy2TR7IKaVOLrRiDrzT/rnZoroRg7QLLUb+L185S/OYMEo8MIa7lMu/ULhmbhk0+JT2pukEofPkOZAlF8BIf1EGXGp6gObo4w=="
}, },
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0oZB2Kt7AzRFNqf8FuO3C3kepHPAIQYiDPYdQxHcsiaFCwyKVx6K1cE/3vBhb8/2rj+QIIWNfAAuu1Y+2VK90ZBeq6HciukWzQRO/HWhfdy0c7JwDAslmyGI5olj0ZQkNLhkde1MiMxjDPpRhZtdJaryVO5cFJaJESpv3dV6m0qXsaQCluWYOSNfSjP9C8o2zRVjSi3ZQZnZIV5pnk9K2MtlZIPXrN9iJiM5zZ9DTSnqApI6dC9mX4R3LvGN+GTovm9C8Crl+qb106nGRR3LcweicDnPyMtZLa/E0DBpWYxUVLDp6WeLhxoUBr+6+t3Xp9IDnPoANDQXJXD0f1vQxQIDAQAB", "publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0oZB2Kt7AzRFNqf8FuO3C3kepHPAIQYiDPYdQxHcsiaFCwyKVx6K1cE/3vBhb8/2rj+QIIWNfAAuu1Y+2VK90ZBeq6HciukWzQRO/HWhfdy0c7JwDAslmyGI5olj0ZQkNLhkde1MiMxjDPpRhZtdJaryVO5cFJaJESpv3dV6m0qXsaQCluWYOSNfSjP9C8o2zRVjSi3ZQZnZIV5pnk9K2MtlZIPXrN9iJiM5zZ9DTSnqApI6dC9mX4R3LvGN+GTovm9C8Crl+qb106nGRR3LcweicDnPyMtZLa/E0DBpWYxUVLDp6WeLhxoUBr+6+t3Xp9IDnPoANDQXJXD0f1vQxQIDAQAB",
"desktop": true "desktop": true,
"versionMin": "1.5.1"
} }

View File

@ -1,3 +1,9 @@
let server;
let uninstalled = false;
setTimeout(run, 500);
function run() {
const nodeRequire = window.require; const nodeRequire = window.require;
const http = nodeRequire('http'); const http = nodeRequire('http');
@ -6,26 +12,22 @@ const fs = nodeRequire('fs');
const path = nodeRequire('path'); const path = nodeRequire('path');
const electron = nodeRequire('electron'); const electron = nodeRequire('electron');
const AutoType = require('auto-type/index'); const AppModel = require('models/app-model');
const AutoTypeFilter = require('auto-type/auto-type-filter'); const AutoTypeFilter = require('auto-type/auto-type-filter');
const Logger = require('util/logger'); const Logger = require('util/logger');
const Alerts = require('comp/alerts'); const Alerts = require('comp/alerts');
const Generator = require('util/password-generator');
// const appModel = ...; TODO: use AppModel.instance const GeneratorPresets = require('comp/generator-presets');
const Version = '1.8.4.2'; const Version = '1.8.4.2';
const DebugMode = true; const DebugMode = true;
const keys = {}; const keys = {};
const logger = new Logger('keewebhttp'); const logger = new Logger('keewebhttp');
let server; startServer();
let uninstalled = false;
setTimeout(init, 0); function startServer() {
function init() {
if (uninstalled) { if (uninstalled) {
return; return;
} }
@ -120,8 +122,10 @@ class RequestContext {
} }
} }
makeError(e) { makeError(e, skipLog) {
if (!skipLog) {
logger.error('handleRequest error', e); logger.error('handleRequest error', e);
}
return { return {
Error: e ? e.toString() : '', Error: e ? e.toString() : '',
Success: false, Success: false,
@ -145,7 +149,7 @@ class RequestContext {
encrypt(value) { encrypt(value) {
if (!this.aesKey) { if (!this.aesKey) {
throw 'No key'; throw 'No aes key';
} }
if (!this.resp || !this.resp.Nonce) { if (!this.resp || !this.resp.Nonce) {
throw 'No nonce'; throw 'No nonce';
@ -153,10 +157,21 @@ class RequestContext {
const key = Buffer.from(this.aesKey, 'base64'); const key = Buffer.from(this.aesKey, 'base64');
const nonce = Buffer.from(this.resp.Nonce, 'base64'); const nonce = Buffer.from(this.resp.Nonce, 'base64');
const cipher = crypto.createCipheriv('aes-256-cbc', key, nonce); const cipher = crypto.createCipheriv('aes-256-cbc', key, nonce);
return Buffer.concat([cipher.update(value, 'utf8'), cipher.final()]).toString('base64'); let data;
if (value.isProtected) {
const binaryData = value.getBinary();
data = Buffer.from(binaryData);
binaryData.fill(0);
} else {
data = Buffer.from(value, 'utf8');
}
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]).toString('base64');
data.fill(0);
return encrypted;
} }
getKeyById() { getKeyById() {
// TODO
return keys[this.req.Id]; return keys[this.req.Id];
} }
@ -200,7 +215,10 @@ class RequestContext {
testAssociate() { testAssociate() {
if (!this.req.Id) { if (!this.req.Id) {
return this.makeError(''); return this.makeError('', true);
}
if (!this.getKeyById(this.req.Id)) {
return this.makeError('Unknown Id', true);
} }
this.verifyRequest(); this.verifyRequest();
this.createResponse(); this.createResponse();
@ -211,7 +229,7 @@ class RequestContext {
throw 'Id not expected'; throw 'Id not expected';
} }
if (!this.req.Key) { if (!this.req.Key) {
throw 'No key'; throw 'No request key';
} }
this.aesKey = this.req.Key; this.aesKey = this.req.Key;
this.verifyRequest(); this.verifyRequest();
@ -220,11 +238,16 @@ class RequestContext {
Alerts.yesno({ Alerts.yesno({
header: 'External Connection', header: 'External Connection',
body: 'Some app is trying to connect to KeeWeb. If you are setting up your plugin, please allow the connection. Otherwise, click No.', body: 'Some app is trying to connect to KeeWeb. If you are setting up your plugin, please allow the connection. Otherwise, click No.',
success: () => { resolve(); }, success: () => {
cancel: () => { reject('Rejected by user'); } resolve();
},
cancel: () => {
reject('Rejected by user');
}
}); });
}).then(() => { }).then(() => {
this.req.Id = 'KeeWeb_' + new Date().toISOString() + '_' + crypto.randomBytes(16).toString('hex'); this.req.Id = 'KeeWeb_' + new Date().toISOString() + '_' + crypto.randomBytes(16).toString('hex');
logger.info('associate: ', this.req.Id);
this.saveKeyWithId(); this.saveKeyWithId();
this.createResponse(); this.createResponse();
return this.resp; return this.resp;
@ -233,20 +256,23 @@ class RequestContext {
getLogins(config) { getLogins(config) {
this.verifyRequest(); this.verifyRequest();
if (!this.req.Url) { if (!this.req.Url && !config.all) {
throw 'No url'; throw 'No url';
} }
const url = this.decrypt(this.req.Url); const url = this.req.Url ? this.decrypt(this.req.Url) : '';
logger.debug('get-logins', url);
this.createResponse(); this.createResponse();
const filter = new AutoTypeFilter({ url }, AutoType.appModel); const filter = new AutoTypeFilter({url}, AppModel.instance);
if (config.all) {
filter.ignoreWindowInfo = true;
}
const entries = filter.getEntries(); const entries = filter.getEntries();
this.resp.Count = entries.length; this.resp.Count = entries.length;
logger.info(`getLogins(${url}): ${this.resp.Count}`);
if (!config.onlyCount) { if (!config.onlyCount) {
this.resp.Entries = entries.map(entry => ({ this.resp.Entries = entries.map(entry => ({
Login: entry.user ? this.encrypt(entry.user) : '', Login: entry.user ? this.encrypt(entry.user) : '',
Name: entry.title ? this.encrypt(entry.title) : '', Name: entry.title ? this.encrypt(entry.title) : '',
Password: entry.password ? this.encrypt(entry.password.getText()) : '', Password: entry.password ? this.encrypt(entry.password) : '',
StringFields: null, StringFields: null,
Uuid: this.encrypt(entry.id) Uuid: this.encrypt(entry.id)
})); }));
@ -261,23 +287,26 @@ class RequestContext {
const url = this.decrypt(this.req.Url); const url = this.decrypt(this.req.Url);
const login = this.decrypt(this.req.Login); const login = this.decrypt(this.req.Login);
const password = this.decrypt(this.req.Password); const password = this.decrypt(this.req.Password);
logger.debug('set-login', url, login, password); logger.info('setLogin', url, login, password);
this.createResponse(); this.createResponse();
} }
generatePassword() { generatePassword() {
this.verifyRequest(); this.verifyRequest();
this.createResponse(); this.createResponse();
const preset = GeneratorPresets.all.filter(p => p.default)[0] || GeneratorPresets.defaultPreset;
const password = Generator.generate(preset);
this.resp.Count = 1; this.resp.Count = 1;
this.resp.Entries = [{ this.resp.Entries = [{
Login: '', Login: '',
Name: '', Name: '',
Password: this.encrypt('I am generated password: ' + new Date()), Password: this.encrypt(password),
StringFields: null, StringFields: null,
Uuid: '' Uuid: ''
}]; }];
} }
} }
}
module.exports.uninstall = function() { module.exports.uninstall = function() {
if (server) { if (server) {