summaryrefslogtreecommitdiff
path: root/src/node/hooks
diff options
context:
space:
mode:
authorEgil Moeller <egil.moller@freecode.no>2012-04-19 14:25:12 +0200
committerEgil Moeller <egil.moller@freecode.no>2012-04-19 14:25:12 +0200
commitac36a99a7226e1092c2e7e28c9b6e32d8f82e6fb (patch)
treea1de593a6290b66454c7f3684df63441ebb7bd83 /src/node/hooks
parent4c1d94343fe1b1a8ff02d7cfaef9f9506676d072 (diff)
downloadetherpad-lite-ac36a99a7226e1092c2e7e28c9b6e32d8f82e6fb.zip
More general basic auth
Diffstat (limited to 'src/node/hooks')
-rw-r--r--src/node/hooks/express/adminplugins.js2
-rw-r--r--src/node/hooks/express/socketio.js18
-rw-r--r--src/node/hooks/express/webaccess.js108
3 files changed, 95 insertions, 33 deletions
diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js
index 1d1320ab..6cc80cf2 100644
--- a/src/node/hooks/express/adminplugins.js
+++ b/src/node/hooks/express/adminplugins.js
@@ -21,6 +21,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
exports.socketio = function (hook_name, args, cb) {
var io = args.io.of("/pluginfw/installer");
io.on('connection', function (socket) {
+ if (!socket.handshake.session.user.is_admin) return;
+
socket.on("load", function (query) {
socket.emit("installed-results", {results: plugins.plugins});
});
diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js
index e040f7ac..6774b653 100644
--- a/src/node/hooks/express/socketio.js
+++ b/src/node/hooks/express/socketio.js
@@ -7,11 +7,27 @@ var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
var padMessageHandler = require("../../handler/PadMessageHandler");
var timesliderMessageHandler = require("../../handler/TimesliderMessageHandler");
-
+var connect = require('connect');
+
exports.expressCreateServer = function (hook_name, args, cb) {
//init socket.io and redirect all requests to the MessageHandler
var io = socketio.listen(args.app);
+ /* Require an express session cookie to be present, and load the
+ * session. See http://www.danielbaulig.de/socket-ioexpress for more
+ * info */
+ io.set('authorization', function (data, accept) {
+ if (!data.headers.cookie) return accept('No session cookie transmitted.', false);
+ data.cookie = connect.utils.parseCookie(data.headers.cookie);
+ data.sessionID = data.cookie.express_sid;
+ args.app.sessionStore.get(data.sessionID, function (err, session) {
+ if (err || !session) return accept('Bad session / session has expired', false);
+ data.session = new connect.middleware.session.Session(data, session);
+ accept(null, true);
+ });
+ });
+
+
//this is only a workaround to ensure it works with all browers behind a proxy
//we should remove this when the new socket.io version is more stable
io.set('transports', ['xhr-polling']);
diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js
index 48b5edae..499451d8 100644
--- a/src/node/hooks/express/webaccess.js
+++ b/src/node/hooks/express/webaccess.js
@@ -2,55 +2,99 @@ var express = require('express');
var log4js = require('log4js');
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');
//checks for basic http auth
exports.basicAuth = function (req, res, next) {
-
- // When handling HTTP-Auth, an undefined password will lead to no authorization at all
- var pass = settings.httpAuth || '';
-
- if (req.path.indexOf('/admin') == 0) {
- var pass = settings.adminHttpAuth;
-
- }
-
- // Just pass if password is an empty string
- if (pass === '') {
- return next();
- }
-
-
- // If a password has been set and auth headers are present...
- if (pass && req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
- // ...check login and password
- if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() === pass) {
- return next();
+ var authorize = function (cb) {
+ // Do not require auth for static paths...this could be a bit brittle
+ if (req.path.match(/^\/(static|javascripts|pluginfw)/)) return cb(true);
+
+ if (req.path.indexOf('/admin') != 0) {
+ if (!settings.requireAuthentication) return cb(true);
+ if (!settings.requireAuthorization && req.session && req.session.user) return cb(true);
}
+
+ if (req.session && req.session.user && req.session.user.is_admin) return cb(true);
+
+ // hooks.aCallFirst("authorize", {resource: req.path, req: req}, cb);
+ cb(false);
}
- // Do not require auth for static paths...this could be a bit brittle
- else if (req.path.match(/^\/(static|javascripts|pluginfw)/)) {
- return next();
+
+ var authenticate = function (cb) {
+ // If auth headers are present use them to authenticate...
+ if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
+ var userpass = new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString().split(":")
+ var username = userpass[0];
+ var password = userpass[1];
+
+ if (settings.users[username] != undefined && settings.users[username].password == password) {
+ settings.users[username].username = username;
+ req.session.user = settings.users[username];
+ return cb(true);
+ }
+ // return hooks.aCallFirst("authenticate", {req: req, username: username, password: password}, cb);
+ }
+ // hooks.aCallFirst("authenticate", {req: req}, cb);
+ cb(false);
}
- // Otherwise return Auth required Headers, delayed for 1 second, if auth failed.
- res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
- if (req.headers.authorization) {
- setTimeout(function () {
+ var failure = function () {
+ /* Authentication OR authorization failed. Return Auth required
+ * Headers, delayed for 1 second, if authentication failed. */
+ res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
+ if (req.headers.authorization) {
+ setTimeout(function () {
+ res.send('Authentication required', 401);
+ }, 1000);
+ } else {
res.send('Authentication required', 401);
- }, 1000);
- } else {
- res.send('Authentication required', 401);
+ }
}
+
+
+ /* This is the actual authentication/authorization hoop. It is done in four steps:
+
+ 1) Try to just access the thing
+ 2) If not allowed using whatever creds are in the current session already, try to authenticate
+ 3) If authentication using already supplied credentials succeeds, try to access the thing again
+ 4) If all els fails, give the user a 401 to request new credentials
+
+ Note that the process could stop already in step 3 with a redirect to login page.
+
+ */
+
+ authorize(function (ok) {
+ if (ok) return next();
+ authenticate(function (ok) {
+ if (!ok) return failure();
+ authorize(function (ok) {
+ if (ok) return next();
+ failure();
+ });
+ });
+ });
}
exports.expressConfigure = function (hook_name, args, cb) {
- args.app.use(exports.basicAuth);
-
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
args.app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
args.app.use(express.cookieParser());
+
+ /* Do not let express create the session, so that we can retain a
+ * reference to it for socket.io to use. Also, set the key (cookie
+ * name) to a javascript identifier compatible string. Makes code
+ * handling it cleaner :) */
+
+ args.app.sessionStore = new express.session.MemoryStore();
+ args.app.use(express.session({store: args.app.sessionStore,
+ key: 'express_sid',
+ secret: apikey = randomString(32)}));
+
+ args.app.use(exports.basicAuth);
}