'use strict'; var PromiseA = require('bluebird'); var path = require('path'); var fs = PromiseA.promisifyAll(require('fs')); module.exports.create = function (deps, conf) { var hrIds = require('human-readable-ids').humanReadableIds; var scmp = require('scmp'); var storageDir = path.join(__dirname, '..', 'var'); function read(fileName) { return fs.readFileAsync(path.join(storageDir, fileName)) .then(JSON.parse, function (err) { if (err.code === 'ENOENT') { return {}; } throw err; }); } function write(fileName, obj) { return fs.mkdirAsync(storageDir).catch(function (err) { if (err.code !== 'EEXIST') { console.error('failed to mkdir', storageDir, err.toString()); } }).then(function () { return fs.writeFileAsync(path.join(storageDir, fileName), JSON.stringify(obj), 'utf8'); }); } var owners = { _filename: 'owners.json' , all: function () { return read(this._filename).then(function (owners) { return Object.keys(owners).map(function (id) { var owner = owners[id]; owner.id = id; return owner; }); }); } , get: function (id) { // While we could directly read the owners file and access the id directly from // the resulting object I'm not sure of the details of how the object key lookup // works or whether that would expose us to timing attacks. // See https://codahale.com/a-lesson-in-timing-attacks/ return this.all().then(function (owners) { return owners.filter(function (owner) { return scmp(id, owner.id); })[0]; }); } , exists: function (id) { return this.get(id).then(function (owner) { return !!owner; }); } , set: function (id, obj) { var self = this; return read(self._filename).then(function (owners) { obj.id = id; owners[id] = obj; return write(self._filename, owners); }); } }; var config = { save: function (changes) { deps.messenger.send({ type: 'com.daplie.goldilocks/config' , changes: changes }); } }; var mdnsId = { _filename: 'mdns-id' , get: function () { var self = this; return read("mdns-id").then(function (result) { if (typeof result !== 'string') { throw new Error('mDNS ID not present'); } return result; }).catch(function () { return self.set(hrIds.random()); }); } , set: function (value) { var self = this; return write(self._filename, value).then(function () { return self.get(); }); } }; return { owners: owners , config: config , mdnsId: mdnsId }; };