diff --git a/controllers/publishController.js b/controllers/publishController.js index d832c814..fa82a525 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -10,7 +10,7 @@ module.exports = { // publish the file return lbryApi.publishClaim(publishParams) .then(tx => { - logger.info(`Successfully published ${fileName}`, tx); + logger.info(`Successfully published ${publishParams.name} ${fileName}`, tx); publishResults = tx; // get the channel information if (publishParams.channel_name) { @@ -79,6 +79,7 @@ module.exports = { resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim; }) .catch(error => { + logger.error('PUBLISH ERROR', error); publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file reject(error); }); 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 c790fc5f..75fff5de 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.'); - } - }, 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/certificate.js b/models/certificate.js index 206b3e40..50a24479 100644 --- a/models/certificate.js +++ b/models/certificate.js @@ -93,7 +93,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { Certificate.associate = db => { Certificate.belongsTo(db.Channel, { - onDelete : 'cascade', foreignKey: { allowNull: true, }, diff --git a/models/claim.js b/models/claim.js index 5d85a4ab..126d2c7a 100644 --- a/models/claim.js +++ b/models/claim.js @@ -235,7 +235,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { Claim.associate = db => { Claim.belongsTo(db.File, { - onDelete : 'cascade', foreignKey: { allowNull: true, }, diff --git a/models/request.js b/models/request.js index 846e12c6..501fb441 100644 --- a/models/request.js +++ b/models/request.js @@ -27,7 +27,6 @@ module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => { Request.associate = db => { Request.belongsTo(db.File, { - onDelete : 'cascade', foreignKey: { allowNull: true, }, 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/css/general.css b/public/assets/css/general.css index 57c26354..629a999c 100644 --- a/public/assets/css/general.css +++ b/public/assets/css/general.css @@ -466,6 +466,10 @@ table { display: inline-block; } +.nav-bar-logo { + cursor: pointer; +} + /* PUBLISH FORM */ .dropzone { diff --git a/public/assets/js/createChannelFunctions.js b/public/assets/js/createChannelFunctions.js index 96081df6..75816deb 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/public/assets/js/publishFileFunctions.js b/public/assets/js/publishFileFunctions.js index 1f552bb8..15a4c5a9 100644 --- a/public/assets/js/publishFileFunctions.js +++ b/public/assets/js/publishFileFunctions.js @@ -123,12 +123,14 @@ const publishFileFunctions = { xhr.open("POST", uri, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { + console.log('publish response:', xhr.response) if (xhr.status == 200) { - console.log('publish complete!'); - that.showFilePublishComplete(JSON.parse(xhr.response).message); + console.log('publish complete!'); + that.showFilePublishComplete(JSON.parse(xhr.response).message); + } else if (xhr.status == 502){ + that.showFilePublishFailure('Spee.ch was not able to get a response from the LBRY network.'); } else { - console.log(xhr.response); - that.showFilePublishFailure(JSON.parse(xhr.response).message); + that.showFilePublishFailure(JSON.parse(xhr.response).message); } } else { console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status); diff --git a/routes/api-routes.js b/routes/api-routes.js index a8edf90d..e47ec7e1 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -134,6 +134,7 @@ module.exports = (app) => { res.status(400).json({success: false, message: error.message}); return; } + logger.debug('publish req.files:', files); // validate file, name, license, and nsfw file = files.file; fileName = file.path.substring(file.path.lastIndexOf('/') + 1); 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 70f4e4c7..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.headers:', req.headers); 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); + });