Added validation for signup and login screens

Closes #374
* Included node-validator as a package
* Implemented server side validation (the client side js is a mess, need a LOT of work)
* Validates email address both on signup and login screens, gives error message on malformed email addresses
* Requires at least 8 chars of password
* Tells user if password is too short
* Tells user if no such user on login
* Tells user if wrong password on login
* Tells user if server responds with a 404 (goes away, dies, etc)
* Added middleware between req and login / signup for validation
This commit is contained in:
Gabor Javorszky 2013-08-18 22:50:42 +01:00
parent 3b6b5a085d
commit be7ed2dfdc
6 changed files with 67 additions and 31 deletions

View File

@ -45,7 +45,8 @@
submitHandler: function (event) { submitHandler: function (event) {
event.preventDefault(); event.preventDefault();
var email = this.$el.find('.email').val(), var email = this.$el.find('.email').val(),
password = this.$el.find('.password').val(); password = this.$el.find('.password').val(),
self = this;
$.ajax({ $.ajax({
url: '/ghost/login/', url: '/ghost/login/',
@ -60,7 +61,7 @@
error: function (obj, string, status) { error: function (obj, string, status) {
Ghost.notifications.addItem({ Ghost.notifications.addItem({
type: 'error', type: 'error',
message: obj.responseText, message: self.getRequestErrorMessage(obj),
status: 'passive' status: 'passive'
}); });
} }
@ -79,7 +80,8 @@
submitHandler: function (event) { submitHandler: function (event) {
event.preventDefault(); event.preventDefault();
var email = this.$el.find('.email').val(), var email = this.$el.find('.email').val(),
password = this.$el.find('.password').val(); password = this.$el.find('.password').val(),
self = this;
$.ajax({ $.ajax({
url: '/ghost/signup/', url: '/ghost/signup/',
@ -92,10 +94,9 @@
window.location.href = msg.redirect; window.location.href = msg.redirect;
}, },
error: function (obj, string, status) { error: function (obj, string, status) {
var msgobj = $.parseJSON(obj.responseText);
Ghost.notifications.addItem({ Ghost.notifications.addItem({
type: 'error', type: 'error',
message: msgobj.message, message: self.getRequestErrorMessage(obj),
status: 'passive' status: 'passive'
}); });
} }

View File

@ -102,10 +102,11 @@
}); });
}, },
saveError: function () { saveError: function (message) {
message = message || 'Something went wrong, not saved :(';
Ghost.notifications.addItem({ Ghost.notifications.addItem({
type: 'error', type: 'error',
message: 'Something went wrong, not saved :(', message: message,
status: 'passive' status: 'passive'
}); });
} }
@ -208,8 +209,13 @@
newPassword = this.$('#user-password-new').val(), newPassword = this.$('#user-password-new').val(),
ne2Password = this.$('#user-new-password-verification').val(); ne2Password = this.$('#user-new-password-verification').val();
if (newPassword !== ne2Password || newPassword.length < 6 || oldPassword.length < 6) { if (newPassword !== ne2Password) {
this.saveError(); this.saveError('The passwords do not match');
return;
}
if (newPassword.length < 8) {
this.saveError('The password is not long enough. Have at least 8 characters');
return; return;
} }

View File

@ -97,7 +97,7 @@ adminControllers = {
req.session.user = user.id; req.session.user = user.id;
res.json(200, {redirect: req.query.r ? '/ghost/' + req.query.r : '/ghost/'}); res.json(200, {redirect: req.query.r ? '/ghost/' + req.query.r : '/ghost/'});
}, function (error) { }, function (error) {
res.send(401, error.message); res.json(401, {error: error.message});
}); });
}, },
changepw: function (req, res) { changepw: function (req, res) {
@ -124,22 +124,18 @@ adminControllers = {
var email = req.body.email, var email = req.body.email,
password = req.body.password; password = req.body.password;
if (email !== '' && password.length > 5) { api.users.add({
api.users.add({ email_address: email,
email_address: email, password: password
password: password }).then(function (user) {
}).then(function (user) { if (req.session.user === undefined) {
// Automatically log the new user in, unless created by an existing account req.session.user = user.id;
if (req.session.user === undefined) { }
req.session.user = user.id; res.json(200, {redirect: '/ghost/'});
} }, function (error) {
res.json(200, {redirect: '/ghost/'}); res.json(401, {error: error.message});
}, function (error) { });
res.json(401, {message: error.message});
});
} else {
res.json(400, {message: 'The password is too short. Have at least 6 characters in there'});
}
}, },
'logout': function (req, res) { 'logout': function (req, res) {
delete req.session.user; delete req.session.user;

View File

@ -85,7 +85,7 @@ User = GhostBookshelf.Model.extend({
}).then(function (addedUserRole) { }).then(function (addedUserRole) {
// Return the added user as expected // Return the added user as expected
return when.resolve(userData); return when.resolve(userData);
}, errors.logAndThrowError); });
/** /**
* Temporarily replacing the function below with another one that checks * Temporarily replacing the function below with another one that checks
@ -118,7 +118,7 @@ User = GhostBookshelf.Model.extend({
return user; return user;
}, errors.logAndThrowError); }, errors.logAndThrowError);
}, function (error) { }, function (error) {
return when.reject(new Error('Email address or password is incorrect')); return when.reject(new Error('There is no user with that email address.'));
}); });
}, },

View File

@ -20,11 +20,16 @@ var express = require('express'),
filters = require('./core/server/filters'), filters = require('./core/server/filters'),
helpers = require('./core/server/helpers'), helpers = require('./core/server/helpers'),
packageInfo = require('./package.json'), packageInfo = require('./package.json'),
Validator = require('validator').Validator,
v = new Validator(),
// Variables // Variables
loading = when.defer(), loading = when.defer(),
ghost = new Ghost(); ghost = new Ghost();
v.error = function () {
return false;
};
// ##Custom Middleware // ##Custom Middleware
@ -45,6 +50,33 @@ function auth(req, res, next) {
next(); next();
} }
/**
* Validation middleware
* Checks on signup whether email is actually a valid email address
* and if password is at least 8 characters long
*
* To change validation rules, see https://github.com/chriso/node-validator
*
* @author javorszky
* @issue https://github.com/TryGhost/Ghost/issues/374
*/
function signupValidate(req, res, next) {
var email = req.body.email,
password = req.body.password;
if (!v.check(email).isEmail()) {
res.json(401, {error: "Please check your email address. It does not seem to be valid."});
return;
}
if (!v.check(password).len(8)) {
res.json(401, {error: 'Your password is not long enough. It must be at least 8 chars long.'});
return;
}
next();
}
// ## AuthApi Middleware // ## AuthApi Middleware
// Authenticate a request to the API by responding with a 401 and json error details // Authenticate a request to the API by responding with a 401 and json error details
function authAPI(req, res, next) { function authAPI(req, res, next) {
@ -185,8 +217,8 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
ghost.app().get(/^\/logout\/?$/, admin.logout); ghost.app().get(/^\/logout\/?$/, admin.logout);
ghost.app().get('/ghost/login/', admin.login); ghost.app().get('/ghost/login/', admin.login);
ghost.app().get('/ghost/signup/', admin.signup); ghost.app().get('/ghost/signup/', admin.signup);
ghost.app().post('/ghost/login/', admin.auth); ghost.app().post('/ghost/login/', signupValidate, admin.auth);
ghost.app().post('/ghost/signup/', admin.doRegister); ghost.app().post('/ghost/signup/', signupValidate, admin.doRegister);
ghost.app().post('/ghost/changepw/', auth, admin.changepw); ghost.app().post('/ghost/changepw/', auth, admin.changepw);
ghost.app().get('/ghost/editor/:id', auth, admin.editor); ghost.app().get('/ghost/editor/:id', auth, admin.editor);
ghost.app().get('/ghost/editor', auth, admin.editor); ghost.app().get('/ghost/editor', auth, admin.editor);

View File

@ -27,7 +27,8 @@
"colors": "0.6.1", "colors": "0.6.1",
"semver": "2.1.0", "semver": "2.1.0",
"fs-extra": "0.6.3", "fs-extra": "0.6.3",
"downsize": "0.0.2" "downsize": "0.0.2",
"validator": "1.4.0"
}, },
"devDependencies": { "devDependencies": {
"grunt": "~0.4.1", "grunt": "~0.4.1",