From c5393b050ecba8c71e7df6724328b7b637d7afa2 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 24 Sep 2014 17:58:53 -0400 Subject: [PATCH] Database-based login/logout --- app.js | 38 +++++++++++++++++++++++--- package.json | 1 - routes/index.js | 68 +++++++++++++++++++++++++++++++++++++++++++++-- routes/task.js | 18 ++++++++++++- views/csrf.dust | 1 + views/index.dust | 4 +-- views/layout.dust | 20 +++++++++++--- views/login.dust | 27 +++++++++++++++++++ 8 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 views/csrf.dust create mode 100644 views/login.dust diff --git a/app.js b/app.js index 0054fd7..453c78d 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ // ------------ Basic Dependencies ------------------------------------------- var express = require('express'), session = require('express-session'), + csrf = require('csurf'), path = require('path'), favicon = require('serve-favicon'), logger = require('morgan'), @@ -11,7 +12,7 @@ var express = require('express'), requireDir = require('require-dir'), connection = require('express-myconnection'), bcrypt = require('bcrypt-nodejs'), - mysql = require('mysql'); + mysql = require('mysql2'); // ---------------------------------------------------------------------------- var app = express(); @@ -20,13 +21,13 @@ app.set('trust proxy', true); // Trust X-Forwarded-* headers // Database connection app.use( - connection('mysql', { + connection(mysql, { host: 'localhost', user: 'node', password: 'node', port: 3306, database: 'node' - }, 'request') + }, 'pool') ); // view engine setup @@ -42,7 +43,28 @@ app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); +app.use(session({ + resave: true, + saveUninitialized: true, + secret: 'j2uyc0hjh2;clkjang1ddojj' +})); app.use(express.static(path.join(__dirname, 'public'), {redirect:false})); +app.use(csrf({ + ignoreMethods: ['GET', 'HEAD', 'OPTIONS'] +})); + +//Check session for any pages that require authentication +app.use(function(err, req, res, next) { + if ( ! req.session.uid) + { + console.log("This should redirect to index!") + + ['/', '/login', '/logout'].forEach(function(item) { + if (req.path.match(item)) return next(); + }); + res.redirect(303, '/'); + } +}); // Route mapping // Routes are prefixed by the filename, @@ -54,6 +76,7 @@ Object.keys(routes).forEach(function(route) { var path = (route != 'index') ? '/' + route : '/'; + app.use(path, routes[route]); }); @@ -66,6 +89,15 @@ app.use(function(req, res, next) { // error handlers +// csrf error handler +app.use(function(err, req, res, next) { + if (err.code !== 'EBADCSRFTOKEN') return next(err); + + // Bad CSRF Token + res.status(403); + res.send('Session has expired, or has been tampered with.'); +}); + // development error handler // will print stacktrace if (app.get('env') === 'development') { diff --git a/package.json b/package.json index 869bc6c..059a276 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "dustjs-linkedin":"*", "dustjs-helpers":"*", "nodeunit":"*", - "mysql":"*", "node-memcache":"*" } } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 13fc30a..de7aaf7 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,9 +1,73 @@ var express = require('express'); var router = express.Router(); -/* GET home page. */ +/* GET Home / Login Form */ router.get('/', function(req, res) { - res.render('index', { title: 'Express'}); + var util = require('util'); + var request = util.inspect(req, {depth: 2}); + + // If the user isn't logged in + if ( ! req.session.uid) + { + res.render('login', { + title: 'Node Task Manager', + csrfToken: req.csrfToken() + }); + } + else + { + res.render('index', { + title: 'Node Task Manager', + user: req.session.username, + req: request + }); + } +}); + +/* Login action */ +router.post('/login', function(req, res) { + var bcrypt = require('bcrypt-nodejs'); + + var user = req.body.user, + pass = req.body.pass; + + req.getConnection(function(err, connection) { + if (err) throw err; + + var sql = " SELECT id, username, email, password, timezone, num_format " + + " FROM todo_user " + + " WHERE email = ? OR username = ? "; + + // Find the username / email + connection.execute(sql, [user, user], function(err, rows, fields) { + if (err) throw err; + + var user = rows[0]; + + // Verify the password hash + bcrypt.compare(pass, user.password, function(err, passRes) { + if (err) throw err; + + // Password is good, set session data and redirect + if (passRes === true) + { + req.session.uid = user.id; + req.session.num_format = user.num_format; + req.session.username = user.username; + + res.redirect(303, '/'); + } + }); + }); + }) +}); + +/* Logout action */ +router.get('/logout', function(req, res) { + // Destroy the session, and redirect to the index page + req.session.destroy(function(err) { + res.redirect(303, '/'); + }); }); module.exports = router; diff --git a/routes/task.js b/routes/task.js index d2bbe6a..b3cdf89 100644 --- a/routes/task.js +++ b/routes/task.js @@ -3,7 +3,23 @@ var router = express.Router(); /* GET list of tasks */ router.get('/list', function(req, res) { - res.json([{}]); + req.getConnection(function(err, connection) { + if (err) throw err; + + var uid = req.session.uid; + + if ( ! uid) + { + console.log("Redirect because of bad session in list route"); + res.redirect('/'); + return; + } + + connection.execute('SELECT * from todo_task_view WHERE user_id=?', [uid], function(err, rows) { + if (err) throw err; + res.json(rows); + }); + }); }); diff --git a/views/csrf.dust b/views/csrf.dust new file mode 100644 index 0000000..fdb4037 --- /dev/null +++ b/views/csrf.dust @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/views/index.dust b/views/index.dust index ad973c4..55fc06d 100644 --- a/views/index.dust +++ b/views/index.dust @@ -1,6 +1,6 @@ {>layout/} {{title} -

Welcome to {title}

+

Hi, {user}. Welcome to {title}

+
{req}
{/content} diff --git a/views/layout.dust b/views/layout.dust index b54c6ad..d0fbe09 100644 --- a/views/layout.dust +++ b/views/layout.dust @@ -2,17 +2,29 @@ {title} - + + + +
+

{title}

+ +
+
{+content} This is the base content. {/content} +
+ + + - - - diff --git a/views/login.dust b/views/login.dust new file mode 100644 index 0000000..26f4576 --- /dev/null +++ b/views/login.dust @@ -0,0 +1,27 @@ +{>layout/} + +{ +
+ Login +
+ +
+ +
+

This field is required

+
+
+ +
+ +
+

This field is required

+
+
+ + +
+
+ +{/content} \ No newline at end of file