var usersdb = redisurl.createClient(config.usersdburl);
songsdb.on('error', function(err) {
- console.log(err.toString());
+ console.log(err.message);
});
usersdb.on('error', function(err) {
- console.log(err.toString());
+ console.log(err.message);
});
/**
// Routes
site.use({db:songsdb,rooms:config.rooms});
-user.use({db:usersdb});
+user.use({db:usersdb,rooms:config.rooms});
app.get('/', site.index);
app.get('/artworks', site.artworks);
-app.get('/leaderboard', user.leaderboard);
+app.get('/changepasswd', site.changePasswd);
+app.post('/changepasswd', user.validateChangePasswd, user.checkOldPasswd, user.changePasswd);
+app.get('/leaderboards', user.leaderboards);
app.get('/login', site.login);
app.post('/login', user.validateLogin, user.checkUser, user.authenticate);
app.get('/logout', user.logout);
var cookie = parseCookie(data.headers.cookie);
sessionstore.get(cookie['connect.sid'], function(err, session) {
if (err) {
- return accept(err.toString(), false);
+ return accept(err.message, false);
}
else if (!session) {
return accept('session not found', false);
"dependencies": {
"async": "0.1.x",
"canvas": "0.12.x",
- "connect": "1.8.x",
- "connect-redis": "1.3.x",
+ "connect": "1.9.x",
+ "connect-redis": "1.4.x",
"express": "2.5.x",
"jade": "0.26.x",
"redis-url": "0.1.x",
"engines": {
"node": "0.6.x"
},
- "version": "0.3.1-4"
+ "version": "0.3.1-7"
}
\ No newline at end of file
font-size: 34px;
color: #0088CC;
}
-.navbar .navbar-text {
- line-height:19px;
- padding: 9px 10px 11px;
-}
.form-horizontal .control-group {
margin-bottom: 10px;
}
.alert {
margin-bottom: 9px;
}
-#signup-button {
+.submit-button {
margin-left: 120px;
margin-top: 9px;
}
rooms = options.rooms;
};
-exports.index = function(req, res) {
- if (req.session.user) {
- res.local('loggedin', req.session.user);
- }
- res.render('index', {rooms:rooms});
-};
-
/**
* Extract at random in each room, some album covers and return the result as a JSON.
*/
});
};
-exports.login = function(req, res) {
- res.render('login');
+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});
};
-exports.signup = function(req, res) {
- var captcha = new Captcha();
- req.session.captchacode = captcha.getCode();
- res.render('signup', {captchaurl:captcha.toDataURL()});
+exports.index = function(req, res) {
+ res.render('index', {loggedin:req.session.user,rooms:rooms});
};
+exports.login = function(req, res) {
+ res.render('login', {followup:req.query['followup']});
+};
exports.room = function(req, res) {
if (rooms.indexOf(req.params.room) !== -1) {
- if (req.session.user) {
- res.local('loggedin', req.session.user);
- }
- res.render('room', {roomname:req.params.room,rooms:rooms});
+ res.render('room', {loggedin:req.session.user,roomname:req.params.room,rooms:rooms});
}
else {
res.send(404);
}
};
+
+exports.signup = function(req, res) {
+ var captcha = new Captcha();
+ req.session.captchacode = captcha.getCode();
+ res.render('signup', {captchaurl:captcha.toDataURL(),followup:req.query['followup']});
+};
var crypto = require('crypto')
, db
+ , followupurls = []
, User = require('../lib/user');
/**
return this.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/);
};
+/**
+ * Check if a URL is in the whitelist of follow-up URLs.
+ */
+
+var safeFollowup = function(url) {
+ if (followupurls.indexOf(url) !== -1) {
+ return true;
+ }
+ return false;
+};
+
/**
* Parameters to get users ordered by best guess time.
*/
];
/**
- * Leaderboard helper function.
+ * Helper function used to build leaderboards.
* Rearrange database results in an object.
*/
-var buildLeaderboard = function(pointsresults, timesresults) {
+var buildLeaderboards = function(pointsresults, timesresults) {
var obj = {
pointsleaderboard: [],
timesleaderboard: []
exports.use = function(options) {
db = options.db;
+ rooms = options.rooms;
+ // Populate the whitelist of follow-up URLs
+ followupurls.push('/');
+ followupurls.push('/changepasswd');
+ for (var i=0; i<rooms.length; i++) {
+ followupurls.push('/'+rooms[i]);
+ }
};
/**
- * Show a list of users ordered by points and best guess time (limit set to 30).
+ * Show two lists of users, one ordered by points and one by best guess time (limit set to 30).
*/
-exports.leaderboard = function(req, res) {
+exports.leaderboards = function(req, res) {
db.zrevrange('users', 0, 29, 'withscores', function(err, pointsresults) {
db.sort(sortparams, function (e, timesresults) {
- var leaderboard = buildLeaderboard(pointsresults, timesresults);
- res.render('leaderboard', leaderboard);
+ var leaderboards = buildLeaderboards(pointsresults, timesresults);
+ res.render('leaderboards', leaderboards);
+ });
+ });
+};
+
+/**
+ * Change password middlewares.
+ */
+
+exports.validateChangePasswd = function(req, res, next) {
+ if (!req.session.user || !req.body.oldpassword || !req.body.newpassword) {
+ return res.send('Missing data');
+ }
+
+ var errors = {};
+
+ req.body.oldpassword = req.body.oldpassword.trim();
+ req.body.newpassword = req.body.newpassword.trim();
+ if (req.body.oldpassword === '') {
+ errors.oldpassword = "can't be empty";
+ }
+ if (!req.body.newpassword.match(/^[A-Za-z0-9]{6,15}$/)) {
+ errors.newpassword = '6 to 15 alphanumeric characters required';
+ }
+ else if(req.body.newpassword === req.body.oldpassword) {
+ errors.newpassword = "can't be changed to the old one";
+ }
+
+ if (errors.oldpassword || errors.newpassword) {
+ req.session.errors = errors;
+ return res.redirect(req.url);
+ }
+
+ next();
+};
+
+exports.checkOldPasswd = function(req, res, next) {
+ var key = 'user:'+req.session.user;
+ db.hmget(key, 'salt', 'password', function(err, data) {
+ var hash = crypto.createHash('sha256').update(data[0]+req.body.oldpassword).digest('hex');
+ if (hash !== data[1]) {
+ req.session.errors = {oldpassword: 'is incorrect'};
+ return res.redirect(req.url);
+ }
+ next();
+ });
+};
+
+exports.changePasswd = function(req, res) {
+ var followup = (safeFollowup(req.query['followup'])) ? req.query['followup'] : '/'
+ , user = req.session.user
+ , key = 'user:'+user
+ , salt = crypto.randomBytes(6).toString('base64')
+ , password = crypto.createHash('sha256').update(salt+req.body.newpassword).digest('hex');
+ db.hmset(key, 'salt', salt, 'password', password, function(err, data) {
+ // Regenerate the session
+ req.session.regenerate(function() {
+ req.session.cookie.maxAge = 604800000; // One week
+ req.session.user = user;
+ res.redirect(followup);
});
});
};
*/
exports.validateLogin = function(req, res, next) {
+ if (!req.body.username || !req.body.password) {
+ return res.send('Missing data');
+ }
+
var errors = {};
req.body.username = req.body.username.trim(); // Username sanitization
req.session.oldvalues = {username: req.body.username};
if (errors.username || errors.password) {
req.session.errors = errors;
- return res.redirect('/login');
+ return res.redirect(req.url);
}
-
next();
};
return next();
}
req.session.errors = {alert: 'The username you specified does not exists.'};
- res.redirect('/login');
+ res.redirect(req.url);
});
};
db.hmget(key, 'salt', 'password', function(err, data) {
var hash = crypto.createHash('sha256').update(data[0]+req.body.password).digest('hex');
if (hash === data[1]) {
+ var followup = (safeFollowup(req.query['followup'])) ? req.query['followup'] : '/';
// Authentication succeeded, regenerate the session
req.session.regenerate(function() {
req.session.cookie.maxAge = 604800000; // One week
req.session.user = req.body.username;
- res.redirect('/');
+ res.redirect(followup);
});
return;
}
req.session.errors = {alert: 'The password you specified is not correct.'};
- res.redirect('/login');
+ res.redirect(req.url);
});
};
*/
exports.validateSignUp = function(req, res, next) {
+ if (!req.body.username || !req.body.email || !req.body.password || !req.body.captcha) {
+ return res.send('Missing data');
+ }
+
var errors = {};
req.body.username = req.body.username.trim(); // Username sanitization
if (errors.username || errors.email || errors.password || errors.captcha) {
req.session.errors = errors;
- return res.redirect('/signup');
+ return res.redirect(req.url);
}
next();
if (data === 1) {
// User already exists
req.session.errors = {alert: 'A user with that name already exists.'};
- return res.redirect('/signup');
+ return res.redirect(req.url);
}
next();
});
if (data === 1) {
// Email already exists
req.session.errors = {alert: 'A user with that email already exists.'};
- return res.redirect('/signup');
+ return res.redirect(req.url);
}
next();
});
// Delete old fields values (we don't want these to be available in login view)
delete req.session.oldvalues;
var msg = 'You successfully created your account. You are now ready to login.';
- res.render('login', {success:msg});
+ res.render('login', {followup:req.query['followup'],success:msg});
};
/**
--- /dev/null
+followup = (typeof(followup) !== "undefined") ? '?followup='+followup : '?followup=/'
+doctype html
+html
+ include header
+ title binb :: Change password
+ script(src="/static/js/bootstrap.min.js")
+ body
+ include uv.jade
+ .navbar.navbar-fixed-top
+ .navbar-inner
+ .container
+ a.brand(href="/")
+ .logo #{motto}
+ ul.nav.pull-right
+ li
+ a(href="/") Home
+ li.dropdown
+ a.dropdown-toggle(data-toggle="dropdown",
+ href="#") Logged in as #{loggedin.replace(/&/g, '&')}
+ span.caret
+ ul.dropdown-menu
+ li
+ a(href="/user/#{encodeURIComponent(loggedin)}",
+ target="_blank") Profile
+ li
+ a(href="/logout") Logout
+ .container
+ section
+ .row
+ .span12.offset2
+ form.form-horizontal.well(method="post",action="/changepasswd#{followup}")
+ fieldset
+ if (typeof(errors) !== "undefined")
+ if (typeof(errors.oldpassword) !== "undefined")
+ .control-group.error
+ label.control-label(for="oldpassword") Old password
+ .controls
+ input#oldpassword(type="password",name="oldpassword")
+ span.help-inline #{errors.oldpassword}
+ else
+ .control-group
+ label.control-label(for="oldpassword") Old password
+ .controls
+ input#username(type="password",name="oldpassword",
+ placeholder="enter your current password...")
+ if (typeof(errors.newpassword) !== 'undefined')
+ .control-group.error
+ label.control-label(for="newpassword") New password
+ .controls
+ input#password(type="password",name="newpassword")
+ span.help-inline #{errors.newpassword}
+ else
+ .control-group
+ label.control-label(for="newpassword") New password
+ .controls
+ input#password(type="password",name="newpassword",
+ placeholder="enter your new password...")
+ else
+ .control-group
+ label.control-label(for="oldpassword") Old password
+ .controls
+ input#username(type="password",name="oldpassword",
+ placeholder="enter your current password...")
+ .control-group
+ label.control-label(for="password") New password
+ .controls
+ input#password(type="password",name="newpassword",
+ placeholder="enter your new password...")
+ button.submit-button.btn.btn-primary(type="submit")
+ i.icon-lock.icon-white
+ | Update
+ include footer
include header
title binb
script(src="/socket.io/socket.io.js")
+ script(src="/static/js/bootstrap.min.js")
script(src="/static/js/home.js")
body
include uv.jade
li.active
a(href="/") Home
li
- a(target="_blank", href="/leaderboard")
+ a(target="_blank", href="/leaderboards")
i.icon-list-alt.icon-white
| Leaderboards
if (typeof(loggedin) !== "undefined")
- li
- p.navbar-text Logged in as
- a#loggedin(href="/user/#{encodeURIComponent(loggedin)}",
- target="_blank") #{loggedin.replace(/&/g, '&')}
- li
- a(href="/logout") Logout
+ li.dropdown
+ a.dropdown-toggle(data-toggle="dropdown",
+ href="#") Logged in as #{loggedin.replace(/&/g, '&')}
+ span.caret
+ ul.dropdown-menu
+ li
+ a(href="/user/#{encodeURIComponent(loggedin)}",
+ target="_blank") Profile
+ li
+ a(href="/changepasswd") Change password
+ li
+ a(href="/logout") Logout
else
li
a(href="/signup") Sign up
doctype html
html
include header
- title binb :: Leaderboard
+ title binb :: Leaderboards
body
include uv.jade
.navbar.navbar-fixed-top
+followup = (typeof(followup) !== "undefined") ? '?followup='+followup : '?followup=/'
doctype html
html
include header
li
a(href="/") Home
li
- a(href="/signup") Sign up
+ a(href="/signup#{followup}") Sign up
li.active
- a(href="/login") Login
+ a(href="/login#{followup}") Login
.container
section
.row
.span3
h3 New user?
- a(href="/signup") Click here to create an account.
+ a(href="/signup#{followup}") Click here to create an account.
.span13
if ((typeof(errors) !== "undefined") && (typeof(errors.alert) !== "undefined"))
.alert.alert-error
a.close(data-dismiss="alert") ×
strong Well done!
| #{success}
- form.form-horizontal.well(method="post",action="/login")
+ form.form-horizontal.well(method="post",action="/login#{followup}")
fieldset
if (typeof(errors) !== "undefined")
if (typeof(errors.username) !== "undefined")
.controls
input#password(type="password",name="password",
placeholder="enter your password...")
- button#signup-button.btn.btn-primary(type="submit")
- i.icon-user.icon-white
+ button.submit-button.btn.btn-primary(type="submit")
+ i.icon-lock.icon-white
| Login
include footer
li
a(href="/") Home
li
- a(target="_blank", href="/leaderboard")
+ a(target="_blank", href="/leaderboards")
i.icon-list-alt.icon-white
| Leaderboards
li.active.dropdown
li
a(href="/#{item}") #{item}
if (typeof(loggedin) !== "undefined")
- li
- p.navbar-text Logged in as
- a#loggedin(href="/user/#{encodeURIComponent(loggedin)}",
- target="_blank") #{loggedin.replace(/&/g, '&')}
- li
- a(href="/logout") Logout
+ li.dropdown
+ a.dropdown-toggle(data-toggle="dropdown",
+ href="#") Logged in as #{loggedin.replace(/&/g, '&')}
+ span.caret
+ ul.dropdown-menu
+ li
+ a(href="/user/#{encodeURIComponent(loggedin)}",
+ target="_blank") Profile
+ li
+ a(href="/changepasswd?followup=/#{roomname}")
+ | Change password
+ li
+ a(href="/logout") Logout
else
li
- a(href="/signup") Sign up
+ a(href="/signup?followup=/#{roomname}") Sign up
li
- a(href="/login") Login
+ a(href="/login?followup=/#{roomname}") Login
#player
#modal.modal.fade
.container
+followup = (typeof(followup) !== "undefined") ? '?followup='+followup : '?followup=/'
doctype html
html
include header
li
a(href="/") Home
li.active
- a(href="/signup") Sign up
+ a(href="/signup#{followup}") Sign up
li
- a(href="/login") Login
+ a(href="/login#{followup}") Login
.container
section
.row
.span3
h3 Not a new user?
- a(href="/login") Click here to log in.
+ a(href="/login#{followup}") Click here to log in.
.span13
h3 Why sign up?
p Registration is optional, but if you are a regular user consider creating
a.close(data-dismiss="alert") ×
strong Oh snap!
| #{errors.alert}
- form.form-horizontal.well(method="post",action="/signup")
+ form.form-horizontal.well(method="post",action="/signup#{followup}")
fieldset
if (typeof(errors) !== "undefined")
if (typeof(errors.username) !== "undefined")
img#captcha(src="#{captchaurl}")
input#captcha-input(type="text",name="captcha",
placeholder="type what you see...")
- button#signup-button.btn.btn-success(type="submit")
+ button.submit-button.btn.btn-success(type="submit")
i.icon-user.icon-white
| Sign up!
include footer