/* * This file is part of the authRxbn eco-system. * * (c) Ruben Meyer * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ // init var mongoose = require('mongoose'); var crypto = require('crypto'); var methods = {}; // connect mongoose.connect(global['gds'].cfg.mongoose.uri, { useNewUrlParser: true }); global['gds'].db = mongoose.connection; var mdls = require('./models.js'); var models = mdls(global['gds'].db); (async function() { global['gds'].db = await global['gds'].db.useDb(global['gds'].cfg.mongoose.db); models = mdls(global['gds'].db); })(); // connection error handling global['gds'].db.on('error', (data) => { global['modules'].logs.error('MongoDB connection error:\n', data); process.exit(); // exit on connection error }); // // // //////// //////// /////// // // // // // // // // // // ////// ////// ////// // // // // // // // // //////// ////// //////// // // // ///////////////////////////////////////////// /** * Adds User to Database * @author Ruben Meyer * @param {String} nick nickname * @param {String} email email * @param {String} passhash hashed password * @param {Number} group Group id (normally 0 -> user) * @param {Function} callback Callback function (error, reply) */ methods.addUser = (nick, email, passhash, group, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof nick !== 'string') return callback(new TypeError('nick is not a string::database.addUser('+nick+','+email+','+passhash+','+group+',callback)', module.filename)); if(typeof email !== 'string') return callback(new TypeError('email is not a string::database.addUser('+nick+','+email+','+passhash+','+group+',callback)', module.filename)); if(typeof passhash !== 'string') return callback(new TypeError('passhash is not a string::database.addUser('+nick+','+email+','+passhash+','+group+',callback)', module.filename)); if(isNaN(group)) return callback(new TypeError('group is not a number::database.addUser('+nick+','+email+','+passhash+','+group+',callback)', module.filename)); let userModel = models.user; let user = new userModel(); user.nickname = nick; user.email = email; user.passhash = passhash; user.group = group; user.save((err) => { if(!err) callback(null, 1); else callback(err); }); }; /** * Deletes User from Database * @author Ruben Meyer * @TODO * @param {String} haystack email or nick * @param {Function} callback Callback function (error, reply) */ methods.delUser = (haystack, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof haystack !== 'string') return callback(new TypeError('haystack is not a string::database.delUser('+haystack+',callback)', module.filename)); let userModel = models.user; userModel.findOneAndDelete().or([{nickname: haystack}, {email: haystack}]) .then((rep) => { // TODO delete user global['logs'].debug('deleted user: '+haystack); callback(null, 1); }).catch((err) => { callback(err); }); }; /** * get all users * @author Ruben Meyer * @param {Function} callback Callback function (reply -> Array users) */ methods.getUsers = (callback) => { if(typeof callback !== 'function') callback = function() {}; let userModel = models.user; userModel.find({}) .then((users) => { if(users.length > 0) return callback(null, users); else return callback(null, false); }).catch((err) => { return callback(err); }); }; /** * query users by email, nickname or rememberme token * @author Ruben Meyer * @param {String|String[]} haystack email or nick * @param {Function} callback Callback function (reply -> Array users) */ methods.getUser = (haystack, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof haystack !== 'string' && typeof haystack !== 'object') return callback(new TypeError('email or nickname is not a string|object::database.getUser('+haystack+',callback)', module.filename)); let userModel = models.user; let or = []; if(typeof haystack === 'string') { or = [{nickname: haystack}, {email: haystack}, {token: haystack}]; if(haystack.match(/^[0-9a-fA-F]{24}$/)) or.push({_id: haystack}); } else { or = []; for(let i = 0; i < haystack.length; i++) { if(haystack[i].match(/^[0-9a-fA-F]{24}$/)) or.push({_id: haystack[i]}); or.push({nickname: haystack[i]}); or.push({email: haystack[i]}); or.push({token: haystack[i]}); } } userModel.find().or(or) .then((users) => { if(users.length > 0) return callback(null, users); else return callback(null, false); }).catch((err) => { return callback(err); }); }; /** * updates obj keys in user entry * @author Ruben Meyer * @param {Number} id User ID * @param {Object} obj data * @param {Function} callback Callback function */ methods.updateUser = (id, obj, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof id !== 'string') return callback(new TypeError('id is not a string::database.updateUser('+id+','+JSON.stringify(obj)+',callback)', module.filename)); if(typeof obj !== 'object') return callback(new TypeError('obj is not an object::database.updateUser('+id+','+JSON.stringify(obj)+',callback)', module.filename)); let userModel = models.user; userModel.findByIdAndUpdate(id, obj, (err, data) => { if(err) callback(err); else callback(null, data); }); }; /** * updates data based on login * @author Ruben Meyer * @TODO UPDATE METHOD; PROBABLY OUTDATED * @param {Number} id User ID * @param {Object} data data JSON -> remember * @param {Function} callback Callback function (date => 'Login Date', token => 'RememberMe Cookie Token') */ methods.addActivity = (id, data, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof id !== 'string') return callback(new TypeError('id is not a string::database.updateNewAction('+id+','+JSON.stringify(options)+',callback)', module.filename)); if(typeof options !== 'object' && options !== null) return callback(new TypeError('obj is not an object::database.updateUserProfile('+id+','+JSON.stringify(obj)+',callback)', module.filename)); let date = new Date().toISOString(); let timestamp = new Date(date).getTime(); functions.updateUserProfile(id, { last_action: date }, (err, rep) => { if(err) return callback(err); if(options.rememberme && options.new_token !== false) { var token = ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, (c) => (c ^ crypto.randomBytes(new Uint8Array(1).length)[0] & 15 >> c / 4).toString(16)); var Remember = models.remember; Remember.findOneAndUpdate({userId: id}, {token: token, timestamp: Date.now()}, {upsert: true}, (err1, data) => { if(err1) callback(err1); return callback(null, { date: date, timestamp: timestamp, token: token }); }); } else { callback(null, { date: date, timestamp: timestamp, token: options.old_token }); } }); }; // //////// //////// //////// ////// // // // // // // // // // //////// /////// //////// ////// // // // // // // // // // // // ////// // ////////////////////////////////////////////// /** * get Applications * @author Ruben Meyer * @param {Function} callback Callback function (err, apps) */ methods.getApps = (callback) => { if(typeof callback !== 'function') callback = function() {}; var Application = models.application; Application.find({}, (err, apps) => { if(err) callback(err); else callback(null, apps); }); }; /** * return auth obj * @author Ruben Meyer * @param {Object} obj data obj (aId, uId) * @param {Function} callback Callback function (err, obj) obj -> (aId, uId, token) */ methods.setAuthCode = (obj, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof obj !== 'object') return callback(new TypeError('obj is not an object::database.setAuthCode('+JSON.stringify(obj)+',callback)', module.filename)); var AuthCode = models.authCode; let query = { applicationId: obj.aId, userId: obj.uId }; let change = { token: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, (c) => (c ^ crypto.randomBytes(new Uint8Array(1).length)[0] & 15 >> c / 4).toString(16)), timestamp: Date.now() }; AuthCode.findOneAndUpdate(query, change, {upsert: true}, (err1, data) => { if(err1) callback(err1); return callback(null, { timestamp: change.timestamp, token: change.token }); }); }; /** * return auth obj * @author Ruben Meyer * @param {Object} obj data obj (aId, aSecret, uId, token) * @param {Function} callback Callback function (err, bool) */ methods.getAuth = (obj, callback) => { if(typeof callback !== 'function') callback = function() {}; if(typeof obj !== 'object') return callback(new TypeError('obj is not an object::database.getAuthCode('+JSON.stringify(obj)+',callback)', module.filename)); var AuthCode = models.authCode; AuthCode.findOne({ $and: [ {applicationId: mongoose.Types.ObjectId(obj.aId)}, {userId: mongoose.Types.ObjectId(obj.uId)}, {token: obj.token} ] }, (err, data) => { if(err) callback(err); else { if(typeof data === "object") { if(data === null || data === []) return callback(null, false); var Application = models.application; Application.findOne({ $and: [ {_id: mongoose.Types.ObjectId(obj.aId)}, {secret: obj.aSecret} ] }, (err1, data1) => { if(err1) callback(err1); else { if(obj.token == data.token && obj.aId == String(data.applicationId) && obj.uId == String(data.userId) && obj.aSecret == data1.secret) { callback(null, true); //functions.setAuthCode({ // aId: obj.aId, // uId: obj.uId //}, () => {}); } else callback(null, false); } }); } else callback(null, false); } }); }; /** * return if app is permitted to do access call * @author Ruben Meyer * @param {Object} obj data obj (aId, redirectUrl) * @param {Function} callback Callback function (err, bool) */ methods.verifyAppCall = (obj, callback) => { }; // //////// //////// //////// //////// //////// // // // // // // // // //////// // // // // //////// // // // //////// // // // //////// // // // // //////// // //////////////////////////////////////////////////////// /** * callback with user count * @author Ruben Meyer * @param {Function} callback Callback function (reply -> int) */ methods.userCount = (callback) => { if(typeof callback !== 'function') callback = function() {}; let userModel = models.user; userModel.countDocuments({}, (err, count) => { callback((err) ? err : count); }); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////// module.exports = methods;