diff --git a/app/public/js/controllers/activatedController.js b/app/public/js/controllers/activatedController.js new file mode 100644 index 00000000..f0493dd4 --- /dev/null +++ b/app/public/js/controllers/activatedController.js @@ -0,0 +1,7 @@ + +function ActivatedController() +{ + +// redirect to homepage on account activation, add short delay so user can read alert window // + $('.modal-alert #ok').click(function(){ setTimeout(function(){window.location.href = '/';}, 300)}); +} \ No newline at end of file diff --git a/app/public/js/form-validators/accountValidator.js b/app/public/js/form-validators/accountValidator.js index 6de2dfcf..62e99528 100644 --- a/app/public/js/form-validators/accountValidator.js +++ b/app/public/js/form-validators/accountValidator.js @@ -3,8 +3,8 @@ function AccountValidator(){ // build array maps of the form inputs & control groups // - this.formFields = [$('#name-tf'), $('#email-tf'), $('#user-tf'), $('#pass-tf')]; - this.controlGroups = [$('#name-cg'), $('#email-cg'), $('#user-cg'), $('#pass-cg')]; + this.formFields = [$('#name-tf'), $('#email-tf'), $('#user-tf'), $('#pass-tf'), $('#pass-confirm-tf')]; + this.controlGroups = [$('#name-cg'), $('#email-cg'), $('#user-cg'), $('#pass-cg'), $('#pass-confirm-cg')]; // bind the form-error modal window to this controller to display any errors // @@ -73,6 +73,10 @@ AccountValidator.prototype.validateForm = function() this.controlGroups[3].addClass('error'); e.push('Password Should Be At Least 6 Characters'); } + if (this.formFields[4].val() != this.formFields[3].val()) { + this.controlGroups[4].addClass('error'); + e.push('Passwords don\'t match'); + } if (e.length) this.showErrors(e); return e.length === 0; } diff --git a/app/public/js/views/activated.js b/app/public/js/views/activated.js new file mode 100644 index 00000000..19c4bcef --- /dev/null +++ b/app/public/js/views/activated.js @@ -0,0 +1,13 @@ + +$(document).ready(function(){ + + var ac = new ActivatedController(); + + $('.modal-alert').modal('show'); +// setup the alert that displays when an account has been activated // + + $('.modal-alert').modal({ show : false, keyboard : false, backdrop : 'static' }); + $('.modal-alert .modal-header h3').text('Activated!'); + $('.modal-alert .modal-body p').html('Your account has been activated.
Click OK to return to the login page.'); + +}) \ No newline at end of file diff --git a/app/public/js/views/activation-failed.js b/app/public/js/views/activation-failed.js new file mode 100644 index 00000000..4e201674 --- /dev/null +++ b/app/public/js/views/activation-failed.js @@ -0,0 +1,13 @@ + +$(document).ready(function(){ + + var ac = new ActivatedController(); + + $('.modal-alert').modal('show'); +// setup the alert that displays when an account has not been activated // + + $('.modal-alert').modal({ show : false, keyboard : false, backdrop : 'static' }); + $('.modal-alert .modal-header h3').text('Activation Failed.'); + $('.modal-alert .modal-body p').html('The activation code provided was invalid.
Click OK to return to the login page.'); + +}) \ No newline at end of file diff --git a/app/public/js/views/signup.js b/app/public/js/views/signup.js index 52de1978..25ace8fb 100644 --- a/app/public/js/views/signup.js +++ b/app/public/js/views/signup.js @@ -34,6 +34,6 @@ $(document).ready(function(){ $('.modal-alert').modal({ show : false, keyboard : false, backdrop : 'static' }); $('.modal-alert .modal-header h3').text('Success!'); - $('.modal-alert .modal-body p').html('Your account has been created.
Click OK to return to the login page.'); + $('.modal-alert .modal-body p').html('Please check your email for a verification link which will confirm your email address.
Click OK to return to the login page.'); }) \ No newline at end of file diff --git a/app/server/modules/account-manager.js b/app/server/modules/account-manager.js index 71ea3f8d..0f1b9792 100644 --- a/app/server/modules/account-manager.js +++ b/app/server/modules/account-manager.js @@ -24,7 +24,7 @@ var accounts = db.collection('accounts'); exports.autoLogin = function(user, pass, callback) { - accounts.findOne({user:user}, function(e, o) { + accounts.findOne({user:user, activated:true}, function(e, o) { if (o){ o.pass == pass ? callback(o) : callback(null); } else{ @@ -35,7 +35,7 @@ exports.autoLogin = function(user, pass, callback) exports.manualLogin = function(user, pass, callback) { - accounts.findOne({user:user}, function(e, o) { + accounts.findOne({user:user, activated:true}, function(e, o) { if (o == null){ callback('user-not-found'); } else{ @@ -64,9 +64,19 @@ exports.addNewAccount = function(newData, callback) } else{ saltAndHash(newData.pass, function(hash){ newData.pass = hash; - // append date stamp when record was created // - newData.date = moment().format('MMMM Do YYYY, h:mm:ss a'); - accounts.insert(newData, {safe: true}, callback); + saltAndHash(newData.user, function(hash){ + newData.activationCode = hash; + newData.activated = false; + // append date stamp when record was created // + newData.date = moment().format('MMMM Do YYYY, h:mm:ss a'); + accounts.insert(newData, {safe: true}, function(e,o){ + if (o.length > 0){ + callback(e,o[0]); + } else{ + callback(e, null); + } + }); + }); }); } }); @@ -74,9 +84,23 @@ exports.addNewAccount = function(newData, callback) }); } +exports.activateAccount = function(activationCode, callback) +{ + accounts.findOne({activationCode:activationCode, activated:false}, function(e, o) { + if(!o){ + callback('invalid-activation-code', null); + } else{ + o.activated = true; + accounts.save(o, {safe: true}, function(err, o){ + callback(err, o); + }); + } + }); +} + exports.updateAccount = function(newData, callback) { - accounts.findOne({user:newData.user}, function(e, o){ + accounts.findOne({user:newData.user, activated:true}, function(e, o){ o.name = newData.name; o.email = newData.email; o.country = newData.country; @@ -151,10 +175,10 @@ var md5 = function(str) { return crypto.createHash('md5').update(str).digest('hex'); } -var saltAndHash = function(pass, callback) +var saltAndHash = function(data, callback) { var salt = generateSalt(); - callback(salt + md5(pass + salt)); + callback(salt + md5(data + salt)); } var validatePassword = function(plainPass, hashedPass, callback) diff --git a/app/server/modules/email-dispatcher.js b/app/server/modules/email-dispatcher.js index 3918d0bb..ca5ab5bc 100644 --- a/app/server/modules/email-dispatcher.js +++ b/app/server/modules/email-dispatcher.js @@ -19,11 +19,11 @@ EM.dispatchResetPasswordLink = function(account, callback) to : account.email, subject : 'Password Reset', text : 'something went wrong... :(', - attachment : EM.composeEmail(account) + attachment : EM.composePasswordResetEmail(account) }, callback ); } -EM.composeEmail = function(o) +EM.composePasswordResetEmail = function(o) { var link = 'http://node-login.braitsch.io/reset-password?e='+o.email+'&p='+o.pass; var html = ""; @@ -34,4 +34,28 @@ EM.composeEmail = function(o) html += "braitsch

"; html += ""; return [{data:html, alternative:true}]; +} + +EM.dispatchActivationLink = function(account, callback) +{ + EM.server.send({ + from : ES.sender, + to : account.email, + subject : 'Account Activation', + text : 'something went wrong... :(', + attachment : EM.composeActivationEmail(account) + }, callback ); +} + +EM.composeActivationEmail = function(o) +{ + var link = 'http://node-login.braitsch.io/activation?c='+o.activationCode; + var html = ""; + html += "Hi "+o.name+",

"; + html += "Your username is :: "+o.user+"

"; + html += "Please click here to activate your account

"; + html += "Cheers,
"; + html += "braitsch

"; + html += ""; + return [{data:html, alternative:true}]; } \ No newline at end of file diff --git a/app/server/router.js b/app/server/router.js index 1ba5fd2e..e094e6a9 100644 --- a/app/server/router.js +++ b/app/server/router.js @@ -95,15 +95,42 @@ module.exports = function(app) { user : req.param('user'), pass : req.param('pass'), country : req.param('country') - }, function(e){ + }, function(e,o){ if (e){ res.send(e, 400); } else{ res.send('ok', 200); + EM.dispatchActivationLink(o, function(e, m){ + // this callback takes a moment to return // + // should add an ajax loader to give user feedback // + if (!e) { + // res.send('ok', 200); + } else{ + res.send('email-server-error', 400); + for (k in e) console.log('error : ', k, e[k]); + } + }); } }); }); +// activating account // + + app.get('/activation', function(req, res) { + if (req.param('c', null)) + { + AM.activateAccount(req.param('c'),function(e, o){ + if (o){ + res.render('activated', { title: 'Activation'}); + } else{ + res.render('activation-failed', { title: 'Activation' }); + } + }); + } else{ + res.send("activation code required", 400); + } + }); + // password reset // app.post('/lost-password', function(req, res){ diff --git a/app/server/views/account.jade b/app/server/views/account.jade index ee7b5ce8..81df7766 100644 --- a/app/server/views/account.jade +++ b/app/server/views/account.jade @@ -34,6 +34,10 @@ input(type='hidden', value= user._id)#userId label.control-label(for='pass-tf') Password .controls input#pass-tf.input-xlarge(type='password', name='pass', value='') + #pass-confirm-cg.control-group + label.control-label(for='pass-confirm-tf') Confirm + .controls + input#pass-confirm-tf.input-xlarge(type='password', name='pass-confirm', value='') .form-actions button(type='button')#account-form-btn1.btn button(type='submit')#account-form-btn2.btn diff --git a/app/server/views/activated.jade b/app/server/views/activated.jade new file mode 100644 index 00000000..6afb23ae --- /dev/null +++ b/app/server/views/activated.jade @@ -0,0 +1,13 @@ + +extends layout + +block content + include modals/alert + +block scripts + script(src='/vendor/jquery.min.js') + script(src='/vendor/jquery.form.js') + script(src='/vendor/bootstrap-modal.js') + script(src='/vendor/bootstrap-transition.js') + script(src='/js/views/activated.js') + script(src='/js/controllers/activatedController.js') diff --git a/app/server/views/activation-failed.jade b/app/server/views/activation-failed.jade new file mode 100644 index 00000000..44ef0609 --- /dev/null +++ b/app/server/views/activation-failed.jade @@ -0,0 +1,13 @@ + +extends layout + +block content + include modals/alert + +block scripts + script(src='/vendor/jquery.min.js') + script(src='/vendor/jquery.form.js') + script(src='/vendor/bootstrap-modal.js') + script(src='/vendor/bootstrap-transition.js') + script(src='/js/views/activation-failed.js') + script(src='/js/controllers/activatedController.js')