started putting together basic components, specifically proxies to get the session object to behave properly

This commit is contained in:
Ben Adida 2011-12-28 21:58:19 -08:00
Родитель 2696233bda
Коммит caca8a9454
6 изменённых файлов: 261 добавлений и 7 удалений

Просмотреть файл

@ -1,12 +1,20 @@
secure sessions stored in cookies, for node.js
Secure sessions stored in cookies, for node.js
Middleware for Connect / Express apps.
Session content is secure and tamper-free.
This does *not* use connect's built-int session middleware, because,
if it did, things would get nasty in implementation given the conflict
between the session ID and the session content itself. Also, this library
uses its own cookie parser so that setup is easier and less error-prone.
I don't recommend using both this middleware and connect's built-in
session middleware.
The session content is built to be secure and tamper-free.
API
===
We don't want this library to depend on making any other calls, e.g. cookieParser.
var cookieSessions = require("cookie-sessions");
app.use(cookieSessions({
cookieName: 'session',

107
lib/ProxyHandler.js Normal file
Просмотреть файл

@ -0,0 +1,107 @@
/*
* Default handler for proxies
*
* based on
* http://wiki.ecmascript.org/doku.php?id=harmony:proxy_defaulthandler
*/
function Handler(target) {
this.target = target;
};
Handler.prototype = {
// == fundamental traps ==
// Object.getOwnPropertyDescriptor(proxy, name) -> pd | undefined
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getPropertyDescriptor(proxy, name) -> pd | undefined
getPropertyDescriptor: function(name) {
var desc = Object.getPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getOwnPropertyNames(proxy) -> [ string ]
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(this.target);
},
// Object.getPropertyNames(proxy) -> [ string ]
getPropertyNames: function() {
return Object.getPropertyNames(this.target);
},
// Object.defineProperty(proxy, name, pd) -> undefined
defineProperty: function(name, desc) {
return Object.defineProperty(this.target, name, desc);
},
// delete proxy[name] -> boolean
delete: function(name) { return delete this.target[name]; },
// Object.{freeze|seal|preventExtensions}(proxy) -> proxy
fix: function() {
// As long as target is not frozen, the proxy won't allow itself to be fixed
if (!Object.isFrozen(this.target)) {
return undefined;
}
var props = {};
Object.getOwnPropertyNames(this.target).forEach(function(name) {
props[name] = Object.getOwnPropertyDescriptor(this.target, name);
}.bind(this));
return props;
},
// == derived traps ==
// name in proxy -> boolean
has: function(name) { return name in this.target; },
// ({}).hasOwnProperty.call(proxy, name) -> boolean
hasOwn: function(name) { return ({}).hasOwnProperty.call(this.target, name); },
// proxy[name] -> any
get: function(receiver, name) { return this.target[name]; },
// proxy[name] = value
set: function(receiver, name, value) {
if (canPut(this.target, name)) { // canPut as defined in ES5 8.12.4 [[CanPut]]
this.target[name] = value;
return true;
}
return false; // causes proxy to throw in strict mode, ignore otherwise
},
// for (var name in proxy) { ... }
enumerate: function() {
var result = [];
for (var name in this.target) { result.push(name); };
return result;
},
/*
// if iterators would be supported:
// for (var name in proxy) { ... }
iterate: function() {
var props = this.enumerate();
var i = 0;
return {
next: function() {
if (i === props.length) throw StopIteration;
return props[i++];
}
};
},*/
// Object.keys(proxy) -> [ string ]
keys: function() { return Object.keys(this.target); }
};
module.exports = Handler;

66
lib/cookie-session.js Normal file
Просмотреть файл

@ -0,0 +1,66 @@
var Cookies = require("cookies");
var Proxy = require("node-proxy");
var Handler = require("./ProxyHandler.js");
/*
* Session object
*
* this should be implemented with proxies at some point
*/
function Session(req, res, cookies, opts) {
this.req = req;
this.res = res;
this.cookies = cookies;
this.opts = opts;
this.content = {};
}
Session.prototype = {
clear: function() {
},
setExpires: function(numSeconds) {
},
updateCookie: function() {
this.cookies.set(this.opts.cookieName, JSON.stringify(this.content));
},
// called to create a proxy that monitors the session
// for new properties being set
monitor: function() {
// set up proxies
var sessionHandler = new Handler(this);
// all values from content except special values
sessionHandler.get = function(rcvr, name) {
if (['clear', 'setExpires'].indexOf(name) > -1)
return this.target[name];
else
return this.target.content[name];
};
// set all values to content
sessionHandler.set = function(rcvr, name, value) {
this.target.content[name] = value;
this.target.updateCookie();
};
var proxySession = Proxy.create(sessionHandler);
return proxySession;
}
};
var cookieSession = function(opts) {
opts.cookieName = opts.cookieName || "session";
return function(req, res, next) {
var cookies = new Cookies(req, res);
req.session = new Session(req, res, cookies, opts).monitor();
next();
};
};
module.exports = cookieSession;

Просмотреть файл

@ -10,16 +10,20 @@
},
"dependencies" : {
"vows": "0.5.13",
"cookies" : "https://github.com/jed/cookies/tarball/588822c"
"cookies" : "https://github.com/jed/cookies/tarball/588822c",
"express": "2.5.0",
"tobi": "https://github.com/Cowboy-coder/tobi/tarball/master",
"zombie": "0.12.9",
"node-proxy": "0.5.2"
},
"author" : {
"name" : "Ben Adida",
"email" : "ben@adida.net"
},
"scripts" : {
"test": "./node_modules/vows/bin/vows"
"test": "./scripts/run_all_tests.sh"
},
"engines": {
"node": ">= 0.6.2"
"node": ">= 0.6.5"
}
}

19
scripts/run_all_tests.sh Executable file
Просмотреть файл

@ -0,0 +1,19 @@
#!/bin/bash
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
BASEDIR=$(dirname $SCRIPT_DIR)
export PATH=$PATH:$SCRIPT_DIR/../node_modules/.bin
VOWS=`which vows 2> /dev/null`
if [ ! -x "$VOWS" ]; then
echo "vows not found in your path. try: npm install"
exit 1
fi
# vows hates absolute paths. sheesh.
cd $BASEDIR
vows
# don't trigger npm doing funny stuff
exit 0

50
test/all-test.js Normal file
Просмотреть файл

@ -0,0 +1,50 @@
var vows = require("vows"),
assert = require("assert"),
cookieSessions = require("../index"),
express = require("express"),
tobi = require("tobi"),
Browser = require("zombie");
// set up the session middleware
var middleware = cookieSessions({
cookieName: 'session',
secret: 'yo'
});
var suite = vows.describe('all');
suite.addBatch({
"request object" : {
topic: function() {
var self = this;
// simple app
var app = express.createServer();
app.use(middleware);
app.get("/foo", function(req, res) {
console.log("yay");
self.callback(null, req);
res.send("hello");
});
var browser = tobi.createBrowser(app);
browser.get("/foo", function(res, $) {});
},
"includes a session object": function(err, req) {
assert.isObject(req.session);
},
"session object has clear method": function(err, req) {
assert.isFunction(req.session.clear);
},
"session object has setExpires method": function(err, req) {
assert.isFunction(req.session.setExpires);
},
"session object stores and retrieves values properly": function(err, req) {
req.session.foo = 'bar';
assert.equal(req.session.foo, 'bar');
}
}
});
suite.export(module);