Add support for client certificates

This commit is contained in:
Chris Clark
2015-11-30 14:04:52 -08:00
parent cfc21fde8c
commit 5f5e48e414
3 changed files with 44 additions and 19 deletions

View File

@@ -112,7 +112,10 @@ The RippleAPI constructor optionally takes one argument, an object with the foll
Name | Type | Description Name | Type | Description
---- | ---- | ----------- ---- | ---- | -----------
authorization | string | *Optional* Username and password for HTTP basic authentication to the rippled server in the format **username:password**. authorization | string | *Optional* Username and password for HTTP basic authentication to the rippled server in the format **username:password**.
certificate | string | *Optional* A string containing the certificate key of the client in PEM format. (Can be an array of certificates).
feeCushion | number | *Optional* Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`. feeCushion | number | *Optional* Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`.
key | string | *Optional* A string containing the private key of the client in PEM format. (Can be an array of keys).
passphrase | string | *Optional* The passphrase for the private key of the client.
proxy | uri string | *Optional* URI for HTTP/HTTPS proxy to use to connect to the rippled server. proxy | uri string | *Optional* URI for HTTP/HTTPS proxy to use to connect to the rippled server.
proxyAuthorization | string | *Optional* Username and password for HTTP basic authentication to the proxy in the format **username:password**. proxyAuthorization | string | *Optional* Username and password for HTTP basic authentication to the proxy in the format **username:password**.
server | uri string | *Optional* URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`. server | uri string | *Optional* URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.

View File

@@ -1,4 +1,5 @@
'use strict'; 'use strict';
const _ = require('lodash');
const {EventEmitter} = require('events'); const {EventEmitter} = require('events');
const WebSocket = require('ws'); const WebSocket = require('ws');
const parseURL = require('url').parse; const parseURL = require('url').parse;
@@ -26,6 +27,9 @@ class Connection extends EventEmitter {
this._proxyAuthorization = options.proxyAuthorization; this._proxyAuthorization = options.proxyAuthorization;
this._authorization = options.authorization; this._authorization = options.authorization;
this._trustedCertificates = options.trustedCertificates; this._trustedCertificates = options.trustedCertificates;
this._key = options.key;
this._passphrase = options.passphrase;
this._certificate = options.certificate;
this._timeout = options.timeout || (20 * 1000); this._timeout = options.timeout || (20 * 1000);
this._isReady = false; this._isReady = false;
this._ws = null; this._ws = null;
@@ -104,20 +108,21 @@ class Connection extends EventEmitter {
}); });
} }
_createWebSocket(url, proxyURL, proxyAuthorization, authorization, _createWebSocket() {
trustedCertificates) {
const options = {}; const options = {};
if (proxyURL !== undefined) { if (this._proxyURL !== undefined) {
const parsedURL = parseURL(url); const parsedURL = parseURL(this._url);
const proxyOptions = parseURL(proxyURL); const parsedProxyURL = parseURL(this._proxyURL);
proxyOptions.secureEndpoint = (parsedURL.protocol === 'wss:'); const proxyOverrides = _.omit({
proxyOptions.secureProxy = (proxyOptions.protocol === 'https:'); secureEndpoint: (parsedURL.protocol === 'wss:'),
if (proxyAuthorization !== undefined) { secureProxy: (parsedProxyURL.protocol === 'https:'),
proxyOptions.auth = proxyAuthorization; auth: this._proxyAuthorization,
} ca: this._trustedCertificates,
if (trustedCertificates !== undefined) { key: this._key,
proxyOptions.ca = trustedCertificates; passphrase: this._passphrase,
} cert: this._certificate
}, _.isUndefined);
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
let HttpsProxyAgent; let HttpsProxyAgent;
try { try {
HttpsProxyAgent = require('https-proxy-agent'); HttpsProxyAgent = require('https-proxy-agent');
@@ -126,11 +131,18 @@ class Connection extends EventEmitter {
} }
options.agent = new HttpsProxyAgent(proxyOptions); options.agent = new HttpsProxyAgent(proxyOptions);
} }
if (authorization !== undefined) { if (this._authorization !== undefined) {
const base64 = new Buffer(authorization).toString('base64'); const base64 = new Buffer(this._authorization).toString('base64');
options.headers = {Authorization: `Basic ${base64}`}; options.headers = {Authorization: `Basic ${base64}`};
} }
const websocket = new WebSocket(url, options); const optionsOverrides = _.omit({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const websocketOptions = _.assign({}, options, optionsOverrides);
const websocket = new WebSocket(this._url, websocketOptions);
// we will have a listener for each outstanding request, // we will have a listener for each outstanding request,
// so we have to raise the limit (the default is 10) // so we have to raise the limit (the default is 10)
websocket.setMaxListeners(Infinity); websocket.setMaxListeners(Infinity);
@@ -148,9 +160,7 @@ class Connection extends EventEmitter {
} else if (this._state === WebSocket.CONNECTING) { } else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve); this._ws.once('open', resolve);
} else { } else {
this._ws = this._createWebSocket(this._url, this._proxyURL, this._ws = this._createWebSocket();
this._proxyAuthorization, this._authorization,
this._trustedCertificates);
this._ws.on('message', this._onMessage.bind(this)); this._ws.on('message', this._onMessage.bind(this));
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this); this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this);
this._ws.once('close', this._onUnexpectedCloseBound); this._ws.once('close', this._onUnexpectedCloseBound);

View File

@@ -42,6 +42,18 @@
"type": "string", "type": "string",
"description": "A PEM-formatted SSL certificate to trust when connecting to a proxy." "description": "A PEM-formatted SSL certificate to trust when connecting to a proxy."
} }
},
"key": {
"type": "string",
"description": "A string containing the private key of the client in PEM format. (Can be an array of keys)."
},
"passphrase": {
"type": "string",
"description": "The passphrase for the private key of the client."
},
"certificate": {
"type": "string",
"description": "A string containing the certificate key of the client in PEM format. (Can be an array of certificates)."
} }
}, },
"additionalProperties": false "additionalProperties": false