From cd1af7e23057358bfa6ef6e2c8d92495d16d7d28 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Wed, 13 Nov 2013 22:11:21 -0500 Subject: [PATCH] moved helper functions to auth.js as well as all passport strategies --- models/user.js | 31 ------ server.js | 16 +-- server/controllers/auth.js | 195 +++++++++++++++++++++++++++++------- server/models/User.js | 200 ++----------------------------------- 4 files changed, 180 insertions(+), 262 deletions(-) diff --git a/models/user.js b/models/user.js index 141153dae7..18d108a048 100755 --- a/models/user.js +++ b/models/user.js @@ -30,37 +30,6 @@ UserSchema.virtual('password').set(function(password) { return this._password; }); -/** - * Validations - */ -var validatePresenceOf = function(value) { - return value && value.length; -}; - -// the below 4 validations only apply if you are signing up traditionally -UserSchema.path('name').validate(function(name) { - // if you are authenticating by any of the oauth strategies, don't validate - if (authTypes.indexOf(this.provider) !== -1) return true; - return name.length; -}, 'Name cannot be blank'); - -UserSchema.path('email').validate(function(email) { - // if you are authenticating by any of the oauth strategies, don't validate - if (authTypes.indexOf(this.provider) !== -1) return true; - return email.length; -}, 'Email cannot be blank'); - -UserSchema.path('username').validate(function(username) { - // if you are authenticating by any of the oauth strategies, don't validate - if (authTypes.indexOf(this.provider) !== -1) return true; - return username.length; -}, 'Username cannot be blank'); - -UserSchema.path('hashed_password').validate(function(hashed_password) { - // if you are authenticating by any of the oauth strategies, don't validate - if (authTypes.indexOf(this.provider) !== -1) return true; - return hashed_password.length; -}, 'Password cannot be blank'); /** diff --git a/server.js b/server.js index 31cd296181..6aadb24c27 100755 --- a/server.js +++ b/server.js @@ -6,7 +6,7 @@ var express = require('express'), passport = require('passport'), config = require('./conf'); -var User = require('./server/models/User'); +var Auth = require('./server/controllers/auth'); var db = mongoose.connect(config.db); var app = express(); @@ -25,13 +25,13 @@ app.use(passport.session()); app.use(app.router); app.use(express.static(config.root + '/public')); -passport.use(User.localStrategy); -passport.use(User.twitterStrategy()); -passport.use(User.facebookStrategy()); -passport.use(User.googleStrategy()); -passport.use(User.linkedInStrategy()); -passport.serializeUser(User.serializeUser); -passport.deserializeUser(User.deserializeUser); +passport.use(Auth.localStrategy); +passport.use(Auth.twitterStrategy()); +passport.use(Auth.facebookStrategy()); +passport.use(Auth.googleStrategy()); +passport.use(Auth.linkedInStrategy()); +passport.serializeUser(Auth.serializeUser); +passport.deserializeUser(Auth.deserializeUser); /** diff --git a/server/controllers/auth.js b/server/controllers/auth.js index 88d0580ab8..8184dd08b2 100755 --- a/server/controllers/auth.js +++ b/server/controllers/auth.js @@ -1,46 +1,173 @@ -var passport = require('passport') - , User = require('../models/User.js'); +var passport = require('passport'), + User = require('../models/User'), + crypto = require('crypto'), + LocalStrategy = require('passport-local').Strategy, + TwitterStrategy = require('passport-twitter').Strategy, + FacebookStrategy = require('passport-facebook').Strategy, + GoogleStrategy = require('passport-google').Strategy, + LinkedInStrategy = require('passport-linkedin').Strategy, + userRoles = require('../../client/js/routingConfig').userRoles; + + +function findByUsername(username, callback) { + User.findOne({ username: username }, function(err, user) { + if (user) callback(err, user); + }); +} + +function findOrCreateOauthUser(provider, providerId, callback) { + User.findOne({ $where: provider + '===' + providerId }, function(err, user) { + if (user) return user; + user = { + username: provider + '_user', + role: userRoles.user, + provider: provider + }; + user[provider] = providerId; + user.save(function(err) { + callback(err, user); + }); + }); +} + +function findAll(callback) { + User.find(function(err, users) { + callback(err, users); + }); +} + +function findById(id, callback) { + User.findById(id, function(err, user) { + if (user) callback(err, user); + }); +} + module.exports = { - register: function(req, res, next) { - try { - User.validate(req.body); - } - catch(err) { - return res.send(400, err.message); - } + register: function(req, res, next) { - User.addUser(req.body.username, req.body.password, req.body.role, function(err, user) { - if(err === 'UserAlreadyExists') return res.send(403, "User already exists"); - else if(err) return res.send(500); + User.findOne({ username: req.body.username }, function(err, user) { + if (user) return res.send(403, 'User already exists'); - req.logIn(user, function(err) { - if(err) { next(err); } - else { res.json(200, { "role": user.role, "username": user.username }); } - }); + user = new User({ + username: req.body.username, + password: req.body.password, + role: req.body.role + }); + + user.save(function(err) { + req.logIn(user, function(err) { + if (err) { + next(err); + } else { + res.send(200, { 'role': user.role, 'username': user.username }); + } }); - }, + }); + }); + }, - login: function(req, res, next) { - passport.authenticate('local', function(err, user) { + login: function(req, res, next) { + passport.authenticate('local', function(err, user) { + if (err) return next(err); + if (!user) return res.send(400); + req.logIn(user, function(err) { + if (err) return next(err); + if (req.body.rememberme) req.session.cookie.maxAge = 1000 * 60 * 60 * 24 * 7; + res.json(200, { "role": user.role, "username": user.username }); + }); + })(req, res, next); + }, - if(err) { return next(err); } - if(!user) { return res.send(400); } + logout: function(req, res) { + req.logout(); + res.send(200); + }, - req.logIn(user, function(err) { - if(err) { - return next(err); - } - if(req.body.rememberme) req.session.cookie.maxAge = 1000 * 60 * 60 * 24 * 7; - res.json(200, { "role": user.role, "username": user.username }); - }); - })(req, res, next); - }, - logout: function(req, res) { - req.logout(); - res.send(200); - } + + + + + localStrategy: new LocalStrategy(function(username, password, done) { + this.findByUsername(username, function(err, user) { + if (!user) { + done(null, false, { message: 'Username does not exist' }); + } else if (user.password !== password) { + done(null, false, { message: 'Incorrect password' }); + } else { + return done(null, user); + } + }); + }), + + twitterStrategy: function() { + if(!process.env.TWITTER_CONSUMER_KEY) throw new Error('A Twitter Consumer Key is required if you want to enable login via Twitter.'); + if(!process.env.TWITTER_CONSUMER_SECRET) throw new Error('A Twitter Consumer Secret is required if you want to enable login via Twitter.'); + + return new TwitterStrategy({ + consumerKey: process.env.TWITTER_CONSUMER_KEY, + consumerSecret: process.env.TWITTER_CONSUMER_SECRET, + callbackURL: process.env.TWITTER_CALLBACK_URL || 'http://localhost:8000/auth/twitter/callback' + }, + function(token, tokenSecret, profile, done) { + var user = module.exports.findOrCreateOauthUser(profile.provider, profile.id); + done(null, user); + }); + }, + + facebookStrategy: function() { + if(!process.env.FACEBOOK_APP_ID) throw new Error('A Facebook App ID is required if you want to enable login via Facebook.'); + if(!process.env.FACEBOOK_APP_SECRET) throw new Error('A Facebook App Secret is required if you want to enable login via Facebook.'); + + return new FacebookStrategy({ + clientID: process.env.FACEBOOK_APP_ID, + clientSecret: process.env.FACEBOOK_APP_SECRET, + callbackURL: process.env.FACEBOOK_CALLBACK_URL || "http://localhost:8000/auth/facebook/callback" + }, + function(accessToken, refreshToken, profile, done) { + var user = module.exports.findOrCreateOauthUser(profile.provider, profile.id); + done(null, user); + }); + }, + + googleStrategy: function() { + + return new GoogleStrategy({ + returnURL: process.env.GOOGLE_RETURN_URL || "http://localhost:8000/auth/google/return", + realm: process.env.GOOGLE_REALM || "http://localhost:8000/" + }, + function(identifier, profile, done) { + var user = module.exports.findOrCreateOauthUser('google', identifier); + done(null, user); + }); + }, + + linkedInStrategy: function() { + if(!process.env.LINKED_IN_KEY) throw new Error('A LinkedIn App Key is required if you want to enable login via LinkedIn.'); + if(!process.env.LINKED_IN_SECRET) throw new Error('A LinkedIn App Secret is required if you want to enable login via LinkedIn.'); + + return new LinkedInStrategy({ + consumerKey: process.env.LINKED_IN_KEY, + consumerSecret: process.env.LINKED_IN_SECRET, + callbackURL: process.env.LINKED_IN_CALLBACK_URL || "http://localhost:8000/auth/linkedin/callback" + }, + function(token, tokenSecret, profile, done) { + var user = module.exports.findOrCreateOauthUser('linkedin', profile.id); + done(null,user); + } + ); + }, + serializeUser: function(user, done) { + done(null, user.id); + }, + + deserializeUser: function(id, done) { + var user = module.exports.findById(id); + + if(user) { done(null, user); } + else { done(null, false); } + } }; \ No newline at end of file diff --git a/server/models/User.js b/server/models/User.js index e951f19acf..b6bdb01d12 100755 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,197 +1,19 @@ -var _ = require('underscore'), - mongoose = require('mongoose'), - crypto = require('crypto'), - passport = require('passport'), - LocalStrategy = require('passport-local').Strategy, - TwitterStrategy = require('passport-twitter').Strategy, - FacebookStrategy = require('passport-facebook').Strategy, - GoogleStrategy = require('passport-google').Strategy, - LinkedInStrategy = require('passport-linkedin').Strategy, - check = require('validator').check, - userRoles = require('../../client/js/routingConfig').userRoles; +var mongoose = require('mongoose'); var UserSchema = new mongoose.Schema({ - name: String, - email: String, username: { type: String, - unique: true + unique: true, + required: true }, + password: String, + name: String, + email: String, provider: String, - hashed_password: String, - salt: String, - facebook: {}, - twitter: {}, - google: {} + facebook: String, + twitter: String, + google: String, + linkedin: String }); -var User = mongoose.model('User', UserSchema); - -var users = [ - { - id: 1, - username: "user", - password: "123", - role: userRoles.user - }, - { - id: 2, - username: "admin", - password: "123", - role: userRoles.admin - } -]; - -module.exports = { - - addUser: function(username, password, role, callback) { - if (this.findByUsername(username)) { - return callback("UserAlreadyExists"); - } - - var user = new User({ - username: username, - password: password, - role: role - }); - - user.save(function(err) { - callback(null, user); - }); - }, - - findOrCreateOauthUser: function(provider, providerId) { - User.findOne({ $where: provider + '===' + providerId }, function(err, user) { - if (user) { - return user; - } else { - user = { - username: provider + '_user', - role: userRoles.user, - provider: provider - }; - user[provider] = providerId; - - user.save(function(err) { - return user; - }); - } - }); - }, - - findAll: function() { - User.find(function(err, users) { - return users; - }); - }, - - findById: function(id) { - User.findById(id, function(err, user) { - if (user) return user; - }); - }, - - findByUsername: function(username) { - User.findOne({ username: username }, function(err, user) { - if (user) return user; - }); - }, - - validate: function(user) { - check(user.username, 'Username must be 1-20 characters long').len(1, 20); - check(user.password, 'Password must be 5-60 characters long').len(5, 60); - check(user.username, 'Invalid username').not(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/); - - // TODO: Seems node-validator's isIn function doesn't handle Number arrays very well... - // Till this is rectified Number arrays must be converted to string arrays - // https://github.com/chriso/node-validator/issues/185 - var stringArr = _.map(_.values(userRoles), function(val) { return val.toString() }); - check(user.role, 'Invalid user role given').isIn(stringArr); - }, - - localStrategy: new LocalStrategy( - function(username, password, done) { - - var user = module.exports.findByUsername(username); - - if(!user) { - done(null, false, { message: 'Incorrect username.' }); - } - else if(user.password != password) { - done(null, false, { message: 'Incorrect username.' }); - } - else { - return done(null, user); - } - - } - ), - - twitterStrategy: function() { - if(!process.env.TWITTER_CONSUMER_KEY) throw new Error('A Twitter Consumer Key is required if you want to enable login via Twitter.'); - if(!process.env.TWITTER_CONSUMER_SECRET) throw new Error('A Twitter Consumer Secret is required if you want to enable login via Twitter.'); - - return new TwitterStrategy({ - consumerKey: process.env.TWITTER_CONSUMER_KEY, - consumerSecret: process.env.TWITTER_CONSUMER_SECRET, - callbackURL: process.env.TWITTER_CALLBACK_URL || 'http://localhost:8000/auth/twitter/callback' - }, - function(token, tokenSecret, profile, done) { - var user = module.exports.findOrCreateOauthUser(profile.provider, profile.id); - done(null, user); - }); - }, - - facebookStrategy: function() { - if(!process.env.FACEBOOK_APP_ID) throw new Error('A Facebook App ID is required if you want to enable login via Facebook.'); - if(!process.env.FACEBOOK_APP_SECRET) throw new Error('A Facebook App Secret is required if you want to enable login via Facebook.'); - - return new FacebookStrategy({ - clientID: process.env.FACEBOOK_APP_ID, - clientSecret: process.env.FACEBOOK_APP_SECRET, - callbackURL: process.env.FACEBOOK_CALLBACK_URL || "http://localhost:8000/auth/facebook/callback" - }, - function(accessToken, refreshToken, profile, done) { - var user = module.exports.findOrCreateOauthUser(profile.provider, profile.id); - done(null, user); - }); - }, - - googleStrategy: function() { - - return new GoogleStrategy({ - returnURL: process.env.GOOGLE_RETURN_URL || "http://localhost:8000/auth/google/return", - realm: process.env.GOOGLE_REALM || "http://localhost:8000/" - }, - function(identifier, profile, done) { - var user = module.exports.findOrCreateOauthUser('google', identifier); - done(null, user); - }); - }, - - linkedInStrategy: function() { - if(!process.env.LINKED_IN_KEY) throw new Error('A LinkedIn App Key is required if you want to enable login via LinkedIn.'); - if(!process.env.LINKED_IN_SECRET) throw new Error('A LinkedIn App Secret is required if you want to enable login via LinkedIn.'); - - return new LinkedInStrategy({ - consumerKey: process.env.LINKED_IN_KEY, - consumerSecret: process.env.LINKED_IN_SECRET, - callbackURL: process.env.LINKED_IN_CALLBACK_URL || "http://localhost:8000/auth/linkedin/callback" - }, - function(token, tokenSecret, profile, done) { - var user = module.exports.findOrCreateOauthUser('linkedin', profile.id); - done(null,user); - } - ); - }, - serializeUser: function(user, done) { - done(null, user.id); - }, - - deserializeUser: function(id, done) { - var user = module.exports.findById(id); - - if(user) { done(null, user); } - else { done(null, false); } - } -}; \ No newline at end of file +module.exports = mongoose.model('User', UserSchema); \ No newline at end of file