insomnia-plugin-keepass
Version:
This is a plugin for [insomnia](https://insomnia.rest) which provides access to Keepass and KeepassXC for fetching credentials.
143 lines (125 loc) • 3.85 kB
JavaScript
const
DEFAULT_URL = new URL('http://localhost:19455'),
DEFAULT_HEADERS = {accept: 'application/json', 'content-type': 'application/json'},
{createDecipheriv, createCipheriv, randomBytes} = require('crypto'),
BaseClient = require('./BaseClient');
class HttpClient extends BaseClient
{
/**
* @returns {URL}
* @constructor
*/
static get DEFAULT_URL ()
{
return DEFAULT_URL;
}
/**
* @returns {number}
* @constructor
*/
static get NONCE_SIZE ()
{
return 16;
}
/**
* @param {URL|string} url
*/
constructor (url = DEFAULT_URL)
{
super(HttpClient);
this._url = url instanceof URL ? url : new URL(url);
this._lastResponse = null;
}
get lastResponse ()
{
return this._lastResponse;
}
/**
* @returns {string}
*/
generateRandomKey ()
{
return randomBytes(32).toString('base64');
}
/**
* @param {string} key
* @param {string} iv
* @param {string} payload
* @returns {string}
* @override
*/
decrypt (key, iv, payload)
{
const civ = createDecipheriv('aes-256-cbc', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'));
return Buffer.concat([civ.update(Buffer.from(payload, 'base64')), civ.final()]).toString('utf8');
}
/**
* @param {string} key
* @param {string} iv
* @param {string} payload
* @returns {string}
* @override
*/
encrypt (key, iv, payload)
{
const civ = createCipheriv('aes-256-cbc', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'));
return Buffer.concat([civ.update(Buffer.from(payload, 'utf8')), civ.final()]).toString('base64');
}
/**
* @param {string} string
* @returns {string}
* @private
*/
_camelCase (string)
{
const [first, ...rest] = string;
return first.toLowerCase().concat(...rest);
}
/**
* @param {object} obj
* @returns {object}
* @private
*/
_camelCaseObjectKeys (obj)
{
if (typeof obj !== 'object') {
return obj;
}
return Object.entries(obj).reduce((p, [k, v]) => {
p[this._camelCase(k)] = Array.isArray(v) ? v.map(this._camelCaseObjectKeys.bind(this)) : v;
return p;
}, {});
}
/**
* @param {{requestType: string, key: string, id?: string, url?: string}} request
* @returns {Promise}
*/
async request (request)
{
const
method = 'POST',
compress = false,
{host} = this._url,
nonce = this.generateNonce(),
verifier = this.encrypt(request.key, nonce, nonce),
url = request.url ? this.encrypt(request.key, nonce, request.url) : undefined,
body = JSON.stringify({...request, url, nonce, verifier}),
headers = Object.assign({}, DEFAULT_HEADERS, {...(host ? {host} : {})});
let rawResponse;
try {
rawResponse = await fetch(this._url.toString(), {body, compress, method, headers});
} catch (err) {
throw new Error(`Fetch error: ${err}`);
}
if (!rawResponse.ok) {
throw new Error(`Fetch error: ${rawResponse.status} ${rawResponse.statusText}`);
}
const response = this._camelCaseObjectKeys(await rawResponse.json());
this._lastResponse = response;
if (!response || !response.success || response.error) {
throw new Error(`Response error: ${JSON.stringify(response, null, 4)}`);
}
return response;
}
}
module.exports = HttpClient;