From d0e378f63aa92fe7257aef89ec747809a461a48f Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sat, 13 Aug 2011 22:07:21 +0100 Subject: [PATCH] added a security manager to control pad access on group pads --- node/db/SecurityManager.js | 235 ++++++++++++++++++++++++++++++ node/handler/PadMessageHandler.js | 91 +++++++----- static/js/pad2.js | 3 + 3 files changed, 291 insertions(+), 38 deletions(-) create mode 100644 node/db/SecurityManager.js diff --git a/node/db/SecurityManager.js b/node/db/SecurityManager.js new file mode 100644 index 00000000..7ad8f8d2 --- /dev/null +++ b/node/db/SecurityManager.js @@ -0,0 +1,235 @@ +/** + * Controls the security of pad access + */ + +/* + * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var db = require("./DB").db; +var async = require("async"); +var authorManager = require("./AuthorManager"); +var padManager = require("./PadManager"); +var sessionManager = require("./SessionManager"); + +/** + * This function controlls the access to a pad, it checks if the user can access a pad. + * @param padID the pad the user wants to access + * @param sesssionID the session the user has (set via api) + * @param token the token of the author (randomly generated at client side, used for public pads) + * @param password the password the user has given to access this pad, can be null + * @param callback will be called with (err, {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx}) + */ +exports.checkAccess = function (padID, sessionID, token, password, callback) +{ + // it's not a group pad, means we can grant access + if(padID.indexOf("$") == -1) + { + //get author for this token + authorManager.getAuthor4Token(token, function(err, author) + { + // grant access, with author of token + callback(err, {accessStatus: "grant", authorID: author}); + }) + + //don't continue + return; + } + + var groupID = padID.split("$")[0]; + var padExists = false; + var validSession = false; + var sessionAuthor; + var tokenAuthor; + var isPublic; + var isPasswordProtected; + var passwordStatus = password == null ? "notGiven" : "wrong"; // notGiven, correct, wrong + + var statusObject; + + async.series([ + //get basic informations from the database + function(callback) + { + async.parallel([ + //does pad exists + function(callback) + { + padManager.doesPadExists(padID, function(err, exists) + { + padExists = exists; + callback(err); + }); + }, + //get informations about this session + function(callback) + { + sessionManager.getSessionInfo(sessionID, function(err, sessionInfo) + { + //skip session validation if the session doesn't exists + if(err && err.stop == "sessionID does not exist") + { + callback(); + return; + } + + if(err) {callback(err); return} + + var now = Math.floor(new Date().getTime()/1000); + + //is it for this group? and is validUntil still ok? --> validSession + if(sessionInfo.groupID == groupID && sessionInfo.validUntil > now) + { + validSession = true; + } + + sessionAuthor = sessionInfo.authorID; + + callback(); + }); + }, + //get author for token + function(callback) + { + //get author for this token + authorManager.getAuthor4Token(token, function(err, author) + { + tokenAuthor = author; + callback(err); + }); + } + ], callback); + }, + //get more informations of this pad, if avaiable + function(callback) + { + //skip this if the pad doesn't exists + if(padExists == false) + { + callback(); + return; + } + + padManager.getPad(padID, function(err, pad) + { + if(err) {callback(err); return} + + //is it a public pad? + isPublic = pad.getPublicStatus(); + + //is it password protected? + isPasswordProtected = pad.isPasswordProtected(); + + //is password correct? + if(isPasswordProtected && password && pad.isCorrectPassword(password)) + { + passwordStatus = "correct"; + } + + callback(); + }); + }, + function(callback) + { + //- a valid session for this group is avaible AND pad exists + if(validSession && padExists) + { + //- the pad is not password protected + if(!isPasswordProtected) + { + //--> grant access + statusObject = {accessStatus: "grant", authorID: sessionAuthor}; + } + //- the pad is password protected and password is correct + else if(isPasswordProtected && passwordStatus == "correct") + { + //--> grant access + statusObject = {accessStatus: "grant", authorID: sessionAuthor}; + } + //- the pad is password protected but wrong password given + else if(isPasswordProtected && passwordStatus == "wrong") + { + //--> deny access, ask for new password and tell them that the password is wrong + statusObject = {accessStatus: "wrongPassword"}; + } + //- the pad is password protected but no password given + else if(isPasswordProtected && passwordStatus == "notGiven") + { + //--> ask for password + statusObject = {accessStatus: "needPassword"}; + } + else + { + throw new Error("Ops, something wrong happend"); + } + } + //- a valid session for this group avaible but pad doesn't exists + else if(validSession && !padExists) + { + //--> grant access + statusObject = {accessStatus: "grant", authorID: sessionAuthor}; + } + // there is no valid session avaiable AND pad exists + else if(!validSession && padExists) + { + //-- its public and not password protected + if(isPublic && !isPasswordProtected) + { + //--> grant access, with author of token + statusObject = {accessStatus: "grant", authorID: tokenAuthor}; + } + //- its public and password protected and password is correct + else if(isPublic && isPasswordProtected && passwordStatus == "correct") + { + //--> grant access, with author of token + statusObject = {accessStatus: "grant", authorID: tokenAuthor}; + } + //- its public and the pad is password protected but wrong password given + else if(isPublic && isPasswordProtected && passwordStatus == "wrong") + { + //--> deny access, ask for new password and tell them that the password is wrong + statusObject = {accessStatus: "wrongPassword"}; + } + //- its public and the pad is password protected but no password given + else if(isPublic && isPasswordProtected && passwordStatus == "notGiven") + { + //--> ask for password + statusObject = {accessStatus: "needPassword"}; + } + //- its not public + else if(!isPublic) + { + //--> deny access + statusObject = {accessStatus: "deny"}; + } + else + { + throw new Error("Ops, something wrong happend"); + } + } + // there is no valid session avaiable AND pad doesn't exists + else + { + //--> deny access + statusObject = {accessStatus: "deny"}; + } + + callback(); + } + ], function(err) + { + callback(err, statusObject); + }); +} diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js index 55044088..846c1973 100644 --- a/node/handler/PadMessageHandler.js +++ b/node/handler/PadMessageHandler.js @@ -25,6 +25,7 @@ var AttributePoolFactory = require("../utils/AttributePoolFactory"); var authorManager = require("../db/AuthorManager"); var readOnlyManager = require("../db/ReadOnlyManager"); var settings = require('../utils/Settings'); +var securityManager = require("../db/SecurityManager"); /** * A associative array that translates a session to a pad @@ -585,51 +586,65 @@ function handleClientReady(client, message) var chatMessages; async.series([ + //check permissions + function(callback) + { + securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject) + { + if(err) {callback(err); return} + + //access was granted + if(statusObject.accessStatus == "grant") + { + author = statusObject.authorID; + callback(); + } + //no access, send the client a message that tell him why + else + { + client.send({accessStatus: statusObject.accessStatus}) + } + }); + }, //get all authordata of this new user function(callback) { - //Ask the author Manager for a author of this token. - authorManager.getAuthor4Token(message.token, function(err,value) - { - author = value; - - async.parallel([ - //get colorId - function(callback) + async.parallel([ + //get colorId + function(callback) + { + authorManager.getAuthorColorId(author, function(err, value) { - authorManager.getAuthorColorId(author, function(err, value) - { - authorColorId = value; - callback(err); - }); - }, - //get author name - function(callback) + authorColorId = value; + callback(err); + }); + }, + //get author name + function(callback) + { + authorManager.getAuthorName(author, function(err, value) { - authorManager.getAuthorName(author, function(err, value) - { - authorName = value; - callback(err); - }); - }, - function(callback) + authorName = value; + callback(err); + }); + }, + function(callback) + { + padManager.getPad(message.padId, function(err, value) { - padManager.getPad(message.padId, function(err, value) - { - pad = value; - callback(err); - }); - }, - function(callback) + pad = value; + callback(err); + }); + }, + function(callback) + { + readOnlyManager.getReadOnlyId(message.padId, function(err, value) { - readOnlyManager.getReadOnlyId(message.padId, function(err, value) - { - readOnlyId = value; - callback(err); - }); - } - ], callback); - }); + readOnlyId = value; + callback(err); + }); + } + ], callback); }, //these db requests all need the pad object function(callback) diff --git a/static/js/pad2.js b/static/js/pad2.js index 2eb615c5..84106aff 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -104,11 +104,14 @@ function handshake() token = randomString(); createCookie("token", token, 60); } + + var sessionID = readCookie("sessionID"); var msg = { "component": "pad", "type": "CLIENT_READY", "padId": padId, + "sessionID": sessionID, "token": token, "protocolVersion": 2 };