From: Luigi Pinca Date: Sun, 1 Jul 2012 08:00:28 +0000 (+0200) Subject: added ability for registered users to reset their forgotten password X-Git-Url: https://git.saalbach.dev/?a=commitdiff_plain;h=b99e88d817f8e7f6451ff40126a04c598cc4e536;p=binbsis50.git added ability for registered users to reset their forgotten password --- diff --git a/app.js b/app.js index e092453..48fec1c 100644 --- a/app.js +++ b/app.js @@ -14,8 +14,8 @@ var config = require('./config') * Setting up redis. */ -var songsdb = redisurl.createClient(config.songsdburl); -var usersdb = redisurl.createClient(config.usersdburl); +var songsdb = redisurl.createClient(config.songsdburl) + , usersdb = redisurl.createClient(config.usersdburl); songsdb.on('error', function(err) { console.log(err.message); @@ -56,7 +56,7 @@ app.dynamicHelpers({ // Routes site.use({db:songsdb,rooms:config.rooms}); -user.use({db:usersdb,rooms:config.rooms}); +user.use({db:usersdb,rooms:config.rooms,sendgrid:config.sendgrid}); app.get('/', site.index); app.get('/artworks', site.artworks); @@ -68,6 +68,10 @@ app.post('/login', user.validateLogin, user.checkUser, user.authenticate); app.get('/logout', user.logout); app.get('/signup', site.signup); app.post('/signup', user.validateSignUp, user.userExists, user.emailExists, user.createAccount); +app.get('/recoverpasswd', site.recoverPasswd); +app.post('/recoverpasswd', user.validateRecoverPasswd, user.sendEmail); +app.get('/resetpasswd', site.resetPasswd); +app.post('/resetpasswd', user.resetPasswd); app.get('/:room', site.room); app.get('/user/*', user.profile); @@ -135,7 +139,7 @@ io.sockets.on('connection', function(socket) { } }); socket.on('joinanonymously', function(nickname, roomname) { - if (!socket.nickname && typeof nickname === 'string' && nickname !== '' && + if (!socket.nickname && typeof nickname === 'string' && nickname !== '' && typeof roomname === 'string' && config.rooms.indexOf(roomname) !== -1) { rooms[roomname].setNickName(socket, nickname); } diff --git a/config.json b/config.json index 3d08ff1..827a469 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,10 @@ "songsdburl": "", "usersdburl": "", "sessionsecret": "", + "sendgrid": { + "user": "", + "pass": "" + }, "songsinarun": 15, "gameswithnorepeats": 3, "allowederrors": 2, diff --git a/lib/email/mailer.js b/lib/email/mailer.js new file mode 100644 index 0000000..516ef30 --- /dev/null +++ b/lib/email/mailer.js @@ -0,0 +1,57 @@ +/** + * Module dependencies. + */ + +var fs = require('fs') + , jade = require('jade') + , nodemailer = require('nodemailer') + , jadetemplate = fs.readFileSync(__dirname + '/template.jade') + , texttemplate = fs.readFileSync(__dirname + '/template.txt', 'utf-8') + , transport; + +/** + * Generate the HTML version of the message. + */ + +var HTMLMessage = jade.compile(jadetemplate); + +/** + * Generate the plaintext version of the message. + */ + +var plaintextMessage = function(token) { + return texttemplate.replace(//, token); +}; + +/** + * Send the reset password email. + */ + +exports.sendEmail = function(to, token, callback) { + transport.sendMail({ + from: 'binb ', + to: to, + subject: 'binb password recovery', + html: HTMLMessage({token:token}), + text: plaintextMessage(token) + }, function(err, response) { + if(err) { + return callback(err); + } + callback(null, response); + }); +}; + +/** + * Create a reusable transport method. + */ + +exports.setTransport = function(sendgrid) { + transport = nodemailer.createTransport ('SMTP', { + service: 'SendGrid', + auth: { + user: sendgrid.user, + pass: sendgrid.pass + } + }); +}; diff --git a/lib/email/template.jade b/lib/email/template.jade new file mode 100644 index 0000000..99041e0 --- /dev/null +++ b/lib/email/template.jade @@ -0,0 +1,22 @@ +doctype html +html + head + meta(charset="UTF-8") + title binb password recovery + body(style="font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;") + div(style="width:600px;margin:0 auto;border:3px solid #CCC;border-radius:5px;background-color:#F0F0F0;") + div(style="padding:16px 16px 0;font-size:15px;color:#505050;") + img(alt="logo",src="https://dl.dropbox.com/u/58444696/binb-logo.png") + h2 Password recovery + p To initiate the password reset process for your binb account, + | click the link below: + a(style="color:#0088CC;text-decoration:none;", + href="http://binb.nodejitsu.com/resetpasswd?token=#{token}") + | http://bind.nodejitsu.com/resetpasswd?token=#{token} + p If you can't click it, please copy and paste the URL in a new tab/window. + p If you haven't requested a password reset, you can disregard this message, + | because it's likely that another user entered your email address + | by mistake while trying to reset a password. + p.note(style="font-size:13px;") + b Note: + | This email cannot accept replies. diff --git a/lib/email/template.txt b/lib/email/template.txt new file mode 100644 index 0000000..5b73c2f --- /dev/null +++ b/lib/email/template.txt @@ -0,0 +1,25 @@ + + +============================================================ +binb password recovery +============================================================ + +To initiate the password reset process for your binb +account, click the link below: + + +http://binb.nodejitsu.com/resetpasswd?token= + + +If you can't click it, please copy and paste the URL in a +new tab or window. + +If you haven't requested a password reset, you can +disregard this message, because it's likely that another +user enteredyour email address by mistake while trying to +reset a password. + +============================================================ +============================================================ + +Note: This email cannot accept replies. diff --git a/package.json b/package.json index e9ac03f..9f044e5 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "connect-redis": "1.4.x", "express": "2.5.x", "jade": "0.26.x", + "nodemailer": "0.3.x", "redis-url": "0.1.x", "socket.io": "0.9.x" }, @@ -17,5 +18,5 @@ "engines": { "node": "0.6.x" }, - "version": "0.3.1-7" -} \ No newline at end of file + "version": "0.3.2" +} diff --git a/public/static/css/style.css b/public/static/css/style.css index aec9cb9..18d5adf 100644 --- a/public/static/css/style.css +++ b/public/static/css/style.css @@ -59,6 +59,11 @@ form .clearfix { margin-left: 120px; margin-top: 9px; } +.forgot-passwd { + display: inline-block; + vertical-align: top; + margin: 14px 0 0 15px; +} #captcha-input { width: 126px; } @@ -83,7 +88,7 @@ input { float: right; } .thumbnails { - margin-bottom: 0px; + margin-bottom: 0; } .thumbnails > li { margin: 0 0 18px 80px; @@ -230,7 +235,7 @@ input { width: 24px; height:24px; top: 49px; - background: url('/static/images/sprites.png') no-repeat 0px -32px; + background: url('/static/images/sprites.png') no-repeat 0 -32px; } #wheel-left { left:51px; @@ -260,9 +265,9 @@ input { -webkit-border-radius:4px; -moz-border-radius:4px; border-radius:4px; - -webkit-box-shadow: inset 0px 1px 2px 0px #333; - -moz-box-shadow: inset 0px 1px 2px 0px #333; - box-shadow: inset 0px 1px 2px 0px #333; + -webkit-box-shadow: inset 0 1px 2px 0 #333; + -moz-box-shadow: inset 0 1px 2px 0 #333; + box-shadow: inset 0 1px 2px 0 #333; } #progress-bar { position:absolute; @@ -274,7 +279,7 @@ input { } #progress { background-color: #6184b7; - width:0px; + width:0; } #touch-backdrop { width:220px; @@ -319,15 +324,15 @@ input { } #volume-button .button:hover { border-color:#999999; - -webkit-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25); - -moz-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25); - box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25); + -webkit-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.25); } #volume-button .button:active { border-color:#999999 #AAAAAA #CCCCCC; - -webkit-box-shadow: 0px 1px 2px 0px #aaa inset; - -moz-box-shadow: 0px 1px 2px 0px #aaa inset; - box-shadow: 0px 1px 2px 0px #aaa inset; + -webkit-box-shadow: 0 1px 2px 0 #aaa inset; + -moz-box-shadow: 0 1px 2px 0 #aaa inset; + box-shadow: 0 1px 2px 0 #aaa inset; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#e6e6e6,EndColorStr=#dcdcdc); background-image:-moz-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%); background-image:-ms-linear-gradient(top,#e6e6e6 0,#dcdcdc 100%); @@ -376,14 +381,14 @@ input { border-radius: 2px; } #volume-total { - -webkit-box-shadow: inset 0px 0px 3px 0px #333; - -moz-box-shadow: inset 0px 0px 3px 0px #333; - box-shadow: inset 0px 0px 3px 0px #333; + -webkit-box-shadow: inset 0 0 3px 0 #333; + -moz-box-shadow: inset 0 0 3px 0 #333; + box-shadow: inset 0 0 3px 0 #333; } #volume-current { - -webkit-box-shadow: inset 0px 0px 1px 0px #000; - -moz-box-shadow: inset 0px 0px 1px 0px #000; - box-shadow: inset 0px 0px 1px 0px #000; + -webkit-box-shadow: inset 0 0 1px 0 #000; + -moz-box-shadow: inset 0 0 1px 0 #000; + box-shadow: inset 0 0 1px 0 #000; background-color: #6184b7; } #volume-handle { @@ -432,10 +437,10 @@ input { .registered, .round-rank { height: 16px; width: 16px; - margin: 1px 2px 0px 0px; + margin: 1px 2px 0 0; } .registered { - background: url('/static/images/sprites.png') no-repeat 0px -16px; + background: url('/static/images/sprites.png') no-repeat 0 -16px; } .registered:hover { background: url('/static/images/sprites.png') no-repeat -16px -16px; diff --git a/routes/site.js b/routes/site.js index 5f8b8d8..19abf3d 100644 --- a/routes/site.js +++ b/routes/site.js @@ -52,7 +52,7 @@ exports.changePasswd = function(req, res) { if (!req.session.user) { return res.redirect('/login?followup=/changepasswd'); } - res.render('changepasswd', {followup:req.query['followup'],loggedin:req.session.user}); + res.render('changepasswd', {followup:req.query.followup,loggedin:req.session.user}); }; exports.index = function(req, res) { @@ -60,7 +60,17 @@ exports.index = function(req, res) { }; exports.login = function(req, res) { - res.render('login', {followup:req.query['followup']}); + res.render('login', {followup:req.query.followup}); +}; + +exports.recoverPasswd = function(req, res) { + var captcha = new Captcha(); + req.session.captchacode = captcha.getCode(); + res.render('recoverpasswd', {captchaurl:captcha.toDataURL(),followup:req.query.followup}); +}; + +exports.resetPasswd = function(req, res) { + res.render('resetpasswd', {token:req.query.token}); }; exports.room = function(req, res) { @@ -75,5 +85,5 @@ exports.room = function(req, res) { exports.signup = function(req, res) { var captcha = new Captcha(); req.session.captchacode = captcha.getCode(); - res.render('signup', {captchaurl:captcha.toDataURL(),followup:req.query['followup']}); + res.render('signup', {captchaurl:captcha.toDataURL(),followup:req.query.followup}); }; diff --git a/routes/user.js b/routes/user.js index e26cfc6..42da220 100644 --- a/routes/user.js +++ b/routes/user.js @@ -5,6 +5,7 @@ var crypto = require('crypto') , db , followupurls = [] + , mailer = require('../lib/email/mailer') , User = require('../lib/user'); /** @@ -56,7 +57,7 @@ var buildLeaderboards = function(pointsresults, timesresults) { var obj = { pointsleaderboard: [], timesleaderboard: [] - } + }; for (var i=0; iTo start the password reset + | process, open this email and follow the given instructions.
+ | If you don't receive it in a reasonable amount of time, please + | use the support form on the left. + else + if ((typeof(errors) !== "undefined") && (typeof(errors.alert) !== "undefined")) + .alert.alert-error + a.close(data-dismiss="alert") × + strong Oh snap! + | #{errors.alert} + form.form-horizontal.well(method="post", + action="/recoverpasswd#{followup}") + fieldset + if (typeof(errors) !== "undefined") + if (typeof(errors.email) !== "undefined") + .control-group.error + label.control-label(for="email") Email + .controls + input#oldpassword(type="text",name="email", + value="#{oldvalues.email}") + span.help-inline #{errors.email} + else + .control-group + label.control-label(for="email") Email + .controls + input#username(type="text",name="email", + value="#{oldvalues.email}") + if (typeof(errors.captcha) !== 'undefined') + .control-group.error + label.control-label(for="captcha-input") + | Are you human? + .controls + img#captcha(src="#{captchaurl}") + input#captcha-input(type="text",name="captcha") + span.help-inline #{errors.captcha} + else + .control-group + label.control-label(for="captcha-input") + | Are you human? + .controls + img#captcha(src="#{captchaurl}") + input#captcha-input(type="text",name="captcha", + placeholder="type what you see...") + else + .control-group + label.control-label(for="email") Email + .controls + input#email(type="text",name="email", + placeholder="type the email you used to sign up...") + .control-group + label.control-label(for="captcha-input") Are you human? + .controls + img#captcha(src="#{captchaurl}") + input#captcha-input(type="text",name="captcha", + placeholder="type what you see...") + button.submit-button.btn.btn-primary(type="submit") + i.icon-envelope.icon-white + | Send password reset link + include footer diff --git a/views/resetpasswd.jade b/views/resetpasswd.jade new file mode 100644 index 0000000..9a6d916 --- /dev/null +++ b/views/resetpasswd.jade @@ -0,0 +1,47 @@ +token = (typeof(token) !== "undefined") ? '?token='+token : '' +doctype html +html + include header + title binb :: Reset password + script(src="/static/js/bootstrap.min.js") + body + include uv.jade + .navbar.navbar-fixed-top + .navbar-inner + .container + a.brand(href="#") + .logo #{motto} + .container + section + .row + .span12.offset2 + if ((typeof(errors) !== "undefined") && (typeof(errors.alert) !== "undefined")) + .alert.alert-error + a.close(data-dismiss="alert") × + strong Oh snap! + | #{errors.alert} + form.form-horizontal.well(method="post",action="/resetpasswd#{token}") + fieldset + if (typeof(errors) !== "undefined") + if (typeof(errors.password) !== "undefined") + .control-group.error + label.control-label(for="password") New password + .controls + input#oldpassword(type="password",name="password") + span.help-inline #{errors.password} + else + .control-group + label.control-label(for="password") New password + .controls + input#username(type="password",name="password", + placeholder="enter your new password...") + else + .control-group + label.control-label(for="oldpassword") New password + .controls + input#username(type="password",name="password", + placeholder="enter your new password...") + button.submit-button.btn.btn-primary(type="submit") + i.icon-edit.icon-white + | Update + include footer