summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--notes.txt10
-rw-r--r--protoype/app.js39
-rw-r--r--protoype/auth.js76
-rw-r--r--protoype/router/driver.js3
-rw-r--r--protoype/router/user.js111
-rw-r--r--protoype/views/login.jade14
6 files changed, 208 insertions, 45 deletions
diff --git a/notes.txt b/notes.txt
index dd286bc..50b5adf 100644
--- a/notes.txt
+++ b/notes.txt
@@ -1,7 +1,13 @@
[express]
$ express
-do '# npm install -g express' beforehand, express command
-generates nice template dir structure to get started
+- do '# npm install -g express' beforehand, express command
+ generates nice template dir structure to get started
+- last param in functions usually implies a callback
+- app.param
+ map/assosiate some logic to a 'route parameter(s)', so when you enter such route
+ you can expect to have a variable filled for you, e.g. route: /sys/:id, let's say
+ id is 'listdb', then a db would get preloaded by the time we stepped into the route logic
+- doing next(new Error('foo')) will stall and end middleware chain
[code analysis]
# npm install -g jslint
diff --git a/protoype/app.js b/protoype/app.js
index 0a698c2..82f2ec2 100644
--- a/protoype/app.js
+++ b/protoype/app.js
@@ -8,6 +8,7 @@
* - redis for active users
* - load from db once, and refetch when necessary
* - if (verbose) log; can choose to use process.env and/or app.settings.env
+ * - jsdoc?
*
*/
@@ -19,7 +20,6 @@ var express = require('express');
var RedisStore = require('connect-redis')(express);
var db = require('./mydb.js');
-var driver = require('./router/driver.js');
var myplatform = require('./router/myplatform.js');
var user = require('./router/user.js');
var index = require('./router/index.js');
@@ -27,15 +27,30 @@ var index = require('./router/index.js');
var app = express();
function deadend(req, res, next) {
- util.log('[deadend] couldn\'t serve');
+ util.log('[deadend] couldn\'t serve, requested path: ' + req.url);
/* collect possible info here */
/* if (critical_wrong) then; throw new Error('da fuck this entity is doing!'); */
- res.send(404, 'page not found');
+ res.send(404, 'page not found\n');
}
function error_handler(err, req, res, next) { /* error handling, arity of 4 */
console.error(err.stack);
- res.send(500, 'something broke!');
+ res.send(500, 'something broke!\n');
+}
+
+/* delete req.session.user on close connection? */
+function restrict(req, res, next) {
+ if (req.session.user)
+ {
+ util.log('[restrict] granted ' + req.session.user);
+ next();
+ }
+ else
+ {
+ util.log('[restrict] blocked access');
+ res.send(401, 'access restricted\n');
+ /* res.redirect(/login); */
+ }
}
app.configure(function() {
@@ -47,22 +62,22 @@ app.configure(function() {
app.use(express.favicon());
app.use(express.compress()); /* gzip */
app.use(express.bodyParser()); /* creates req.body which req.param() uses */
- app.use(express.cookieParser()); /* req.session can be populated with user defined vars */
- app.use(express.session({ secret: "keyboard cat", store: new RedisStore() }));
+ app.use(express.cookieParser()); /* req.session.* can be populated with user defined vars */
+ app.use(express.session({ secret: "keyboard cat", store: new RedisStore() })); /* populates req.session */
app.use(app.router); /* when there's no match, we go static file handling below */
app.use(require('stylus').middleware(__dirname + '/public'));
app.use(express.static(path.join(__dirname, 'public'))); /* GET /stylesheets/style.css */
app.use(deadend); /* we get here if we couldn't serve */
+ app.use(error_handler); /* is this correct? */
});
+app.get('/', index.root);
+app.get('/create', user.create_get);
app.post('/create', user.create_post);
+app.get('/login', user.login_get);
app.post('/login', user.login_post);
-app.get('/sys/:id([a-z]+)', myplatform.system);
-
-/* routing to handlers that can drive the server's functionality */
-app.get('/create', driver.create_get);
-
-app.get('/', index.root);
+//app.all('*', auth.check); /* not applicable, I want router list to hit the end in case auth fails */
+app.get('/sys/:id([a-z]+)', restrict, myplatform.system);
app.listen(8081, function() {
util.log(util.format('[server] listening on port %d in %s mode', this.address().port, app.settings.env));
diff --git a/protoype/auth.js b/protoype/auth.js
new file mode 100644
index 0000000..57b0abb
--- /dev/null
+++ b/protoype/auth.js
@@ -0,0 +1,76 @@
+/*
+ * auth.js
+ *
+ * authentication
+ *
+ */
+
+/* helpful links:
+ * http://stackoverflow.com/questions/7990890/how-to-implement-login-auth-in-node-js/8003291#8003291
+ *
+ */
+
+var util = require('util');
+var crypto = require('crypto');
+
+var db = require('./mydb.js');
+
+var len = 128;
+var iterations = 12000;
+
+/**
+ * Hashes a password with optional `salt`, otherwise
+ * generate a salt for `pass` and invoke `fn(err, salt, hash)`.
+ *
+ * @param {String} password to hash
+ * @param {String} optional salt
+ * @param {Function} callback
+ * @api public
+ */
+
+function hash(pwd, salt, fn) {
+ if (3 == arguments.length) {
+ crypto.pbkdf2(pwd, salt, iterations, len, fn);
+ } else {
+ fn = salt;
+ crypto.randomBytes(len, function(err, salt) {
+ if (err) return fn(err);
+ salt = salt.toString('base64');
+ crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash) {
+ if (err) return fn(err);
+ fn(null, salt, hash);
+ });
+ });
+ }
+};
+
+/* pull out a user record from db along with http status code */
+function auth_user(tag, id, fn) {
+ db.users.find({tag: tag}, function(err, user_record) {
+ if (err)
+ {
+ fn(err, null, -1);
+ }
+ else if (!user_record || user_record.length == 0)
+ {
+ fn(null, null, 403);
+ }
+ else
+ {
+ /* util.log('[auth] retrived user: ' + util.inspect(user_record)); */
+ hash(id, user_record[0].salt, function(err, hash) {
+ if (err)
+ return fn(err, null, -1);
+
+ if (hash == user_record[0].hash)
+ return fn(null, user_record[0], 200); /* granted */
+
+ fn(null, null, 401);
+ });
+ }
+ });
+}
+
+exports.hash = hash;
+exports.auth_user = auth_user;
+
diff --git a/protoype/router/driver.js b/protoype/router/driver.js
index 5e79856..99c101a 100644
--- a/protoype/router/driver.js
+++ b/protoype/router/driver.js
@@ -2,7 +2,7 @@
*
*/
-function create_get(req, res) {
+function create_get(req, res, next) {
res.send('<!DOCTYPE html>\n' +
'<html>\n' +
'<body>\n' +
@@ -15,6 +15,7 @@ function create_get(req, res) {
' Desc: <input type="text" name="desc" /><br />\n' +
' Signature: <input type="text" name="sig" /><br />\n' +
' Location: <input type="text" name="loc" /><br />\n' +
+ ' Secret: <input type="text" name="secret" /><br />\n' +
' <input type="submit" value="add" />\n' +
'</form>\n' +
'</body>\n' +
diff --git a/protoype/router/user.js b/protoype/router/user.js
index 4215054..5df1469 100644
--- a/protoype/router/user.js
+++ b/protoype/router/user.js
@@ -6,28 +6,70 @@
*/
var util = require('util');
+var crypto = require('crypto')
+
var db = require('../mydb.js');
+var driver = require('./driver.js');
+var auth = require('../auth.js');
var connected_clients = {};
function create_get(req, res, next) {
+ driver.create_get(req, res, next);
}
+/* how much input sanitazion do we want? */
function create_post(req, res, next) {
- db.users.save({tag: req.param('tag'), id: req.param('id'), status: "offline",
- vehicle: {make: req.param('make'), model: req.param('model'), year: req.param('year'),
- desc: req.param('desc')}, userinfo: {sig: req.param('sig')}, location: {loc: req.param('loc')},
- stats: {matches: 0, won: 0, lost: 0}},
- function(err, thing) {
- if (err || !thing)
- util.log('[create] error saving');
- else
- util.log('[create] successfully saved');
+ /* would need to be in the if statement */
+ //crypto.createHash('md5').update(req.param('secret') + req.param('id') + '0xdeadbeef').digest('hex')
+ var secret_hash = '0xdeadbeef'; /* grant creation */
+ if (req.param('secret') == undefined || req.param('secret').length < 1 || !(req.param('secret') === secret_hash)) {
+ util.log('[create] unauthorized creation attempt');
+ return res.send(403, 'creation aborted, invalid secret\n'); /* return could be after this statement for clarity */
+ } else if (req.param('tag') == undefined || req.param('tag').length < 2 || req.param('tag').length > 16 || /* tag */
+ req.param('id') == undefined || req.param('id').length < 8 || req.param('id').length > 30) /* pass */ {
+ util.log('[create] client input sanitazation failed');
+ return res.send(400, 'invalid registration attributes\n');
+ }
+
+ var user_record = {
+ tag: req.param('tag'), /* really an id */
+ //id: req.param('id'), /* this is evil */
+ salt: '',
+ hash: '',
+ status: "offline",
+ vehicle: {make: req.param('make'), model: req.param('model'), year: req.param('year'), desc: req.param('desc')},
+ userinfo: {sig: req.param('sig')},
+ location: {loc: req.param('loc')},
+ stats: {matches: 0, won: 0, lost: 0}
+ };
+
+ /* not sure how this fits the async routine */
+ /* yep, had to move code into the callback */
+ auth.hash(req.param('id'), function(err, salt, hash) {
+ if (err) {
+ next(new Error('failed to compute hash'));
+ } else {
+ user_record.salt = salt;
+ user_record.hash = hash;
+
+ db.users.save(user_record, function(err, thing) {
+ if (err || !thing)
+ util.log('[create] error saving');
+ else
+ util.log('[create] successfully saved');
+ });
+ res.redirect('/');
+ }
});
- res.redirect('/');
}
function login_get(req, res, next) {
+ /* todo: jade page for this? */
+ /* res.send('just use curl to login'); */
+ res.render('login', {
+ title: 'Challenger 2.0', /* might use app.locals */
+ });
}
/*
@@ -35,27 +77,36 @@ function login_get(req, res, next) {
*
*/
function login_post(req, res, next) {
- db.users.find({tag: req.param('tag')}, function(err, thing) {
- if (err || !thing || thing.length == 0) {
- util.log('[login] user does not exist');
- res.send('user does not exist\n', 403);
- }
- else {
- /* util.log('[login] retrived user: ' + util.inspect(thing)); */
- if (req.param('id') === thing[0].id) { /* insert md5 hashing here */
- util.log('[login] ' + thing[0].tag + ' authenticated');
- db.users.update({tag: req.param('tag')}, {$set: {status: 'online'}}, function(err, updated) {
- if (err || !updated)
- util.log('[login] failed to set status to online');
- });
+ auth.auth_user(req.param('tag'), req.param('id'), function(err, user, code) {
+ if (err)
+ return next(err);
+ switch (code) {
+ case 200:
+ {
+ /* util.log('[login] retrived user: ' + util.inspect(user)); */
+ util.log('[login] ' + user.tag + ' authenticated');
+ db.users.update({tag: user.tag}, {$set: {status: 'online'}}, function(err, updated) {
+ if (err || !updated)
+ {
+ util.log('[login] failed to set status to online');
+ return next(new Error('failed to set status to online'));
+ }
/* real deal? */
- connected_clients[thing[0].tag] = {ip: res.connection.myip, port: res.connection.myport};
- res.send('successfully logged in\n', 200);
- }
- else {
- util.log('[login] could not authenticate');
- res.send('could not authenticate\n', 401);
- }
+ connected_clients[user.tag] = {ip: res.connection.myip, port: res.connection.myport};
+ /* req.session.regenerate(function() { */
+ req.session.user = user.tag; /* keep track of auth'ed user */
+ res.send(200, 'successfully logged in\n');
+ });
+ break;
+ }
+ case 401:
+ util.log('[login] could not authenticate');
+ res.send(401, 'could not authenticate\n');
+ break;
+ case 403:
+ util.log('[login] user does not exist');
+ res.send(403, 'user does not exist\n');
+ break;
}
});
}
diff --git a/protoype/views/login.jade b/protoype/views/login.jade
new file mode 100644
index 0000000..834b71c
--- /dev/null
+++ b/protoype/views/login.jade
@@ -0,0 +1,14 @@
+extends layout
+
+block content
+ h2(style='border-bottom: dashed #FF9900; letter-spacing: -2px') Login
+ form(name="input", action="/login", method="post")
+ | Username:
+ input(type="text", name="tag")
+ | <br />
+
+ | Password:
+ input(type="text", name="id")
+ | <br />
+ input(type="submit", value="Submit")
+