diff --git a/controllers/auth.js b/controllers/auth.js index b9218ed66c..457f6e0734 100755 --- a/controllers/auth.js +++ b/controllers/auth.js @@ -1,179 +1,47 @@ -var passport = require('passport'), - User = require('../models/User'), - crypto = require('crypto'), +var 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, - userRoles = require('../../client/js/routingConfig').userRoles; + User = require('../models/user'); +passport.serializeUser(function(user, done) { + done(null, user.id); +}); -function findByUsername(username, callback) { +passport.deserializeUser(function(id, done) { + User.findById(id, function (err, user) { + done(err, user); + }); +}); + +passport.use(new LocalStrategy(function(username, password, done) { 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) { - findByUsername(req.body.username, function(err, user) { - if (user) return res.send(403, 'User already exists'); - - 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) { - 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); - }, - - logout: function(req, res) { - req.logout(); - res.send(200); - }, - - - - - - - - - localStrategy: new LocalStrategy(function(username, password, done) { - 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 { + 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); - } - }); - }), - - 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) { - findOrCreateOauthUser(profile.provider, profile.id, function(err, user) { - done(err, 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) { - findOrCreateOauthUser(profile.provider, profile.id, function(err, user) { - done(err, 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) { - findOrCreateOauthUser('google', identifier, function(err, user) { - done(err, 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) { - findOrCreateOauthUser('linkedin', profile.id, function(err, user) { - done(err, user); - }); - }); - }, - - serializeUser: function(user, done) { - done(null, user.id); - }, - - deserializeUser: function(id, done) { - findById(id, function(err, user) { - if (user) { - done(null, user); } else { - done(null, false); + return done(null, false, { message: 'Invalid password' }); } }); - } + }); +})); +// Simple route middleware to ensure user is authenticated. Otherwise send to login page. +exports.ensureAuthenticated = function ensureAuthenticated(req, res, next) { + if (req.isAuthenticated()) { return next(); } + res.redirect('/login'); +}; + + +// Check for admin middleware, this is unrelated to passport.js +// You can delete this if you use different method to check for admins or don't need admins +exports.ensureAdmin = function ensureAdmin(req, res, next) { + return function(req, res, next) { + console.log(req.user); + if(req.user && req.user.admin === true) + next(); + else + res.send(403); + }; }; \ No newline at end of file diff --git a/controllers/index.js b/controllers/home.js similarity index 53% rename from controllers/index.js rename to controllers/home.js index 62dcb7b5f2..ce97123e06 100644 --- a/controllers/index.js +++ b/controllers/home.js @@ -2,7 +2,7 @@ exports.index = function(req, res){ res.render('index'); }; -exports.partial = function (req, res) { +exports.partials = function (req, res) { var name = req.params.name; - res.render('partials/partial' + name); + res.render('partials/' + name); }; \ No newline at end of file diff --git a/controllers/users.js b/controllers/users.js old mode 100755 new mode 100644 index 3337a7988e..4ab2c61a59 --- a/controllers/users.js +++ b/controllers/users.js @@ -1,17 +1,33 @@ -//var _ = require('underscore') -// , User = require('../models/User.js') -// , userRoles = require('../../client/js/routingConfig').userRoles; -// -//module.exports = { -// index: function(req, res) { -// var users = User.findAll(); -// _.each(users, function(user) { -// delete user.password; -// delete user.twitter; -// delete user.facebook; -// delete user.google; -// delete user.linkedin; -// }); -// res.json(users); -// } -//}; \ No newline at end of file +var mongoose = require('mongoose'); +var User = require('../models/user'); + +exports.account = function(req, res) { + res.render('account', { user: req.user }); +}; + +exports.getlogin = function(req, res) { + res.render('partials/login', { user: req.user, message: req.session.messages }); +}; + +exports.admin = function(req, res) { + res.send('access granted admin!'); +}; + +exports.postlogin = function(req, res, next) { + passport.authenticate('local', function(err, user, info) { + if (err) { return next(err) } + if (!user) { + req.session.messages = [info.message]; + return res.redirect('/login') + } + req.logIn(user, function(err) { + if (err) { return next(err); } + return res.redirect('/'); + }); + })(req, res, next); +}; + +exports.logout = function(req, res) { + req.logout(); + res.redirect('/'); +}; \ No newline at end of file diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000000..b140481af8 --- /dev/null +++ b/models/user.js @@ -0,0 +1,34 @@ +var mongoose = require('mongoose'), + bcrypt = require('bcrypt'); + +var userSchema = new mongoose.Schema({ + username: { type: String, required: true, unique: true }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true}, + admin: { type: Boolean, required: true } +}); + +userSchema.pre('save', function(next) { + var user = this; + + if (!user.isModified('password')) return next(); + + bcrypt.genSalt(32, 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(); + }); + }); +}); + +userSchema.methods.comparePassword = function(candidatePassword, cb) { + bcrypt.compare(candidatePassword, this.password, function(err, isMatch) { + if(err) return cb(err); + cb(null, isMatch); + }); +}; + +module.exports = mongoose.model('User', userSchema); \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index f9986ebcb4..dba6d62258 100755 --- a/public/js/app.js +++ b/public/js/app.js @@ -4,8 +4,18 @@ // Declare app level module which depends on filters, and services angular.module('myApp', ['ngRoute', 'myApp.filters', 'myApp.services', 'myApp.directives']). config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { - $routeProvider.when('/view1', {templateUrl: 'partial/1', controller: MyCtrl1}); - $routeProvider.when('/view2', {templateUrl: 'partial/2', controller: MyCtrl2}); - $routeProvider.otherwise({redirectTo: '/view1'}); + $routeProvider.when('/view1', { + templateUrl: 'partials/partial1', + controller: MyCtrl1 + }); + $routeProvider.when('/view2', { + templateUrl: 'partials/partial2', + controller: MyCtrl2 + }); + $routeProvider.when('/login', { + templateUrl: 'partials/login', + controller: MyCtrl3 + }); + $routeProvider.otherwise({ redirectTo: '/view1' }); $locationProvider.html5Mode(true); }]); \ No newline at end of file diff --git a/public/js/controllers.js b/public/js/controllers.js index 60e6d20ca1..d19f8418b7 100755 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -8,7 +8,7 @@ function AppCtrl($scope, $http) { $scope.name = data.name; }). error(function(data, status, headers, config) { - $scope.name = 'Error!' + $scope.name = 'Error!'; }); } @@ -19,3 +19,5 @@ MyCtrl1.$inject = []; function MyCtrl2() { } MyCtrl2.$inject = []; +function MyCtrl3() {} +MyCtrl3.$inject = []; diff --git a/server.js b/server.js index a5263a8ef6..37fdf9444c 100755 --- a/server.js +++ b/server.js @@ -5,46 +5,50 @@ var express = require('express'), mongoose = require('mongoose'), passport = require('passport'), - index = require('./controllers'), - api = require('./controllers/api'); + config = require('./config'), + // Load controllers + home = require('./controllers/home'), + api = require('./controllers/api'), + auth = require('./controllers/auth'), + users = require('./controllers/users'); +// Connect to database +mongoose.connect(config.db, function(err, res) { + if (err) { + console.log ('Error connecting to database: ' + err); + } else { + console.log ('Successfully connected to database!'); + } +}); -var db = module.exports = mongoose.connect('localhost'); var app = module.exports = express(); app.set('port', process.env.PORT || 3000); app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); -app.use(express.favicon()); app.use(express.logger('dev')); +app.use(express.errorHandler()); +app.use(express.favicon()); app.use(express.cookieParser()); app.use(express.bodyParser()); app.use(express.methodOverride()); -app.use(express.cookieSession({ secret: 'secret' })); +app.use(express.session({ secret: 'Bob-Alice' })); app.use(passport.initialize()); app.use(passport.session()); app.use(express.static(path.join(__dirname, 'public'))); app.use(app.router); - -if (app.get('env') === 'development') { - app.use(express.errorHandler()); -}; - - -/** - * API Routes - */ -app.get('/', index.index); -app.get('/partial/:name', index.partial); - - -// JSON API -app.get('/api/name', api.name); - -// redirect all others to the index (HTML5 history) -app.get('*', index.index); +// Routes +app.get('/', home.index); +app.get('/partials/:name', home.partials); +//app.get('/account', auth.ensureAuthenticated, users.account); +//app.get('/logout', users.logout); +//app.post('/login', users.postlogin); +//app.get('/login', users.getlogin); +//app.get('/admin', auth.ensureAuthenticated, auth.ensureAdmin(), users.admin); +//app.get('/api/name', api.name); +app.get('*', home.index); app.listen(app.get('port'), function() { diff --git a/views/index.jade b/views/index.jade index ff8c0279e5..1fd98c0d02 100644 --- a/views/index.jade +++ b/views/index.jade @@ -13,7 +13,9 @@ block body li a(href="view1") View1 li - a(href="view2") View2 + a(href="view2") View2 + li + a(href="login") login .container div(ng-controller='AppCtrl') .jumbotron diff --git a/views/partials/login.jade b/views/partials/login.jade new file mode 100644 index 0000000000..15f6375538 --- /dev/null +++ b/views/partials/login.jade @@ -0,0 +1,12 @@ +form(action='/login', method='post') + div + label Username: + input(type='text', name='username') + br + div + label Password: + input(type='password', name='password') + div + input(type='submit', value='Submit') + p + small Hint - bob:secret