From 756cf4ab72bfff705f7c18f9d4b5e34ad0532e2b Mon Sep 17 00:00:00 2001 From: joates Date: Mon, 22 Sep 2014 03:00:54 +0100 Subject: [PATCH] implement the 2-stage email verification process --- config.js | 16 +++++++ config.json | 13 ------ package.json | 4 ++ server.js | 100 ++++++++----------------------------------- src/email-confirm.js | 39 +++++++++++++++++ src/email-submit.js | 54 +++++++++++++++++++++++ 6 files changed, 131 insertions(+), 95 deletions(-) create mode 100644 config.js delete mode 100644 config.json create mode 100644 src/email-confirm.js create mode 100644 src/email-submit.js diff --git a/config.js b/config.js new file mode 100644 index 0000000..662be52 --- /dev/null +++ b/config.js @@ -0,0 +1,16 @@ +var join = require('path').join + , name = 'squatconf' + , cwd = process.cwd() + +module.exports = require('rc')(name, { + db_opts: { valueEncoding: 'json' } + , db_path: join(cwd, 'db', name) + , port: 8000 + , host: "squatconf.eu" + , email: { + from : "no-reply@squatconf.eu" + , subject : "Hello, everyone is welcome at SquatConf.." + , bodyText : "Please verify that you wish to signup by following this link\n%link%\nYou can ignore this message if you DID NOT request to signup at our website\nhttp://squatconf.eu\n\nThe next event is in Paris, we hope to see you there !!\n\nKind regards from the team,\nSquatConf Paris 2014" + } +}) + diff --git a/config.json b/config.json deleted file mode 100644 index 5f8ced6..0000000 --- a/config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "db": { - "path": "" - }, - - "email": { - "from" : "no-reply@squatconf.eu" - , "subject" : "Hello, everyone is welcome at SquatConf.." - , "bodyText": "Please verify that you wish to signup by following this link\n%link%\nYou can ignore this message if you DID NOT request to signup at our website\nhttp://squatconf.eu\n\nThe next event is in Paris, we hope to see you there !!\n\nKind regards from the team,\nSquatConf Paris 2014" - - } -} - diff --git a/package.json b/package.json index f9c265b..039fd1e 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,11 @@ "browserify": "^5.11.2", "domready": "^1.0.7", "level": "^0.18.0", + "ecstatic": "^0.5.4", "nodemailer": "^1.3.0", + "rc": "^0.5.1", + "stack": "^0.1.0", + "tiny-route": "^2.1.1", "valid-email": "0.0.1", "xss-escape": "0.0.5" } diff --git a/server.js b/server.js index c7d895f..425747a 100644 --- a/server.js +++ b/server.js @@ -1,13 +1,14 @@ #!/usr/bin/env node -var server = require('http').createServer(handler) - , sanitize = require('xss-escape') - , rn = require('./src/rng') - , fs = require('fs') - , re = new RegExp('\.js$', 'i') - , ip = require('./src/ip-trace') - , db = require('level')('./db/squatconf', { valueEncoding: 'json' }) - , port = process.env.PORT || /*80*/ 8000 +var fs = require('fs') + , http = require('http') + , stack = require('stack') + , route = require('tiny-route') + , assets = require('ecstatic') + , join = require('path').join + , config = require('./config') + , db = require('level')(config.db_path, config.db_opts) + , port = process.env.PORT || config.port // create the level db folder if it does not exists if(!fs.existsSync('./db/squatconf')){ @@ -18,84 +19,19 @@ if(!fs.existsSync('./db/squatconf')){ }); } -function handler(req, res) { - - // process incoming requests. - if (req.url == '/') req.url = '/index.html' - else if (re.test(req.url)) - res.setHeader('Content-Type', 'application/javascript') - - if (/^\/confirm\?/.test(req.url)) { - - // @TODO - // compare submitted token with the token stored in our database. - - res.statusCode = 302 - res.setHeader('Location', '/') - return res.end() - } - - if (/^\/email\?/.test(req.url)) { - var params = require('url').parse(req.url, true) - if (params && params.query.email) { - //console.log('got email:', params.query) - - var obj = {} - , email = sanitize(params.query.email) - obj.token = rn() - obj.verified = false - obj.events = { paris: params.query.paris ? true : false } - obj.trace = ip(req) - - db.put(email, obj, function(err) { - if (err) throw err - - // else.. db updated OK - db.get(email, function (err, value) { - if (err) return console.error('Ooops!', err) - - console.log('> '+ email, value) - }) - }) - - var nodemailer = require('nodemailer') - , transporter = nodemailer.createTransport() - , config = require('./config.json') - , url = 'http://squatconf.eu/confirm' - , link = url +'?email='+ email +'&token='+ obj.token +'\n\n' - - var opts = { - from : config.email.from - , to : email - , subject: config.email.subject - //, text : config.email.bodyText.replace(/\%link\%/, link) - } -console.log('mockmail sent...', opts) - /* - transporter.sendMail(opts, function(err, data) { - if (err) throw err - // validation email sent - console.log('email sent..', data) - }) - */ - } - - res.statusCode = 302 - res.setHeader('Location', '/') - return res.end() - } - - // serve static assets - var rs = fs.createReadStream(__dirname +'/html'+ req.url) - rs.pipe(res) -} +var app = stack( + route('/email', require('./src/email-submit')(db)) + , route('/confirm', require('./src/email-confirm')(db)) + , assets(join(__dirname, 'html')) +) process.on('uncaughtException', function (err) { console.error('Error at:', new Date) console.error(err.stack) }) -server.listen(port) -console.log('['+ process.pid +'] server started on port '+ port) -console.log('(use Ctrl+c to stop the server)') +http.createServer(app).listen(port, function() { + console.log('['+ process.pid +'] server started on port '+ port) + console.log('(use Ctrl+c to stop the server)') +}) diff --git a/src/email-confirm.js b/src/email-confirm.js new file mode 100644 index 0000000..a2e5df9 --- /dev/null +++ b/src/email-confirm.js @@ -0,0 +1,39 @@ +var sanitize = require('xss-escape') + , ip = require('./ip-trace') + +module.exports = function(db) { + return function (req, res, next) { + req.resume() + + var params = require('url').parse(req.url, true) + + if (params && params.query.email && params.query.token) { + //console.log('got token:', params.query) + + var email = sanitize(params.query.email) + , token = sanitize(params.query.token) + + db.get(email, function(err, obj) { + if (err) next(err) + + // db read OK.. + if (obj && obj.token === token) { + obj.verified = true + obj.trace = obj.trace.concat(ip(req)) + + db.put(email, obj, function(err) { + if (err) next(err) + + // db write OK.. + res.statusCode = 302 + res.setHeader('Location', '/verified.html') + return res.end() + }) + } + }) + + if (next) return next() + } + } +} + diff --git a/src/email-submit.js b/src/email-submit.js new file mode 100644 index 0000000..4b45b69 --- /dev/null +++ b/src/email-submit.js @@ -0,0 +1,54 @@ +var sanitize = require('xss-escape') + , rn = require('./rng') + , ip = require('./ip-trace') + , config = require('../config') + +module.exports = function(db) { + return function (req, res, next) { + req.resume() + + var params = require('url').parse(req.url, true) + + if (params && params.query.email) { + console.log('got email:', params.query) + + var obj = {} + , email = sanitize(params.query.email) + + obj.token = rn() + obj.verified = false + obj.events = { paris: params.query.paris ? true : false } + obj.trace = ip(req) + + db.put(email, obj, function(err) { + if (err) next(err) + + // db write OK.. + var nodemailer = require('nodemailer') + , transporter = nodemailer.createTransport() + , url = 'http://squatconf.eu/confirm' + , link = url +'?email='+ email +'&token='+ obj.token +'\n\n' + + var opts = { + from : config.email.from + , to : email + , subject: config.email.subject + , text : config.email.bodyText.replace(/\%link\%/, link) + } + + transporter.sendMail(opts, function(err, data) { + if (err) throw err + // validation email sent + console.log('email sent..', data) + }) + + res.statusCode = 302 + res.setHeader('Location', '/') + return res.end() + }) + } + + if (next) return next() + } +} +