diff --git a/settings.json.template b/settings.json.template index 4b18d780..28f0192e 100644 --- a/settings.json.template +++ b/settings.json.template @@ -15,6 +15,10 @@ "ip": "0.0.0.0", "port" : 9001, + // Session Key, used for reconnecting user sessions + // Set this to a secure string at least 10 characters long. Do not share this value. + "sessionKey" : "", + /* // Node native SSL support // this is disabled by default diff --git a/src/node/db/API.js b/src/node/db/API.js index 07141fec..3955d495 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -253,9 +253,7 @@ exports.getHTML = function(padID, rev, callback) exportHtml.getPadHTML(pad, undefined, function (err, html) { if(ERR(err, callback)) return; - data = {html: html}; - callback(null, data); }); } diff --git a/src/node/db/SessionManager.js b/src/node/db/SessionManager.js index 5ce4f748..60e0a7ac 100644 --- a/src/node/db/SessionManager.js +++ b/src/node/db/SessionManager.js @@ -1,5 +1,5 @@ /** - * The Session Manager provides functions to manage session in the database + * The Session Manager provides functions to manage session in the database, it only provides session management for sessions created by the API */ /* diff --git a/src/node/db/SessionStore.js b/src/node/db/SessionStore.js new file mode 100644 index 00000000..09ea7333 --- /dev/null +++ b/src/node/db/SessionStore.js @@ -0,0 +1,82 @@ + /* + * Stores session data in the database + * Source; https://github.com/edy-b/SciFlowWriter/blob/develop/available_plugins/ep_sciflowwriter/db/DirtyStore.js + * This is not used for authors that are created via the API at current + */ + +var Store = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/store'), + utils = require('ep_etherpad-lite/node_modules/connect/lib/utils'), + Session = require('ep_etherpad-lite/node_modules/connect/lib/middleware/session/session'), + db = require('ep_etherpad-lite/node/db/DB').db, + log4js = require('ep_etherpad-lite/node_modules/log4js'), + messageLogger = log4js.getLogger("SessionStore"); + +var SessionStore = module.exports = function SessionStore() {}; + +SessionStore.prototype.__proto__ = Store.prototype; + +SessionStore.prototype.get = function(sid, fn){ + messageLogger.debug('GET ' + sid); + var self = this; + db.get("sessionstorage:" + sid, function (err, sess) + { + if (sess) { + sess.cookie.expires = 'string' == typeof sess.cookie.expires ? new Date(sess.cookie.expires) : sess.cookie.expires; + if (!sess.cookie.expires || new Date() < expires) { + fn(null, sess); + } else { + self.destroy(sid, fn); + } + } else { + fn(); + } + }); +}; + +SessionStore.prototype.set = function(sid, sess, fn){ + messageLogger.debug('SET ' + sid); + db.set("sessionstorage:" + sid, sess); + process.nextTick(function(){ + if(fn) fn(); + }); +}; + +SessionStore.prototype.destroy = function(sid, fn){ + messageLogger.debug('DESTROY ' + sid); + db.remove("sessionstorage:" + sid); + process.nextTick(function(){ + if(fn) fn(); + }); +}; + +SessionStore.prototype.all = function(fn){ + messageLogger.debug('ALL'); + var sessions = []; + db.forEach(function(key, value){ + if (key.substr(0,15) === "sessionstorage:") { + sessions.push(value); + } + }); + fn(null, sessions); +}; + +SessionStore.prototype.clear = function(fn){ + messageLogger.debug('CLEAR'); + db.forEach(function(key, value){ + if (key.substr(0,15) === "sessionstorage:") { + db.db.remove("session:" + key); + } + }); + if(fn) fn(); +}; + +SessionStore.prototype.length = function(fn){ + messageLogger.debug('LENGTH'); + var i = 0; + db.forEach(function(key, value){ + if (key.substr(0,15) === "sessionstorage:") { + i++; + } + }); + fn(null, i); +}; diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 7ffeb6e5..08ddde0e 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -928,6 +928,8 @@ function handleClientReady(client, message) //If this is a reconnect, we don't have to send the client the ClientVars again if(message.reconnect == true) { + //Join the pad and start receiving updates + client.join(padIds.padId); //Save the revision in sessioninfos, we take the revision from the info the client send to us sessioninfos[client.id].rev = message.client_rev; } diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index 50323ef6..c39f91da 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -4,7 +4,7 @@ var httpLogger = log4js.getLogger("http"); var settings = require('../../utils/Settings'); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); - +var ueberStore = require('../../db/SessionStore'); //checks for basic http auth exports.basicAuth = function (req, res, next) { @@ -102,15 +102,14 @@ exports.expressConfigure = function (hook_name, args, cb) { * handling it cleaner :) */ if (!exports.sessionStore) { - exports.sessionStore = new express.session.MemoryStore(); - exports.secret = randomString(32); + exports.sessionStore = new ueberStore(); + exports.secret = settings.sessionKey; // Isn't this being reset each time the server spawns? } - - args.app.use(express.cookieParser(exports.secret)); + args.app.use(express.cookieParser(exports.secret)); args.app.sessionStore = exports.sessionStore; - args.app.use(express.session({store: args.app.sessionStore, - key: 'express_sid' })); + args.app.use(express.session({secret: exports.secret, store: args.app.sessionStore, key: 'express_sid' })); args.app.use(exports.basicAuth); } + diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 8435ab2c..04404a1a 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -26,6 +26,8 @@ var argv = require('./Cli').argv; var npm = require("npm/lib/npm.js"); var vm = require('vm'); var log4js = require("log4js"); +var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; + /* Root path of the installation */ exports.root = path.normalize(path.join(npm.dir, "..")); @@ -112,6 +114,11 @@ exports.loglevel = "INFO"; */ exports.logconfig = { appenders: [{ type: "console" }]}; +/* +* Session Key, do not sure this. +*/ +exports.sessionKey = false; + /* This setting is used if you need authentication and/or * authorization. Note: /admin always requires authentication, and * either authorization by a module, or a user with is_admin set */ @@ -132,8 +139,6 @@ exports.abiwordAvailable = function() } } - - exports.reloadSettings = function reloadSettings() { // Discover where the settings file lives var settingsFilename = argv.settings || "settings.json"; @@ -184,6 +189,11 @@ exports.reloadSettings = function reloadSettings() { log4js.setGlobalLogLevel(exports.loglevel);//set loglevel log4js.replaceConsole(); + if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here + exports.sessionKey = randomString(32); + console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts"); + } + if(exports.dbType === "dirty"){ console.warn("DirtyDB is used. This is fine for testing but not recommended for production.") } diff --git a/src/static/js/pad_connectionstatus.js b/src/static/js/pad_connectionstatus.js index c592afbd..2d9354ab 100644 --- a/src/static/js/pad_connectionstatus.js +++ b/src/static/js/pad_connectionstatus.js @@ -43,9 +43,8 @@ var padconnectionstatus = (function() status = { what: 'connected' }; - padmodals.showModal('connected'); - padmodals.hideOverlay(500); + padmodals.hideOverlay(); }, reconnecting: function() { @@ -54,7 +53,7 @@ var padconnectionstatus = (function() }; padmodals.showModal('reconnecting'); - padmodals.showOverlay(500); + padmodals.showOverlay(); }, disconnected: function(msg) { @@ -73,10 +72,11 @@ var padconnectionstatus = (function() } padmodals.showModal(k); - padmodals.showOverlay(500); + padmodals.showOverlay(); }, isFullyConnected: function() { + padmodals.hideOverlay(); return status.what == 'connected'; }, getStatus: function()