added endpoint to update password

This commit is contained in:
bill bittner 2017-11-30 14:46:32 -08:00
parent 6ae9b78dbf
commit f16abf08c2
7 changed files with 136 additions and 55 deletions

View file

@ -14,31 +14,42 @@ module.exports = {
}
next();
},
serializeSpeechUser (user, done) {
serializeSpeechUser (user, done) { // returns user id to be serialized into session token
logger.debug('serializing session');
done(null, user.id);
},
deserializeSpeechUser (id, done) {
let userInfo = {};
db.User.findOne({ where: { id } })
deserializeSpeechUser (id, done) { // deserializes session token and provides user from user id
logger.debug('deserializing session');
return db.User.findOne({ where: { id } })
.then(user => {
userInfo['id'] = user.id;
userInfo['userName'] = user.userName;
return user.getChannel();
return module.exports.returnUserAndChannelInfo(user);
})
.then(channel => {
userInfo['channelName'] = channel.channelName;
userInfo['channelClaimId'] = channel.channelClaimId;
return db.Certificate.getShortChannelIdFromLongChannelId(channel.channelClaimId, channel.channelName);
.then((userInfo) => {
return done(null, userInfo);
})
.catch(error => {
return done(error);
});
},
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;
// return done(null, userInfo);
done(null, userInfo);
return null;
resolve(userInfo);
})
.catch(error => {
logger.error(error);
done(error, null);
reject(error);
});
});
},
};

View file

@ -76,13 +76,23 @@ module.exports = {
}
},
cleanseChannelName (channelName) {
if (channelName) {
if (!channelName) {
return null;
}
if (channelName.indexOf('@') !== 0) {
channelName = `@${channelName}`;
}
}
return channelName;
},
cleanseUserName (userName) {
if (!userName) {
return null;
}
if (userName.indexOf('@') !== -1) {
userName = userName.substring(userName.indexOf('@'));
}
return userName;
},
createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) {
logger.debug(`Creating Publish Parameters`);
// provide defaults for title

View file

@ -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) => {

View file

@ -2,6 +2,7 @@
const PassportLocalStrategy = require('passport-local').Strategy;
const db = require('../models');
const logger = require('winston');
const { returnUserAndChannelInfo } = require('../helpers/authHelpers.js');
module.exports = new PassportLocalStrategy(
{
@ -11,43 +12,31 @@ module.exports = new PassportLocalStrategy(
passReqToCallback: true,
},
(req, username, password, done) => {
logger.debug(`verifying loggin attempt ${username} ${password}`);
let userInfo = {};
return db.User
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) => {
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);
});
});
})

View file

@ -5,7 +5,7 @@ const multipartMiddleware = multipart({uploadDir: config.files.uploadDirectory})
const db = require('../models');
const { publish } = require('../controllers/publishController.js');
const { getClaimList, resolveUri } = require('../helpers/lbryApi.js');
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, cleanseUserName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
const errorHandlers = require('../helpers/errorHandlers.js');
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const { authenticateOrSkip } = require('../auth/authentication.js');
@ -185,4 +185,44 @@ module.exports = (app) => {
errorHandlers.handleApiError('short channel id', originalUrl, ip, error, res);
});
});
app.put('/api/password', ({ body, ip, originalUrl }, res) => {
let userName;
let { channelName, oldPassword, newPassword } = body;
// validate all necessary params were provided
if (!channelName || !oldPassword || !newPassword) {
res.status(400).json({success: false, message: 'provide channelName, oldPassword, and newPassword'});
}
// cleanse channel name
userName = cleanseUserName(channelName);
// validate password and respond
db
.User
.findOne({where: {userName: userName}})
.then(user => {
if (!user) {
return res.status(401).json({success: false, message: 'Incorrect username or password.'});
}
return user.comparePassword(oldPassword, (passwordErr, isMatch) => {
if (passwordErr) {
throw passwordErr;
}
if (!isMatch) {
return res.status(401).json({success: false, message: 'Incorrect username or password.'});
}
logger.debug('Password was a match, updating password');
return user
.changePassword(newPassword)
.then(() => {
logger.debug('Password successfully updated');
res.status(200).json({success: true, message: 'password successfully changed'});
})
.catch(error => {
throw error;
});
});
})
.catch(error => {
errorHandlers.handleApiError('password reset', originalUrl, ip, error, res);
});
});
};

View file

@ -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,

View file

@ -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({