2012-02-25 13:38:09 +01:00
|
|
|
var express = require('express');
|
|
|
|
var log4js = require('log4js');
|
|
|
|
var httpLogger = log4js.getLogger("http");
|
2012-02-25 17:23:44 +01:00
|
|
|
var settings = require('../../utils/Settings');
|
2012-04-19 14:25:12 +02:00
|
|
|
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
|
|
|
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
2012-02-25 13:38:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
//checks for basic http auth
|
|
|
|
exports.basicAuth = function (req, res, next) {
|
2012-04-19 16:04:03 +02:00
|
|
|
var hookResultMangle = function (cb) {
|
|
|
|
return function (err, data) {
|
|
|
|
return cb(!err && data.length && data[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var authorize = function (cb) {
|
2012-04-19 14:25:12 +02:00
|
|
|
// 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);
|
2012-02-25 13:38:09 +01:00
|
|
|
}
|
2012-04-19 14:25:12 +02:00
|
|
|
|
|
|
|
if (req.session && req.session.user && req.session.user.is_admin) return cb(true);
|
|
|
|
|
2012-04-19 16:04:03 +02:00
|
|
|
hooks.aCallFirst("authorize", {req: req, res:res, next:next, resource: req.path}, hookResultMangle(cb));
|
2012-02-25 13:38:09 +01:00
|
|
|
}
|
2012-04-19 14:25:12 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-04-19 16:04:03 +02:00
|
|
|
return hooks.aCallFirst("authenticate", {req: req, res:res, next:next, username: username, password: password}, hookResultMangle(cb));
|
2012-04-19 14:25:12 +02:00
|
|
|
}
|
2012-04-19 16:04:03 +02:00
|
|
|
hooks.aCallFirst("authenticate", {req: req, res:res, next:next}, hookResultMangle(cb));
|
2012-04-13 11:17:48 +02:00
|
|
|
}
|
|
|
|
|
2012-02-25 13:38:09 +01:00
|
|
|
|
2012-04-19 16:04:03 +02:00
|
|
|
/* Authentication OR authorization failed. */
|
2012-04-19 14:25:12 +02:00
|
|
|
var failure = function () {
|
2012-04-19 16:04:03 +02:00
|
|
|
return hooks.aCallFirst("authFailure", {req: req, res:res, next:next}, hookResultMangle(function (ok) {
|
|
|
|
if (ok) return;
|
|
|
|
/* No plugin handler for invalid auth. Return Auth required
|
|
|
|
* Headers, delayed for 1 second, if authentication failed
|
|
|
|
* before. */
|
|
|
|
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
|
|
|
if (req.headers.authorization) {
|
|
|
|
setTimeout(function () {
|
2012-09-22 13:51:39 +02:00
|
|
|
res.send(401, 'Authentication required');
|
2012-04-19 16:04:03 +02:00
|
|
|
}, 1000);
|
|
|
|
} else {
|
2012-09-22 13:51:39 +02:00
|
|
|
res.send(401, 'Authentication required');
|
2012-04-19 16:04:03 +02:00
|
|
|
}
|
|
|
|
}));
|
2012-02-25 13:38:09 +01:00
|
|
|
}
|
2012-04-19 14:25:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* 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();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2012-02-25 13:38:09 +01:00
|
|
|
}
|
|
|
|
|
2012-09-22 16:03:40 +02:00
|
|
|
exports.secret = null;
|
2012-07-03 23:30:40 +02:00
|
|
|
|
2012-02-25 13:38:09 +01:00
|
|
|
exports.expressConfigure = function (hook_name, args, cb) {
|
|
|
|
// 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'}));
|
2012-04-19 14:25:12 +02:00
|
|
|
|
|
|
|
/* 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 :) */
|
|
|
|
|
2012-07-03 23:30:40 +02:00
|
|
|
if (!exports.sessionStore) {
|
|
|
|
exports.sessionStore = new express.session.MemoryStore();
|
2012-09-22 16:03:40 +02:00
|
|
|
exports.secret = randomString(32);
|
2012-07-03 23:30:40 +02:00
|
|
|
}
|
2012-09-22 14:05:41 +02:00
|
|
|
|
2012-09-22 16:03:40 +02:00
|
|
|
args.app.use(express.cookieParser(exports.secret));
|
2012-07-03 23:30:40 +02:00
|
|
|
|
|
|
|
args.app.sessionStore = exports.sessionStore;
|
2012-04-19 14:25:12 +02:00
|
|
|
args.app.use(express.session({store: args.app.sessionStore,
|
2012-09-22 14:05:41 +02:00
|
|
|
key: 'express_sid' }));
|
2012-04-19 14:25:12 +02:00
|
|
|
|
|
|
|
args.app.use(exports.basicAuth);
|
2012-02-25 13:38:09 +01:00
|
|
|
}
|