Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

express-session-mongodb

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-session-mongodb - npm Package Compare versions

Comparing version 1.2.0 to 1.3.0

99

lib/ExpressSessionMongoDB.js

@@ -106,2 +106,3 @@ //Copyright (c) 2014 Eric Vallee <eric_vallee2003@yahoo.ca>

this.Filter = Options && Options.Filter;
this.DeleteFlag = Options && Options.DeleteFlag ? Options.DeleteFlag : false;
this.ConvertToUtf8 = Options && Options.ConvertToUtf8;

@@ -189,5 +190,3 @@ this.DB = DB;

{
SessionCollection.remove({'SessionID': SessionID}, function(Err, Result) {
Callback(Err, null);
});
Callback(null, null);
}

@@ -234,3 +233,3 @@ else

Store.prototype.destroy = function(SessionID, Callback) {
Store.prototype.FlagDeletion = function(SessionID, Callback) {
var Self = this;

@@ -241,8 +240,43 @@ EnsureDependencies.call(Self, function(Err) {

HandleError(Err, Callback, function() {
SessionCollection.remove({'SessionID': SessionID}, function(Err, Result) {
HandleError(Err, Callback, function() {
if(Callback)
{
Callback();
}
var UpdateSelector = SessionID ? {'SessionID': SessionID} : {};
var UpdateOptions = SessionID ? {'multi': false} : {'multi': true};
SessionCollection.update(UpdateSelector, {'$set': {'Delete': true}}, UpdateOptions, function(Err, Result) {
Callback(Err, Result);
});
});
});
});
});
}
Store.prototype.Cleanup = function(Callback) {
var Self = this;
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
Self.DB.collection(Self.CollectionName, function(Err, SessionCollection) {
HandleError(Err, Callback, function() {
SessionCollection.remove({'Delete': true}, function(Err, Result) {
Callback(Err, Result);
});
});
});
});
});
}
Store.prototype.destroy = function(SessionID, Callback) {
if(!this.DeleteFlag)
{
var Self = this;
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
Self.DB.collection(Self.CollectionName, function(Err, SessionCollection) {
HandleError(Err, Callback, function() {
SessionCollection.remove({'SessionID': SessionID}, function(Err, Result) {
HandleError(Err, Callback, function() {
if(Callback)
{
Callback();
}
});
});

@@ -253,3 +287,7 @@ });

});
});
}
else
{
Store.prototype.FlagDeletion.call(this, SessionID, Callback);
}
};

@@ -263,3 +301,3 @@

