var fs = require('fs');
var logger = require('../logger');
var whitelist = require('../resources/aws_whitelist.json');
var paramTypes = {
REQ_DESC: 'request_descriptors',
REQ_PARAMS: 'request_parameters',
RES_DESC: 'response_descriptors',
RES_PARAMS: 'response_parameters'
};
/**
* Represents a set of AWS services, operations and keys or params to capture.
* @constructor
* @param {string|Object} [source] - The location or source JSON object of the custom AWS whitelist file. If none is provided, the default file will be used.
*/
function CallCapturer (source) {
this.init(source);
}
CallCapturer.prototype.init = function init(source) {
if (source) {
if (typeof source === 'string') {
logger.getLogger().info('Using custom AWS whitelist file: ' + source);
this.services = loadWhitelist(JSON.parse(fs.readFileSync(source, 'utf8')));
} else {
logger.getLogger().info('Using custom AWS whitelist source.');
this.services = loadWhitelist(source);
}
} else {
this.services = whitelist.services;
}
};
CallCapturer.prototype.append = function append(source) {
var newServices = {};
if (typeof source === 'string') {
logger.getLogger().info('Appending AWS whitelist with custom file: ' + source);
newServices = loadWhitelist(JSON.parse(fs.readFileSync(source, 'utf8')));
} else {
logger.getLogger().info('Appending AWS whitelist with a custom source.');
newServices = loadWhitelist(source);
}
for (var attribute in newServices) {
this.services[attribute] = newServices[attribute];
}
};
CallCapturer.prototype.capture = function capture(serviceName, response) {
var operation = response.request.operation;
var call = this.services[serviceName] !== undefined ? this.services[serviceName].operations[operation] : null;
if (call === null) {
logger.getLogger().debug('Call "' + serviceName + '.' + operation + '" is not whitelisted for additional data capturing. Ignoring.');
return;
}
var dataCaptured = {};
for (var paramType in call) {
var params = call[paramType];
if (paramType === paramTypes.REQ_PARAMS) {
captureCallParams(params, response.request.params, dataCaptured);
} else if (paramType === paramTypes.REQ_DESC) {
captureDescriptors(params, response.request.params, dataCaptured);
} else if (paramType === paramTypes.RES_PARAMS) {
if (response.data) {
captureCallParams(params, response.data, dataCaptured);
}
} else if (paramType === paramTypes.RES_DESC) {
if (response.data) {
captureDescriptors(params, response.data, dataCaptured);
}
} else {
logger.getLogger().error('Unknown parameter type "' + paramType + '". Must be "request_descriptors", "response_descriptors", ' +
'"request_parameters" or "response_parameters".');
}
}
return dataCaptured;
};
function captureCallParams(params, call, data) {
params.forEach(function(param) {
if (typeof call[param] !== 'undefined') {
var formatted = toSnakeCase(param);
this[formatted] = call[param];
}
}, data);
}
function captureDescriptors(descriptors, params, data) {
for (var paramName in descriptors) {
var attributes = descriptors[paramName];
if (typeof params[paramName] !== 'undefined') {
var paramData;
if (attributes.list && attributes.get_count) {
paramData = params[paramName] ? params[paramName].length : 0;
} else {
paramData = attributes.get_keys === true ? Object.keys(params[paramName]) : params[paramName];
}
if (typeof attributes.rename_to === 'string') {
data[attributes.rename_to] = paramData;
} else {
var formatted = toSnakeCase(paramName);
data[formatted] = paramData;
}
}
}
}
function toSnakeCase(param) {
if (param === 'IPAddress') {
return 'ip_address';
} else {
return param.split(/(?=[A-Z])/).join('_').toLowerCase();
}
}
function loadWhitelist(source) {
var doc = source;
if (doc.services === undefined) {
throw new Error('Document formatting is incorrect. Expecting "services" param.');
}
return doc.services;
}
module.exports = CallCapturer;