UNPKG

periodicjs.ext.asyncadmin

Version:

An authentication extension for periodicjs that uses passport to authenticate user sessions.

508 lines (483 loc) 12.8 kB
'use strict'; var mongoose = require('mongoose'), merge = require('utils-merge'), async = require('async'), path = require('path'), complexity = require('complexity'), Schema = mongoose.Schema, ObjectId = Schema.ObjectId, logger = console; var userSchema = new Schema({ id: ObjectId, email: { type: String, index: { unique: true, sparse: false } }, firstname: String, lastname: String, username: { type: String, index: { unique: true, sparse: true } }, password: String, url: String, birthday: Date, userid: { type: Number, index: { sparse: true } }, accesstoken: String, description: { type: String, 'default': 'No profile' }, activated: { type: Boolean, 'default': false }, location: { city: String, country: String, state: String, zip: String, loc: { longitude: Number, latitude: Number } }, createdat: { type: Date, 'default': Date.now }, updatedat: { type: Date, 'default': Date.now }, accounttype: { type: String, 'default': 'basic' }, gender: { type: String }, assets: [{ type: ObjectId, ref: 'Asset' }], primaryasset: { type: ObjectId, ref: 'Asset' }, coverimages: [{ type: ObjectId, ref: 'Asset' }], coverimage: { type: ObjectId, ref: 'Asset' }, userroles: [{ type: ObjectId, ref: 'Userrole' }], contenttypes: [{ type: ObjectId, ref: 'Contenttype' }], tags: [{ type: ObjectId, ref: 'Tag' }], categories: [{ type: ObjectId, ref: 'Category' }], apikey: String, entitytype: { type: String, 'default': 'account' }, changes: [{ createdat: { type: Date, 'default': Date.now }, editor: { type: ObjectId, ref: 'User' }, editor_username: String, changeset: Schema.Types.Mixed }], attributes: Schema.Types.Mixed, //moved facebook/socialdata to attributes contenttypeattributes: Schema.Types.Mixed, extensionattributes: Schema.Types.Mixed, random: Number }); userSchema.pre('save', function (next, done) { this._wasNew = this.isNew; this.random = Math.random(); // var badusername = new RegExp(/\badmin\b|\bconfig\b|\bprofile\b|\bindex\b|\bcreate\b|\bdelete\b|\bdestroy\b|\bedit\b|\btrue\b|\bfalse\b|\bupdate\b|\blogin\b|\blogut\b|\bdestroy\b|\bwelcome\b|\bdashboard\b/i); // if (this.password !== undefined && this.password.length < 8) { // done(new Error('Password is too short')); // } // else if (this.username !== undefined && this.username.length < 4) { // done(new Error('Username is too short')); // } // else if (this.email !== undefined && this.email.match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i) === null) { done(new Error('Invalid email')); } // else if (this.username !== undefined && badusername.test(this.username)) { // done(new Error('Invalid username')); // } else { next(); } }); // userSchema.post('init', function (doc) { // logger.info('model - account.js - ' + doc._id + ' has been initialized from the db'); // }); // userSchema.post('validate', function (doc) { // logger.info('model - account.js - ' + doc._id + ' has been validated (but not saved yet)'); // }); // userSchema.post('save', function (doc) { // logger.info('model - account.js - ' + doc._id + ' has been saved'); // }); // userSchema.pre('remove', function (doc) { // console.log('==================deleted============'); // logger.info('model - account.js - ' + doc._id + ' has been removed'); // }); // Password verification userSchema.methods.comparePassword = function (candidatePassword, cb) { var bcrypt = require('bcrypt'); if (this.password) { bcrypt.compare(candidatePassword, this.password, function (err, isMatch) { if (err) { return cb(err); } cb(null, isMatch); }); } else { logger.info('user has no pw'); return cb(null, false); } }; // Remember Me implementation helper method userSchema.methods.generateRandomToken = function () { // var user = this, var chars = '_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', token = new Date().getTime() + '_'; for (var x = 0; x < 16; x++) { var i = Math.floor(Math.random() * 62); token += chars.charAt(i); } return token; }; userSchema.statics.generateRandomTokenStatic = function () { // var user = this, var chars = '_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', token = new Date().getTime() + '_'; for (var x = 0; x < 16; x++) { var i = Math.floor(Math.random() * 62); token += chars.charAt(i); } return token; }; userSchema.statics.checkValidation = function (options) { var userdata = options.newuser, min_username_length = (options && options.length_of_username) ? options.length_of_username : 4, min_password_length = (options && options.length_of_password) ? options.length_of_password : 8; if ((typeof options.checkusername==='undefined' || options.checkusername ===true) && (userdata.username === undefined || userdata.username.length < min_username_length)) { return new Error('Username is too short'); } else if ( (typeof options.checkemail==='undefined' || options.checkemail===true ) && (userdata.email === undefined || userdata.email.match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i) === null) ){ return new Error('Invalid email'); } else if (options.useComplexity ===true && userdata.password && !complexity.check(userdata.password, options.complexity)) { return new Error('Password does not meet complexity requirements'); } else if (typeof userdata.password !=='undefined' && options.checkpassword && (userdata.password === undefined || userdata.password.length < min_password_length)) { return new Error('Password is too short'); } else if (options.checkpassword ===true && (userdata.password !== userdata.passwordconfirm)) { return new Error('Passwords do not match'); } else { return null; } }; userSchema.statics.validApiKey = function (userid, apikey, callback) { var User = mongoose.model('Account'); User.find({ _id: userid, apikey: apikey }, function (err, user) { if (err) { logger.error(err); callback(err, false); } else if (user) { callback(false, user); } else { logger.silly('model - account.js - invalid apikey'); callback(new Error('invalid apikey'), false); } }); }; userSchema.statics.hasPrivilege = function (user, privilege) { // console.log(' hasPrivilege user, privilege',user,privilege); return user.accounttype === 'admin' || user.privileges[privilege]; }; userSchema.statics.checkExistingUser = function(options,callback){ var User = mongoose.model('Account'), userdata = options.userdata, searchUsernameRegEx = (userdata.username)? new RegExp(userdata.username, 'gi') : null, searchEmailRegEx = (userdata.email)? new RegExp(userdata.email, 'gi') : null, query = {}; if (userdata.username && userdata.email) { query = { $or: [{ username: searchUsernameRegEx }, { email: searchEmailRegEx }] }; } else if (userdata.username) { query = { username: searchUsernameRegEx }; } else { query = { email: searchEmailRegEx }; } User.findOne(query, function (err, user) { if(err){ callback(err); } else if(user){ callback(new Error('you already have an account')); } else{ callback(null,'no existing user'); } }); }; userSchema.statics.fastRegisterUser = function (userdataparam, callback) { var bcrypt = require('bcrypt'); var userdata = userdataparam; // console.log(userdata); if (userdata._csrf) { delete userdata._csrf; } if (userdata.submit) { delete userdata.submit; } if ( userdata.password === undefined || !userdata.password || userdata.password === '' || userdata.password === ' ') { delete userdata.password; delete userdata.passwordconfirm; if (callback) { callback(new Error('missing password'), userdata); } } else if (userdata.password.length < 1) { if (callback) { callback(new Error('password is too short'), userdata); } } else if(userdata.use_encrypted_password===true){ if (userdata.username && !userdata.email) { userdata.email = userdata.username; delete userdata.username; } var User = mongoose.model('Account'); userdata.apikey = User.generateRandomTokenStatic(); // console.log(__dirname, userdata); var newUser = new User(userdata); newUser.save(function (err, user) { if (err) { logger.error(err); if (callback) { callback(err, userdata); } } else { if (callback) { callback(false, user); } } }); } else { delete userdata.passwordconfirm; bcrypt.genSalt(10, function (err, salt) { bcrypt.hash(userdata.password, salt, function (err, hash) { userdata.password = hash; if (userdata.username && !userdata.email) { userdata.email = userdata.username; delete userdata.username; } var User = mongoose.model('Account'); userdata.apikey = User.generateRandomTokenStatic(); // console.log(__dirname, userdata); var newUser = new User(userdata); newUser.save(function (err, user) { if (err) { logger.error(err); if (callback) { callback(err, userdata); } } else { if (callback) { callback(false, user); } } }); }); }); } }; userSchema.statics.logInNewUser = function(options, callback){ try{ var req = options.req; req.login( options.newuser, function(loginerr){ if(loginerr){ callback(loginerr,null); } else{ callback(null,options.newuser); } }); } catch(e){ callback(e,null); } }; userSchema.statics.sendNewUserWelcomeEmail = function(options, callback){ try{ if(options.requireactivation){ options.welcomeemaildata.emailviewname = 'email/user/welcome_with_validation'; } options.welcomeemaildata.getEmailTemplateFunction({ viewname: options.welcomeemaildata.emailviewname, themefileext: options.welcomeemaildata.themefileext }, function (err, templatepath) { if (err) { callback(err); } else { // console.log('user for forgot password', user); if (templatepath === options.welcomeemaildata.emailviewname) { templatepath = path.resolve(process.cwd(), 'app/views', templatepath + '.' + options.welcomeemaildata.themefileext); } options.welcomeemaildata.sendEmailFunction({ appenvironment: options.welcomeemaildata.appenvironment, to: options.newuser.email, cc: options.welcomeemaildata.replyto, replyTo: options.welcomeemaildata.replyto, from: options.welcomeemaildata.replyto, subject: options.welcomeemaildata.subject || options.welcomeemaildata.appname + ' New User Registration', emailtemplatefilepath: templatepath, emailtemplatedata: { user: options.newuser, hostname: options.welcomeemaildata.hostname, appname: options.welcomeemaildata.appname, filename: templatepath } }, callback); } } ); } catch(e){ callback(e,null); } }; userSchema.statics.createNewUserAccount = function(options,callback){ var validationErrors, newuseroptions, newelycreateduser, defaultUserOptions = { newuser: {}, checkusername: false, checkpassword: true, length_of_username: 4, length_of_password: 8 }, User = mongoose.model('Account'); newuseroptions = merge(defaultUserOptions, options); validationErrors = User.checkValidation(newuseroptions); if(validationErrors){ callback(validationErrors,null); } else{ async.series({ checkExisitingUser:function(asyncCB){ User.checkExistingUser({ userdata : newuseroptions.newuser },asyncCB); }, fastRegister:function(asyncCB){ User.fastRegisterUser(newuseroptions.newuser,function(err,newfastregisteruser){ if(err){ asyncCB(err); } else{ newelycreateduser = newfastregisteruser; asyncCB(null,newfastregisteruser); } }); }, loginNewlyCreatedUser:function(asyncCB){ if(newuseroptions.lognewuserin){ User.logInNewUser({ req: newuseroptions.req, newuser: newelycreateduser },asyncCB); } else{ asyncCB(null,'skipping logging in user'); } }, sendUserWelcomeEmail:function(asyncCB){ if(newuseroptions.send_new_user_email && newelycreateduser.email){ User.sendNewUserWelcomeEmail({ requireactivation: newuseroptions.requireuseractivation, welcomeemaildata: newuseroptions.welcomeemaildata, newuser: newelycreateduser },asyncCB); } else{ asyncCB(null,'skipping new user welcome email'); } } }, function(asyncErr,createduser){ if(asyncErr){ callback(asyncErr,null); } else{ callback(null,createduser); } }); } }; exports = module.exports = userSchema;