mirror of
https://github.com/taobataoma/meanTorrent.git
synced 2026-03-06 04:01:04 +01:00
Adding Password Reset
This commit is contained in:
@@ -4,33 +4,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
var mongoose = require('mongoose'),
|
||||
errorHandler = require('./errors'),
|
||||
Article = mongoose.model('Article'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
* Get the error message from error object
|
||||
*/
|
||||
var getErrorMessage = function(err) {
|
||||
var message = '';
|
||||
|
||||
if (err.code) {
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
case 11001:
|
||||
message = 'Article already exists';
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
} else {
|
||||
for (var errName in err.errors) {
|
||||
if (err.errors[errName].message) message = err.errors[errName].message;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a article
|
||||
*/
|
||||
@@ -41,7 +18,7 @@ exports.create = function(req, res) {
|
||||
article.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
res.jsonp(article);
|
||||
@@ -67,7 +44,7 @@ exports.update = function(req, res) {
|
||||
article.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
res.jsonp(article);
|
||||
@@ -84,7 +61,7 @@ exports.delete = function(req, res) {
|
||||
article.remove(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
res.jsonp(article);
|
||||
@@ -99,7 +76,7 @@ exports.list = function(req, res) {
|
||||
Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
res.jsonp(articles);
|
||||
|
||||
42
app/controllers/errors.server.controller.js
Normal file
42
app/controllers/errors.server.controller.js
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Get unique error field name
|
||||
*/
|
||||
var getUniqueErrorMessage = function(err) {
|
||||
var output;
|
||||
|
||||
try {
|
||||
var fieldName = err.err.substring(err.err.lastIndexOf('.$') + 2, err.err.lastIndexOf('_1'));
|
||||
output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exist';
|
||||
|
||||
} catch(ex) {
|
||||
output = 'Unique field already exist';
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the error message from error object
|
||||
*/
|
||||
exports.getErrorMessage = function(err) {
|
||||
var message = '';
|
||||
|
||||
if (err.code) {
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
case 11001:
|
||||
message = getUniqueErrorMessage(err);
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
} else {
|
||||
for (var errName in err.errors) {
|
||||
if (err.errors[errName].message) message = err.errors[errName].message;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
@@ -1,542 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
User = mongoose.model('User'),
|
||||
_ = require('lodash');
|
||||
/* Requires for reset password */
|
||||
var nodemailer = require('nodemailer');
|
||||
var LocalStrategy = require('passport-local').Strategy;
|
||||
var bcrypt = require('bcrypt-nodejs');
|
||||
var async = require('async');
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Get the error message from error object
|
||||
*/
|
||||
var getErrorMessage = function(err) {
|
||||
var message = '';
|
||||
|
||||
if (err.code) {
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
case 11001:
|
||||
message = 'Username already exists';
|
||||
break;
|
||||
default:
|
||||
message = 'Something went wrong';
|
||||
}
|
||||
} else {
|
||||
for (var errName in err.errors) {
|
||||
if (err.errors[errName].message) message = err.errors[errName].message;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Signup
|
||||
*/
|
||||
exports.signup = function(req, res) {
|
||||
// For security measurement we remove the roles from the req.body object
|
||||
delete req.body.roles;
|
||||
|
||||
// Init Variables
|
||||
var user = new User(req.body);
|
||||
var message = null;
|
||||
|
||||
// Add missing user fields
|
||||
user.provider = 'local';
|
||||
user.displayName = user.firstName + ' ' + user.lastName;
|
||||
|
||||
// Then save the user
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
// Remove sensitive data before login
|
||||
user.password = undefined;
|
||||
user.salt = undefined;
|
||||
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Signin after passport authentication
|
||||
*/
|
||||
exports.signin = function(req, res, next) {
|
||||
passport.authenticate('local', function(err, user, info) {
|
||||
if (err || !user) {
|
||||
res.send(400, info);
|
||||
} else {
|
||||
// Remove sensitive data before login
|
||||
user.password = undefined;
|
||||
user.salt = undefined;
|
||||
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
})(req, res, next);
|
||||
};
|
||||
|
||||
/**
|
||||
* Forgot for reset password (forgot POST)
|
||||
*/
|
||||
exports.forgot = function(req, res, next) {
|
||||
async.waterfall([
|
||||
// Generate random token
|
||||
function(done) {
|
||||
crypto.randomBytes(20, function(err, buf) {
|
||||
var token = buf.toString('hex');
|
||||
done(err, token);
|
||||
});
|
||||
},
|
||||
// Lookup user by email address
|
||||
function(token, done) {
|
||||
if (req.body.email) {
|
||||
User.findOne({ email: req.body.email }, function(err, user) {
|
||||
if (!user) {
|
||||
return res.send(400, {
|
||||
message: 'No account with that email address exists'
|
||||
});
|
||||
}
|
||||
|
||||
user.resetPasswordToken = token;
|
||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||
|
||||
user.save(function(err) {
|
||||
done(err, token, user);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Email field must not be blank'
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
// If valid email, send reset email using service
|
||||
function(token, user, done) {
|
||||
var smtpTransport = nodemailer.createTransport('SMTP', {
|
||||
service: 'SendGrid', // Choose email service, default SendGrid
|
||||
auth: {
|
||||
user: 'your_sendgrid_email@domain.com',
|
||||
pass: 'your_sendgrid_password'
|
||||
}
|
||||
});
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: 'your_email@domain.com',
|
||||
subject: 'Password Reset',
|
||||
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
|
||||
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
|
||||
'http://' + req.headers.host + '/auth/reset/' + token + '\n\n' +
|
||||
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
res.send(200, {
|
||||
message: 'An email has been sent to ' + user.email + ' with further instructions.'
|
||||
});
|
||||
done(err, 'done');
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err) return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password GET from email token
|
||||
*/
|
||||
exports.resetGet = function(req, res) {
|
||||
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
|
||||
if (!user) {
|
||||
// res.render('404');
|
||||
res.send(400, {
|
||||
message: 'Password reset token is invalid or has expired.'
|
||||
});
|
||||
return res.redirect('/#!/forgot');
|
||||
}
|
||||
|
||||
res.redirect('/#!/reset/' + req.params.token);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password POST from email token
|
||||
*/
|
||||
exports.resetPost = function(req, res) {
|
||||
// Init Variables
|
||||
var passwordDetails = req.body;
|
||||
var message = null;
|
||||
|
||||
async.waterfall([
|
||||
function(done) {
|
||||
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
|
||||
if (!err && user) {
|
||||
if (passwordDetails.newPassword === passwordDetails.verifyPassword) {
|
||||
user.password = passwordDetails.newPassword;
|
||||
user.resetPasswordToken = undefined;
|
||||
user.resetPasswordExpires = undefined;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
done(err, user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Passwords do not match'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Password reset token is invalid or has expired.'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
function(user, done) {
|
||||
var smtpTransport = nodemailer.createTransport('SMTP', {
|
||||
service: 'SendGrid',
|
||||
auth: {
|
||||
user: 'your_sendgrid_email@domain.com',
|
||||
pass: 'your_sendgrid_password'
|
||||
}
|
||||
});
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: 'your_email@domain.com',
|
||||
subject: 'Your password has been changed',
|
||||
text: 'Hello,\n\n' +
|
||||
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
res.send(200, {
|
||||
message: 'Password changed successfully'
|
||||
});
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
res.redirect('/');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user details
|
||||
*/
|
||||
exports.update = function(req, res) {
|
||||
// Init Variables
|
||||
var user = req.user;
|
||||
var message = null;
|
||||
|
||||
// For security measurement we remove the roles from the req.body object
|
||||
delete req.body.roles;
|
||||
|
||||
if (user) {
|
||||
// Merge existing user
|
||||
user = _.extend(user, req.body);
|
||||
user.updated = Date.now();
|
||||
user.displayName = user.firstName + ' ' + user.lastName;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not signed in'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change Password
|
||||
*/
|
||||
exports.changePassword = function(req, res, next) {
|
||||
// Init Variables
|
||||
var passwordDetails = req.body;
|
||||
var message = null;
|
||||
|
||||
if (req.user) {
|
||||
if (passwordDetails.newPassword) {
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (!err && user) {
|
||||
if (user.authenticate(passwordDetails.currentPassword)) {
|
||||
if (passwordDetails.newPassword === passwordDetails.verifyPassword) {
|
||||
user.password = passwordDetails.newPassword;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.send({
|
||||
message: 'Password changed successfully'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Passwords do not match'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Current password is incorrect'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not found'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Please provide a new password'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not signed in'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Signout
|
||||
*/
|
||||
exports.signout = function(req, res) {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* Send User
|
||||
*/
|
||||
exports.me = function(req, res) {
|
||||
res.jsonp(req.user || null);
|
||||
};
|
||||
|
||||
/**
|
||||
* OAuth callback
|
||||
*/
|
||||
exports.oauthCallback = function(strategy) {
|
||||
return function(req, res, next) {
|
||||
passport.authenticate(strategy, function(err, user, redirectURL) {
|
||||
if (err || !user) {
|
||||
return res.redirect('/#!/signin');
|
||||
}
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
return res.redirect('/#!/signin');
|
||||
}
|
||||
|
||||
return res.redirect(redirectURL || '/');
|
||||
});
|
||||
})(req, res, next);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* User middleware
|
||||
*/
|
||||
exports.userByID = function(req, res, next, id) {
|
||||
User.findOne({
|
||||
_id: id
|
||||
}).exec(function(err, user) {
|
||||
if (err) return next(err);
|
||||
if (!user) return next(new Error('Failed to load User ' + id));
|
||||
req.profile = user;
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Require login routing middleware
|
||||
*/
|
||||
exports.requiresLogin = function(req, res, next) {
|
||||
if (!req.isAuthenticated()) {
|
||||
return res.send(401, {
|
||||
message: 'User is not logged in'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* User authorizations routing middleware
|
||||
*/
|
||||
exports.hasAuthorization = function(roles) {
|
||||
var _this = this;
|
||||
|
||||
return function(req, res, next) {
|
||||
_this.requiresLogin(req, res, function() {
|
||||
if (_.intersection(req.user.roles, roles).length) {
|
||||
return next();
|
||||
} else {
|
||||
return res.send(403, {
|
||||
message: 'User is not authorized'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to save or update a OAuth user profile
|
||||
*/
|
||||
exports.saveOAuthUserProfile = function(req, providerUserProfile, done) {
|
||||
if (!req.user) {
|
||||
// Define a search query fields
|
||||
var searchMainProviderIdentifierField = 'providerData.' + providerUserProfile.providerIdentifierField;
|
||||
var searchAdditionalProviderIdentifierField = 'additionalProvidersData.' + providerUserProfile.provider + '.' + providerUserProfile.providerIdentifierField;
|
||||
|
||||
// Define main provider search query
|
||||
var mainProviderSearchQuery = {};
|
||||
mainProviderSearchQuery.provider = providerUserProfile.provider;
|
||||
mainProviderSearchQuery[searchMainProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];
|
||||
|
||||
// Define additional provider search query
|
||||
var additionalProviderSearchQuery = {};
|
||||
additionalProviderSearchQuery[searchAdditionalProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];
|
||||
|
||||
// Define a search query to find existing user with current provider profile
|
||||
var searchQuery = {
|
||||
$or: [mainProviderSearchQuery, additionalProviderSearchQuery]
|
||||
};
|
||||
|
||||
User.findOne(searchQuery, function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
} else {
|
||||
if (!user) {
|
||||
var possibleUsername = providerUserProfile.username || ((providerUserProfile.email) ? providerUserProfile.email.split('@')[0] : '');
|
||||
|
||||
User.findUniqueUsername(possibleUsername, null, function(availableUsername) {
|
||||
user = new User({
|
||||
firstName: providerUserProfile.firstName,
|
||||
lastName: providerUserProfile.lastName,
|
||||
username: availableUsername,
|
||||
displayName: providerUserProfile.displayName,
|
||||
email: providerUserProfile.email,
|
||||
provider: providerUserProfile.provider,
|
||||
providerData: providerUserProfile.providerData
|
||||
});
|
||||
|
||||
// And save the user
|
||||
user.save(function(err) {
|
||||
return done(err, user);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return done(err, user);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// User is already logged in, join the provider data to the existing user
|
||||
var user = req.user;
|
||||
|
||||
// Check if user exists, is not signed in using this provider, and doesn't have that provider data already configured
|
||||
if (user.provider !== providerUserProfile.provider && (!user.additionalProvidersData || !user.additionalProvidersData[providerUserProfile.provider])) {
|
||||
// Add the provider data to the additional provider data field
|
||||
if (!user.additionalProvidersData) user.additionalProvidersData = {};
|
||||
user.additionalProvidersData[providerUserProfile.provider] = providerUserProfile.providerData;
|
||||
|
||||
// Then tell mongoose that we've updated the additionalProvidersData field
|
||||
user.markModified('additionalProvidersData');
|
||||
|
||||
// And save the user
|
||||
user.save(function(err) {
|
||||
return done(err, user, '/#!/settings/accounts');
|
||||
});
|
||||
} else {
|
||||
return done(new Error('User is already connected using this provider'), user);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove OAuth provider
|
||||
*/
|
||||
exports.removeOAuthProvider = function(req, res, next) {
|
||||
var user = req.user;
|
||||
var provider = req.param('provider');
|
||||
|
||||
if (user && provider) {
|
||||
// Delete the additional provider
|
||||
if (user.additionalProvidersData[provider]) {
|
||||
delete user.additionalProvidersData[provider];
|
||||
|
||||
// Then tell mongoose that we've updated the additionalProvidersData field
|
||||
user.markModified('additionalProvidersData');
|
||||
}
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
module.exports.authentication = require('./users/users.authentication');
|
||||
module.exports.authorization = require('./users/users.authorization');
|
||||
module.exports.password = require('./users/users.password');
|
||||
module.exports.profile = require('./users/users.profile');
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var _ = require('lodash'),
|
||||
errorHandler = require('../errors'),
|
||||
mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
User = mongoose.model('User');
|
||||
|
||||
/**
|
||||
* Signup
|
||||
*/
|
||||
exports.signup = function(req, res) {
|
||||
// For security measurement we remove the roles from the req.body object
|
||||
delete req.body.roles;
|
||||
|
||||
// Init Variables
|
||||
var user = new User(req.body);
|
||||
var message = null;
|
||||
|
||||
// Add missing user fields
|
||||
user.provider = 'local';
|
||||
user.displayName = user.firstName + ' ' + user.lastName;
|
||||
|
||||
// Then save the user
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
// Remove sensitive data before login
|
||||
user.password = undefined;
|
||||
user.salt = undefined;
|
||||
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Signin after passport authentication
|
||||
*/
|
||||
exports.signin = function(req, res, next) {
|
||||
passport.authenticate('local', function(err, user, info) {
|
||||
if (err || !user) {
|
||||
res.send(400, info);
|
||||
} else {
|
||||
// Remove sensitive data before login
|
||||
user.password = undefined;
|
||||
user.salt = undefined;
|
||||
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
})(req, res, next);
|
||||
};
|
||||
|
||||
/**
|
||||
* Signout
|
||||
*/
|
||||
exports.signout = function(req, res) {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* OAuth callback
|
||||
*/
|
||||
exports.oauthCallback = function(strategy) {
|
||||
return function(req, res, next) {
|
||||
passport.authenticate(strategy, function(err, user, redirectURL) {
|
||||
if (err || !user) {
|
||||
return res.redirect('/#!/signin');
|
||||
}
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
return res.redirect('/#!/signin');
|
||||
}
|
||||
|
||||
return res.redirect(redirectURL || '/');
|
||||
});
|
||||
})(req, res, next);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var _ = require('lodash'),
|
||||
mongoose = require('mongoose'),
|
||||
User = mongoose.model('User');
|
||||
|
||||
/**
|
||||
* User middleware
|
||||
*/
|
||||
exports.userByID = function(req, res, next, id) {
|
||||
User.findOne({
|
||||
_id: id
|
||||
}).exec(function(err, user) {
|
||||
if (err) return next(err);
|
||||
if (!user) return next(new Error('Failed to load User ' + id));
|
||||
req.profile = user;
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Require login routing middleware
|
||||
*/
|
||||
exports.requiresLogin = function(req, res, next) {
|
||||
if (!req.isAuthenticated()) {
|
||||
return res.send(401, {
|
||||
message: 'User is not logged in'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* User authorizations routing middleware
|
||||
*/
|
||||
exports.hasAuthorization = function(roles) {
|
||||
var _this = this;
|
||||
|
||||
return function(req, res, next) {
|
||||
_this.requiresLogin(req, res, function() {
|
||||
if (_.intersection(req.user.roles, roles).length) {
|
||||
return next();
|
||||
} else {
|
||||
return res.send(403, {
|
||||
message: 'User is not authorized'
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
244
app/controllers/users/users.password.server.controller.js
Normal file
244
app/controllers/users/users.password.server.controller.js
Normal file
@@ -0,0 +1,244 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var _ = require('lodash'),
|
||||
errorHandler = require('../errors'),
|
||||
mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
User = mongoose.model('User'),
|
||||
config = require('../../../config/config'),
|
||||
swig = require('swig'),
|
||||
nodemailer = require('nodemailer'),
|
||||
crypto = require('crypto'),
|
||||
async = require('async'),
|
||||
crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Forgot for reset password (forgot POST)
|
||||
*/
|
||||
exports.forgot = function(req, res, next) {
|
||||
async.waterfall([
|
||||
// Generate random token
|
||||
function(done) {
|
||||
crypto.randomBytes(20, function(err, buffer) {
|
||||
var token = buffer.toString('hex');
|
||||
done(err, token);
|
||||
});
|
||||
},
|
||||
// Lookup user by username
|
||||
function(token, done) {
|
||||
if (req.body.username) {
|
||||
User.findOne({
|
||||
username: req.body.username
|
||||
}, function(err, user) {
|
||||
if (!user) {
|
||||
return res.send(400, {
|
||||
message: 'No account with that username has been found'
|
||||
});
|
||||
} else if (user.provider !== 'local') {
|
||||
return res.send(400, {
|
||||
message: 'It seems like you signed up using your ' + user.provider + ' account'
|
||||
});
|
||||
} else {
|
||||
user.resetPasswordToken = token;
|
||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||
|
||||
user.save(function(err) {
|
||||
done(err, token, user);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Username field must not be blank'
|
||||
});
|
||||
}
|
||||
},
|
||||
function(token, user, done) {
|
||||
res.render('templates/reset-password-email', {
|
||||
name: user.displayName,
|
||||
appName: config.app.title,
|
||||
url: 'http://' + req.headers.host + '/auth/reset/' + token
|
||||
}, function(err, emailHTML) {
|
||||
done(err, emailHTML, user);
|
||||
});
|
||||
},
|
||||
// If valid email, send reset email using service
|
||||
function(emailHTML, user, done) {
|
||||
var smtpTransport = nodemailer.createTransport(config.mailer.options);
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: config.mailer.fromEmail,
|
||||
subject: 'Password Reset',
|
||||
html: emailHTML
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
res.send(200, {
|
||||
message: 'An email has been sent to ' + user.email + ' with further instructions.'
|
||||
});
|
||||
done(err, 'done');
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err) return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password GET from email token
|
||||
*/
|
||||
exports.validateResetToken = function(req, res) {
|
||||
User.findOne({
|
||||
resetPasswordToken: req.params.token,
|
||||
resetPasswordExpires: {
|
||||
$gt: Date.now()
|
||||
}
|
||||
}, function(err, user) {
|
||||
if (!user) {
|
||||
return res.redirect('/#!/password/reset/invalid');
|
||||
}
|
||||
|
||||
res.redirect('/#!/password/reset/' + req.params.token);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password POST from email token
|
||||
*/
|
||||
exports.reset = function(req, res, next) {
|
||||
// Init Variables
|
||||
var passwordDetails = req.body;
|
||||
var message = null;
|
||||
|
||||
async.waterfall([
|
||||
|
||||
function(done) {
|
||||
User.findOne({
|
||||
resetPasswordToken: req.params.token,
|
||||
resetPasswordExpires: {
|
||||
$gt: Date.now()
|
||||
}
|
||||
}, function(err, user) {
|
||||
if (!err && user) {
|
||||
if (passwordDetails.newPassword === passwordDetails.verifyPassword) {
|
||||
user.password = passwordDetails.newPassword;
|
||||
user.resetPasswordToken = undefined;
|
||||
user.resetPasswordExpires = undefined;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
// Return authenticated user
|
||||
res.jsonp(user);
|
||||
|
||||
done(err, user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Passwords do not match'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return res.send(400, {
|
||||
message: 'Password reset token is invalid or has expired.'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
function(user, done) {
|
||||
res.render('templates/reset-password-confirm-email', {
|
||||
name: user.displayName
|
||||
}, function(err, emailHTML) {
|
||||
done(err, emailHTML, user);
|
||||
});
|
||||
},
|
||||
// If valid email, send reset email using service
|
||||
function(emailHTML, user, done) {
|
||||
var smtpTransport = nodemailer.createTransport(config.mailer.options);
|
||||
var mailOptions = {
|
||||
to: user.email,
|
||||
from: config.mailer.fromEmail,
|
||||
subject: 'Your password has been changed',
|
||||
html: emailHTML
|
||||
};
|
||||
smtpTransport.sendMail(mailOptions, function(err) {
|
||||
done(err, 'done');
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err) return next(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Change Password
|
||||
*/
|
||||
exports.changePassword = function(req, res, next) {
|
||||
// Init Variables
|
||||
var passwordDetails = req.body;
|
||||
var message = null;
|
||||
|
||||
if (req.user) {
|
||||
if (passwordDetails.newPassword) {
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (!err && user) {
|
||||
if (user.authenticate(passwordDetails.currentPassword)) {
|
||||
if (passwordDetails.newPassword === passwordDetails.verifyPassword) {
|
||||
user.password = passwordDetails.newPassword;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.send({
|
||||
message: 'Password changed successfully'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Passwords do not match'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Current password is incorrect'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not found'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'Please provide a new password'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not signed in'
|
||||
});
|
||||
}
|
||||
};
|
||||
164
app/controllers/users/users.profile.server.controller.js
Normal file
164
app/controllers/users/users.profile.server.controller.js
Normal file
@@ -0,0 +1,164 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var _ = require('lodash'),
|
||||
errorHandler = require('../errors'),
|
||||
mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
User = mongoose.model('User');
|
||||
|
||||
/**
|
||||
* Update user details
|
||||
*/
|
||||
exports.update = function(req, res) {
|
||||
// Init Variables
|
||||
var user = req.user;
|
||||
var message = null;
|
||||
|
||||
// For security measurement we remove the roles from the req.body object
|
||||
delete req.body.roles;
|
||||
|
||||
if (user) {
|
||||
// Merge existing user
|
||||
user = _.extend(user, req.body);
|
||||
user.updated = Date.now();
|
||||
user.displayName = user.firstName + ' ' + user.lastName;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send(400, {
|
||||
message: 'User is not signed in'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send User
|
||||
*/
|
||||
exports.me = function(req, res) {
|
||||
res.jsonp(req.user || null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to save or update a OAuth user profile
|
||||
*/
|
||||
exports.saveOAuthUserProfile = function(req, providerUserProfile, done) {
|
||||
if (!req.user) {
|
||||
// Define a search query fields
|
||||
var searchMainProviderIdentifierField = 'providerData.' + providerUserProfile.providerIdentifierField;
|
||||
var searchAdditionalProviderIdentifierField = 'additionalProvidersData.' + providerUserProfile.provider + '.' + providerUserProfile.providerIdentifierField;
|
||||
|
||||
// Define main provider search query
|
||||
var mainProviderSearchQuery = {};
|
||||
mainProviderSearchQuery.provider = providerUserProfile.provider;
|
||||
mainProviderSearchQuery[searchMainProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];
|
||||
|
||||
// Define additional provider search query
|
||||
var additionalProviderSearchQuery = {};
|
||||
additionalProviderSearchQuery[searchAdditionalProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];
|
||||
|
||||
// Define a search query to find existing user with current provider profile
|
||||
var searchQuery = {
|
||||
$or: [mainProviderSearchQuery, additionalProviderSearchQuery]
|
||||
};
|
||||
|
||||
User.findOne(searchQuery, function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
} else {
|
||||
if (!user) {
|
||||
var possibleUsername = providerUserProfile.username || ((providerUserProfile.email) ? providerUserProfile.email.split('@')[0] : '');
|
||||
|
||||
User.findUniqueUsername(possibleUsername, null, function(availableUsername) {
|
||||
user = new User({
|
||||
firstName: providerUserProfile.firstName,
|
||||
lastName: providerUserProfile.lastName,
|
||||
username: availableUsername,
|
||||
displayName: providerUserProfile.displayName,
|
||||
email: providerUserProfile.email,
|
||||
provider: providerUserProfile.provider,
|
||||
providerData: providerUserProfile.providerData
|
||||
});
|
||||
|
||||
// And save the user
|
||||
user.save(function(err) {
|
||||
return done(err, user);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return done(err, user);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// User is already logged in, join the provider data to the existing user
|
||||
var user = req.user;
|
||||
|
||||
// Check if user exists, is not signed in using this provider, and doesn't have that provider data already configured
|
||||
if (user.provider !== providerUserProfile.provider && (!user.additionalProvidersData || !user.additionalProvidersData[providerUserProfile.provider])) {
|
||||
// Add the provider data to the additional provider data field
|
||||
if (!user.additionalProvidersData) user.additionalProvidersData = {};
|
||||
user.additionalProvidersData[providerUserProfile.provider] = providerUserProfile.providerData;
|
||||
|
||||
// Then tell mongoose that we've updated the additionalProvidersData field
|
||||
user.markModified('additionalProvidersData');
|
||||
|
||||
// And save the user
|
||||
user.save(function(err) {
|
||||
return done(err, user, '/#!/settings/accounts');
|
||||
});
|
||||
} else {
|
||||
return done(new Error('User is already connected using this provider'), user);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove OAuth provider
|
||||
*/
|
||||
exports.removeOAuthProvider = function(req, res, next) {
|
||||
var user = req.user;
|
||||
var provider = req.param('provider');
|
||||
|
||||
if (user && provider) {
|
||||
// Delete the additional provider
|
||||
if (user.additionalProvidersData[provider]) {
|
||||
delete user.additionalProvidersData[provider];
|
||||
|
||||
// Then tell mongoose that we've updated the additionalProvidersData field
|
||||
user.markModified('additionalProvidersData');
|
||||
}
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.send(400, {
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.send(400, err);
|
||||
} else {
|
||||
res.jsonp(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -50,7 +50,7 @@ var UserSchema = new Schema({
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
unique: true,
|
||||
unique: 'testing error message',
|
||||
required: 'Please fill in a username',
|
||||
trim: true
|
||||
},
|
||||
|
||||
@@ -10,12 +10,12 @@ module.exports = function(app) {
|
||||
// Article Routes
|
||||
app.route('/articles')
|
||||
.get(articles.list)
|
||||
.post(users.requiresLogin, articles.create);
|
||||
.post(users.authorization.requiresLogin, articles.create);
|
||||
|
||||
app.route('/articles/:articleId')
|
||||
.get(articles.read)
|
||||
.put(users.requiresLogin, articles.hasAuthorization, articles.update)
|
||||
.delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
|
||||
.put(users.authorization.requiresLogin, articles.hasAuthorization, articles.update)
|
||||
.delete(users.authorization.requiresLogin, articles.hasAuthorization, articles.delete);
|
||||
|
||||
// Finish by binding the article middleware
|
||||
app.param('articleId', articles.articleByID);
|
||||
|
||||
@@ -8,28 +8,32 @@ var passport = require('passport');
|
||||
module.exports = function(app) {
|
||||
// User Routes
|
||||
var users = require('../../app/controllers/users');
|
||||
app.route('/users/me').get(users.me);
|
||||
app.route('/users').put(users.update);
|
||||
app.route('/users/password').post(users.changePassword);
|
||||
app.route('/users/accounts').delete(users.removeOAuthProvider);
|
||||
|
||||
// Setting up the users api
|
||||
app.route('/auth/signup').post(users.signup);
|
||||
app.route('/auth/signin').post(users.signin);
|
||||
app.route('/auth/signout').get(users.signout);
|
||||
app.route('/auth/forgot').post(users.forgot);
|
||||
app.route('/auth/reset/:token').get(users.resetGet);
|
||||
app.route('/auth/reset/:token').post(users.resetPost);
|
||||
// Setting up the users profile api
|
||||
app.route('/users/me').get(users.profile.me);
|
||||
app.route('/users').put(users.profile.update);
|
||||
app.route('/users/accounts').delete(users.profile.removeOAuthProvider);
|
||||
|
||||
// Setting up the users password api
|
||||
app.route('/users/password').post(users.password.changePassword);
|
||||
app.route('/auth/forgot').post(users.password.forgot);
|
||||
app.route('/auth/reset/:token').get(users.password.validateResetToken);
|
||||
app.route('/auth/reset/:token').post(users.password.reset);
|
||||
|
||||
// Setting up the users authentication api
|
||||
app.route('/auth/signup').post(users.authentication.signup);
|
||||
app.route('/auth/signin').post(users.authentication.signin);
|
||||
app.route('/auth/signout').get(users.authentication.signout);
|
||||
|
||||
// Setting the facebook oauth routes
|
||||
app.route('/auth/facebook').get(passport.authenticate('facebook', {
|
||||
scope: ['email']
|
||||
}));
|
||||
app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));
|
||||
app.route('/auth/facebook/callback').get(users.authentication.oauthCallback('facebook'));
|
||||
|
||||
// Setting the twitter oauth routes
|
||||
app.route('/auth/twitter').get(passport.authenticate('twitter'));
|
||||
app.route('/auth/twitter/callback').get(users.oauthCallback('twitter'));
|
||||
app.route('/auth/twitter/callback').get(users.authentication.oauthCallback('twitter'));
|
||||
|
||||
// Setting the google oauth routes
|
||||
app.route('/auth/google').get(passport.authenticate('google', {
|
||||
@@ -38,12 +42,12 @@ module.exports = function(app) {
|
||||
'https://www.googleapis.com/auth/userinfo.email'
|
||||
]
|
||||
}));
|
||||
app.route('/auth/google/callback').get(users.oauthCallback('google'));
|
||||
app.route('/auth/google/callback').get(users.authentication.oauthCallback('google'));
|
||||
|
||||
// Setting the linkedin oauth routes
|
||||
app.route('/auth/linkedin').get(passport.authenticate('linkedin'));
|
||||
app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin'));
|
||||
app.route('/auth/linkedin/callback').get(users.authentication.oauthCallback('linkedin'));
|
||||
|
||||
// Finish by binding the user middleware
|
||||
app.param('userId', users.userByID);
|
||||
app.param('userId', users.authorization.userByID);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Dear {{name}},</p>
|
||||
<p></p>
|
||||
<p>This is a confirmation that the password for your account has just been changed</p>
|
||||
<br>
|
||||
<br>
|
||||
<p>The {{appName}} Support Team</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
22
app/views/templates/reset-password-email.server.view.html
Normal file
22
app/views/templates/reset-password-email.server.view.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Dear {{name}},</p>
|
||||
<br>
|
||||
<p>
|
||||
You have requested to have your password reset for your account at {{appName}}
|
||||
</p>
|
||||
<p>Please visit this url to reset your password:</p>
|
||||
<p>{{url}}</p>
|
||||
<strong>If you didn't make this request, you can ignore this email.</strong>
|
||||
<br>
|
||||
<br>
|
||||
<p>The {{appName}} Support Team</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meanjs",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"description": "Fullstack JavaScript with MongoDB, Express, AngularJS, and Node.js.",
|
||||
"dependencies": {
|
||||
"bootstrap": "~3",
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports.getGlobbedFiles = function(globPatterns, removeRoot) {
|
||||
var _this = this;
|
||||
|
||||
// URL paths regex
|
||||
var urlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
|
||||
var urlRegex = new RegExp('^(?:[a-z]+:)?\/\/', 'i');
|
||||
|
||||
// The output array
|
||||
var output = [];
|
||||
|
||||
14
config/env/development.js
vendored
14
config/env/development.js
vendored
@@ -1,9 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var DB_HOST = process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost';
|
||||
|
||||
module.exports = {
|
||||
db: 'mongodb://' + DB_HOST + '/mean-dev',
|
||||
db: 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-dev',
|
||||
app: {
|
||||
title: 'MEAN.JS - Development Environment'
|
||||
},
|
||||
@@ -26,5 +24,15 @@ module.exports = {
|
||||
clientID: process.env.LINKEDIN_ID || 'APP_ID',
|
||||
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
|
||||
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
|
||||
},
|
||||
mailer: {
|
||||
fromEmail: process.env.MAILER_FROM_EMAIL || 'MAILER_FROM_EMAIL',
|
||||
options: {
|
||||
service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
|
||||
auth: {
|
||||
user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
|
||||
pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
14
config/env/production.js
vendored
14
config/env/production.js
vendored
@@ -1,9 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var DB_HOST = process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost';
|
||||
|
||||
module.exports = {
|
||||
db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + DB_HOST + '/mean',
|
||||
db: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean',
|
||||
assets: {
|
||||
lib: {
|
||||
css: [
|
||||
@@ -41,5 +39,15 @@ module.exports = {
|
||||
clientID: process.env.LINKEDIN_ID || 'APP_ID',
|
||||
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
|
||||
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
|
||||
},
|
||||
mailer: {
|
||||
fromEmail: process.env.MAILER_FROM_EMAIL || 'MAILER_FROM_EMAIL',
|
||||
options: {
|
||||
service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
|
||||
auth: {
|
||||
user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
|
||||
pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
14
config/env/test.js
vendored
14
config/env/test.js
vendored
@@ -1,9 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var DB_HOST = process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost';
|
||||
|
||||
module.exports = {
|
||||
db: 'mongodb://' + DB_HOST + '/mean-test',
|
||||
db: 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-test',
|
||||
port: 3001,
|
||||
app: {
|
||||
title: 'MEAN.JS - Test Environment'
|
||||
@@ -27,5 +25,15 @@ module.exports = {
|
||||
clientID: process.env.LINKEDIN_ID || 'APP_ID',
|
||||
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
|
||||
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
|
||||
},
|
||||
mailer: {
|
||||
fromEmail: process.env.MAILER_FROM_EMAIL || 'MAILER_FROM_EMAIL',
|
||||
options: {
|
||||
service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
|
||||
auth: {
|
||||
user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
|
||||
pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -73,7 +73,9 @@ module.exports = function(db) {
|
||||
}
|
||||
|
||||
// Request body parsing middleware should be above methodOverride
|
||||
app.use(bodyParser.urlencoded());
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
app.use(bodyParser.json());
|
||||
app.use(methodOverride());
|
||||
|
||||
@@ -85,6 +87,8 @@ module.exports = function(db) {
|
||||
|
||||
// Express MongoDB session storage
|
||||
app.use(session({
|
||||
saveUninitialized: true,
|
||||
resave: true,
|
||||
secret: config.sessionSecret,
|
||||
store: new mongoStore({
|
||||
db: db.connection.db,
|
||||
@@ -101,8 +105,8 @@ module.exports = function(db) {
|
||||
|
||||
// Use helmet to secure Express headers
|
||||
app.use(helmet.xframe());
|
||||
app.use(helmet.iexss());
|
||||
app.use(helmet.contentTypeOptions());
|
||||
app.use(helmet.xssFilter());
|
||||
app.use(helmet.nosniff());
|
||||
app.use(helmet.ienoopen());
|
||||
app.disable('x-powered-by');
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ module.exports = function() {
|
||||
console.log();
|
||||
if (!environmentFiles.length) {
|
||||
if (process.env.NODE_ENV) {
|
||||
console.log('\x1b[31m', 'No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead');
|
||||
console.error('\x1b[31m', 'No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead');
|
||||
} else {
|
||||
console.log('\x1b[31m', 'NODE_ENV is not defined! Using default development environment');
|
||||
console.error('\x1b[31m', 'NODE_ENV is not defined! Using default development environment');
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = function(grunt) {
|
||||
var watchFiles = {
|
||||
serverViews: ['app/views/**/*.*'],
|
||||
serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js'],
|
||||
clientViews: ['public/modules/**/views/*.html'],
|
||||
clientViews: ['public/modules/**/views/**/*.html'],
|
||||
clientJS: ['public/js/*.js', 'public/modules/**/*.js'],
|
||||
clientCSS: ['public/modules/**/*.css'],
|
||||
mochaTests: ['app/tests/**/*.js']
|
||||
|
||||
45
package.json
45
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meanjs",
|
||||
"description": "Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js.",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"private": false,
|
||||
"author": "https://github.com/meanjs/mean/graphs/contributors",
|
||||
"repository": {
|
||||
@@ -18,18 +18,18 @@
|
||||
"postinstall": "bower install --config.interactive=false"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "~4.2.0",
|
||||
"express-session": "~1.1.0",
|
||||
"body-parser": "~1.2.0",
|
||||
"cookie-parser": "~1.1.0",
|
||||
"compression": "~1.0.1",
|
||||
"method-override": "~1.0.0",
|
||||
"morgan": "~1.1.0",
|
||||
"connect-mongo": "~0.4.0",
|
||||
"express": "~4.7.2",
|
||||
"express-session": "~1.7.2",
|
||||
"body-parser": "~1.5.2",
|
||||
"cookie-parser": "~1.3.2",
|
||||
"compression": "~1.0.9",
|
||||
"method-override": "~2.1.2",
|
||||
"morgan": "~1.2.2",
|
||||
"connect-mongo": "~0.4.1",
|
||||
"connect-flash": "~0.1.1",
|
||||
"helmet": "~0.2.1",
|
||||
"helmet": "~0.4.0",
|
||||
"consolidate": "~0.10.0",
|
||||
"swig": "~1.3.2",
|
||||
"swig": "~1.4.1",
|
||||
"mongoose": "~3.8.8",
|
||||
"passport": "~0.2.0",
|
||||
"passport-local": "~1.0.0",
|
||||
@@ -39,29 +39,28 @@
|
||||
"passport-google-oauth": "~0.1.5",
|
||||
"lodash": "~2.4.1",
|
||||
"forever": "~0.11.0",
|
||||
"bower": "~1.3.1",
|
||||
"bower": "~1.3.8",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"glob": "~3.2.9",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"async": "~0.8.0",
|
||||
"nodemailer": "~0.6.3"
|
||||
"glob": "~4.0.5",
|
||||
"async": "~0.9.0",
|
||||
"nodemailer": "~1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"supertest": "~0.12.1",
|
||||
"should": "~3.3.1",
|
||||
"supertest": "~0.13.0",
|
||||
"should": "~4.0.4",
|
||||
"grunt-env": "~0.4.1",
|
||||
"grunt-node-inspector": "~0.1.3",
|
||||
"grunt-contrib-watch": "~0.6.1",
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-contrib-csslint": "^0.2.0",
|
||||
"grunt-ngmin": "0.0.3",
|
||||
"grunt-contrib-uglify": "~0.4.0",
|
||||
"grunt-contrib-cssmin": "~0.9.0",
|
||||
"grunt-nodemon": "~0.2.1",
|
||||
"grunt-contrib-uglify": "~0.5.1",
|
||||
"grunt-contrib-cssmin": "~0.10.0",
|
||||
"grunt-nodemon": "~0.3.0",
|
||||
"grunt-concurrent": "~0.5.0",
|
||||
"grunt-mocha-test": "~0.10.0",
|
||||
"grunt-mocha-test": "~0.11.0",
|
||||
"grunt-karma": "~0.8.2",
|
||||
"load-grunt-tasks": "~0.4.0",
|
||||
"load-grunt-tasks": "~0.6.0",
|
||||
"karma": "~0.12.0",
|
||||
"karma-jasmine": "~0.2.1",
|
||||
"karma-coverage": "~0.2.0",
|
||||
|
||||
@@ -7,9 +7,9 @@ var ApplicationConfiguration = (function() {
|
||||
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils'];
|
||||
|
||||
// Add a new vertical module
|
||||
var registerModule = function(moduleName) {
|
||||
var registerModule = function(moduleName, dependencies) {
|
||||
// Create angular module
|
||||
angular.module(moduleName, []);
|
||||
angular.module(moduleName, dependencies || []);
|
||||
|
||||
// Add the module to the AngularJS configuration file
|
||||
angular.module(applicationModuleName).requires.push(moduleName);
|
||||
|
||||
@@ -49,7 +49,7 @@ angular.module('articles').controller('ArticlesController', ['$scope', '$statePa
|
||||
$scope.error = errorResponse.data.message;
|
||||
});
|
||||
} else {
|
||||
scope.submitted = true;
|
||||
$scope.submitted = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,19 +19,27 @@ angular.module('users').config(['$stateProvider',
|
||||
}).
|
||||
state('signup', {
|
||||
url: '/signup',
|
||||
templateUrl: 'modules/users/views/signup.client.view.html'
|
||||
templateUrl: 'modules/users/views/authentication/signup.client.view.html'
|
||||
}).
|
||||
state('signin', {
|
||||
url: '/signin',
|
||||
templateUrl: 'modules/users/views/signin.client.view.html'
|
||||
templateUrl: 'modules/users/views/authentication/signin.client.view.html'
|
||||
}).
|
||||
state('forgot', {
|
||||
url: '/forgot',
|
||||
templateUrl: 'modules/users/views/forgot.client.view.html'
|
||||
url: '/password/forgot',
|
||||
templateUrl: 'modules/users/views/password/forgot-password.client.view.html'
|
||||
}).
|
||||
state('reset-invlaid', {
|
||||
url: '/password/reset/invalid',
|
||||
templateUrl: 'modules/users/views/password/reset-password-invalid.client.view.html'
|
||||
}).
|
||||
state('reset-success', {
|
||||
url: '/password/reset/success',
|
||||
templateUrl: 'modules/users/views/password/reset-password-success.client.view.html'
|
||||
}).
|
||||
state('reset', {
|
||||
url: '/reset/:token',
|
||||
templateUrl: 'modules/users/views/reset.client.view.html'
|
||||
url: '/password/reset/:token',
|
||||
templateUrl: 'modules/users/views/password/reset-password.client.view.html'
|
||||
});
|
||||
}
|
||||
]);
|
||||
@@ -1,19 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('users').controller('AuthenticationController', ['$scope', '$stateParams', '$http', '$location', 'Authentication',
|
||||
function($scope, $stateParams, $http, $location, Authentication) {
|
||||
$scope.authentication = Authentication;
|
||||
function($scope, $stateParams, $http, $location, Authentication) {
|
||||
$scope.authentication = Authentication;
|
||||
|
||||
//If user is signed in then redirect back home
|
||||
// If user is signed in then redirect back home
|
||||
if ($scope.authentication.user) $location.path('/');
|
||||
|
||||
$scope.signup = function(isValid) {
|
||||
if (isValid){
|
||||
if (isValid) {
|
||||
$http.post('/auth/signup', $scope.credentials).success(function(response) {
|
||||
//If successful we assign the response to the global user model
|
||||
// If successful we assign the response to the global user model
|
||||
$scope.authentication.user = response;
|
||||
|
||||
//And redirect to the index page
|
||||
|
||||
// And redirect to the index page
|
||||
$location.path('/');
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
@@ -25,45 +25,14 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$stat
|
||||
|
||||
$scope.signin = function() {
|
||||
$http.post('/auth/signin', $scope.credentials).success(function(response) {
|
||||
//If successful we assign the response to the global user model
|
||||
// If successful we assign the response to the global user model
|
||||
$scope.authentication.user = response;
|
||||
|
||||
//And redirect to the index page
|
||||
$location.path('/');
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.forgot = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/forgot', $scope.credentials).success(function(response) {
|
||||
// Show user success message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.success = response.message;
|
||||
|
||||
}).error(function(response) {
|
||||
// Show user error message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
// Change user password
|
||||
$scope.reset = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/reset/' + $stateParams.token,
|
||||
$scope.passwordDetails).success(function(response) {
|
||||
|
||||
// If successful show success message and clear form
|
||||
$scope.success = response.message;
|
||||
$scope.passwordDetails = null;
|
||||
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
}
|
||||
]);
|
||||
// And redirect to the index page
|
||||
$location.path('/');
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
}
|
||||
]);
|
||||
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$http', '$location', 'Authentication',
|
||||
function($scope, $stateParams, $http, $location, Authentication) {
|
||||
$scope.authentication = Authentication;
|
||||
|
||||
//If user is signed in then redirect back home
|
||||
if ($scope.authentication.user) $location.path('/');
|
||||
|
||||
// Submit forgotten password account id
|
||||
$scope.askForPasswordReset = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/forgot', $scope.credentials).success(function(response) {
|
||||
// Show user success message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.success = response.message;
|
||||
|
||||
}).error(function(response) {
|
||||
// Show user error message and clear form
|
||||
$scope.credentials = null;
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
|
||||
// Change user password
|
||||
$scope.resetUserPassword = function() {
|
||||
$scope.success = $scope.error = null;
|
||||
|
||||
$http.post('/auth/reset/' + $stateParams.token, $scope.passwordDetails).success(function(response) {
|
||||
// If successful show success message and clear form
|
||||
$scope.passwordDetails = null;
|
||||
|
||||
// Attach user profile
|
||||
Authentication.user = response;
|
||||
|
||||
// And redirect to the index page
|
||||
$location.path('/password/reset/success');
|
||||
}).error(function(response) {
|
||||
$scope.error = response.message;
|
||||
});
|
||||
};
|
||||
}
|
||||
]);
|
||||
@@ -31,7 +31,7 @@
|
||||
<a href="/#!/signup">Sign up</a>
|
||||
</div>
|
||||
<div class"forgot-password">
|
||||
<a href="/#!/forgot">Forgot your password?</a>
|
||||
<a href="/#!/password/forgot">Forgot your password?</a>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong data-ng-bind="error"></strong>
|
||||
@@ -1,11 +1,12 @@
|
||||
<section class="row" data-ng-controller="AuthenticationController">
|
||||
<h3 class="col-md-12 text-center">Forgot your password?</h3>
|
||||
<section class="row" data-ng-controller="PasswordController">
|
||||
<h3 class="col-md-12 text-center">Restore your password</h3>
|
||||
<p class="small text-center">Enter your account username.</p>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
|
||||
<form data-ng-submit="forgot()" class="signin form-horizontal" autocomplete="off">
|
||||
<form data-ng-submit="askForPasswordReset()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="email" name="email" class="form-control" data-ng-model="credentials.email" placeholder="Account Email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="credentials.username" placeholder="Username">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<section class="row text-center">
|
||||
<h3 class="col-md-12">Password reset is invalid</h3>
|
||||
<a href="/#!/password/forgot" class="col-md-12">Ask for a new password reset</a>
|
||||
</section>
|
||||
@@ -0,0 +1,4 @@
|
||||
<section class="row text-center">
|
||||
<h3 class="col-md-12">Password successfully reset</h3>
|
||||
<a href="/#!/" class="col-md-12">Continue to home page</a>
|
||||
</section>
|
||||
@@ -1,7 +1,7 @@
|
||||
<section class="row" data-ng-controller="AuthenticationController">
|
||||
<section class="row" data-ng-controller="PasswordController">
|
||||
<h3 class="col-md-12 text-center">Reset your password</h3>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
|
||||
<form data-ng-submit="reset()" class="signin form-horizontal" autocomplete="off">
|
||||
<form data-ng-submit="resetUserPassword()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="newPassword">New Password</label>
|
||||
29
server.js
29
server.js
@@ -12,19 +12,24 @@ var init = require('./config/init')(),
|
||||
*/
|
||||
|
||||
// Bootstrap db connection
|
||||
var db = mongoose.connect(config.db);
|
||||
var db = mongoose.connect(config.db, function(err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
console.error('\x1b[31m', 'Could not connect to MongoDB!');
|
||||
} else {
|
||||
// Init the express application
|
||||
var app = require('./config/express')(db);
|
||||
|
||||
// Init the express application
|
||||
var app = require('./config/express')(db);
|
||||
// Bootstrap passport config
|
||||
require('./config/passport')();
|
||||
|
||||
// Bootstrap passport config
|
||||
require('./config/passport')();
|
||||
// Start the app by listening on <port>
|
||||
app.listen(config.port);
|
||||
|
||||
// Start the app by listening on <port>
|
||||
app.listen(config.port);
|
||||
// Expose app
|
||||
exports = module.exports = app;
|
||||
|
||||
// Expose app
|
||||
exports = module.exports = app;
|
||||
|
||||
// Logging initialization
|
||||
console.log('MEAN.JS application started on port ' + config.port);
|
||||
// Logging initialization
|
||||
console.log('MEAN.JS application started on port ' + config.port);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user