From a875ca6c3089a6a3f02f3fa0403aba302f42daa5 Mon Sep 17 00:00:00 2001 From: Ray Bellis Date: Fri, 25 Jan 2019 14:53:24 +0000 Subject: [PATCH] db/SessionManager.js: mostly converted to Promises --- src/node/db/SessionManager.js | 394 ++++++++++++---------------------- 1 file changed, 133 insertions(+), 261 deletions(-) diff --git a/src/node/db/SessionManager.js b/src/node/db/SessionManager.js index f1f759da..67d3e381 100644 --- a/src/node/db/SessionManager.js +++ b/src/node/db/SessionManager.js @@ -21,160 +21,106 @@ var ERR = require("async-stacktrace"); var customError = require("../utils/customError"); var randomString = require("../utils/randomstring"); -var db = require("./DB").db; -var async = require("async"); +var db = require("./DB"); var groupManager = require("./GroupManager"); var authorManager = require("./AuthorManager"); const thenify = require("thenify").withCallback; -exports.doesSessionExist = thenify(function(sessionID, callback) +exports.doesSessionExist = async function(sessionID) { //check if the database entry of this session exists - db.get("session:" + sessionID, function (err, session) - { - if(ERR(err, callback)) return; - callback(null, session != null); - }); -}); + let session = await db.get("session:" + sessionID); + return (session !== null); +} /** * Creates a new session between an author and a group */ -exports.createSession = thenify(function(groupID, authorID, validUntil, callback) +exports.createSession = async function(groupID, authorID, validUntil) { - var sessionID; + // check if the group exists + let groupExists = await groupManager.doesGroupExist(groupID); + if (!groupExists) { + throw new customError("groupID does not exist", "apierror"); + } - async.series([ - // check if the group exists - function(callback) - { - groupManager.doesGroupExist(groupID, function(err, exists) - { - if(ERR(err, callback)) return; + // check if the author exists + let authorExists = await authorManager.doesAuthorExist(authorID); + if (!authorExists) { + throw new customError("authorID does not exist", "apierror"); + } - // group does not exist - if (exists == false) { - callback(new customError("groupID does not exist", "apierror")); - } else { - // everything is fine, continue - callback(); - } - }); - }, + // try to parse validUntil if it's not a number + if (typeof validUntil !== "number") { + validUntil = parseInt(validUntil); + } - // check if the author exists - function(callback) - { - authorManager.doesAuthorExists(authorID, function(err, exists) - { - if(ERR(err, callback)) return; + // check it's a valid number + if (isNaN(validUntil)) { + throw new customError("validUntil is not a number", "apierror"); + } - if (exists == false) { - // author does not exist - callback(new customError("authorID does not exist", "apierror")); - } else { - // everything is fine, continue - callback(); - } - }); - }, + // ensure this is not a negative number + if (validUntil < 0) { + throw new customError("validUntil is a negative number", "apierror"); + } - // check validUntil and create the session db entry - function(callback) - { - // check if rev is a number - if (typeof validUntil != "number") - { - // try to parse the number - if (isNaN(parseInt(validUntil))) { - callback(new customError("validUntil is not a number", "apierror")); - return; - } + // ensure this is not a float value + if (!is_int(validUntil)) { + throw new customError("validUntil is a float value", "apierror"); + } - validUntil = parseInt(validUntil); - } + // check if validUntil is in the future + if (validUntil < Math.floor(Date.now() / 1000)) { + throw new customError("validUntil is in the past", "apierror"); + } - // ensure this is not a negative number - if (validUntil < 0) { - callback(new customError("validUntil is a negative number", "apierror")); - return; - } + // generate sessionID + let sessionID = "s." + randomString(16); - // ensure this is not a float value - if (!is_int(validUntil)) { - callback(new customError("validUntil is a float value", "apierror")); - return; - } + // set the session into the database + await db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil}); - // check if validUntil is in the future - if (Math.floor(Date.now()/1000) > validUntil) { - callback(new customError("validUntil is in the past", "apierror")); - return; - } + // get the entry + let group2sessions = await db.get("group2sessions:" + groupID); - // generate sessionID - sessionID = "s." + randomString(16); + /* + * In some cases, the db layer could return "undefined" as well as "null". + * Thus, it is not possible to perform strict null checks on group2sessions. + * In a previous version of this code, a strict check broke session + * management. + * + * See: https://github.com/ether/etherpad-lite/issues/3567#issuecomment-468613960 + */ + if (!group2sessions || !group2sessions.sessionIDs) { + // the entry doesn't exist so far, let's create it + group2sessions = {sessionIDs : {}}; + } - // set the session into the database - db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil}); + // add the entry for this session + group2sessions.sessionIDs[sessionID] = 1; - callback(); - }, + // save the new element back + await db.set("group2sessions:" + groupID, group2sessions); - // set the group2sessions entry - function(callback) - { - // get the entry - db.get("group2sessions:" + groupID, function(err, group2sessions) - { - if(ERR(err, callback)) return; + // get the author2sessions entry + let author2sessions = await db.get("author2sessions:" + authorID); - if (group2sessions == null || group2sessions.sessionIDs == null) { - // the entry doesn't exist so far, let's create it - group2sessions = {sessionIDs : {}}; - } + if (author2sessions == null || author2sessions.sessionIDs == null) { + // the entry doesn't exist so far, let's create it + author2sessions = {sessionIDs : {}}; + } - // add the entry for this session - group2sessions.sessionIDs[sessionID] = 1; + // add the entry for this session + author2sessions.sessionIDs[sessionID] = 1; - // save the new element back - db.set("group2sessions:" + groupID, group2sessions); + //save the new element back + await db.set("author2sessions:" + authorID, author2sessions); - callback(); - }); - }, - - // set the author2sessions entry - function(callback) - { - // get the entry - db.get("author2sessions:" + authorID, function(err, author2sessions) - { - if(ERR(err, callback)) return; - - if (author2sessions == null || author2sessions.sessionIDs == null) { - // the entry doesn't exist so far, let's create it - author2sessions = {sessionIDs : {}}; - } - - // add the entry for this session - author2sessions.sessionIDs[sessionID] = 1; - - //save the new element back - db.set("author2sessions:" + authorID, author2sessions); - - callback(); - }); - } - ], function(err) - { - if(ERR(err, callback)) return; - - // return error and sessionID - callback(null, {sessionID: sessionID}); - }) -}); + return { sessionID }; +} +// @TODO once external dependencies are using Promises exports.getSessionInfo = thenify(function(sessionID, callback) { // check if the database entry of this session exists @@ -195,160 +141,86 @@ exports.getSessionInfo = thenify(function(sessionID, callback) /** * Deletes a session */ -exports.deleteSession = thenify(function(sessionID, callback) +exports.deleteSession = async function(sessionID) { - var authorID, groupID; - var group2sessions, author2sessions; + // ensure that the session exists + let session = await db.get("session:" + sessionID); + if (session == null) { + throw new customError("sessionID does not exist", "apierror"); + } - async.series([ - function(callback) - { - // get the session entry - db.get("session:" + sessionID, function (err, session) - { - if(ERR(err, callback)) return; + // everything is fine, use the sessioninfos + let groupID = session.groupID; + let authorID = session.authorID; - if (session == null) { - // session does not exist - callback(new customError("sessionID does not exist", "apierror")) - } else { - // everything is fine, use the sessioninfos - authorID = session.authorID; - groupID = session.groupID; + // get the group2sessions and author2sessions entries + let group2sessions = await db.get("group2sessions:" + groupID); + let author2sessions = await db.get("author2sessions:" + authorID); - callback(); - } - }); - }, + // remove the session + await db.remove("session:" + sessionID); - // get the group2sessions entry - function(callback) - { - db.get("group2sessions:" + groupID, function (err, _group2sessions) - { - if(ERR(err, callback)) return; - group2sessions = _group2sessions; - callback(); - }); - }, + // remove session from group2sessions + if (group2sessions != null) { // Maybe the group was already deleted + delete group2sessions.sessionIDs[sessionID]; + await db.set("group2sessions:" + groupID, group2sessions); + } - // get the author2sessions entry - function(callback) - { - db.get("author2sessions:" + authorID, function (err, _author2sessions) - { - if(ERR(err, callback)) return; - author2sessions = _author2sessions; - callback(); - }); - }, + // remove session from author2sessions + if (author2sessions != null) { // Maybe the author was already deleted + delete author2sessions.sessionIDs[sessionID]; + await db.set("author2sessions:" + authorID, author2sessions); + } +} - // remove the values from the database - function(callback) - { - //remove the session - db.remove("session:" + sessionID); - - // remove session from group2sessions - if(group2sessions != null) { // Maybe the group was already deleted - delete group2sessions.sessionIDs[sessionID]; - db.set("group2sessions:" + groupID, group2sessions); - } - - // remove session from author2sessions - if(author2sessions != null) { // Maybe the author was already deleted - delete author2sessions.sessionIDs[sessionID]; - db.set("author2sessions:" + authorID, author2sessions); - } - - callback(); - } - ], function(err) - { - if(ERR(err, callback)) return; - callback(); - }) -}); - -exports.listSessionsOfGroup = thenify(function(groupID, callback) +exports.listSessionsOfGroup = async function(groupID) { - groupManager.doesGroupExist(groupID, function(err, exists) - { - if(ERR(err, callback)) return; + // check that the group exists + let exists = await groupManager.doesGroupExist(groupID); + if (!exists) { + throw new customError("groupID does not exist", "apierror"); + } - if (exists == false) { - // group does not exist - callback(new customError("groupID does not exist", "apierror")); - } else { - // everything is fine, continue - listSessionsWithDBKey("group2sessions:" + groupID, callback); - } - }); -}); + let sessions = await listSessionsWithDBKey("group2sessions:" + groupID); + return sessions; +} -exports.listSessionsOfAuthor = thenify(function(authorID, callback) +exports.listSessionsOfAuthor = async function(authorID) { - authorManager.doesAuthorExists(authorID, function(err, exists) - { - if(ERR(err, callback)) return; + // check that the author exists + let exists = await authorManager.doesAuthorExist(authorID) + if (!exists) { + throw new customError("authorID does not exist", "apierror"); + } - if (exists == false) { - // group does not exist - callback(new customError("authorID does not exist", "apierror")); - } else { - // everything is fine, continue - listSessionsWithDBKey("author2sessions:" + authorID, callback); - } - }); -}); + let sessions = await listSessionsWithDBKey("author2sessions:" + authorID); + return sessions; +} // this function is basically the code listSessionsOfAuthor and listSessionsOfGroup has in common -function listSessionsWithDBKey (dbkey, callback) +// required to return null rather than an empty object if there are none +async function listSessionsWithDBKey(dbkey, callback) { - var sessions; + // get the group2sessions entry + let sessionObject = await db.get(dbkey); + let sessions = sessionObject ? sessionObject.sessionIDs : null; - async.series([ - function(callback) - { - // get the group2sessions entry - db.get(dbkey, function(err, sessionObject) - { - if(ERR(err, callback)) return; - sessions = sessionObject ? sessionObject.sessionIDs : null; - callback(); - }); - }, - - function(callback) - { - // collect all sessionIDs in an arrary - var sessionIDs = []; - for (var i in sessions) - { - sessionIDs.push(i); + // iterate through the sessions and get the sessioninfos + for (let sessionID in sessions) { + try { + let sessionInfo = await exports.getSessionInfo(sessionID); + sessions[sessionID] = sessionInfo; + } catch (err) { + if (err == "apierror: sessionID does not exist") { + console.warn(`Found bad session ${sessionID} in ${dbkey}`); + sessions[sessionID] = null; + } else { + throw err; } - - // iterate through the sessions and get the sessioninfos - async.forEach(sessionIDs, function(sessionID, callback) - { - exports.getSessionInfo(sessionID, function(err, sessionInfo) - { - if (err == "apierror: sessionID does not exist") { - console.warn(`Found bad session ${sessionID} in ${dbkey}`); - } else if(ERR(err, callback)) { - return; - } - - sessions[sessionID] = sessionInfo; - callback(); - }); - }, callback); } - ], function(err) - { - if(ERR(err, callback)) return; - callback(null, sessions); - }); + } + + return sessions; } // checks if a number is an int