From 6eb150af06ccb6a2de817d7f5783814e90ca765b Mon Sep 17 00:00:00 2001 From: Dennis Eriksen Date: Thu, 18 Apr 2013 13:34:38 +0200 Subject: changing to passport-local --- app.js | 14 ++- modules/account-manager.js | 196 ----------------------------------------- modules/account-manager.js.old | 196 +++++++++++++++++++++++++++++++++++++++++ modules/passport-local.js | 85 ++++++++++++++++++ package.json | 4 +- router.js | 3 +- 6 files changed, 295 insertions(+), 203 deletions(-) delete mode 100644 modules/account-manager.js create mode 100644 modules/account-manager.js.old create mode 100644 modules/passport-local.js diff --git a/app.js b/app.js index 5bf2796..3247fc5 100644 --- a/app.js +++ b/app.js @@ -4,8 +4,9 @@ */ var express = require('express') - , http = require('http') - , path = require('path'); + , path = require('path') + , bcrypt = require('bcrypt') + , passport = require('passport'); var app = express(); // initiates express @@ -30,6 +31,8 @@ app.configure(function(){ app.use(express.methodOverride()); app.use(require('less-middleware')({ src: __dirname + '/public' })); app.use(express.static(path.join(__dirname, 'public'))); + app.use(passport.initialize()); + app.use(passport.session()); }); app.configure('development', function(){ @@ -50,6 +53,9 @@ require('./router')(app); * Server initiation */ -http.createServer(app).listen(app.get('port'), function(){ - console.log("Express server listening on port " + app.get('port')); +app.listen(app.get('port'), function() { + console.log("Express server listening on port " + app.get('port')); }); + + + diff --git a/modules/account-manager.js b/modules/account-manager.js deleted file mode 100644 index b9cb1f9..0000000 --- a/modules/account-manager.js +++ /dev/null @@ -1,196 +0,0 @@ - -var crypto = require('crypto') -var MongoDB = require('mongodb').Db; -var Server = require('mongodb').Server; -var moment = require('moment'); - -var dbPort = 27017; -var dbHost = 'localhost'; -var dbName = 'DERStest'; - -/* establish the database connection */ - -var db = new MongoDB(dbName, new Server(dbHost, dbPort, {auto_reconnect: true}), {w: 1}); - db.open(function(e, d){ - if (e) { - console.log(e); - } else{ - console.log('connected to database :: ' + dbName); - } -}); -var accounts = db.collection('accounts'); - -/* login validation methods */ - -exports.autoLogin = function(user, pass, callback) -{ - accounts.findOne({user:user}, function(e, o) { - if (o){ - o.pass == pass ? callback(o) : callback(null); - } else{ - callback(null); - } - }); -} - -exports.manualLogin = function(user, pass, callback) -{ - accounts.findOne({user:user}, function(e, o) { - if (o == null){ - callback('user-not-found'); - } else{ - validatePassword(pass, o.pass, function(err, res) { - if (res){ - callback(null, o); - } else{ - callback('invalid-password'); - } - }); - } - }); -} - -/* record insertion, update & deletion methods */ - -exports.addNewAccount = function(newData, callback) -{ - accounts.findOne({user:newData.user}, function(e, o) { - if (o){ - callback('username-taken'); - } else{ - accounts.findOne({email:newData.email}, function(e, o) { - if (o){ - callback('email-taken'); - } else{ - saltAndHash(newData.pass, function(hash){ - newData.pass = hash; - // append date stamp when record was created // - newData.date = moment().format('MMMM Do YYYY, h:mm:ss a'); - accounts.insert(newData, {safe: true}, callback); - }); - } - }); - } - }); -} - -exports.updateAccount = function(newData, callback) -{ - accounts.findOne({user:newData.user}, function(e, o){ - o.name = newData.name; - o.email = newData.email; - o.country = newData.country; - if (newData.pass == ''){ - accounts.save(o, {safe: true}, callback); - } else{ - saltAndHash(newData.pass, function(hash){ - o.pass = hash; - accounts.save(o, {safe: true}, callback); - }); - } - }); -} - -exports.updatePassword = function(email, newPass, callback) -{ - accounts.findOne({email:email}, function(e, o){ - if (e){ - callback(e, null); - } else{ - saltAndHash(newPass, function(hash){ - o.pass = hash; - accounts.save(o, {safe: true}, callback); - }); - } - }); -} - -/* account lookup methods */ - -exports.deleteAccount = function(id, callback) -{ - accounts.remove({_id: getObjectId(id)}, callback); -} - -exports.getAccountByEmail = function(email, callback) -{ - accounts.findOne({email:email}, function(e, o){ callback(o); }); -} - -exports.validateResetLink = function(email, passHash, callback) -{ - accounts.find({ $and: [{email:email, pass:passHash}] }, function(e, o){ - callback(o ? 'ok' : null); - }); -} - -exports.getAllRecords = function(callback) -{ - accounts.find().toArray( - function(e, res) { - if (e) callback(e) - else callback(null, res) - }); -}; - -exports.delAllRecords = function(callback) -{ - accounts.remove({}, callback); // reset accounts collection for testing // -} - -/* private encryption & validation methods */ - -var generateSalt = function() -{ - var set = '0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ'; - var salt = ''; - for (var i = 0; i < 10; i++) { - var p = Math.floor(Math.random() * set.length); - salt += set[p]; - } - return salt; -} - -var md5 = function(str) { - return crypto.createHash('md5').update(str).digest('hex'); -} - -var saltAndHash = function(pass, callback) -{ - var salt = generateSalt(); - callback(salt + md5(pass + salt)); -} - -var validatePassword = function(plainPass, hashedPass, callback) -{ - var salt = hashedPass.substr(0, 10); - var validHash = salt + md5(plainPass + salt); - callback(null, hashedPass === validHash); -} - -/* auxiliary methods */ - -var getObjectId = function(id) -{ - return accounts.db.bson_serializer.ObjectID.createFromHexString(id) -} - -var findById = function(id, callback) -{ - accounts.findOne({_id: getObjectId(id)}, - function(e, res) { - if (e) callback(e) - else callback(null, res) - }); -}; - - -var findByMultipleFields = function(a, callback) -{ -// this takes an array of name/val pairs to search against {fieldName : 'value'} // - accounts.find( { $or : a } ).toArray( - function(e, results) { - if (e) callback(e) - else callback(null, results) - }); -} diff --git a/modules/account-manager.js.old b/modules/account-manager.js.old new file mode 100644 index 0000000..b9cb1f9 --- /dev/null +++ b/modules/account-manager.js.old @@ -0,0 +1,196 @@ + +var crypto = require('crypto') +var MongoDB = require('mongodb').Db; +var Server = require('mongodb').Server; +var moment = require('moment'); + +var dbPort = 27017; +var dbHost = 'localhost'; +var dbName = 'DERStest'; + +/* establish the database connection */ + +var db = new MongoDB(dbName, new Server(dbHost, dbPort, {auto_reconnect: true}), {w: 1}); + db.open(function(e, d){ + if (e) { + console.log(e); + } else{ + console.log('connected to database :: ' + dbName); + } +}); +var accounts = db.collection('accounts'); + +/* login validation methods */ + +exports.autoLogin = function(user, pass, callback) +{ + accounts.findOne({user:user}, function(e, o) { + if (o){ + o.pass == pass ? callback(o) : callback(null); + } else{ + callback(null); + } + }); +} + +exports.manualLogin = function(user, pass, callback) +{ + accounts.findOne({user:user}, function(e, o) { + if (o == null){ + callback('user-not-found'); + } else{ + validatePassword(pass, o.pass, function(err, res) { + if (res){ + callback(null, o); + } else{ + callback('invalid-password'); + } + }); + } + }); +} + +/* record insertion, update & deletion methods */ + +exports.addNewAccount = function(newData, callback) +{ + accounts.findOne({user:newData.user}, function(e, o) { + if (o){ + callback('username-taken'); + } else{ + accounts.findOne({email:newData.email}, function(e, o) { + if (o){ + callback('email-taken'); + } else{ + saltAndHash(newData.pass, function(hash){ + newData.pass = hash; + // append date stamp when record was created // + newData.date = moment().format('MMMM Do YYYY, h:mm:ss a'); + accounts.insert(newData, {safe: true}, callback); + }); + } + }); + } + }); +} + +exports.updateAccount = function(newData, callback) +{ + accounts.findOne({user:newData.user}, function(e, o){ + o.name = newData.name; + o.email = newData.email; + o.country = newData.country; + if (newData.pass == ''){ + accounts.save(o, {safe: true}, callback); + } else{ + saltAndHash(newData.pass, function(hash){ + o.pass = hash; + accounts.save(o, {safe: true}, callback); + }); + } + }); +} + +exports.updatePassword = function(email, newPass, callback) +{ + accounts.findOne({email:email}, function(e, o){ + if (e){ + callback(e, null); + } else{ + saltAndHash(newPass, function(hash){ + o.pass = hash; + accounts.save(o, {safe: true}, callback); + }); + } + }); +} + +/* account lookup methods */ + +exports.deleteAccount = function(id, callback) +{ + accounts.remove({_id: getObjectId(id)}, callback); +} + +exports.getAccountByEmail = function(email, callback) +{ + accounts.findOne({email:email}, function(e, o){ callback(o); }); +} + +exports.validateResetLink = function(email, passHash, callback) +{ + accounts.find({ $and: [{email:email, pass:passHash}] }, function(e, o){ + callback(o ? 'ok' : null); + }); +} + +exports.getAllRecords = function(callback) +{ + accounts.find().toArray( + function(e, res) { + if (e) callback(e) + else callback(null, res) + }); +}; + +exports.delAllRecords = function(callback) +{ + accounts.remove({}, callback); // reset accounts collection for testing // +} + +/* private encryption & validation methods */ + +var generateSalt = function() +{ + var set = '0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ'; + var salt = ''; + for (var i = 0; i < 10; i++) { + var p = Math.floor(Math.random() * set.length); + salt += set[p]; + } + return salt; +} + +var md5 = function(str) { + return crypto.createHash('md5').update(str).digest('hex'); +} + +var saltAndHash = function(pass, callback) +{ + var salt = generateSalt(); + callback(salt + md5(pass + salt)); +} + +var validatePassword = function(plainPass, hashedPass, callback) +{ + var salt = hashedPass.substr(0, 10); + var validHash = salt + md5(plainPass + salt); + callback(null, hashedPass === validHash); +} + +/* auxiliary methods */ + +var getObjectId = function(id) +{ + return accounts.db.bson_serializer.ObjectID.createFromHexString(id) +} + +var findById = function(id, callback) +{ + accounts.findOne({_id: getObjectId(id)}, + function(e, res) { + if (e) callback(e) + else callback(null, res) + }); +}; + + +var findByMultipleFields = function(a, callback) +{ +// this takes an array of name/val pairs to search against {fieldName : 'value'} // + accounts.find( { $or : a } ).toArray( + function(e, results) { + if (e) callback(e) + else callback(null, results) + }); +} diff --git a/modules/passport-local.js b/modules/passport-local.js new file mode 100644 index 0000000..939873f --- /dev/null +++ b/modules/passport-local.js @@ -0,0 +1,85 @@ + +mongoose.connect('localhost', 'test'); + +var db = mongoose.connection; +db.on('error', console.error.bind(console, 'connection error:')); +db.once('open', function callback() { + console.log('Connected to MongoDB'); +}) + +// User schema +var userSchema = mongoose.Schema({ + username: { type: String, required: true, unique: true }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true, unique: true}, +}); + +// Bcrypt middleware +userSchema.pre('save', function(next) { + var user = this; + + if (!user.isModified('password')) return next(); + + bcrypt genSalt(SALT_WORK_FACTOR, function(err, salt) { + if (err) return next(err); + + bcrypt.hash(user.password, salt, function(err, hash) { + if (err) return next(err); + user.password = hash; + next(); + }); + }); +}); + +// Password verification +userSchema.methods.comparePassword = function(candidatePassword, callback) { + bcrypt.compare(candidatePassword, this.password, function(err, isMatch) { + if (err) return callback(err); + callback(null, isMatch); + }); +}; + + + + +// Passport session setup. +// To support persistent login sessions, Passport needs to be able to +// serialize users into and deserialize users out of the session. Typically, +// this will be as simple as storing the user ID when serializing, and finding +// the user by ID when deserializing. +exports.passport.serializeUser(function(user, done) { + done(null, user.id); +}); + +exports.passport.deserializeUser(function(id, done) { + User.findById(id, function (err, user) { + done(err, user); + }); +}); + + +// Use the LocalStrategy within Passport. +// Strategies in passport require a `verify` function, which accept +// credentials (in this case, a username and password), and invoke a callback +// with a user object. In the real world, this would query a database; +// however, in this example we are using a baked-in set of users. +exports.passport.use(new LocalStrategy(function(username, password, done) { + User.findOne({ username: username }, function(err, user) { + if (err) return done(err); + if (!user) { return done(null, false, { message: 'Unknown user ' + username }); } + user.comparePassword(password, function(err, isMatch) { + if (err) return done(err); + if(isMatch) { + return done(null, user); + } else { + return done(null, false, { message: 'Invalid password' }); + } + }); + }); +})); + + + + + + diff --git a/package.json b/package.json index 6bbfa1f..d08311c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "emailjs": "*", "mongodb": "*", "moment": "*", - "less-middleware": "*" + "less-middleware": "*", + "passport": "*", + "bcrypt": "*" } } diff --git a/router.js b/router.js index b757e79..5463614 100644 --- a/router.js +++ b/router.js @@ -1,5 +1,4 @@ - -var AM = require('./modules/account-manager'); +var passlo = require('./modules/passport-local'); var EM = require('./modules/email-dispatcher'); -- cgit v1.2.3