From 815f058ffedfc670564ae330d9294ef8a5b06b29 Mon Sep 17 00:00:00 2001 From: Ruben Meyer <46384706+rxbnDE@users.noreply.github.com> Date: Sun, 23 Jun 2019 23:27:52 +0200 Subject: [PATCH] database module; partly outdated --- README.md | 2 +- app.js | 9 +- bin/database/models.js | 1 + bin/database/module.js | 345 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 355 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf949d0..4a74c4b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ $ DB_URL=mongodb://user:pass@ip:port/db DB_NAME=authRxbn NODE_ENV=debug node app # TODO | title | description | - | | ----- | ----------- | --- | -| database models | user, service, etc. pp. | - | +| database module | remove rememberMe part; add more stats methods | - | | modules | database, authentication, etc. pp. | - | | cli user management | get, add, modify, delete user data | - | | event handling | handle CLI events | - | diff --git a/app.js b/app.js index aca61a9..f96e9c1 100644 --- a/app.js +++ b/app.js @@ -7,7 +7,7 @@ // GDS: Global Data System global['gds'] = { - debug: (process.env.NODE_ENV === 'debug') ? 1 : 0, + debug: (process.env.NODE_ENV === 'debug') ? true : false, db: null, cache: {}, cfg: require(__dirname+'/bin/config') @@ -22,6 +22,13 @@ let load = (name) => { return require(__dirname+'/bin/'+name+'/module'); }; +// environment variable check +if(typeof process.env.DB_URL == 'undefined' || typeof process.env.DB_NAME == 'undefined') { + if(typeof process.env.DB_URL == 'undefined') console.error("environment variable DB_URL is not set"); + if(typeof process.env.DB_NAME == 'undefined') console.error("environment variable DB_NAME is not set"); + process.exit(); +} + global['modules'].events = load('events'); // event handler global['modules'].cli = load('cli'); // command line interface global['modules'].logs = load('logs'); // log handler diff --git a/bin/database/models.js b/bin/database/models.js index 14924af..9391fc0 100644 --- a/bin/database/models.js +++ b/bin/database/models.js @@ -20,6 +20,7 @@ models.user = new Schema({ type: String, validate: [ { validator: function(value) { + // @url: http://emailregex.com/ let regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; return regex.test(value); } diff --git a/bin/database/module.js b/bin/database/module.js index e69de29..0c337de 100644 --- a/bin/database/module.js +++ b/bin/database/module.js @@ -0,0 +1,345 @@ +/* + * 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} firstname First name + * @param {String} lastname Last name + * @param {String} email E-Mail + * @param {String} password hashed PW + * @param {Number} group Group id (normally 0 -> user) + * @param {Function} callback Callback function (error, reply) + */ +methods.addUser = (nick, email, pass, 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+','+pass+','+group+',callback)', module.filename)); + if(typeof email !== 'string') return callback(new TypeError('email is not a string::database.addUser('+nick+','+email+','+pass+','+group+',callback)', module.filename)); + if(typeof pass !== 'string') return callback(new TypeError('pass is not a string::database.addUser('+nick+','+email+','+pass+','+group+',callback)', module.filename)); + if(isNaN(group)) return callback(new TypeError('group is not a number::database.addUser('+nick+','+email+','+pass+','+group+',callback)', module.filename)); + + var User = models.user; + + let tempUser = new User(); + tempUser.nickname = nick; + tempUser.email = email; + tempUser.passhash = pass; + tempUser.group = group; + + tempUser.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)); + + var User = models.user; + + User.find().or([{nickname: haystack}, {email: haystack}]) + .then((users) => { + // TODO delete user + callback(null, 1); + }).catch((err) => { + callback(err); + }); +}; + + +/** + * gets entry by email + * @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)); + + var User = 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]}); + } + } + + User.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.updateUserProfile = (id, obj, callback) => { + if(typeof callback !== 'function') callback = function() {}; + if(typeof id !== 'string') return callback(new TypeError('id is not a string::database.updateUserProfile('+id+','+JSON.stringify(obj)+',callback)', module.filename)); + if(typeof obj !== 'object') return callback(new TypeError('obj is not an object::database.updateUserProfile('+id+','+JSON.stringify(obj)+',callback)', module.filename)); + + var User = models.user; + User.findByIdAndUpdate(id, obj, (err, data) => { + if(err) callback(err); + else callback(null, data); + }); + +}; + + +/** + * updates data based on login + * @author Ruben Meyer + * @param {Number} id User ID + * @param {Object} options options JSON -> remember + * @param {Function} callback Callback function (date => 'Login Date', token => 'RememberMe Cookie Token') + */ +methods.updateNewAction = (id, options, 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() {}; + + var User = models.user; + User.countDocuments({}, (err, count) => { + callback((err) ? err : count); + }); +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +module.exports = methods;