Repaired email sending, implement password reset

Closes #288
* I use SendGrid for sending the emails, and it works fine (provided you supply the correct credentials in `config.mail` in `config.js`)
* Generates a random 12 char long alphanumeric password, replaces user's pw, and sends an email about it.
This commit is contained in:
Gabor Javorszky 2013-09-01 00:20:12 +02:00
parent 56619a87f8
commit 5999d01b7d
10 changed files with 148 additions and 20 deletions

View File

@ -21,6 +21,19 @@ config.activePlugins = [
'FancyFirstChar'
];
config.mail = {
transport: 'sendgrid',
host: 'smtp.sendgrid.net',
options: {
service: 'Sendgrid',
auth: {
user: '', // Super secret username
pass: '' // Super secret password
}
}
};
// ## Default Navigation Items
// Add new objects here to extend the menu output by {{nav}}
config.nav = [
@ -107,4 +120,4 @@ config.env = {
};
// Export config
module.exports = config;
module.exports = config;

View File

@ -14,7 +14,8 @@
'debug/' : 'debug',
'register/' : 'register',
'signup/' : 'signup',
'signin/' : 'login'
'signin/' : 'login',
'forgotten/' : 'forgotten'
},
signup: function () {
@ -25,6 +26,10 @@
Ghost.currentView = new Ghost.Views.Login({ el: '.js-login-container' });
},
forgotten: function () {
Ghost.currentView = new Ghost.Views.Forgotten({ el: '.js-login-container' });
},
blog: function () {
var posts = new Ghost.Collections.Posts();
posts.fetch({ data: { status: 'all', orderBy: ['updated_at', 'DESC'] } }).then(function () {

View File

@ -0,0 +1,9 @@
<form id="forgotten" method="post" novalidate="novalidate">
<div class="email-wrap">
<input class="email" type="email" placeholder="Email Address" name="email" autocapitalize="off" autocorrect="off">
</div>
<button class="button-save" type="submit">Send new password</button>
<section class="meta">
<a href="/ghost/login/">Log in</a>
</section>
</form>

View File

@ -7,6 +7,6 @@
</div>
<button class="button-save" type="submit">Log in</button>
<section class="meta">
<a class="forgotten-password" href="#">Forgotten password?</a> &bull; <a href="/ghost/signup/">Register new user</a>
<a class="forgotten-password" href="/ghost/forgotten/">Forgotten password?</a> &bull; <a href="/ghost/signup/">Register new user</a>
</section>
</form>

View File

@ -109,4 +109,38 @@
});
}
});
Ghost.Views.Forgotten = Ghost.SimpleFormView.extend({
templateName: "forgotten",
events: {
'submit #forgotten': 'submitHandler'
},
submitHandler: function (event) {
event.preventDefault();
var email = this.$el.find('.email').val();
$.ajax({
url: '/ghost/forgotten/',
type: 'POST',
data: {
email: email
},
success: function (msg) {
window.location.href = msg.redirect;
},
error: function (xhr) {
Ghost.notifications.addItem({
type: 'error',
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
status: 'passive'
});
}
});
}
});
}());

View File

@ -141,6 +141,10 @@ users = {
changePassword: function changePassword(userData) {
// **returns:** on success, returns a promise for the resulting user in a json object
return dataProvider.User.changePassword(userData);
},
forgottenPassword: function forgottenPassword(email) {
return dataProvider.User.forgottenPassword(email);
}
};

View File

