[api test] Encrypt individual keys instead of entire stringified contents. Added basic unit tests.
This commit is contained in:
parent
d2b3561e09
commit
04c0f3a001
2 changed files with 84 additions and 55 deletions
|
@ -5,7 +5,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var fs = require('fs'),
|
var crypto = require('crypto'),
|
||||||
|
fs = require('fs'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
formats = require('../formats'),
|
formats = require('../formats'),
|
||||||
|
@ -71,10 +72,7 @@ File.prototype.save = function (value, callback) {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var contents = this.format.stringify(this.store, null, this.spacing),
|
fs.writeFile(this.file, this.stringify(), callback);
|
||||||
bytes = this.tryEncrypt(contents);
|
|
||||||
|
|
||||||
fs.writeFile(this.file, bytes, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -85,10 +83,7 @@ File.prototype.save = function (value, callback) {
|
||||||
// using the format specified by `this.format` synchronously.
|
// using the format specified by `this.format` synchronously.
|
||||||
//
|
//
|
||||||
File.prototype.saveSync = function (value) {
|
File.prototype.saveSync = function (value) {
|
||||||
var contents = this.format.stringify(this.store, null, this.spacing),
|
fs.writeFileSync(this.file, this.stringify());
|
||||||
bytes = this.tryEncrypt(contents);
|
|
||||||
|
|
||||||
fs.writeFileSync(this.file, bytes);
|
|
||||||
return this.store;
|
return this.store;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,8 +115,7 @@ File.prototype.load = function (callback) {
|
||||||
stringData = stringData.substr(1);
|
stringData = stringData.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var contents = self.tryDecrypt(stringData);
|
self.store = self.parse(stringData);
|
||||||
self.store = self.format.parse(contents);
|
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
return callback(new Error("Error parsing your configuration file: [" + self.file + ']: ' + ex.message));
|
return callback(new Error("Error parsing your configuration file: [" + self.file + ']: ' + ex.message));
|
||||||
|
@ -138,66 +132,78 @@ File.prototype.load = function (callback) {
|
||||||
// and responds appropriately.
|
// and responds appropriately.
|
||||||
//
|
//
|
||||||
File.prototype.loadSync = function () {
|
File.prototype.loadSync = function () {
|
||||||
var data, self = this;
|
if (!existsSync(this.file)) {
|
||||||
|
this.store = {};
|
||||||
if (!existsSync(self.file)) {
|
return this.store;
|
||||||
self.store = {};
|
|
||||||
data = {};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//
|
|
||||||
// Else, the path exists, read it from disk
|
|
||||||
//
|
|
||||||
try {
|
|
||||||
// Deals with file that include BOM
|
|
||||||
var fileData = fs.readFileSync(this.file, 'utf8');
|
|
||||||
if (fileData.charAt(0) === '\uFEFF') {
|
|
||||||
fileData = fileData.substr(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
data = this.format.parse(fileData);
|
|
||||||
this.store = data;
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
throw new Error("Error parsing your configuration file: [" + self.file + ']: ' + ex.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
//
|
||||||
|
// Else, the path exists, read it from disk
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
// Deals with file that include BOM
|
||||||
|
var fileData = fs.readFileSync(this.file, 'utf8');
|
||||||
|
if (fileData.charAt(0) === '\uFEFF') {
|
||||||
|
fileData = fileData.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.store = this.parse(fileData);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
throw new Error("Error parsing your configuration file: [" + this.file + ']: ' + ex.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.store;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// ### function tryEncrypt ()
|
// ### function stringify ()
|
||||||
// Returns an encrypted version of the contents IIF
|
// Returns an encrypted version of the contents IIF
|
||||||
// `this.secure` is enabled
|
// `this.secure` is enabled
|
||||||
//
|
//
|
||||||
File.prototype.tryEncrypt = function (contents) {
|
File.prototype.stringify = function () {
|
||||||
if (!this.secure) { return contents; }
|
var data = this.store,
|
||||||
|
self = this;
|
||||||
|
|
||||||
//
|
if (this.secure) {
|
||||||
// Contents have already been stringified by the format
|
data = Object.keys(data).reduce(function (acc, key) {
|
||||||
// so no need to re-stringify here.
|
var value = self.format.stringify(data[key]);
|
||||||
//
|
acc[key] = cipherConvert(value, {
|
||||||
return cipherConvert(contents, {
|
alg: self.secure.alg,
|
||||||
alg: this.secure.alg,
|
secret: self.secure.secret,
|
||||||
secret: this.secure.secret,
|
encs: { input: 'utf8', output: 'hex' }
|
||||||
encs: { input: 'utf8', output: 'hex' }
|
});
|
||||||
});
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.format.stringify(data, null, this.spacing);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// ### function tryDecrypt (contents)
|
// ### function parse (contents)
|
||||||
// Returns a decrypted version of the contents IFF
|
// Returns a decrypted version of the contents IFF
|
||||||
// `this.secure` is enabled.
|
// `this.secure` is enabled.
|
||||||
//
|
//
|
||||||
File.prototype.tryDecrypt = function (contents) {
|
File.prototype.parse = function (contents) {
|
||||||
if (!this.secure) { return contents; }
|
var parsed = this.format.parse(contents),
|
||||||
|
self = this;
|
||||||
|
|
||||||
return cipherConvert(contents, {
|
if (!this.secure) {
|
||||||
alg: this.secure.alg,
|
return parsed;
|
||||||
secret: this.secure.secret,
|
}
|
||||||
encs: { input: 'hex', output: 'utf8' }
|
|
||||||
});
|
return Object.keys(parsed).reduce(function (acc, key) {
|
||||||
|
var decrypted = cipherConvert(parsed[key], {
|
||||||
|
alg: self.secure.alg,
|
||||||
|
secret: self.secure.secret,
|
||||||
|
encs: { input: 'hex', output: 'utf8' }
|
||||||
|
});
|
||||||
|
|
||||||
|
acc[key] = self.format.parse(decrypted);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -222,5 +222,28 @@ vows.describe('nconf/stores/file').addBatch({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).addBatch({
|
||||||
|
"When using the nconf file store": {
|
||||||
|
topic: function () {
|
||||||
|
var secureStore = new nconf.File({
|
||||||
|
file: 'mock-file-path.json',
|
||||||
|
secure: 'super-secretzzz'
|
||||||
|
});
|
||||||
|
|
||||||
|
secureStore.store = data;
|
||||||
|
return secureStore;
|
||||||
|
},
|
||||||
|
"the stringify() method should encrypt properly": function (store) {
|
||||||
|
var contents = JSON.parse(store.stringify());
|
||||||
|
Object.keys(data).forEach(function (key) {
|
||||||
|
assert.isString(contents[key]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"the parse() method should decrypt properly": function (store) {
|
||||||
|
var contents = store.stringify();
|
||||||
|
var parsed = store.parse(contents);
|
||||||
|
assert.deepEqual(parsed, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}).export(module);
|
}).export(module);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue