mirror of
https://github.com/Hopiu/webapi-eca.git
synced 2026-03-16 22:10:31 +00:00
605 lines
16 KiB
JavaScript
605 lines
16 KiB
JavaScript
// Generated by CoffeeScript 1.6.3
|
|
/*
|
|
|
|
DB Interface
|
|
============
|
|
> Handles the connection to the database and provides functionalities for
|
|
> event/action modules, 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 modules are registered in an
|
|
> unordered set in the database, from where they can be retrieved again. For example
|
|
> a new action module has its ID (e.g 'probinder') first registered in the set
|
|
> 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
|
|
> (e.g. action\_module\_probinder).
|
|
>
|
|
*/
|
|
|
|
|
|
(function() {
|
|
var crypto, decrypt, encrypt, exports, getSetRecords, hash, log, redis, replyHandler,
|
|
_this = this;
|
|
|
|
log = require('./logging');
|
|
|
|
crypto = require('crypto-js');
|
|
|
|
redis = require('redis');
|
|
|
|
/*
|
|
Module call
|
|
-----------
|
|
Initializes the DB connection. Requires a valid configuration file which contains
|
|
a db port and a crypto key.
|
|
|
|
@param {Object} args
|
|
*/
|
|
|
|
|
|
exports = module.exports = function(args) {
|
|
var config, _ref;
|
|
args = args != null ? args : {};
|
|
log(args);
|
|
config = require('./config');
|
|
config(args);
|
|
if ((_ref = _this.db) != null) {
|
|
_ref.quit();
|
|
}
|
|
if (config.isReady()) {
|
|
_this.crypto_key = config.getCryptoKey();
|
|
_this.db = redis.createClient(config.getDBPort(), 'localhost', {
|
|
connect_timeout: 2000
|
|
});
|
|
return _this.db.on('error', function(err) {
|
|
err.addInfo = 'message from DB';
|
|
return log.error('DB', err);
|
|
});
|
|
} else {
|
|
return log.error('DB', 'Initialization failed because of missing config file!');
|
|
}
|
|
};
|
|
|
|
/*
|
|
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.db.connected) {
|
|
log.print('DB', 'Successfully connected to DB!');
|
|
return cb();
|
|
} else if (numAttempts++ < 10) {
|
|
return setTimeout(fCheckConnection, 100);
|
|
} else {
|
|
return cb(new Error('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) {
|
|
err.addInfo = "during \"" + action + "\"";
|
|
return log.error('DB', err);
|
|
} else {
|
|
return log.print('DB', "" + action + ": " + reply);
|
|
}
|
|
};
|
|
};
|
|
|
|
/*
|
|
Push an event into the event queue.
|
|
|
|
@public pushEvent( *oEvent* )
|
|
@param {Object} oEvent
|
|
*/
|
|
|
|
|
|
exports.pushEvent = function(oEvent) {
|
|
if (oEvent) {
|
|
log.print('DB', "Event pushed into the queue: " + oEvent.eventid);
|
|
return _this.db.rpush('event_queue', JSON.stringify(oEvent));
|
|
} else {
|
|
return log.error('DB', 'Why would you give me an empty event...');
|
|
}
|
|
};
|
|
|
|
/*
|
|
Pop an event from the event queue and pass it to the callback(err, obj) function.
|
|
|
|
@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;
|
|
err.addInfo = 'during hashing';
|
|
log.error('DB', err);
|
|
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 enciph, err, et;
|
|
if (plainText == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
enciph = crypto.createCipher('aes-256-cbc', _this.crypto_key);
|
|
et = enciph.update(plainText, 'utf8', 'base64');
|
|
return et + enciph.final('base64');
|
|
} catch (_error) {
|
|
err = _error;
|
|
err.addInfo = 'during encryption';
|
|
log.error('DB', err);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/*
|
|
Decrypts an encrypted string and hands it back on success or null.
|
|
|
|
@private decrypt( *crypticText* )
|
|
@param {String} crypticText
|
|
*/
|
|
|
|
|
|
decrypt = function(crypticText) {
|
|
var deciph, dt, err;
|
|
if (crypticText == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
deciph = crypto.createDecipher('aes-256-cbc', _this.crypto_key);
|
|
dt = deciph.update(crypticText, 'base64', 'utf8');
|
|
return dt + deciph.final('utf8');
|
|
} catch (_error) {
|
|
err = _error;
|
|
err.addInfo = 'during decryption';
|
|
log.error('DB', err);
|
|
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 the callback(err, obj) function.
|
|
|
|
@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) {
|
|
log.print('DB', "Fetching set records: " + set);
|
|
return _this.db.smembers(set, function(err, arrReply) {
|
|
var fCallback, objReplies, reply, semaphore, _i, _len, _results;
|
|
if (err) {
|
|
err.addInfo = "fetching " + set;
|
|
return log.error('DB', 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) {
|
|
err.addInfo = "fetching single element: " + prop;
|
|
log.error('DB', err);
|
|
} else if (!data) {
|
|
log.error('DB', 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;
|
|
}
|
|
});
|
|
};
|
|
|
|
/*
|
|
## Action Modules
|
|
#TODO Rename Action Modules into something like Action Caller
|
|
*/
|
|
|
|
|
|
/*
|
|
Store a string representation of an action module in the DB.
|
|
|
|
@public storeActionModule ( *amId, data* )
|
|
@param {String} amId
|
|
@param {String} data
|
|
*/
|
|
|
|
|
|
exports.storeActionModule = function(amId, data) {
|
|
log.print('DB', "storeActionModule: " + amId);
|
|
_this.db.sadd('action-modules', amId, replyHandler("storing action module key " + amId));
|
|
return _this.db.set("action-module:" + amId, data, replyHandler("storing action module " + amId));
|
|
};
|
|
|
|
/*
|
|
Query the DB for an action module and pass it to the callback(err, obj) function.
|
|
|
|
@public getActionModule( *amId, cb* )
|
|
@param {String} amId
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getActionModule = function(amId, cb) {
|
|
log.print('DB', "getActionModule: " + amId);
|
|
return _this.db.get("action-module:" + amId, cb);
|
|
};
|
|
|
|
/*
|
|
Fetch all action modules and hand them to the callback(err, obj) function.
|
|
|
|
@public getActionModules( *cb* )
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getActionModules = function(cb) {
|
|
return getSetRecords('action-modules', exports.getActionModule, cb);
|
|
};
|
|
|
|
/*
|
|
Store user-specific action module parameters .
|
|
|
|
@public storeActionParams( *userId, amId, data* )
|
|
@param {String} userId
|
|
@param {String} amId
|
|
@param {String} data
|
|
*/
|
|
|
|
|
|
exports.storeActionParams = function(userId, amId, data) {
|
|
log.print('DB', "storeActionParams: " + amId + ":" + userId);
|
|
return _this.db.set("action-params:" + amId + ":" + userId, hash(data), replyHandler("storing action params " + amId + ":" + userId));
|
|
};
|
|
|
|
/*
|
|
Query the DB for user-specific action module parameters,
|
|
and pass it to the callback(err, obj) function.
|
|
|
|
@public getActionParams( *userId, amId, cb* )
|
|
@param {String} userId
|
|
@param {String} amId
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getActionParams = function(userId, amId, cb) {
|
|
log.print('DB', "getActionParams: " + amId + ":" + userId);
|
|
return _this.db.get("action-params:" + amId + ":" + userId, function(err, data) {
|
|
return cb(err, decrypt(data));
|
|
});
|
|
};
|
|
|
|
/*
|
|
## Event Modules
|
|
#TODO rename event modules to event puller or something like that
|
|
*/
|
|
|
|
|
|
/*
|
|
Store a string representation of an event module in the DB.
|
|
|
|
@public storeEventModule( *emId, data* )
|
|
@param {String} emId
|
|
@param {String} data
|
|
*/
|
|
|
|
|
|
exports.storeEventModule = function(emId, data) {
|
|
log.print('DB', "storeEventModule: " + emId);
|
|
_this.db.sadd('event-modules', emId, replyHandler("storing event module key " + emId));
|
|
return _this.db.set('event-module:#{ emId }', data, replyHandler("storing event module " + emId));
|
|
};
|
|
|
|
/*
|
|
Query the DB for an event module and pass it to the callback(err, obj) function.
|
|
|
|
@public getEventModule( *emId, cb* )
|
|
@param {String} emId
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getEventModule = function(emId, cb) {
|
|
log.print('DB', "getEventModule: " + emId);
|
|
return _this.db.get("event-module:" + emId, cb);
|
|
};
|
|
|
|
/*
|
|
Fetch all event modules and pass them to the callback(err, obj) function.
|
|
|
|
@public getEventModules( *cb* )
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getEventModules = function(cb) {
|
|
return getSetRecords('event-modules', exports.getEventModule, cb);
|
|
};
|
|
|
|
/*
|
|
Store a string representation of user-specific parameters for an event module.
|
|
|
|
@public storeEventParams( *userId, emId, data* )
|
|
@param {String} userId
|
|
@param {String} emId
|
|
@param {Object} data
|
|
*/
|
|
|
|
|
|
exports.storeEventParams = function(userId, emId, data) {
|
|
log.print('DB', "storeEventParams: " + emId + ":" + userId);
|
|
return _this.db.set("event-params:" + emId + ":" + userId, encrypt(data), replyHandler("storing event auth " + emId + ":" + userId));
|
|
};
|
|
|
|
/*
|
|
Query the DB for an action module authentication token, associated with a user.
|
|
|
|
@public getEventAuth( *userId, emId, data* )
|
|
@param {String} userId
|
|
@param {String} emId
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getEventAuth = function(userId, emId, cb) {
|
|
log.print('DB', "getEventAuth: " + emId + ":" + userId);
|
|
return _this.db.get("event-auth:" + emId + ":" + userId, function(err, data) {
|
|
return cb(err, decrypt(data));
|
|
});
|
|
};
|
|
|
|
/*
|
|
## Rules
|
|
*/
|
|
|
|
|
|
/*
|
|
Store a string representation of a rule in the DB.
|
|
|
|
@public storeRule( *ruleId, userId, data* )
|
|
@param {String} ruleId
|
|
@param {String} userId
|
|
@param {String} data
|
|
*/
|
|
|
|
|
|
exports.storeRule = function(ruleId, userId, data) {
|
|
log.print('DB', "storeRule: " + ruleId);
|
|
_this.db.sadd('rules', "" + ruleId + ":" + userId, replyHandler("storing rule key \"" + ruleId + ":" + userId + "\""));
|
|
_this.db.sadd("user-set:" + userId + ":rules", ruleId, replyHandler("storing rule key to \"user:" + userId + ":rules\""));
|
|
_this.db.sadd("rule-set:" + ruleId + ":users", user, replyHandler("storing user key to \"rule:" + ruleId + ":users\""));
|
|
return _this.db.set("rule:" + ruleId + ":" + userId, data, replyHandler("storing rule \"" + ruleId + ":" + userId + "\""));
|
|
};
|
|
|
|
/*
|
|
Query the DB for a rule and pass it to the callback(err, obj) function.
|
|
|
|
@public getRule( *ruleId, cb* )
|
|
@param {String} ruleId
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getRule = function(ruleId, cb) {
|
|
log.print('DB', "getRule: " + ruleId);
|
|
return _this.db.get("rule:" + ruleId, cb);
|
|
};
|
|
|
|
/*
|
|
Fetch all rules from the database and pass them to the callback function.
|
|
|
|
@public getRules( *cb* )
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.getRules = function(cb) {
|
|
log.print('DB', 'Fetching all Rules');
|
|
return getSetRecords('rules', exports.getRule, cb);
|
|
};
|
|
|
|
/*
|
|
Store a user object (needs to be a flat structure).
|
|
|
|
@public storeUser( *objUser* )
|
|
@param {Object} objUser
|
|
*/
|
|
|
|
|
|
exports.storeUser = function(objUser) {
|
|
log.print('DB', "storeUser: " + objUser.username);
|
|
if (objUser && objUser.username && objUser.password) {
|
|
_this.db.sadd('users', objUser.username, replyHandler("storing user key " + objUser.username));
|
|
objUser.password = hash(objUser.password);
|
|
return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("storing user properties " + objUser.username));
|
|
} else {
|
|
return log.error('DB', new Error('username or password was missing'));
|
|
}
|
|
};
|
|
|
|
/*
|
|
Associate a role with a user.
|
|
|
|
@public storeUserRole( *userId, role* )
|
|
@param {String} userId
|
|
@param {String} role
|
|
*/
|
|
|
|
|
|
exports.storeUserRole = function(userId, role) {
|
|
log.print('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 the callback(err, obj)
|
|
|
|
@public getUserRoles( *userId* )
|
|
@param {String} userId
|
|
*/
|
|
|
|
|
|
exports.getUserRoles = function(userId) {
|
|
log.print('DB', "getUserRole: " + userId);
|
|
return _this.db.get("user-roles:" + userId, cb);
|
|
};
|
|
|
|
/*
|
|
Fetch all users of a role and pass them to the callback(err, obj)
|
|
|
|
@public getUserRoles( *role* )
|
|
@param {String} role
|
|
*/
|
|
|
|
|
|
exports.getRoleUsers = function(role) {
|
|
log.print('DB', "getRoleUsers: " + role);
|
|
return _this.db.get("role-users:" + role, cb);
|
|
};
|
|
|
|
/*
|
|
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 safety reasons.
|
|
|
|
@public loginUser( *userId, password, cb* )
|
|
@param {String} userId
|
|
@param {String} password
|
|
@param {function} cb
|
|
*/
|
|
|
|
|
|
exports.loginUser = function(userId, password, cb) {
|
|
var fCheck;
|
|
log.print('DB', "User \"" + userId + "\" tries to log in");
|
|
fCheck = function(pw) {
|
|
return function(err, obj) {
|
|
if (err) {
|
|
return cb(err);
|
|
} else if (obj && obj.password) {
|
|
if (pw === obj.password) {
|
|
log.print('DB', "User \"" + obj.username + "\" logged in!");
|
|
return cb(null, obj);
|
|
} else {
|
|
return cb(new Error('Wrong credentials!'));
|
|
}
|
|
} else {
|
|
return cb(new Error('User not found!'));
|
|
}
|
|
};
|
|
};
|
|
return _this.db.hgetall("user:" + userId, fCheck(password));
|
|
};
|
|
|
|
/*
|
|
Shuts down the db link.
|
|
|
|
@public shutDown()
|
|
*/
|
|
|
|
|
|
exports.shutDown = function() {
|
|
return _this.db.quit();
|
|
};
|
|
|
|
}).call(this);
|