caesarjs
Version:
A microservice framework for node.js implementing client-server mutual certificate authentication
175 lines (140 loc) • 4.63 kB
JavaScript
'use strict';
const request = require('request');
const os = require('os');
const patrun = require('patrun');
const encryptor = require('simple-encryptor');
// lodash
const isEmpty = require('lodash/isEmpty');
const isObject = require('lodash/isObject');
const has = require('lodash/has');
const isString = require('lodash/isString');
// status
const status = require('statuses')
// package
const pack = require('../../package.json');
class CaesarClient
{
constructor (domain, port, opts = {})
{
this.domain = domain;
this.port = port;
this.routesInfo = [];
this.clientId = this.workOutClientId(opts.clientId);
this.isSecure = false;
if(isObject(opts) && isEmpty(opts) == false)
{
this.ca = opts.ca;
this.isSecure = (this.ca) ? true : false;
}
this.encryptor = encryptor(opts.encryptorKey);
this.protocol = (this.isSecure) ? 'https' : 'http';
this.baseUrl = `${this.protocol}://${this.domain}:${this.port}`;
this.requestOptions = {
url: this.baseUrl,
agentOptions: {
ca: this.ca
}
};
this.patrun = null;
return this;
}
workOutClientId (clientId) {
if(isString(clientId))
{
return clientId;
}
return `${os.hostname()}-${os.type()}-${os.platform()}-${os.release()}`;
}
getRequestOptions (path, dataObject = {})
{
let options = {
url: `${this.baseUrl}${path}`,
json: true,
headers: {
'encryptor-data': this.encryptor.encrypt(dataObject),
'encryptor-client-id': this.encryptor.encrypt(this.clientId)
}
};
if(this.isSecure)
{
options = Object.assign( options, {
agentOptions: {
ca: this.ca
}
});
}
return options;
}
findService (searchPattern)
{
return this.patrun.find(searchPattern);
}
decryptResponse (response)
{
if( has(response, 'encrypted-by-caesar') )
{
response = this.encryptor.decrypt(response['encrypted-by-caesar']);
}
return response;
}
setServerRoutes (responseEncryptedBody)
{
let self = this;
let response = self.decryptResponse(responseEncryptedBody);
self.patrun = patrun();
self.routesInfo = response.routes;
self.routesInfo.forEach( (routeInfo) => {
self.patrun.add(routeInfo.patternObject, routeInfo);
});
}
init ()
{
let self = this;
return new Promise((resolve, reject) => {
let requestOptions = this.getRequestOptions('/get-user-routes', {version: pack.version});
request.get(requestOptions, (err, resp) => {
if(err)
{
return reject(err);
}
if(resp.statusCode !== 200)
{
const errorMessage = resp.body.error;
return reject(new Error(`Caesar server has returned an error with code: ${resp.statusCode} - ${errorMessage}`));
}
self.setServerRoutes(resp.body);
return resolve(self);
});
});
}
call ( searchPattern, dataObject = {})
{
let self = this;
let service = self.findService(searchPattern);
return new Promise((resolve, reject) => {
if(!service || isEmpty(service))
{
return reject(new Error('No service found with search pattern provided'));
}
let requestOptions = this.getRequestOptions(`/user-route/${service.urlId}`, dataObject);
request.get(requestOptions, (err, resp) => {
if(err)
{
return reject(err);
}
if(resp.statusCode !== 200)
{
let error = new Error(`${status[resp.statusCode]} error with status: ${resp.statusCode}`);
error.statusCode = resp.statusCode;
if(resp.statusCode === 404)
{
self.setServerRoutes(resp.body);
}
return reject(error);
}
return resolve(self.decryptResponse(resp.body));
});
});
}
}
module.exports = CaesarClient;