@ -88,7 +88,7 @@ adminControllers = {
}
},
'login': function (req, res) {
res.render('login', {
res.render('signup', {
bodyClass: 'ghost-login',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
@ -138,6 +138,7 @@ adminControllers = {
adminNav: setSelected(adminNavbar, 'login')
});
},
'doRegister': function (req, res) {
var email = req.body.email,
password = req.body.password;
@ -146,6 +147,9 @@ adminControllers = {
email_address: email,
password: password
}).then(function (user) {
ghost.mail.sendWelcomeMessage({email: user.attributes.email_address});
if (req.session.user === undefined) {
req.session.user = user.id;
}
@ -155,6 +159,43 @@ adminControllers = {
});
},
'forgotten': function (req, res) {
res.render('signup', {
bodyClass: 'ghost-forgotten',
hideNavbar: true,
adminNav: setSelected(adminNavbar, 'login')
});
},
'resetPassword': function (req, res) {
var email = req.body.email;
api.users.forgottenPassword(email).then(function (user) {
var message = {
to: email,
subject: 'Your new password',
html: "<p><strong>Hello!</strong></p>" +
"<p>You've reset your password. Here's the new one: " + user.newPassword + "</p>"
},
notification = {
type: 'success',
message: 'Your password was changed successfully. Check your email for details.',
status: 'passive',
id: 'successresetpw'
};
ghost.mail.send(message);
// let's only add the notification once
if (!_.contains(_.pluck(ghost.notifications, 'id'), 'successresetpw')) {
ghost.notifications.push(notification);
}
res.json(200, {redirect: '/ghost/login/'});
}, function (error) {
res.json(401, {error: error.message});
});
},
'logout': function (req, res) {
delete req.session.user;
var msg = {

View File

@ -98,7 +98,7 @@ GhostMailer.prototype.send = function (message) {
var settings = this.ghost.settings(),
from = 'ghost-mailer@' + url.parse(settings.url).hostname,
to = settings.email,
to = message.to || settings.email,
sendMail = nodefn.lift(this.transport.sendMail.bind(this.transport));
message = _.extend(message, {
@ -110,10 +110,13 @@ GhostMailer.prototype.send = function (message) {
return sendMail(message);
};
GhostMailer.prototype.sendWelcomeMessage = function () {
GhostMailer.prototype.sendWelcomeMessage = function (opts) {
var adminURL = this.ghost.settings().url + "/ghost";
opts = opts || {};
opts.email = opts.email || this.ghost.settings().email;
return this.send({
to: opts.email,
subject: "Welcome to Ghost",
html: "<p><strong>Hello!</strong></p>" +
"<p>Welcome to the Ghost platform.</p>" +

View File

@ -115,11 +115,11 @@ User = GhostBookshelf.Model.extend({
}).then(function (addedUser) {
// Assign the userData to our created user so we can pass it back
userData = addedUser;
// Add this user to the admin role (assumes admin = role_id: 1)
return UserRole.add({role_id: 1, user_id: addedUser.id});
}).then(function (addedUserRole) {
// Return the added user as expected
return when.resolve(userData);
});
@ -168,7 +168,9 @@ User = GhostBookshelf.Model.extend({
userid = _userdata.currentUser,
oldPassword = _userdata.oldpw,
newPassword = _userdata.newpw,
ne2Password = _userdata.ne2pw;
ne2Password = _userdata.ne2pw,
user = null;
if (newPassword !== ne2Password) {
return when.reject(new Error('Your new passwords do not match'));
@ -176,19 +178,34 @@ User = GhostBookshelf.Model.extend({
return validatePasswordLength(newPassword).then(function () {
return self.forge({id: userid}).fetch({require: true});
}).then(function (user) {
return nodefn.call(bcrypt.compare, oldPassword, user.get('password'))
.then(function (matched) {
if (!matched) {
return when.reject(new Error('Your password is incorrect'));
}
return nodefn.call(bcrypt.hash, newPassword, null, null).then(function (hash) {
user.save({password: hash});
return user;
});
});
});
}).then(function (_user) {
user = _user;
return nodefn.call(bcrypt.compare, oldPassword, user.get('password'));
}).then(function (matched) {
if (!matched) {
return when.reject(new Error('Your password is incorrect'));
}
return nodefn.call(bcrypt.hash, newPassword, null, null);
}).then(function (hash) {
user.save({password: hash});
return user;
});
},
forgottenPassword: function (email) {
var newPassword = Math.random().toString(36).slice(2, 12), // This is magick
user = null;
return this.forge({email_address: email}).fetch({require: true}).then(function (_user) {
user = _user;
return nodefn.call(bcrypt.hash, newPassword, null, null);
}).then(function (hash) {
user.save({password: hash});
return { user: user, newPassword: newPassword };
}, function (error) {
return when.reject(new Error('There is no user by that email address. Check again.'));
});
},
effectivePermissions: function (id) {

View File

@ -219,6 +219,8 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
});
ghost.app().get('/ghost/signin/', redirectToDashboard, admin.login);
ghost.app().get('/ghost/signup/', redirectToDashboard, admin.signup);
ghost.app().get('/ghost/forgotten/', redirectToDashboard, admin.forgotten);
ghost.app().post('/ghost/forgotten/', admin.resetPassword);
ghost.app().post('/ghost/signin/', admin.auth);
ghost.app().post('/ghost/signup/', admin.doRegister);
ghost.app().post('/ghost/changepw/', auth, admin.changepw);