HandleError(Err, Callback, function() {
SessionCollection.count(function(Err, Count) {
SessionCollection.count({'Delete': {'$ne': true}}, function(Err, Count) {
HandleError(Err, Callback, function() {

@@ -279,16 +317,19 @@ if(Callback)

Store.prototype.clear = function(Callback) {
var Self = this;
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
Self.DB.collection(Self.CollectionName, function(Err, SessionCollection) {
//Dropping and re-creating a collection is faster than removing all elements from it
Self.DependenciesOk = false;
SessionCollection.drop(function(Err, Reply) {
HandleError(Err, Callback, function() {
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
if(Callback)
{
Callback();
}
if(!this.DeleteFlag)
{
var Self = this;
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
Self.DB.collection(Self.CollectionName, function(Err, SessionCollection) {
//Dropping and re-creating a collection is faster than removing all elements from it
Self.DependenciesOk = false;
SessionCollection.drop(function(Err, Reply) {
HandleError(Err, Callback, function() {
EnsureDependencies.call(Self, function(Err) {
HandleError(Err, Callback, function() {
if(Callback)
{
Callback();
}
});
});

@@ -300,3 +341,7 @@ });

});
});
}
else
{
Store.prototype.FlagDeletion.call(this, null, Callback);
}
};

@@ -303,0 +348,0 @@

{
"name": "express-session-mongodb",
"version": "1.2.0",
"version": "1.3.0",
"description": "Session Store for express-session",

@@ -5,0 +5,0 @@ "keywords": [

@@ -82,2 +82,4 @@ ExpressSessionMongoDB

- DeleteFlags: If set to true (default is false), it causes the 'destroy' (which express-session uses to delete sessions in the MongoDB store) and 'clear' calls to call 'FlagDeletion' instead of directly deleting sessions from the database. Read below for more details.
&lt;Callback&gt; is the function that will be called when the session store instance (and its underlying database collection/index dependencies) have been created. It takes the following signature:

@@ -110,2 +112,29 @@

FlagDeletion and Cleanup methods
--------------------------------
In addition to the API, the 'FlagDeletion' and 'Cleanup' calls were implemented.
The reason for this is to more realiably delete sessions when it matters (perhaps for security reasons).
Because express-session holds sessions in memory during requests and save them back to the database before responding, physically deleting a session in the database while a request (or multiple requests) is(are) in progress will cause the said request(s) to be restore the session in the database.
Feel free to look at the 'TestDeleteParallelFails' test in the integration tests for an empirical demonstration of the above phenomenon.
The only way to reliably purge a session's content is to flag the session as deleted, cause the 'get' accessor to report the request as 'not found' when it is flaged as deleted and then wait for all requests holding the session in memory to complete before physically deleting the session from the database.
- 'FlagDeletion' has the following signature: function(&lt;SessionID&gt;, &lt;Callback&gt;)
If &lt;SessionID&gt; is not null, it will flag the corresponding session as deleted, else it will flag all sessions as deleted.
&lt;Callback&gt; is called when the flagging is completed, its first argument being an error (if any is encountered, else null) and its second argument being the number of sessions that were flagged.
- 'Cleanup' has the following signature: function(&lt;Callback&gt;)
'Cleanup' deletes all sessions that are flagged as deleted from the database. It should not immediately be called after a session is flagged for deletion as sufficient time should be given for any request containing the session to complete.
Alternatively, you could simply set the 'TimeToLive' option with a sufficiently big value for sessions to automatically be deleted a certain amount of time after they were last accessed.
Here, &lt;Callback&gt; is called after the cleanup is done and contains as its first argument an error (if any, else null) and as its second argument the number of sessions that were deleted from the database.
Future

@@ -147,3 +176,3 @@ ======

1.1.2
1.2.0
-----

@@ -155,1 +184,9 @@

- Added integration test for the above.
1.3.0
-----
- Changed the delete flag function not to delete on get, but merely report the session as not found
- Added a method to mark a session as for deletion
- Added a method to clean up sessions that are marked for deletion
- Added tests and documentation for the above

@@ -5,6 +5,7 @@ //Copyright (c) 2014 Eric Vallee <eric_vallee2003@yahoo.ca>

var MongoDB = require('mongodb');
Express = require('express');
Session = require('express-session');
Store = require('../lib/ExpressSessionMongoDB');
Http = require('http');
var Express = require('express');
var Session = require('express-session');
var Store = require('../lib/ExpressSessionMongoDB');
var Http = require('http');
var Nimble = require('nimble');

@@ -43,2 +44,16 @@ var RandomIdentifier = 'ExpressSessionMongoDBTestDB'+Math.random().toString(36).slice(-8);

}
Context['App'].get('/Wait/:Var', function(Req, Res) {
if(Req.params.Var==0)
{
setImmediate(function() {
Res.end();
});
}
else
{
setTimeout(function() {
Res.end();
}, Req.params.Var);
}
});
Context['App'].put('/FlagDelete', function(Req, Res) {

@@ -294,3 +309,3 @@ Context['Delete'] = 1;

TearDown(Callback);
},
}/*,
'TestBefore': function(Test) {

@@ -309,3 +324,3 @@ Test.expect(2);

});
},
}*/,
'TestObjectAPIDuring': function(Test) {

@@ -399,22 +414,20 @@ Test.expect(4);

exports.Delete = {
'setUp': function(Callback) {
Setup({'secret': 'qwerty!'}, Callback, {'TimeToLive': 2});
},
'tearDown': function(Callback) {
TearDown(Callback);
},
'TestMain': function(Test) {
'TestDeleteGet': function(Test) {
Test.expect(4);
var Handler = new RequestHandler();
Context['DB'].collection('Sessions', function(Err, SessionsCollection) {
Handler.Request('POST', '/Test/Increment', false, function() {
var InitialSessionID = Handler.SessionID;
SessionsCollection.update({}, {'$set': {'Delete': true}}, function(Err, Result) {
Handler.Request('GET', '/Test', true, function(Body) {
Test.ok(!Context['LastError'], "Confirming that the process was error-free.");
Test.ok(Handler.SessionID != InitialSessionID, "Confirming that a new session ID has been generated.");
Test.ok(!Body['Value'], "Confirming that the session data didn't survive transition.");
SessionsCollection.findOne({'SessionID': InitialSessionID}, function(Err, Session) {
Test.ok(!Session, "Confirming that previous session was wiped out from database.");
Test.done();
Setup({'secret': 'qwerty!'}, function() {
var Handler = new RequestHandler();
Context['DB'].collection('Sessions', function(Err, SessionsCollection) {
Handler.Request('POST', '/Test/Increment', false, function() {
var InitialSessionID = Handler.SessionID;
SessionsCollection.update({}, {'$set': {'Delete': true}}, function(Err, Result) {
Handler.Request('GET', '/Test', true, function(Body) {
Test.ok(!Context['LastError'], "Confirming that the process was error-free.");
Test.ok(Handler.SessionID != InitialSessionID, "Confirming that a new session ID has been generated.");
Test.ok(!Body['Value'], "Confirming that the session data didn't survive transition.");
SessionsCollection.count({}, function(Err, Amount) {
Test.ok(Amount==2, "Confirming that previous session is still in the database, but flagged as deleted.");
TearDown(function() {
Test.done();
});
});
});

@@ -425,3 +438,65 @@ });

});
},
'TestDeleteParallelFails': function(Test) { //This is to confirm the need that FlagDeletion/Cleanup addresses
Test.expect(2);
Setup({'secret': 'qwerty!'}, function() {
var Handler1 = new RequestHandler();
var Handler2 = new RequestHandler();
Handler1.Request('POST', '/Test/Increment', false, function() {
var InitialSessionID = Handler1.SessionID;
Handler2.SessionID = Handler1.SessionID;
Nimble.parallel([
function(Callback) {
Handler1.Request('GET', '/Wait/100', false, function() {
Callback();
});
},
function(Callback) {
Handler2.Request('PUT', '/Session/Destruction', false, function() {
Callback();
});
}],
function(Err) {
Handler1.Request('GET', '/Test', true, function(Body) {
Test.ok(Handler1.SessionID==InitialSessionID, "Confirming the initial session survived desipte deletion.");
Test.ok(Body['Value']==1, "Confirming the initial session data survived.");
TearDown(function() {
Test.done();
});
});
});
});
});
},
'TestDeleteParallelWorks': function(Test) { //The fix, and my apologies for copy-pasting 95%+ of the code from the previous test. Got lazy.
Test.expect(2);
Setup({'secret': 'qwerty!'}, function() {
var Handler1 = new RequestHandler();
var Handler2 = new RequestHandler();
Handler1.Request('POST', '/Test/Increment', false, function() {
var InitialSessionID = Handler1.SessionID;
Handler2.SessionID = Handler1.SessionID;
Nimble.parallel([
function(Callback) {
Handler1.Request('GET', '/Wait/100', false, function() {
Callback();
});
},
function(Callback) {
Handler2.Request('PUT', '/Session/Destruction', false, function() {
Callback();
});
}],
function(Err) {
Handler1.Request('GET', '/Test', true, function(Body) {
Test.ok(Handler1.SessionID!=InitialSessionID, "Confirming the initial session did not survive deletion.");
Test.ok(!Body['Value'], "Confirming the initial session data did not survive.");
TearDown(function() {
Test.done();
});
});
});
});
}, {'DeleteFlag': true});
}
};

@@ -181,4 +181,4 @@ //Copyright (c) 2014 Eric Vallee <eric_vallee2003@yahoo.ca>

'Testsetget': function(Test) {
Test.expect(24);
function TestPermutation(TimeToLive, IndexSessionID, Callback)
Test.expect(48);
function TestPermutation(TimeToLive, IndexSessionID, DeleteFlag, Callback)
{

@@ -213,9 +213,17 @@ Store(Context['DB'], function(Err, TestStore) {

});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID, 'DeleteFlag': DeleteFlag});
}
TestPermutation(0, false, function() {
TestPermutation(100, false, function() {
TestPermutation(0, true, function() {
TestPermutation(100, true, function() {
Test.done();
TestPermutation(0, false, false, function() {
TestPermutation(100, false, false, function() {
TestPermutation(0, true, false, function() {
TestPermutation(100, true, false, function() {
TestPermutation(0, false, true, function() {
TestPermutation(100, false, true, function() {
TestPermutation(0, true, true, function() {
TestPermutation(100, true, true, function() {
Test.done();
});
});
});
});
});

@@ -239,4 +247,4 @@ });

'Testdestroy': function(Test) {
Test.expect(12);
function TestPermutation(TimeToLive, IndexSessionID, Callback)
Test.expect(24);
function TestPermutation(TimeToLive, IndexSessionID, DeleteFlag, Callback)
{

@@ -274,9 +282,17 @@ Store(Context['DB'], function(Err, TestStore) {

});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID, 'DeleteFlag': DeleteFlag});
}
TestPermutation(0, false, function() {
TestPermutation(100, false, function() {
TestPermutation(0, true, function() {
TestPermutation(100, true, function() {
Test.done();
TestPermutation(0, false, false, function() {
TestPermutation(100, false, false, function() {
TestPermutation(0, true, false, function() {
TestPermutation(100, true, false, function() {
TestPermutation(0, false, true, function() {
TestPermutation(100, false, true, function() {
TestPermutation(0, true, true, function() {
TestPermutation(100, true, true, function() {
Test.done();
});
});
});
});
});

@@ -288,4 +304,4 @@ });

'Testlength': function(Test) {
Test.expect(20);
function TestPermutation(TimeToLive, IndexSessionID, Callback)
Test.expect(40);
function TestPermutation(TimeToLive, IndexSessionID, DeleteFlag, Callback)
{

@@ -321,9 +337,17 @@ Store(Context['DB'], function(Err, TestStore) {

});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID, 'DeleteFlag': DeleteFlag});
}
TestPermutation(0, false, function() {
TestPermutation(100, false, function() {
TestPermutation(0, true, function() {
TestPermutation(100, true, function() {
Test.done();
TestPermutation(0, false, false, function() {
TestPermutation(100, false, false, function() {
TestPermutation(0, true, false, function() {
TestPermutation(100, true, false, function() {
TestPermutation(0, false, true, function() {
TestPermutation(100, false, true, function() {
TestPermutation(0, true, true, function() {
TestPermutation(100, true, true, function() {
Test.done();
});
});
});
});
});

@@ -335,4 +359,4 @@ });

'Testclear': function(Test) {
Test.expect(20);
function TestPermutation(TimeToLive, IndexSessionID, Callback)
Test.expect(40);
function TestPermutation(TimeToLive, IndexSessionID, DeleteFlag, Callback)
{

@@ -349,3 +373,9 @@ Store(Context['DB'], function(Err, TestStore) {

ConfirmCollectionState(Indexes, Test, function() {
Callback();
Context.DB.dropDatabase(function(Err, Result) {
Context.DB.close();
MongoDB.MongoClient.connect("mongodb://localhost:27017/"+RandomIdentifier, {native_parser:true}, function(Err, DB) {
Context['DB'] = DB;
Callback();
});
});
});

@@ -358,9 +388,17 @@ });

});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID});
}, {'TimeToLive': TimeToLive, 'IndexSessionID': IndexSessionID, 'DeleteFlag': DeleteFlag});
}
TestPermutation(0, false, function() {
TestPermutation(100, false, function() {
TestPermutation(0, true, function() {
TestPermutation(100, true, function() {
Test.done();
TestPermutation(0, false, false, function() {
TestPermutation(100, false, false, function() {
TestPermutation(0, true, false, function() {
TestPermutation(100, true, false, function() {
TestPermutation(0, false, true, function() {
TestPermutation(100, false, true, function() {
TestPermutation(0, true, true, function() {
TestPermutation(100, true, true, function() {
Test.done();
});
});
});
});
});

@@ -370,2 +408,38 @@ });

});
},
'TestDeleteFlagAndCleanup': function(Test) {
Test.expect(4);
Store(Context['DB'], function(Err, TestStore) {
Context['DB'].collection('Sessions', function(Err, SessionStore) {
TestStore.set('a', {}, function() {
TestStore.set('b', {}, function() {
TestStore.set('c', {}, function() {
TestStore.destroy('a', function(Err) {
SessionStore.findOne({'SessionID': 'a'}, function(Err, SessionA) {
Test.ok(SessionA.Delete, "Confirming that the session is flagged as deleted");
TestStore.FlagDeletion('b', function(Err) {
SessionStore.findOne({'SessionID': 'b'}, function(Err, SessionB) {
Test.ok(SessionB.Delete, "Confirming that the session is flagged as deleted");
TestStore.clear(function(Err) {
TestStore.FlagDeletion('c', function(Err) {
SessionStore.findOne({'SessionID': 'c'}, function(Err, SessionC) {
Test.ok(SessionC.Delete, "Confirming that the session is flagged as deleted");
TestStore.set('d', {}, function() {
TestStore.Cleanup(function(Err, Amount) {
Test.ok(Amount==3, "Confirming that cleanup deleted 3 flagged sessions and left last one alone.");
Test.done();
});
});
});
});
});
});
});
});
});
});
});
});
});
}, {'DeleteFlag': true});
}

@@ -372,0 +446,0 @@ };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc