mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-22 00:16:15 -04:00
webaccess: Move pre-authn authz check to a separate hook
Before this change, the authorize hook was invoked twice: once before authentication and again after (if settings.requireAuthorization is true). Now pre-authentication authorization is instead handled by a new preAuthorize hook, and the authorize hook is only invoked after the user has authenticated. Rationale: Without this change it is too easy to write an authorization plugin that is too permissive. Specifically: * If the plugin does not check the path for /admin then a non-admin user might be able to access /admin pages. * If the plugin assumes that the user has already been authenticated by the time the authorize function is called then unauthenticated users might be able to gain access to restricted resources. This change also avoids calling the plugin's authorize function twice per access, which makes it easier for plugin authors to write an authorization plugin that is easy to understand. This change may break existing authorization plugins: After this change, the authorize hook will no longer be able to authorize non-admin access to /admin pages. This is intentional. Access to admin pages should instead be controlled via the `is_admin` user setting, which can be set in the config file or by an authentication plugin. Also: * Add tests for the authenticate and authorize hooks. * Disable the authentication failure delay when testing.
This commit is contained in:
parent
a51132d712
commit
304318b618
5 changed files with 422 additions and 76 deletions
|
@ -61,14 +61,15 @@ exports.syncMapFirst = function (lst, fn) {
|
|||
return [];
|
||||
}
|
||||
|
||||
exports.mapFirst = function (lst, fn, cb) {
|
||||
exports.mapFirst = function (lst, fn, cb, predicate) {
|
||||
if (predicate == null) predicate = (x) => (x != null && x.length > 0);
|
||||
var i = 0;
|
||||
|
||||
var next = function () {
|
||||
if (i >= lst.length) return cb(null, []);
|
||||
fn(lst[i++], function (err, result) {
|
||||
if (err) return cb(err);
|
||||
if (result.length) return cb(null, result);
|
||||
if (predicate(result)) return cb(null, result);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
@ -142,7 +143,7 @@ exports.callFirst = function (hook_name, args) {
|
|||
});
|
||||
}
|
||||
|
||||
function aCallFirst(hook_name, args, cb) {
|
||||
function aCallFirst(hook_name, args, cb, predicate) {
|
||||
if (!args) args = {};
|
||||
if (!cb) cb = function () {};
|
||||
if (pluginDefs.hooks[hook_name] === undefined) return cb(null, []);
|
||||
|
@ -151,20 +152,21 @@ function aCallFirst(hook_name, args, cb) {
|
|||
function (hook, cb) {
|
||||
hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); });
|
||||
},
|
||||
cb
|
||||
cb,
|
||||
predicate
|
||||
);
|
||||
}
|
||||
|
||||
/* return a Promise if cb is not supplied */
|
||||
exports.aCallFirst = function (hook_name, args, cb) {
|
||||
exports.aCallFirst = function (hook_name, args, cb, predicate) {
|
||||
if (cb === undefined) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
aCallFirst(hook_name, args, function(err, res) {
|
||||
return err ? reject(err) : resolve(res);
|
||||
});
|
||||
}, predicate);
|
||||
});
|
||||
} else {
|
||||
return aCallFirst(hook_name, args, cb);
|
||||
return aCallFirst(hook_name, args, cb, predicate);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue