2021-01-21 21:06:52 +00:00
|
|
|
'use strict';
|
2011-08-13 22:07:21 +01:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2011-12-04 16:50:02 +01:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
const authorManager = require('./AuthorManager');
|
2021-01-21 21:06:52 +00:00
|
|
|
const hooks = require('../../static/js/pluginfw/hooks.js');
|
2020-11-23 13:24:19 -05:00
|
|
|
const padManager = require('./PadManager');
|
|
|
|
const sessionManager = require('./SessionManager');
|
|
|
|
const settings = require('../utils/Settings');
|
2020-09-11 19:26:26 -04:00
|
|
|
const webaccess = require('../hooks/express/webaccess');
|
2020-11-23 13:24:19 -05:00
|
|
|
const log4js = require('log4js');
|
|
|
|
const authLogger = log4js.getLogger('auth');
|
2012-01-28 13:24:58 +01:00
|
|
|
|
2020-09-02 18:43:05 -04:00
|
|
|
const DENY = Object.freeze({accessStatus: 'deny'});
|
|
|
|
|
2011-08-13 22:07:21 +01:00
|
|
|
/**
|
2020-09-02 17:16:02 -04:00
|
|
|
* Determines whether the user can access a pad.
|
|
|
|
*
|
|
|
|
* @param padID identifies the pad the user wants to access.
|
|
|
|
* @param sessionCookie identifies the sessions the user created via the HTTP API, if any.
|
|
|
|
* Note: The term "session" used here is unrelated to express-session.
|
|
|
|
* @param token is a random token of the form t.randomstring_of_length_20 generated by the client
|
|
|
|
* when using the web UI (not the HTTP API). This token is only used if settings.requireSession
|
|
|
|
* is false and the user is accessing a public pad. If there is not an author already associated
|
|
|
|
* with this token then a new author object is created (including generating an author ID) and
|
|
|
|
* associated with this token.
|
2020-09-11 17:12:29 -04:00
|
|
|
* @param userSettings is the settings.users[username] object (or equivalent from an authn plugin).
|
2020-10-07 13:43:54 +01:00
|
|
|
* @return {accessStatus: grant|deny, authorID: a.xxxxxx}. The caller must use the author ID
|
|
|
|
* returned in this object when making any changes associated with the author.
|
2020-09-02 17:16:02 -04:00
|
|
|
*
|
|
|
|
* WARNING: Tokens and session IDs MUST be kept secret, otherwise users will be able to impersonate
|
|
|
|
* each other (which might allow them to gain privileges).
|
2019-02-08 23:20:57 +01:00
|
|
|
*/
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
|
2019-02-08 23:20:57 +01:00
|
|
|
if (!padID) {
|
2020-09-10 12:47:59 -04:00
|
|
|
authLogger.debug('access denied: missing padID');
|
2020-09-02 18:43:05 -04:00
|
|
|
return DENY;
|
2013-10-12 18:41:48 +02:00
|
|
|
}
|
2011-11-21 12:44:33 -05:00
|
|
|
|
2020-09-11 19:46:47 -04:00
|
|
|
let canCreate = !settings.editOnly;
|
|
|
|
|
2020-10-03 17:52:10 -04:00
|
|
|
// Authentication and authorization checks.
|
|
|
|
if (settings.loadTest) {
|
|
|
|
console.warn(
|
|
|
|
'bypassing socket.io authentication and authorization checks due to settings.loadTest');
|
|
|
|
} else if (settings.requireAuthentication) {
|
2020-09-11 19:26:26 -04:00
|
|
|
if (userSettings == null) {
|
|
|
|
authLogger.debug('access denied: authentication is required');
|
|
|
|
return DENY;
|
|
|
|
}
|
2020-09-28 06:22:06 -04:00
|
|
|
if (userSettings.canCreate != null && !userSettings.canCreate) canCreate = false;
|
|
|
|
if (userSettings.readOnly) canCreate = false;
|
|
|
|
// Note: userSettings.padAuthorizations should still be populated even if
|
|
|
|
// settings.requireAuthorization is false.
|
2020-09-11 19:26:26 -04:00
|
|
|
const padAuthzs = userSettings.padAuthorizations || {};
|
|
|
|
const level = webaccess.normalizeAuthzLevel(padAuthzs[padID]);
|
|
|
|
if (!level) {
|
|
|
|
authLogger.debug('access denied: unauthorized');
|
|
|
|
return DENY;
|
|
|
|
}
|
2020-09-11 19:46:47 -04:00
|
|
|
if (level !== 'create') canCreate = false;
|
2020-09-11 17:12:29 -04:00
|
|
|
}
|
|
|
|
|
2017-07-10 20:54:32 +02:00
|
|
|
// allow plugins to deny access
|
2020-09-10 12:47:59 -04:00
|
|
|
const isFalse = (x) => x === false;
|
2020-10-07 13:43:54 +01:00
|
|
|
if (hooks.callAll('onAccessCheck', {padID, token, sessionCookie}).some(isFalse)) {
|
2020-09-10 12:47:59 -04:00
|
|
|
authLogger.debug('access denied: an onAccessCheck hook function returned false');
|
2020-09-02 18:43:05 -04:00
|
|
|
return DENY;
|
2017-07-10 20:54:32 +02:00
|
|
|
}
|
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
// start fetching the info we may need
|
|
|
|
const p_sessionAuthorID = sessionManager.findAuthorID(padID.split('$')[0], sessionCookie);
|
|
|
|
const p_tokenAuthorID = authorManager.getAuthor4Token(token);
|
|
|
|
const p_padExists = padManager.doesPadExist(padID);
|
2019-01-28 13:13:24 +00:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
const padExists = await p_padExists;
|
2020-09-11 19:46:47 -04:00
|
|
|
if (!padExists && !canCreate) {
|
2020-09-10 12:47:59 -04:00
|
|
|
authLogger.debug('access denied: user attempted to create a pad, which is prohibited');
|
|
|
|
return DENY;
|
2019-01-28 13:13:24 +00:00
|
|
|
}
|
2019-02-08 23:20:57 +01:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
const sessionAuthorID = await p_sessionAuthorID;
|
|
|
|
if (settings.requireSession && !sessionAuthorID) {
|
|
|
|
authLogger.debug('access denied: HTTP API session is required');
|
|
|
|
return DENY;
|
2019-01-28 13:13:24 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
const grant = {
|
|
|
|
accessStatus: 'grant',
|
|
|
|
authorID: (sessionAuthorID != null) ? sessionAuthorID : await p_tokenAuthorID,
|
|
|
|
};
|
2018-08-29 02:33:29 +02:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
if (!padID.includes('$')) {
|
2020-10-07 13:43:54 +01:00
|
|
|
// Only group pads can be private, so there is nothing more to check for this non-group pad.
|
2020-09-10 12:47:59 -04:00
|
|
|
return grant;
|
2011-08-13 22:07:21 +01:00
|
|
|
}
|
2019-02-08 23:20:57 +01:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
if (!padExists) {
|
|
|
|
if (sessionAuthorID == null) {
|
|
|
|
authLogger.debug('access denied: must have an HTTP API session to create a group pad');
|
2020-09-02 18:43:05 -04:00
|
|
|
return DENY;
|
2019-01-28 13:13:24 +00:00
|
|
|
}
|
2020-10-07 13:43:54 +01:00
|
|
|
// Creating a group pad, so there is no public status to check.
|
2020-09-10 12:47:59 -04:00
|
|
|
return grant;
|
2019-01-28 13:13:24 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
const pad = await padManager.getPad(padID);
|
2019-02-08 23:20:57 +01:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
if (!pad.getPublicStatus() && sessionAuthorID == null) {
|
|
|
|
authLogger.debug('access denied: must have an HTTP API session to access private group pads');
|
|
|
|
return DENY;
|
|
|
|
}
|
2019-01-28 13:13:24 +00:00
|
|
|
|
2020-09-10 12:47:59 -04:00
|
|
|
return grant;
|
|
|
|
};
|