[api] Allow for "secure" option to be passed to nconf.stores.File
to perform content encryption / decryption with crypto.createCipher
.
This commit is contained in:
parent
4c07028e40
commit
2de2bc0b66
1 changed files with 83 additions and 20 deletions
|
@ -26,11 +26,25 @@ var File = exports.File = function (options) {
|
||||||
|
|
||||||
Memory.call(this, options);
|
Memory.call(this, options);
|
||||||
|
|
||||||
this.type = 'file';
|
this.type = 'file';
|
||||||
this.file = options.file;
|
this.file = options.file;
|
||||||
this.dir = options.dir || process.cwd();
|
this.dir = options.dir || process.cwd();
|
||||||
this.format = options.format || formats.json;
|
this.format = options.format || formats.json;
|
||||||
this.json_spacing = options.json_spacing || 2;
|
this.secure = options.secure;
|
||||||
|
this.spacing = options.json_spacing
|
||||||
|
|| options.spacing
|
||||||
|
|| 2;
|
||||||
|
|
||||||
|
if (this.secure) {
|
||||||
|
this.secure.alg = this.secure.alg || 'aes-256-ctr';
|
||||||
|
if (this.secure.secretPath) {
|
||||||
|
this.secret = fs.readFileSync(this.secure.secretPath, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.secure.secret) {
|
||||||
|
throw new Error('secure.secret option is required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.search) {
|
if (options.search) {
|
||||||
this.search(this.dir);
|
this.search(this.dir);
|
||||||
|
@ -53,9 +67,10 @@ File.prototype.save = function (value, callback) {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFile(this.file, this.format.stringify(this.store, null, this.json_spacing), function (err) {
|
var contents = this.format.stringify(this.store, null, this.spacing),
|
||||||
return err ? callback(err) : callback();
|
bytes = this.tryEncrypt(contents);
|
||||||
});
|
|
||||||
|
fs.writeFile(this.file, bytes, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -66,12 +81,10 @@ 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) {
|
||||||
try {
|
var contents = this.format.stringify(this.store, null, this.spacing),
|
||||||
fs.writeFileSync(this.file, this.format.stringify(this.store, null, this.json_spacing));
|
bytes = this.tryEncrypt(contents);
|
||||||
}
|
|
||||||
catch (ex) {
|
fs.writeFileSync(this.file, bytes);
|
||||||
throw(ex);
|
|
||||||
}
|
|
||||||
return this.store;
|
return this.store;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,12 +110,14 @@ File.prototype.load = function (callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//deals with string that include BOM
|
// Deals with string that include BOM
|
||||||
var stringData = data.toString();
|
var stringData = data.toString();
|
||||||
|
if (stringData.charAt(0) === '\uFEFF') {
|
||||||
|
stringData = stringData.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (stringData.charAt(0) === '\uFEFF') stringData = stringData.substr(1);
|
var contents = self.tryDecrypt(stringData);
|
||||||
self.store = self.format.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));
|
||||||
|
@ -130,9 +145,11 @@ File.prototype.loadSync = function () {
|
||||||
// Else, the path exists, read it from disk
|
// Else, the path exists, read it from disk
|
||||||
//
|
//
|
||||||
try {
|
try {
|
||||||
//deals with file that include BOM
|
// Deals with file that include BOM
|
||||||
var fileData = fs.readFileSync(this.file, 'utf8');
|
var fileData = fs.readFileSync(this.file, 'utf8');
|
||||||
if (fileData.charAt(0) === '\uFEFF') fileData = fileData.substr(1);
|
if (fileData.charAt(0) === '\uFEFF') {
|
||||||
|
fileData = fileData.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
data = this.format.parse(fileData);
|
data = this.format.parse(fileData);
|
||||||
this.store = data;
|
this.store = data;
|
||||||
|
@ -145,6 +162,40 @@ File.prototype.loadSync = function () {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function tryEncrypt ()
|
||||||
|
// Returns an encrypted version of the contents IIF
|
||||||
|
// `this.secure` is enabled
|
||||||
|
//
|
||||||
|
File.prototype.tryEncrypt = function (contents) {
|
||||||
|
if (!this.secure) { return contents; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Contents have already been stringified by the format
|
||||||
|
// so no need to re-stringify here.
|
||||||
|
//
|
||||||
|
return cipherConvert(contents, {
|
||||||
|
alg: this.secure.alg,
|
||||||
|
secret: this.secure.secret,
|
||||||
|
encs: { input: 'utf8', output: 'hex' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function tryDecrypt (contents)
|
||||||
|
// Returns a decrypted version of the contents IFF
|
||||||
|
// `this.secure` is enabled.
|
||||||
|
//
|
||||||
|
File.prototype.tryDecrypt = function (contents) {
|
||||||
|
if (!this.secure) { return contents; }
|
||||||
|
|
||||||
|
return cipherConvert(contents, {
|
||||||
|
alg: this.secure.alg,
|
||||||
|
secret: this.secure.secret,
|
||||||
|
encs: { input: 'hex', output: 'utf8' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// ### function search (base)
|
// ### function search (base)
|
||||||
// #### @base {string} Base directory (or file) to begin searching for the target file.
|
// #### @base {string} Base directory (or file) to begin searching for the target file.
|
||||||
|
@ -235,3 +286,15 @@ File.prototype.search = function (base) {
|
||||||
|
|
||||||
return fullpath;
|
return fullpath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function cipherConvert (contents, opts)
|
||||||
|
// Returns the result of the cipher operation
|
||||||
|
// on the contents contents.
|
||||||
|
//
|
||||||
|
function cipherConvert(contents, opts) {
|
||||||
|
var encs = opts.encs;
|
||||||
|
var cipher = crypto.createCipher(opts.alg, opts.secret);
|
||||||
|
return cipher.update(contents, encs.input, encs.output)
|
||||||
|
+ cipher.final(encs.output);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue