diff --git a/helpers/authHelpers.js b/helpers/authHelpers.js index 455ddd9a..e9bf0210 100644 --- a/helpers/authHelpers.js +++ b/helpers/authHelpers.js @@ -1,9 +1,10 @@ -const db = require('../models'); // require our models for syncing +// const db = require('../models'); // require our models for syncing const logger = require('winston'); module.exports = { populateLocalsDotUser (req, res, next) { if (req.user) { + logger.debug('populating res.locals.user'); res.locals.user = { id : req.user.id, userName : req.user.userName, @@ -14,31 +15,12 @@ module.exports = { } next(); }, - serializeSpeechUser (user, done) { - done(null, user.id); + serializeSpeechUser (user, done) { // returns user data to be serialized into session + logger.debug('serializing user'); + done(null, user); }, - deserializeSpeechUser (id, done) { - let userInfo = {}; - db.User.findOne({ where: { id } }) - .then(user => { - userInfo['id'] = user.id; - userInfo['userName'] = user.userName; - return user.getChannel(); - }) - .then(channel => { - userInfo['channelName'] = channel.channelName; - userInfo['channelClaimId'] = channel.channelClaimId; - return db.Certificate.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName); - }) - .then(shortChannelId => { - userInfo['shortChannelId'] = shortChannelId; - // return done(null, userInfo); - done(null, userInfo); - return null; - }) - .catch(error => { - logger.error(error); - done(error, null); - }); + deserializeSpeechUser (user, done) { // deserializes session and populates additional info to req.user + logger.debug('deserializing user'); + done(null, user); }, }; diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 25c715c1..329f6787 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -70,16 +70,12 @@ module.exports = { throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"'); } }, - validateLicense (license) { - if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { - throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch'); - } - }, cleanseChannelName (channelName) { - if (channelName) { - if (channelName.indexOf('@') !== 0) { - channelName = `@${channelName}`; - } + if (!channelName) { + return null; + } + if (channelName.indexOf('@') !== 0) { + channelName = `@${channelName}`; } return channelName; }, diff --git a/models/user.js b/models/user.js index 7c7bfe13..55f75114 100644 --- a/models/user.js +++ b/models/user.js @@ -28,9 +28,40 @@ module.exports = (sequelize, { STRING }) => { bcrypt.compare(password, this.password, callback); }; + User.prototype.changePassword = function (newPassword) { + return new Promise((resolve, reject) => { + // generate a salt string to use for hashing + bcrypt.genSalt((saltError, salt) => { + if (saltError) { + logger.error('salt error', saltError); + reject(saltError); + return; + } + // generate a hashed version of the user's password + bcrypt.hash(newPassword, salt, (hashError, hash) => { + // if there is an error with the hash generation return the error + if (hashError) { + logger.error('hash error', hashError); + reject(hashError); + return; + } + // replace the current password with the new hash + this + .update({password: hash}) + .then(() => { + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + }); + }); + }; + // pre-save hook method to hash the user's password before the user's info is saved to the db. User.hook('beforeCreate', (user, options) => { - logger.debug('...beforeCreate hook...'); + logger.debug('User.beforeCreate hook...'); return new Promise((resolve, reject) => { // generate a salt string to use for hashing bcrypt.genSalt((saltError, salt) => { diff --git a/passport/local-login.js b/passport/local-login.js index 473f875a..e3d72e88 100644 --- a/passport/local-login.js +++ b/passport/local-login.js @@ -1,53 +1,60 @@ - const PassportLocalStrategy = require('passport-local').Strategy; const db = require('../models'); const logger = require('winston'); +function returnUserAndChannelInfo (userInstance) { + return new Promise((resolve, reject) => { + let userInfo = {}; + userInfo['id'] = userInstance.id; + userInfo['userName'] = userInstance.userName; + userInstance + .getChannel() + .then(({channelName, channelClaimId}) => { + userInfo['channelName'] = channelName; + userInfo['channelClaimId'] = channelClaimId; + return db.Certificate.getShortChannelIdFromLongChannelId(channelClaimId, channelName); + }) + .then(shortChannelId => { + userInfo['shortChannelId'] = shortChannelId; + resolve(userInfo); + }) + .catch(error => { + reject(error); + }); + }); +} + module.exports = new PassportLocalStrategy( { - usernameField : 'username', // username key in the request body - passwordField : 'password', // password key in the request body - session : false, - passReqToCallback: true, + usernameField: 'username', + passwordField: 'password', }, - (req, username, password, done) => { - logger.debug(`verifying loggin attempt ${username} ${password}`); - let userInfo = {}; - return db.User + (username, password, done) => { + logger.debug('logging user in'); + return db + .User .findOne({where: {userName: username}}) .then(user => { if (!user) { - logger.debug('no user found'); + // logger.debug('no user found'); return done(null, false, {message: 'Incorrect username or password.'}); } - logger.debug('user found:', user.dataValues); - logger.debug('...comparing password...'); - return user.comparePassword(password, (passwordErr, isMatch) => { + user.comparePassword(password, (passwordErr, isMatch) => { if (passwordErr) { logger.error('passwordErr:', passwordErr); - return done(passwordErr); + return done(null, false, {message: passwordErr}); } - if (!isMatch) { - logger.debug('incorrect password'); + // logger.debug('incorrect password'); return done(null, false, {message: 'Incorrect username or password.'}); } - logger.debug('...password was a match...'); - userInfo['id'] = user.id; - userInfo['userName'] = user.userName; - // get the User's channel info - return user.getChannel() - .then(channel => { - userInfo['channelName'] = channel.channelName; - userInfo['channelClaimId'] = channel.channelClaimId; - return db.Certificate.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName); - }) - .then(shortChannelId => { - userInfo['shortChannelId'] = shortChannelId; + logger.debug('Password was a match, returning User'); + return returnUserAndChannelInfo(user) + .then((userInfo) => { return done(null, userInfo); }) .catch(error => { - throw error; + return done(error); }); }); }) diff --git a/passport/local-signup.js b/passport/local-signup.js index 48f48e37..83639248 100644 --- a/passport/local-signup.js +++ b/passport/local-signup.js @@ -5,12 +5,10 @@ const logger = require('winston'); module.exports = new PassportLocalStrategy( { - usernameField : 'username', // sets the custom name of parameters in the POST body message - passwordField : 'password', // sets the custom name of parameters in the POST body message - session : false, // set to false because we will use token approach to auth - passReqToCallback: true, // we want to be able to read the post body message parameters in the callback + usernameField: 'username', + passwordField: 'password', }, - (req, username, password, done) => { + (username, password, done) => { logger.verbose(`new channel signup request. user: ${username} pass: ${password} .`); let userInfo = {}; // server-side validaton of inputs (username, password) diff --git a/public/assets/js/createChannelFunctions.js b/public/assets/js/createChannelFunctions.js index dbcf4e49..fb0129d8 100644 --- a/public/assets/js/createChannelFunctions.js +++ b/public/assets/js/createChannelFunctions.js @@ -33,15 +33,13 @@ function publishNewChannel (event) { return sendAuthRequest(userName, password, '/signup') // post the request }) .then(result => { + setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); showChannelCreateDoneDisplay(); - // refresh window logged in as the channel - setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); // set cookies - }) - .then(() => { + // if user is on the home page, update the needed elements without reloading if (window.location.pathname === '/') { - // remove old channel and replace with new one & select it - replaceChannelOptionInPublishChannelSelect(); - replaceChannelOptionInNavBarChannelSelect(); + replaceChannelOptionInPublishChannelSelect(result.channelName); + replaceChannelOptionInNavBarChannelSelect(result.channelName); + // if user is not on home page, redirect to home page } else { window.location = '/'; } diff --git a/public/assets/js/loginFunctions.js b/public/assets/js/loginFunctions.js index 5f3a13b0..569cbb53 100644 --- a/public/assets/js/loginFunctions.js +++ b/public/assets/js/loginFunctions.js @@ -1,11 +1,9 @@ -function replaceChannelOptionInPublishChannelSelect() { +function replaceChannelOptionInPublishChannelSelect(loggedInChannel) { // remove the old channel option const oldChannel = document.getElementById('publish-channel-select-channel-option') if (oldChannel){ oldChannel.parentNode.removeChild(oldChannel); } - // get channel details from cookies - const loggedInChannel = getCookie('channel_name'); // create new channel option const newChannelOption = document.createElement('option'); newChannelOption.setAttribute('value', loggedInChannel); @@ -19,14 +17,12 @@ function replaceChannelOptionInPublishChannelSelect() { toggleSelectedChannel(loggedInChannel); } -function replaceChannelOptionInNavBarChannelSelect () { +function replaceChannelOptionInNavBarChannelSelect (loggedInChannel) { // remove the old channel option const oldChannel = document.getElementById('nav-bar-channel-select-channel-option'); if (oldChannel){ oldChannel.parentNode.removeChild(oldChannel); } - // get channel details from cookies - const loggedInChannel = getCookie('channel_name'); // create new channel option & select it const newChannelOption = document.createElement('option'); newChannelOption.setAttribute('value', loggedInChannel); @@ -49,20 +45,15 @@ function loginToChannel (event) { event.preventDefault() validationFunctions.validateNewChannelLogin(userName, password) .then(() => { - // send request return sendAuthRequest(userName, password, '/login') }) .then(result => { - // update session cookie with new channel name and id's - setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); // replace the current cookies - }) - .then(() => { - // update channel selection + setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId); + // if user is on the home page, update the needed elements without reloading if (window.location.pathname === '/') { - // remove old channel and replace with new one & select it - replaceChannelOptionInPublishChannelSelect(); - // remove old channel and replace with new one & select it - replaceChannelOptionInNavBarChannelSelect(); + replaceChannelOptionInPublishChannelSelect(result.channelName); + replaceChannelOptionInNavBarChannelSelect(result.channelName); + // if user is not on home page, redirect to home page } else { window.location = '/'; } diff --git a/routes/auth-routes.js b/routes/auth-routes.js index c1650009..01419e1b 100644 --- a/routes/auth-routes.js +++ b/routes/auth-routes.js @@ -14,7 +14,7 @@ module.exports = (app) => { }); // route for log in app.post('/login', passport.authenticate('local-login'), (req, res) => { - logger.debug('req.user:', req.user); + // logger.debug('req.user:', req.user); // req.user contains the authenticated user's info logger.debug('successful login'); res.status(200).json({ success : true, diff --git a/speech.js b/speech.js index 7e7a4e5f..3b03adea 100644 --- a/speech.js +++ b/speech.js @@ -33,10 +33,16 @@ app.use(bodyParser.json()); // 'body parser' for parsing application/json app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded app.use((req, res, next) => { // custom logging middleware to log all incoming http requests logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`); - logger.debug('req.body:', req.body); next(); }); +// configure passport +passport.serializeUser(serializeSpeechUser); +passport.deserializeUser(deserializeSpeechUser); +const localSignupStrategy = require('./passport/local-signup.js'); +const localLoginStrategy = require('./passport/local-login.js'); +passport.use('local-signup', localSignupStrategy); +passport.use('local-login', localLoginStrategy); // initialize passport app.use(cookieSession({ name : 'session', @@ -45,12 +51,6 @@ app.use(cookieSession({ })); app.use(passport.initialize()); app.use(passport.session()); -passport.serializeUser(serializeSpeechUser); // takes the user id from the db and serializes it -passport.deserializeUser(deserializeSpeechUser); // this deserializes id then populates req.user with info -const localSignupStrategy = require('./passport/local-signup.js'); -const localLoginStrategy = require('./passport/local-login.js'); -passport.use('local-signup', localSignupStrategy); -passport.use('local-login', localLoginStrategy); // configure handlebars & register it with express app const hbs = expressHandlebars.create({ diff --git a/task-scripts/update-password.js b/task-scripts/update-password.js new file mode 100644 index 00000000..c2cca20c --- /dev/null +++ b/task-scripts/update-password.js @@ -0,0 +1,47 @@ +// load dependencies +const logger = require('winston'); +const db = require('../models/index'); // require our models for syncing +// configure logging +const config = require('../config/speechConfig.js'); +const logLevel = config.logging.logLevel; +require('../config/loggerConfig.js')(logger, logLevel); + +const userName = process.argv[2]; +logger.debug('user name:', userName); +const oldPassword = process.argv[3]; +logger.debug('old password:', oldPassword); +const newPassword = process.argv[4]; +logger.debug('new password:', newPassword); + +db.sequelize.sync() // sync sequelize + .then(() => { + logger.info('finding user profile'); + return db.User.findOne({ + where: { + userName: userName, + }, + }); + }) + .then(user => { + if (!user) { + throw new Error('no user found'); + } + return new Promise((resolve, reject) => { + user.comparePassword(oldPassword, (passwordErr, isMatch) => { + if (passwordErr) { + return reject(passwordErr); + } + if (!isMatch) { + return reject('Incorrect old password.'); + } + logger.debug('Password was a match, updating password'); + return resolve(user.changePassword(newPassword)); + }); + }); + }) + .then(() => { + logger.debug('Password successfully updated'); + }) + .catch((error) => { + logger.error(error); + });