// Generated by CoffeeScript 1.6.3 /* Persistence ============ > Handles the connection to the database and provides functionalities for > event pollers, action invokers, rules and the encrypted storing of authentication tokens. > General functionality as a wrapper for the module holds initialization, > encryption/decryption, the retrieval of modules and shut down. > > The general structure for linked data is that the key is stored in a set. > By fetching all set entries we can then fetch all elements, which is > automated in this function. > For example, modules of the same group, e.g. action invokers are registered in an > unordered set in the database, from where they can be retrieved again. For example > a new action invoker has its ID (e.g 'probinder') first registered in the set > 'action-invokers' and then stored in the db with the key 'action-invoker:' + ID > (e.g. action-invoker:probinder). > */ (function() { var IndexedModules, crypto, decrypt, encrypt, exports, getSetRecords, hash, redis, replyHandler, _this = this, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; crypto = require('crypto-js'); redis = require('redis'); /* Module call ----------- Initializes the DB connection with the given `db-port` property in the `args` object. @param {Object} args */ exports = module.exports = function(args) { var _ref; _this.log = args.logger; if ((_ref = _this.db) != null) { _ref.quit(); } _this.crypto_key = "}f6y1y}B{.an$}2c$Yl.$mSnF\\HX149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT -1) { _this.connRefused = true; return _this.log.error(err, 'DB | Wrong port?'); } }); _this.ep = new IndexedModules('event-poller', _this.db, _this.log); return _this.ai = new IndexedModules('action-invoker', _this.db, _this.log); }; /* Checks whether the db is connected and passes either an error on failure after ten attempts within five seconds, or nothing on success to the callback(err). @public isConnected( *cb* ) @param {function} cb */ exports.isConnected = function(cb) { var fCheckConnection, numAttempts; if (_this.db.connected) { return cb(); } else { numAttempts = 0; fCheckConnection = function() { if (_this.connRefused) { return cb(new Error('DB | Connection refused! Wrong port?')); } else { if (_this.db.connected) { _this.log.info('DB | Successfully connected to DB!'); return cb(); } else if (numAttempts++ < 10) { return setTimeout(fCheckConnection, 100); } else { return cb(new Error('DB | Connection to DB failed!')); } } }; return setTimeout(fCheckConnection, 100); } }; /* Abstracts logging for simple action replies from the DB. @private replyHandler( *action* ) @param {String} action */ replyHandler = function(action) { return function(err, reply) { if (err) { return _this.log.warn(err, "during '" + action + "'"); } else { return _this.log.info("DB | " + action + ": " + reply); } }; }; /* Push an event into the event queue. @public pushEvent( *oEvent* ) @param {Object} oEvent */ exports.pushEvent = function(oEvent) { if (oEvent) { _this.log.info("DB | Event pushed into the queue: '" + oEvent.eventid + "'"); return _this.db.rpush('event_queue', JSON.stringify(oEvent)); } else { return _this.log.warn('DB | Why would you give me an empty event...'); } }; /* Pop an event from the event queue and pass it to cb(err, obj). @public popEvent( *cb* ) @param {function} cb */ exports.popEvent = function(cb) { var makeObj; makeObj = function(pcb) { return function(err, obj) { return pcb(err, JSON.parse(obj)); }; }; return _this.db.lpop('event_queue', makeObj(cb)); }; /* Purge the event queue. @public purgeEventQueue() */ exports.purgeEventQueue = function() { return _this.db.del('event_queue', replyHandler('purging event queue')); }; /* Hashes a string based on SHA-3-512. @private hash( *plainText* ) @param {String} plainText */ hash = function(plainText) { var err; if (plainText == null) { return null; } try { return (crypto.SHA3(plainText, { outputLength: 512 })).toString(); } catch (_error) { err = _error; _this.log.warn(err, 'DB | during hashing'); return null; } }; /* Encrypts a string using the crypto key from the config file, based on aes-256-cbc. @private encrypt( *plainText* ) @param {String} plainText */ encrypt = function(plainText) { var err; if (plainText == null) { return null; } try { return crypto.AES.encrypt(plainText, _this.crypto_key); } catch (_error) { err = _error; _this.log.warn(err, 'DB | during encryption'); return null; } }; /* Decrypts an encrypted string and hands it back on success or null. @private decrypt( *crypticText* ) @param {String} crypticText */ decrypt = function(crypticText) { var dec, err; if (crypticText == null) { return null; } try { dec = crypto.AES.decrypt(crypticText, _this.crypto_key); return dec.toString(crypto.enc.Utf8); } catch (_error) { err = _error; _this.log.warn(err, 'DB | during decryption'); return null; } }; /* Fetches all linked data set keys from a linking set, fetches the single data objects via the provided function and returns the results to cb(err, obj). @private getSetRecords( *set, fSingle, cb* ) @param {String} set the set name how it is stored in the DB @param {function} fSingle a function to retrieve a single data element per set entry @param {function} cb the callback(err, obj) function that receives all the retrieved data or an error */ getSetRecords = function(set, fSingle, cb) { _this.log.info("DB | Fetching set records: '" + set + "'"); return _this.db.smembers(set, function(err, arrReply) { var fCallback, objReplies, reply, semaphore, _i, _len, _results; if (err) { _this.log.warn(err, "DB | fetching '" + set + "'"); return cb(err); } else if (arrReply.length === 0) { return cb(); } else { semaphore = arrReply.length; objReplies = {}; setTimeout(function() { if (semaphore > 0) { return cb(new Error("Timeout fetching '" + set + "'")); } }, 2000); fCallback = function(prop) { return function(err, data) { --semaphore; if (err) { _this.log.warn(err, "DB | fetching single element: '" + prop + "'"); } else if (!data) { _this.log.warn(new Error("Empty key in DB: '" + prop + "'")); } else { objReplies[prop] = data; } if (semaphore === 0) { return cb(null, objReplies); } }; }; _results = []; for (_i = 0, _len = arrReply.length; _i < _len; _i++) { reply = arrReply[_i]; _results.push(fSingle(reply, fCallback(reply))); } return _results; } }); }; IndexedModules = (function() { function IndexedModules(setname, db, log) { this.setname = setname; this.db = db; this.log = log; this.deleteUserParameters = __bind(this.deleteUserParameters, this); this.getUserParametersIds = __bind(this.getUserParametersIds, this); this.getUserParameters = __bind(this.getUserParameters, this); this.storeUserParameters = __bind(this.storeUserParameters, this); this.deleteModule = __bind(this.deleteModule, this); this.getModules = __bind(this.getModules, this); this.getModuleIds = __bind(this.getModuleIds, this); this.getAvailableModuleIds = __bind(this.getAvailableModuleIds, this); this.getModuleParams = __bind(this.getModuleParams, this); this.getModule = __bind(this.getModule, this); this.unpublish = __bind(this.unpublish, this); this.publish = __bind(this.publish, this); this.unlinkModule = __bind(this.unlinkModule, this); this.linkModule = __bind(this.linkModule, this); this.storeModule = __bind(this.storeModule, this); this.log.info("DB | Instantiated indexed modules for '" + this.setname + "'"); } IndexedModules.prototype.storeModule = function(mId, data) { this.log.info("DB | storeModule(" + this.setname + "): " + mId); this.db.sadd("" + this.setname + "s", mId, replyHandler("Storing '" + this.setname + "' key '" + mId + "'")); return this.db.hmset("" + this.setname + ":" + mId, data, replyHandler("Storing '" + this.setname + ":" + mId + "'")); }; IndexedModules.prototype.linkModule = function(mId, userId) { this.log.info("DB | linkModule(" + this.setname + "): " + mId + " to " + userId); this.db.sadd("" + this.setname + ":" + mId + ":users", userId, replyHandler("Linking '" + this.setname + ":" + mId + ":users' " + userId)); return this.db.sadd("user:" + userId + ":" + this.setname + "s", mId, replyHandler("Linking 'user:" + userId + ":" + this.setname + "s' " + mId)); }; IndexedModules.prototype.unlinkModule = function(mId, userId) { this.log.info("DB | unlinkModule(" + this.setname + "): " + mId + " to " + userId); this.db.srem("" + this.setname + ":" + mId + ":users", userId, replyHandler("Unlinking '" + this.setname + ":" + mId + ":users' " + userId)); return this.db.srem("user:" + userId + ":" + this.setname + "s", mId, replyHandler("Unlinking 'user:" + userId + ":" + this.setname + "s' " + mId)); }; IndexedModules.prototype.publish = function(mId) { this.log.info("DB | publish(" + this.setname + "): " + mId); return this.db.sadd("public-" + this.setname + "s", mId, replyHandler("Publishing '" + this.setname + "' key '" + mId + "'")); }; IndexedModules.prototype.unpublish = function(mId) { this.log.info("DB | unpublish(" + this.setname + "): " + mId); return this.db.srem("public-" + this.setname + "s", mId, replyHandler("Unpublishing '" + this.setname + "' key '" + mId + "'")); }; IndexedModules.prototype.getModule = function(mId, cb) { this.log.info("DB | getModule('" + this.setname + "): " + mId + "'"); return this.db.hgetall("" + this.setname + ":" + mId, cb); }; IndexedModules.prototype.getModuleParams = function(mId, cb) { this.log.info("DB | getModule('" + this.setname + "): " + mId + "'"); return this.db.hget("" + this.setname + ":" + mId, "params", cb); }; IndexedModules.prototype.getAvailableModuleIds = function(userId, cb) { this.log.info("DB | getPublicModuleIds(" + this.setname + ")"); return this.db.sunion("public-" + this.setname + "s", "user:" + userId + ":" + this.setname + "s", cb); }; IndexedModules.prototype.getModuleIds = function(cb) { this.log.info("DB | getModuleIds(" + this.setname + ")"); return this.db.smembers("" + this.setname + "s", cb); }; IndexedModules.prototype.getModules = function(cb) { this.log.info("DB | getModules(" + this.setname + ")"); return getSetRecords("" + this.setname + "s", this.getModule, cb); }; IndexedModules.prototype.deleteModule = function(mId) { this.log.info("DB | deleteModule(" + this.setname + "): " + mId); this.db.srem("" + this.setname + "s", mId, replyHandler("Deleting '" + this.setname + "' key '" + mId + "'")); return this.db.del("" + this.setname + ":" + mId, replyHandler("Deleting '" + this.setname + ":" + mId + "'")); }; IndexedModules.prototype.storeUserParameters = function(mId, userId, data) { this.log.info("DB | storeUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'"); this.db.sadd("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("Storing '" + this.setname + "' module parameters key '" + mId + "'")); return this.db.set("" + this.setname + "-params:" + mId + ":" + userId, encrypt(data), replyHandler("Storing '" + this.setname + "' module parameters '" + mId + ":" + userId + "'")); }; IndexedModules.prototype.getUserParameters = function(mId, userId, cb) { this.log.info("DB | getUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'"); return this.db.get("" + this.setname + "-params:" + mId + ":" + userId, function(err, data) { return cb(err, decrypt(data)); }); }; IndexedModules.prototype.getUserParametersIds = function(cb) { this.log.info("DB | getUserParametersIds(" + this.setname + ")"); return this.db.smembers("" + this.setname + "-params", cb); }; IndexedModules.prototype.deleteUserParameters = function(mId, userId) { this.log.info("DB | deleteUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'"); this.db.srem("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("Deleting '" + this.setname + "-params' key '" + mId + ":" + userId + "'")); return this.db.del("" + this.setname + "-params:" + mId + ":" + userId, replyHandler("Deleting '" + this.setname + "-params:" + mId + ":" + userId + "'")); }; return IndexedModules; })(); /* ## Action Invokers */ /* Store a string representation of an action invoker in the DB. @public storeActionInvoker ( *aiId, userId, data* ) @param {String} aiId @param {String} userId @param {String} data */ exports.storeActionInvoker = function(aiId, userId, data) { _this.ai.storeModule(aiId, data); return _this.ai.linkModule(aiId, userId); }; /* Make an action invoker public. @public publishActionInvoker ( *aiId* ) @param {String} aiId */ exports.publishActionInvoker = function(aiId) { return _this.ai.publish(aiId); }; /* Make an action invoker private. @public unpublishActionInvoker ( *aiId* ) @param {String} aiId */ exports.unpublishActionInvoker = function(aiId) { return _this.ai.unpublish(aiId); }; /* Query the DB for an action invoker and pass it to cb(err, obj). @public getActionInvoker( *aiId, cb* ) @param {String} aiId @param {function} cb */ exports.getActionInvoker = function(aiId, cb) { return _this.ai.getModule(aiId, cb); }; /* Query the DB for action invoker required params and pass it to cb(err, obj). @public getActionInvokerEventPollerRequiredParams( *epId, cb* ) @param {String} epId @param {function} cb */ exports.getActionInvokerRequiredParams = function(epId, cb) { return _this.ai.getModuleParams(epId, cb); }; /* Fetch all action invoker IDs and hand them to cb(err, obj). @public getActionInvokerIds( *cb* ) @param {function} cb */ exports.getActionInvokerIds = function(cb) { return _this.ai.getModuleIds(cb); }; /* Fetch all available actin invoker IDs for a user and hand them to cb(err, obj). @public getAvailableActionInvokerIds( *userId, cb* ) @param {function} cb */ exports.getAvailableActionInvokerIds = function(userId, cb) { return _this.ai.getAvailableModuleIds(userId, cb); }; /* Fetch all public action invoker IDs and hand them to cb(err, obj). @public getPublicActionInvokerIds( *cb* ) @param {function} cb */ exports.getPublicActionInvokerIds = function(cb) { return _this.ai.getPublicModuleIds(cb); }; /* Fetch all action invokers and hand them to cb(err, obj). @public getActionInvokers( *cb* ) @param {function} cb */ exports.getActionInvokers = function(cb) { return _this.ai.getModules(cb); }; /* Fetch all action invokers and hand them to cb(err, obj). @public getActionInvokers( *cb* ) @param {function} cb */ exports.deleteActionInvoker = function(aiId) { return _this.ai.deleteModule(aiId); }; /* Store user-specific action invoker parameters . @public storeActionUserParams( *userId, aiId, data* ) @param {String} userId @param {String} aiId @param {String} data */ exports.storeActionUserParams = function(aiId, userId, data) { return _this.ai.storeUserParameters(aiId, userId, data); }; /* Query the DB for user-specific action module parameters, and pass it to cb(err, obj). @public getActionUserParams( *userId, aiId, cb* ) @param {String} userId @param {String} aiId @param {function} cb */ exports.getActionUserParams = function(aiId, userId, cb) { return _this.ai.getUserParameters(aiId, userId, cb); }; /* Fetch all action params IDs and hand them to cb(err, obj). @public getActionUserParamsIds( *cb* ) @param {function} cb */ exports.getActionUserParamsIds = function(cb) { return _this.ai.getUserParametersIds(cb); }; /* Fetch all action modules and hand them to cb(err, obj). @public deleteActionUserParams( *cb* ) @param {function} cb */ exports.deleteActionUserParams = function(aiId, userId) { return _this.ai.deleteUserParameters(aiId, userId); }; /* ## Event Pollers */ /* Store a string representation of an event poller in the DB. @public storeEventPoller ( *epId, userId, data* ) @param {String} epId @param {String} userId @param {String} data */ exports.storeEventPoller = function(epId, userId, data) { _this.ep.storeModule(epId, data); return _this.ep.linkModule(epId, userId); }; /* Make an event poller public. @public publishEventPoller ( *epId* ) @param {String} epId */ exports.publishEventPoller = function(epId) { return _this.ep.publish(epId); }; /* Make an event poller private. @public unpublishEventPoller ( *epId* ) @param {String} epId */ exports.unpublishEventPoller = function(epId) { return _this.ep.unpublish(epId); }; /* Query the DB for an event poller and pass it to cb(err, obj). @public getEventPoller( *epId, cb* ) @param {String} epId @param {function} cb */ exports.getEventPoller = function(epId, cb) { return _this.ep.getModule(epId, cb); }; /* Query the DB for event poller required params and pass it to cb(err, obj). @public getEventPollerRequiredParams( *epId, cb* ) @param {String} epId @param {function} cb */ exports.getEventPollerRequiredParams = function(epId, cb) { return _this.ep.getModuleParams(epId, cb); }; /* Fetch all event poller IDs and hand them to cb(err, obj). @public getEventPollerIds( *cb* ) @param {function} cb */ exports.getEventPollerIds = function(cb) { return _this.ep.getModuleIds(cb); }; /* Fetch all available event poller IDs for a user and hand them to cb(err, obj). @public getAvailableEventPollerIds( *userId, cb* ) @param {function} cb */ exports.getAvailableEventPollerIds = function(userId, cb) { return _this.ep.getAvailableModuleIds(userId, cb); }; /* Fetch all public event poller IDs and hand them to cb(err, obj). @public getPublicEventPollerIds( *cb* ) @param {function} cb */ exports.getPublicEventPollerIds = function(cb) { return _this.ep.getPublicModuleIds(cb); }; /* Fetch all event pollers and hand them to cb(err, obj). @public getEventPollers( *cb* ) @param {function} cb */ exports.getEventPollers = function(cb) { return _this.ep.getModules(cb); }; /* Fetch all event pollers and hand them to cb(err, obj). @public getEventPollers( *cb* ) @param {function} cb */ exports.deleteEventPoller = function(epId) { return _this.ep.deleteModule(epId); }; /* Store user-specific event poller parameters . @public storeEventUserParams( *userId, epId, data* ) @param {String} userId @param {String} epId @param {String} data */ exports.storeEventUserParams = function(epId, userId, data) { return _this.ep.storeUserParameters(epId, userId, data); }; /* Query the DB for user-specific event module parameters, and pass it to cb(err, obj). @public getEventUserParams( *userId, epId, cb* ) @param {String} userId @param {String} epId @param {function} cb */ exports.getEventUserParams = function(epId, userId, cb) { return _this.ep.getUserParameters(epId, userId, cb); }; /* Fetch all event params IDs and hand them to cb(err, obj). @public getEventUserParamsIds( *cb* ) @param {function} cb */ exports.getEventUserParamsIds = function(cb) { return _this.ep.getUserParametersIds(cb); }; /* Fetch all event modules and hand them to cb(err, obj). @public deleteEventUserParams( *cb* ) @param {function} cb */ exports.deleteEventUserParams = function(epId, userId) { return _this.ep.deleteUserParameters(epId, userId); }; /* ## Rules */ /* Query the DB for a rule and pass it to cb(err, obj). @public getRule( *ruleId, cb* ) @param {String} ruleId @param {function} cb */ exports.getRule = function(ruleId, cb) { _this.log.info("DB | getRule: '" + ruleId + "'"); return _this.db.get("rule:" + ruleId, cb); }; /* Fetch all rules and pass them to cb(err, obj). @public getRules( *cb* ) @param {function} cb */ exports.getRules = function(cb) { _this.log.info('DB | Fetching all Rules'); return getSetRecords('rules', exports.getRule, cb); }; /* Fetch all rule IDs and hand it to cb(err, obj). @public getRuleIds( *cb* ) @param {function} cb */ exports.getRuleIds = function(cb) { _this.log.info('DB | Fetching all Rule IDs'); return _this.db.smembers('rules', cb); }; /* Store a string representation of a rule in the DB. @public storeRule( *ruleId, data* ) @param {String} ruleId @param {String} data */ exports.storeRule = function(ruleId, data) { _this.log.info("DB | storeRule: '" + ruleId + "'"); _this.db.sadd('rules', "" + ruleId, replyHandler("storing rule key '" + ruleId + "'")); return _this.db.set("rule:" + ruleId, data, replyHandler("storing rule '" + ruleId + "'")); }; /* Delete a string representation of a rule. @public deleteRule( *ruleId, userId* ) @param {String} ruleId @param {String} userId */ exports.deleteRule = function(ruleId) { _this.log.info("DB | deleteRule: '" + ruleId + "'"); _this.db.srem("rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "'")); _this.db.del("rule:" + ruleId, replyHandler("Deleting rule '" + ruleId + "'")); _this.db.smembers("rule:" + ruleId + ":users", function(err, obj) { var delLinkedUserRule, id, _i, _len, _results; delLinkedUserRule = function(userId) { return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in linked user '" + userId + "'")); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { id = obj[_i]; _results.push(delLinkedUserRule(id)); } return _results; }); _this.db.del("rule:" + ruleId + ":users", replyHandler("Deleting rule '" + ruleId + "' users")); _this.db.smembers("rule:" + ruleId + ":active-users", function(err, obj) { var delActiveUserRule, id, _i, _len, _results; delActiveUserRule = function(userId) { return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in active user '" + userId + "'")); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { id = obj[_i]; _results.push(delActiveUserRule(id)); } return _results; }); return _this.db.del("rule:" + ruleId + ":active-users", replyHandler("Deleting rule '" + ruleId + "' active users")); }; /* Associate a rule to a user. @public linkRule( *ruleId, userId* ) @param {String} ruleId @param {String} userId */ exports.linkRule = function(ruleId, userId) { _this.log.info("DB | linkRule: '" + ruleId + "' for user '" + userId + "'"); _this.db.sadd("rule:" + ruleId + ":users", userId, replyHandler("storing user '" + userId + "' for rule key '" + ruleId + "'")); return _this.db.sadd("user:" + userId + ":rules", ruleId, replyHandler("storing rule key '" + ruleId + "' for user '" + userId + "'")); }; /* Get rules linked to a user and hand it to cb(err, obj). @public getUserLinkRule( *userId, cb* ) @param {String} userId @param {function} cb */ exports.getUserLinkedRules = function(userId, cb) { _this.log.info("DB | getUserLinkedRules: for user '" + userId + "'"); return _this.db.smembers("user:" + userId + ":rules", cb); }; /* Get users linked to a rule and hand it to cb(err, obj). @public getRuleLinkedUsers( *ruleId, cb* ) @param {String} ruleId @param {function} cb */ exports.getRuleLinkedUsers = function(ruleId, cb) { _this.log.info("DB | getRuleLinkedUsers: for rule '" + ruleId + "'"); return _this.db.smembers("rule:" + ruleId + ":users", cb); }; /* Delete an association of a rule to a user. @public unlinkRule( *ruleId, userId* ) @param {String} ruleId @param {String} userId */ exports.unlinkRule = function(ruleId, userId) { _this.log.info("DB | unlinkRule: '" + ruleId + ":" + userId + "'"); _this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("removing user '" + userId + "' for rule key '" + ruleId + "'")); return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("removing rule key '" + ruleId + "' for user '" + userId + "'")); }; /* Activate a rule. @public activateRule( *ruleId, userId* ) @param {String} ruleId @param {String} userId */ exports.activateRule = function(ruleId, userId) { _this.log.info("DB | activateRule: '" + ruleId + "' for '" + userId + "'"); _this.db.sadd("rule:" + ruleId + ":active-users", userId, replyHandler("storing activated user '" + userId + "' in rule '" + ruleId + "'")); return _this.db.sadd("user:" + userId + ":active-rules", ruleId, replyHandler("storing activated rule '" + ruleId + "' in user '" + userId + "'")); }; /* Get rules activated for a user and hand it to cb(err, obj). @public getUserLinkRule( *userId, cb* ) @param {String} userId @param {function} cb */ exports.getUserActivatedRules = function(userId, cb) { _this.log.info("DB | getUserActivatedRules: for user '" + userId + "'"); return _this.db.smembers("user:" + userId + ":active-rules", cb); }; /* Get users activated for a rule and hand it to cb(err, obj). @public getRuleActivatedUsers ( *ruleId, cb* ) @param {String} ruleId @param {function} cb */ exports.getRuleActivatedUsers = function(ruleId, cb) { _this.log.info("DB | getRuleActivatedUsers: for rule '" + ruleId + "'"); return _this.db.smembers("rule:" + ruleId + ":active-users", cb); }; /* Deactivate a rule. @public deactivateRule( *ruleId, userId* ) @param {String} ruleId @param {String} userId */ exports.deactivateRule = function(ruleId, userId) { _this.log.info("DB | deactivateRule: '" + ruleId + "' for '" + userId + "'"); _this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("removing activated user '" + userId + "' in rule '" + ruleId + "'")); return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("removing activated rule '" + ruleId + "' in user '" + userId + "'")); }; /* Fetch all active ruleIds and pass them to cb(err, obj). @public getAllActivatedRuleIds( *cb* ) @param {function} cb */ exports.getAllActivatedRuleIdsPerUser = function(cb) { _this.log.info("DB | Fetching all active rules"); return _this.db.smembers('users', function(err, obj) { var fFetchActiveUserRules, result, semaphore, user, _i, _len, _results; result = {}; if (obj.length === 0) { return cb(null, result); } else { semaphore = obj.length; fFetchActiveUserRules = function(userId) { return _this.db.smembers("user:" + user + ":active-rules", function(err, obj) { if (obj.length > 0) { result[userId] = obj; } if (--semaphore === 0) { return cb(null, result); } }); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { user = obj[_i]; _results.push(fFetchActiveUserRules(user)); } return _results; } }); }; /* ## Users */ /* Store a user object (needs to be a flat structure). The password should be hashed before it is passed to this function. @public storeUser( *objUser* ) @param {Object} objUser */ exports.storeUser = function(objUser) { _this.log.info("DB | storeUser: '" + objUser.username + "'"); if (objUser && objUser.username && objUser.password) { _this.db.sadd('users', objUser.username, replyHandler("storing user key '" + objUser.username + "'")); objUser.password = objUser.password; return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("storing user properties '" + objUser.username + "'")); } else { return _this.log.warn(new Error('DB | username or password was missing')); } }; /* Fetch all user IDs and pass them to cb(err, obj). @public getUserIds( *cb* ) @param {function} cb */ exports.getUserIds = function(cb) { _this.log.info("DB | getUserIds"); return _this.db.smembers("users", cb); }; /* Fetch a user by id and pass it to cb(err, obj). @public getUser( *userId, cb* ) @param {String} userId @param {function} cb */ exports.getUser = function(userId, cb) { _this.log.info("DB | getUser: '" + userId + "'"); return _this.db.hgetall("user:" + userId, cb); }; /* Deletes a user and all his associated linked and active rules. @public deleteUser( *userId* ) @param {String} userId */ exports.deleteUser = function(userId) { _this.log.info("DB | deleteUser: '" + userId + "'"); _this.db.srem("users", userId, replyHandler("Deleting user key '" + userId + "'")); _this.db.del("user:" + userId, replyHandler("Deleting user '" + userId + "'")); _this.db.smembers("user:" + userId + ":rules", function(err, obj) { var delLinkedRuleUser, id, _i, _len, _results; delLinkedRuleUser = function(ruleId) { return _this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in linked rule '" + ruleId + "'")); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { id = obj[_i]; _results.push(delLinkedRuleUser(id)); } return _results; }); _this.db.del("user:" + userId + ":rules", replyHandler("Deleting user '" + userId + "' rules")); _this.db.smembers("user:" + userId + ":active-rules", function(err, obj) { var delActivatedRuleUser, id, _i, _len, _results; delActivatedRuleUser = function(ruleId) { return _this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("Deleting user key '" + userId + "' in active rule '" + ruleId + "'")); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { id = obj[_i]; _results.push(delActivatedRuleUser(id)); } return _results; }); _this.db.del("user:" + userId + ":active-rules", replyHandler("Deleting user '" + userId + "' rules")); _this.db.smembers("user:" + userId + ":roles", function(err, obj) { var delRoleUser, id, _i, _len, _results; delRoleUser = function(roleId) { return _this.db.srem("role:" + roleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in role '" + roleId + "'")); }; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { id = obj[_i]; _results.push(delRoleUser(id)); } return _results; }); return _this.db.del("user:" + userId + ":roles", replyHandler("Deleting user '" + userId + "' roles")); }; /* Checks the credentials and on success returns the user object to the callback(err, obj) function. The password has to be hashed (SHA-3-512) beforehand by the instance closest to the user that enters the password, because we only store hashes of passwords for security6 reasons. @public loginUser( *userId, password, cb* ) @param {String} userId @param {String} password @param {function} cb */ exports.loginUser = function(userId, password, cb) { var fCheck; _this.log.info("DB | User '" + userId + "' tries to log in"); fCheck = function(pw) { return function(err, obj) { if (err) { return cb(err, null); } else if (obj && obj.password) { if (pw === obj.password) { _this.log.info("DB | User '" + obj.username + "' logged in!"); return cb(null, obj); } else { return cb(new Error('Wrong credentials!'), null); } } else { return cb(new Error('User not found!'), null); } }; }; return _this.db.hgetall("user:" + userId, fCheck(password)); }; /* ## User Roles */ /* Associate a role with a user. @public storeUserRole( *userId, role* ) @param {String} userId @param {String} role */ exports.storeUserRole = function(userId, role) { _this.log.info("DB | storeUserRole: '" + userId + ":" + role + "'"); _this.db.sadd('roles', role, replyHandler("adding role '" + role + "' to role index set")); _this.db.sadd("user:" + userId + ":roles", role, replyHandler("adding role '" + role + "' to user '" + userId + "'")); return _this.db.sadd("role:" + role + ":users", userId, replyHandler("adding user '" + userId + "' to role '" + role + "'")); }; /* Fetch all roles of a user and pass them to cb(err, obj). @public getUserRoles( *userId* ) @param {String} userId @param {function} cb */ exports.getUserRoles = function(userId, cb) { _this.log.info("DB | getUserRoles: '" + userId + "'"); return _this.db.smembers("user:" + userId + ":roles", cb); }; /* Fetch all users of a role and pass them to cb(err, obj). @public getUserRoles( *role* ) @param {String} role @param {function} cb */ exports.getRoleUsers = function(role, cb) { _this.log.info("DB | getRoleUsers: '" + role + "'"); return _this.db.smembers("role:" + role + ":users", cb); }; /* Remove a role from a user. @public removeRoleFromUser( *role, userId* ) @param {String} role @param {String} userId */ exports.removeUserRole = function(userId, role) { _this.log.info("DB | removeRoleFromUser: role '" + role + "', user '" + userId + "'"); _this.db.srem("user:" + userId + ":roles", role, replyHandler("Removing role '" + role + "' from user '" + userId + "'")); return _this.db.srem("role:" + role + ":users", userId, replyHandler("Removing user '" + userId + "' from role '" + role + "'")); }; /* Shuts down the db link. @public shutDown() */ exports.shutDown = function() { var _ref; return (_ref = _this.db) != null ? _ref.quit() : void 0; }; }).call(this);