connect-memcached
Advanced tools
Comparing version
@@ -9,5 +9,5 @@ /*! | ||
function ensureCallback(fn) { | ||
return function() { | ||
fn && fn.apply(null, arguments); | ||
}; | ||
return function() { | ||
fn && fn.apply(null, arguments); | ||
}; | ||
} | ||
@@ -23,63 +23,137 @@ | ||
module.exports = function(session) { | ||
var Store = session.Store; | ||
var Store = session.Store; | ||
/** | ||
* Initialize MemcachedStore with the given `options`. | ||
* | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
function MemcachedStore(options) { | ||
options = options || {}; | ||
Store.call(this, options); | ||
/** | ||
* Initialize MemcachedStore with the given `options`. | ||
* | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
function MemcachedStore(options) { | ||
options = options || {}; | ||
Store.call(this, options); | ||
this.prefix = options.prefix || ''; | ||
if (!options.client) { | ||
if (!options.hosts) { | ||
options.hosts = '127.0.0.1:11211'; | ||
} | ||
this.prefix = options.prefix || ''; | ||
this.ttl = options.ttl; | ||
if (!options.client) { | ||
if (!options.hosts) { | ||
options.hosts = '127.0.0.1:11211'; | ||
} | ||
if (options.secret) { | ||
this.crypto = require('crypto'), | ||
this.secret = options.secret; | ||
} | ||
if (options.algorithm) { | ||
this.algorithm = options.algorithm; | ||
} | ||
options.client = new Memcached(options.hosts, options); | ||
} | ||
options.client = new Memcached(options.hosts, options); | ||
} | ||
this.client = options.client; | ||
} | ||
this.client = options.client; | ||
} | ||
MemcachedStore.prototype.__proto__ = Store.prototype; | ||
MemcachedStore.prototype.__proto__ = Store.prototype; | ||
/** | ||
* Translates the given `sid` into a memcached key, optionally with prefix. | ||
* | ||
* @param {String} sid | ||
* @api private | ||
*/ | ||
MemcachedStore.prototype.getKey = function getKey(sid) { | ||
return this.prefix + sid; | ||
}; | ||
/** | ||
* Translates the given `sid` into a memcached key, optionally with prefix. | ||
* | ||
* @param {String} sid | ||
* @api private | ||
*/ | ||
MemcachedStore.prototype.getKey = function getKey(sid) { | ||
return this.prefix + sid; | ||
}; | ||
/** | ||
* Attempt to fetch session by the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.get = function(sid, fn) { | ||
sid = this.getKey(sid); | ||
/** | ||
* Attempt to fetch session by the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.get = function(sid, fn) { | ||
secret = this.secret, self = this, sid = this.getKey(sid); | ||
this.client.get(sid, function(err, data) { | ||
if (err) { return fn(err, {}); } | ||
try { | ||
if (!data) { | ||
return fn(); | ||
} | ||
fn(null, JSON.parse(data.toString())); | ||
} catch (e) { | ||
fn(e); | ||
} | ||
}); | ||
}; | ||
this.client.get(sid, function(err, data) { | ||
if (err) { | ||
return fn(err, {}); | ||
} | ||
try { | ||
if (!data) { | ||
return fn(); | ||
} | ||
if (secret) { | ||
parseable_string = decryptData.call(self, data.toString()); | ||
} | ||
else { | ||
parseable_string = data.toString(); | ||
} | ||
fn(null, JSON.parse(parseable_string)); | ||
} catch (e) { | ||
fn(e); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Commit the given `sess` object associated with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Session} sess | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.set = function(sid, sess, fn) { | ||
sid = this.getKey(sid); | ||
try { | ||
var maxAge = sess.cookie.maxAge; | ||
var ttl = this.ttl || ('number' == typeof maxAge ? maxAge / 1000 | 0 : oneDay); | ||
var sess = JSON.stringify((this.secret) ? | ||
encryptData.call(this, JSON.stringify(sess), | ||
this.secret, this.algorithm) : | ||
sess); | ||
this.client.set(sid, sess, ttl, ensureCallback(fn)); | ||
} catch (err) { | ||
fn && fn(err); | ||
} | ||
}; | ||
/** | ||
* Destroy the session associated with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.destroy = function(sid, fn) { | ||
sid = this.getKey(sid); | ||
this.client.del(sid, ensureCallback(fn)); | ||
}; | ||
/** | ||
* Fetch number of sessions. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.length = function(fn) { | ||
this.client.items(ensureCallback(fn)); | ||
}; | ||
/** | ||
* Clear all sessions. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.clear = function(fn) { | ||
this.client.flush(ensureCallback(fn)); | ||
}; | ||
/** | ||
* Commit the given `sess` object associated with the given `sid`. | ||
* Refresh the time-to-live for the session with the given `sid`. | ||
* | ||
@@ -90,50 +164,60 @@ * @param {String} sid | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.set = function(sid, sess, fn) { | ||
sid = this.getKey(sid); | ||
*/ | ||
try { | ||
var maxAge = sess.cookie.maxAge; | ||
var ttl = 'number' == typeof maxAge ? maxAge / 1000 | 0 : oneDay; | ||
var sess = JSON.stringify(sess); | ||
MemcachedStore.prototype.touch = function (sid, sess, fn) { | ||
this.set(sid, sess, fn); | ||
} | ||
this.client.set(sid, sess, ttl, ensureCallback(fn)); | ||
} catch (err) { | ||
fn && fn(err); | ||
} | ||
}; | ||
/** | ||
* Destroy the session associated with the given `sid`. | ||
* | ||
* @param {String} sid | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.destroy = function(sid, fn) { | ||
sid = this.getKey(sid); | ||
this.client.del(sid, ensureCallback(fn)); | ||
}; | ||
function encryptData(plaintext){ | ||
var pt = encrypt.call(this, this.secret, plaintext, this.algo) | ||
, hmac = digest.call(this, this.secret, pt); | ||
/** | ||
* Fetch number of sessions. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.length = function(fn) { | ||
this.client.items(ensureCallback(fn)); | ||
}; | ||
return { | ||
ct: pt, | ||
mac: hmac | ||
}; | ||
} | ||
/** | ||
* Clear all sessions. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
MemcachedStore.prototype.clear = function(fn) { | ||
this.client.flush(ensureCallback(fn)); | ||
}; | ||
function decryptData(ciphertext){ | ||
ciphertext = JSON.parse(ciphertext) | ||
var hmac = digest.call(this, this.secret, ciphertext.ct); | ||
return MemcachedStore; | ||
if (hmac != ciphertext.mac) { | ||
throw 'Encrypted session was tampered with!'; | ||
} | ||
return decrypt.call(this, this.secret, ciphertext.ct, this.algo); | ||
} | ||
function digest(key, obj) { | ||
var hmac = this.crypto.createHmac('sha512', key); | ||
hmac.setEncoding('hex'); | ||
hmac.write(obj); | ||
hmac.end(); | ||
return hmac.read(); | ||
} | ||
function encrypt(key, pt, algo) { | ||
algo = algo || 'aes-256-ctr'; | ||
pt = (Buffer.isBuffer(pt)) ? pt : new Buffer(pt); | ||
var cipher = this.crypto.createCipher(algo, key), ct = []; | ||
ct.push(cipher.update(pt, 'buffer', 'hex')); | ||
ct.push(cipher.final('hex')); | ||
return ct.join(''); | ||
} | ||
function decrypt(key, ct, algo) { | ||
algo = algo || 'aes-256-ctr'; | ||
var cipher = this.crypto.createDecipher(algo, key), pt = []; | ||
pt.push(cipher.update(ct, 'hex', 'utf8')); | ||
pt.push(cipher.final('utf8')); | ||
return pt.join(''); | ||
} | ||
return MemcachedStore; | ||
}; |
{ | ||
"name": "connect-memcached" | ||
, "version": "0.1.0" | ||
, "version": "0.2.0" | ||
, "description": "Memcached session store for Connect" | ||
@@ -11,3 +11,3 @@ , "keywords": ["memcached", "connection", "session", "store", "cache"] | ||
} | ||
, "dependencies": { "memcached": "0.2.x" } | ||
, "dependencies": { "memcached": "2.2.x" } | ||
, "engines": { "node": ">=0.4.7" } | ||
@@ -14,0 +14,0 @@ , "license": "MIT" |
@@ -28,3 +28,4 @@ # connect-memcached | ||
, store : new MemcachedStore({ | ||
hosts: ['127.0.0.1:11211'] | ||
hosts: ['127.0.0.1:11211'], | ||
secret: '123, easy as ABC. ABC, easy as 123' // Optionally use transparent encryption for memcache session data | ||
}) | ||
@@ -48,4 +49,7 @@ })); | ||
## Options | ||
- `hosts` Memcached servers locations, can by string, array, hash. | ||
- `hosts` Memcached servers locations, can be string, array, hash. | ||
- `prefix` An optional prefix for each memcache key, in case you are sharing your memcached servers with something generating its own keys. | ||
- `ttl` An optional parameter used for setting the default TTL | ||
- `secret` An optional secret can be used to encrypt/decrypt session contents. | ||
- `algorithm` An optional algorithm parameter may be used, but must be valid based on returned `crypto.getCiphers()`. The current default is `aes-256-ctr` and was chosen based on the following [information](http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html) | ||
- ... Rest of given option will be passed directly to the node-memcached constructor. | ||
@@ -52,0 +56,0 @@ |
12163
55.88%8
14.29%242
65.75%83
5.06%2
100%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
Updated