'use strict'; /** * 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. */ const authorManager = require('./AuthorManager'); const hooks = require('../../static/js/pluginfw/hooks.js'); const padManager = require('./PadManager'); const sessionManager = require('./SessionManager'); const settings = require('../utils/Settings'); const webaccess = require('../hooks/express/webaccess'); const log4js = require('log4js'); const authLogger = log4js.getLogger('auth'); const DENY = Object.freeze({accessStatus: 'deny'}); /** * 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. * @param userSettings is the settings.users[username] object (or equivalent from an authn plugin). * @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. * * 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). */ exports.checkAccess = async (padID, sessionCookie, token, userSettings) => { if (!padID) { authLogger.debug('access denied: missing padID'); return DENY; } let canCreate = !settings.editOnly; // Authentication and authorization checks. if (settings.loadTest) { console.warn( 'bypassing socket.io authentication and authorization checks due to settings.loadTest'); } else if (settings.requireAuthentication) { if (userSettings == null) { authLogger.debug('access denied: authentication is required'); return DENY; } 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. const padAuthzs = userSettings.padAuthorizations || {}; const level = webaccess.normalizeAuthzLevel(padAuthzs[padID]); if (!level) { authLogger.debug('access denied: unauthorized'); return DENY; } if (level !== 'create') canCreate = false; } // allow plugins to deny access const isFalse = (x) => x === false; if (hooks.callAll('onAccessCheck', {padID, token, sessionCookie}).some(isFalse)) { authLogger.debug('access denied: an onAccessCheck hook function returned false'); return DENY; } // 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); const padExists = await p_padExists; if (!padExists && !canCreate) { authLogger.debug('access denied: user attempted to create a pad, which is prohibited'); return DENY; } const sessionAuthorID = await p_sessionAuthorID; if (settings.requireSession && !sessionAuthorID) { authLogger.debug('access denied: HTTP API session is required'); return DENY; } const grant = { accessStatus: 'grant', authorID: (sessionAuthorID != null) ? sessionAuthorID : await p_tokenAuthorID, }; if (!padID.includes('$')) { // Only group pads can be private, so there is nothing more to check for this non-group pad. return grant; } if (!padExists) { if (sessionAuthorID == null) { authLogger.debug('access denied: must have an HTTP API session to create a group pad'); return DENY; } // Creating a group pad, so there is no public status to check. return grant; } const pad = await padManager.getPad(padID); if (!pad.getPublicStatus() && sessionAuthorID == null) { authLogger.debug('access denied: must have an HTTP API session to access private group pads'); return DENY; } return grant; };