diff --git a/server-ce/Dockerfile b/server-ce/Dockerfile index 7783e3dc2a..14d090f740 100644 --- a/server-ce/Dockerfile +++ b/server-ce/Dockerfile @@ -67,6 +67,7 @@ RUN chmod +x /usr/local/bin/grunt ENV SHARELATEX_CONFIG /etc/sharelatex/settings.js ENV WEB_API_USER "sharelatex" +ENV ADMIN_PRIVILEGE_AVAILABLE "true" ENV SHARELATEX_APP_NAME "Overleaf Community Edition" diff --git a/services/web/app/src/Features/Authentication/AuthenticationController.js b/services/web/app/src/Features/Authentication/AuthenticationController.js index 3fb323bdea..1db3231c54 100644 --- a/services/web/app/src/Features/Authentication/AuthenticationController.js +++ b/services/web/app/src/Features/Authentication/AuthenticationController.js @@ -24,6 +24,7 @@ const { acceptsJson, } = require('../../infrastructure/RequestContentTypeDetection') const { ParallelLoginError } = require('./AuthenticationErrors') +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') function send401WithChallenge(res) { res.setHeader('WWW-Authenticate', 'OverleafLogin') @@ -107,6 +108,12 @@ const AuthenticationController = { return res.redirect('/login') } // OAuth2 'state' mismatch + if (Settings.adminOnlyLogin && !hasAdminAccess(user)) { + return res.status(403).json({ + message: { type: 'error', text: 'Admin only panel' }, + }) + } + const auditInfo = AuthenticationController.getAuditInfo(req) const anonymousAnalyticsId = req.session.analyticsId @@ -380,7 +387,7 @@ const AuthenticationController = { return next() } const user = SessionManager.getSessionUser(req.session) - if (!(user && user.isAdmin)) { + if (!hasAdminAccess(user)) { return next() } const email = user.email diff --git a/services/web/app/src/Features/Authorization/AuthorizationManager.js b/services/web/app/src/Features/Authorization/AuthorizationManager.js index 5e1037f3bb..7f10fbacfe 100644 --- a/services/web/app/src/Features/Authorization/AuthorizationManager.js +++ b/services/web/app/src/Features/Authorization/AuthorizationManager.js @@ -8,6 +8,8 @@ const PrivilegeLevels = require('./PrivilegeLevels') const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler') const PublicAccessLevels = require('./PublicAccessLevels') const Errors = require('../Errors/Errors') +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') +const Settings = require('@overleaf/settings') function isRestrictedUser(userId, privilegeLevel, isTokenMember) { if (privilegeLevel === PrivilegeLevels.NONE) { @@ -91,8 +93,7 @@ async function getPrivilegeLevelForProjectWithUser( } if (!opts.ignoreSiteAdmin) { - const isAdmin = await isUserSiteAdmin(userId) - if (isAdmin) { + if (await isUserSiteAdmin(userId)) { return PrivilegeLevels.OWNER } } @@ -216,8 +217,9 @@ async function isUserSiteAdmin(userId) { if (!userId) { return false } + if (!Settings.adminPrivilegeAvailable) return false const user = await User.findOne({ _id: userId }, { isAdmin: 1 }).exec() - return user != null && user.isAdmin === true + return hasAdminAccess(user) } module.exports = { diff --git a/services/web/app/src/Features/Authorization/AuthorizationMiddleware.js b/services/web/app/src/Features/Authorization/AuthorizationMiddleware.js index 9855b4c233..f57bc7ce15 100644 --- a/services/web/app/src/Features/Authorization/AuthorizationMiddleware.js +++ b/services/web/app/src/Features/Authorization/AuthorizationMiddleware.js @@ -133,8 +133,7 @@ async function ensureUserCanAdminProject(req, res, next) { async function ensureUserIsSiteAdmin(req, res, next) { const userId = _getUserId(req) - const isAdmin = await AuthorizationManager.promises.isUserSiteAdmin(userId) - if (isAdmin) { + if (await AuthorizationManager.promises.isUserSiteAdmin(userId)) { logger.log({ userId }, 'allowing user admin access to site') return next() } diff --git a/services/web/app/src/Features/Collaborators/CollaboratorsController.js b/services/web/app/src/Features/Collaborators/CollaboratorsController.js index 2155a7723c..330dae2ae6 100644 --- a/services/web/app/src/Features/Collaborators/CollaboratorsController.js +++ b/services/web/app/src/Features/Collaborators/CollaboratorsController.js @@ -10,6 +10,7 @@ const TagsHandler = require('../Tags/TagsHandler') const Errors = require('../Errors/Errors') const logger = require('@overleaf/logger') const { expressify } = require('../../util/promises') +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') module.exports = { removeUserFromProject: expressify(removeUserFromProject), @@ -82,7 +83,7 @@ async function transferOwnership(req, res, next) { projectId, toUserId, { - allowTransferToNonCollaborators: sessionUser.isAdmin, + allowTransferToNonCollaborators: hasAdminAccess(sessionUser), sessionUserId: ObjectId(sessionUser._id), } ) diff --git a/services/web/app/src/Features/Helpers/AdminAuthorizationHelper.js b/services/web/app/src/Features/Helpers/AdminAuthorizationHelper.js new file mode 100644 index 0000000000..bc47dd98ba --- /dev/null +++ b/services/web/app/src/Features/Helpers/AdminAuthorizationHelper.js @@ -0,0 +1,11 @@ +const Settings = require('@overleaf/settings') + +module.exports = { + hasAdminAccess, +} + +function hasAdminAccess(user) { + if (!Settings.adminPrivilegeAvailable) return false + if (!user) return false + return Boolean(user.isAdmin) +} diff --git a/services/web/app/src/Features/Helpers/AuthorizationHelper.js b/services/web/app/src/Features/Helpers/AuthorizationHelper.js index 793d7d3db5..3eb8cd11fd 100644 --- a/services/web/app/src/Features/Helpers/AuthorizationHelper.js +++ b/services/web/app/src/Features/Helpers/AuthorizationHelper.js @@ -1,11 +1,12 @@ const { UserSchema } = require('../../models/User') +const { hasAdminAccess } = require('./AdminAuthorizationHelper') module.exports = { hasAnyStaffAccess, } function hasAnyStaffAccess(user) { - if (user.isAdmin) { + if (hasAdminAccess(user)) { return true } if (!user.staffAccess) { diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 742d3fe424..3333f2ccce 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -39,6 +39,7 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler') const FeaturesUpdater = require('../Subscription/FeaturesUpdater') const SpellingHandler = require('../Spelling/SpellingHandler') const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler') +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => { if (!affiliation.institution) return false @@ -965,7 +966,7 @@ const ProjectController = { refProviders: _.mapValues(user.refProviders, Boolean), alphaProgram: user.alphaProgram, betaProgram: user.betaProgram, - isAdmin: user.isAdmin, + isAdmin: hasAdminAccess(user), }, userSettings: { mode: user.ace.mode, diff --git a/services/web/app/src/Features/Project/ProjectHelper.js b/services/web/app/src/Features/Project/ProjectHelper.js index a7463b3986..f1e30961d0 100644 --- a/services/web/app/src/Features/Project/ProjectHelper.js +++ b/services/web/app/src/Features/Project/ProjectHelper.js @@ -2,6 +2,7 @@ const { ObjectId } = require('mongodb') const _ = require('lodash') const { promisify } = require('util') const Settings = require('@overleaf/settings') +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') const ENGINE_TO_COMPILER_MAP = { latex_dvipdf: 'latex', @@ -165,7 +166,7 @@ function _addNumericSuffixToProjectName(name, allProjectNames, maxLength) { function getAllowedImagesForUser(sessionUser) { const images = Settings.allowedImageNames || [] - if (sessionUser && sessionUser.isAdmin) { + if (hasAdminAccess(sessionUser)) { return images } else { return images.filter(image => !image.adminOnly) diff --git a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js index 23530c3a40..a5c2f8fa48 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js @@ -1,10 +1,11 @@ +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') const UserMembershipAuthorization = { hasStaffAccess(requiredStaffAccess) { return req => { if (!req.user) { return false } - if (req.user.isAdmin) { + if (hasAdminAccess(req.user)) { return true } return ( diff --git a/services/web/app/src/infrastructure/Csrf.js b/services/web/app/src/infrastructure/Csrf.js index 8d83a58328..9a1ffb5b09 100644 --- a/services/web/app/src/infrastructure/Csrf.js +++ b/services/web/app/src/infrastructure/Csrf.js @@ -15,6 +15,8 @@ const csurf = require('csurf') const csrf = csurf() const { promisify } = require('util') +const Settings = require('@overleaf/settings') +const logger = require('@overleaf/logger') // Wrapper for `csurf` middleware that provides a list of routes that can be excluded from csrf checks. // @@ -35,6 +37,18 @@ class Csrf { this.excluded_routes = {} } + static blockCrossOriginRequests() { + return function (req, res, next) { + const { origin } = req.headers + // NOTE: Only cross-origin requests must have an origin header set. + if (origin && !Settings.allowedOrigins.includes(origin)) { + logger.warn({ req }, 'blocking cross-origin request') + return res.sendStatus(403) + } + next() + } + } + disableDefaultCsrfProtection(route, method) { if (!this.excluded_routes[route]) { this.excluded_routes[route] = {} diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js index 597bf00b87..cef8f22615 100644 --- a/services/web/app/src/infrastructure/ExpressLocals.js +++ b/services/web/app/src/infrastructure/ExpressLocals.js @@ -12,6 +12,9 @@ const SessionManager = require('../Features/Authentication/SessionManager') const PackageVersions = require('./PackageVersions') const Modules = require('./Modules') const SafeHTMLSubstitute = require('../Features/Helpers/SafeHTMLSubstitution') +const { + hasAdminAccess, +} = require('../Features/Helpers/AdminAuthorizationHelper') let webpackManifest switch (process.env.NODE_ENV) { @@ -299,6 +302,8 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) { res.locals.getLoggedInUserId = () => SessionManager.getLoggedInUserId(req.session) res.locals.getSessionUser = () => SessionManager.getSessionUser(req.session) + res.locals.hasAdminAccess = () => + hasAdminAccess(SessionManager.getSessionUser(req.session)) next() }) diff --git a/services/web/app/src/infrastructure/Server.js b/services/web/app/src/infrastructure/Server.js index a3f4272dc8..5407e92a4a 100644 --- a/services/web/app/src/infrastructure/Server.js +++ b/services/web/app/src/infrastructure/Server.js @@ -40,6 +40,9 @@ const HttpErrorHandler = require('../Features/Errors/HttpErrorHandler') const UserSessionsManager = require('../Features/User/UserSessionsManager') const AuthenticationController = require('../Features/Authentication/AuthenticationController') const SessionManager = require('../Features/Authentication/SessionManager') +const { + hasAdminAccess, +} = require('../Features/Helpers/AdminAuthorizationHelper') const STATIC_CACHE_AGE = Settings.cacheStaticAssets ? oneDayInMilliseconds * 365 @@ -141,6 +144,11 @@ app.use(methodOverride()) app.use(bearerToken()) app.use(metrics.http.monitor(logger)) + +if (Settings.blockCrossOriginRequests) { + app.use(Csrf.blockCrossOriginRequests()) +} + RedirectManager.apply(webRouter) ProxyManager.apply(publicApiRouter) @@ -228,10 +236,7 @@ webRouter.use(SessionAutostartMiddleware.invokeCallbackMiddleware) webRouter.use(function (req, res, next) { if (Settings.siteIsOpen) { next() - } else if ( - SessionManager.getSessionUser(req.session) && - SessionManager.getSessionUser(req.session).isAdmin - ) { + } else if (hasAdminAccess(SessionManager.getSessionUser(req.session))) { next() } else { HttpErrorHandler.maintenance(req, res) diff --git a/services/web/app/views/layout/navbar-marketing.pug b/services/web/app/views/layout/navbar-marketing.pug index 4325d50e39..2d2aab6672 100644 --- a/services/web/app/views/layout/navbar-marketing.pug +++ b/services/web/app/views/layout/navbar-marketing.pug @@ -15,7 +15,7 @@ nav.navbar.navbar-default.navbar-main else a(href='/', aria-label=settings.appName).navbar-brand - - var canDisplayAdminMenu = getSessionUser() && getSessionUser().isAdmin + - var canDisplayAdminMenu = hasAdminAccess() - var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement))) .navbar-collapse.collapse(data-ol-navbar-main-collapse) ul.nav.navbar-nav.navbar-right diff --git a/services/web/app/views/layout/navbar.pug b/services/web/app/views/layout/navbar.pug index 7ae635ba40..659ebba199 100644 --- a/services/web/app/views/layout/navbar.pug +++ b/services/web/app/views/layout/navbar.pug @@ -10,7 +10,7 @@ nav.navbar.navbar-default.navbar-main else a(href='/', aria-label=settings.appName).navbar-brand - - var canDisplayAdminMenu = getSessionUser() && getSessionUser().isAdmin + - var canDisplayAdminMenu = hasAdminAccess() - var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement))) .navbar-collapse.collapse(collapse="navCollapsed") ul.nav.navbar-nav.navbar-right diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index 8e5abb1a13..3631740d7a 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -283,6 +283,12 @@ module.exports = { bcryptRounds: parseInt(process.env.BCRYPT_ROUNDS, 10) || 12, }, // number of rounds used to hash user passwords (raised to power 2) + adminUrl: process.env.ADMIN_URL, + adminOnlyLogin: process.env.ADMIN_ONLY_LOGIN === 'true', + adminPrivilegeAvailable: process.env.ADMIN_PRIVILEGE_AVAILABLE === 'true', + blockCrossOriginRequests: process.env.BLOCK_CROSS_ORIGIN_REQUESTS === 'true', + allowedOrigins: (process.env.ALLOWED_ORIGINS || siteUrl).split(','), + httpAuthUsers, // Default features diff --git a/services/web/docker-compose.common.env b/services/web/docker-compose.common.env index 98c52aecff..e9e50ab7d8 100644 --- a/services/web/docker-compose.common.env +++ b/services/web/docker-compose.common.env @@ -1,3 +1,4 @@ +ADMIN_PRIVILEGE_AVAILABLE=true BCRYPT_ROUNDS=1 REDIS_HOST=redis QUEUES_REDIS_HOST=redis diff --git a/services/web/modules/launchpad/app/src/LaunchpadController.js b/services/web/modules/launchpad/app/src/LaunchpadController.js index 1ddd90557a..480bb2f87f 100644 --- a/services/web/modules/launchpad/app/src/LaunchpadController.js +++ b/services/web/modules/launchpad/app/src/LaunchpadController.js @@ -25,6 +25,9 @@ const UserGetter = require('../../../../app/src/Features/User/UserGetter') const { User } = require('../../../../app/src/models/User') const AuthenticationController = require('../../../../app/src/Features/Authentication/AuthenticationController') const SessionManager = require('../../../../app/src/Features/Authentication/SessionManager') +const { + hasAdminAccess, +} = require('../../../../app/src/Features/Helpers/AdminAuthorizationHelper') module.exports = LaunchpadController = { _getAuthMethod() { @@ -67,7 +70,7 @@ module.exports = LaunchpadController = { if (err != null) { return next(err) } - if (user && user.isAdmin) { + if (hasAdminAccess(user)) { return res.render(Path.resolve(__dirname, '../views/launchpad'), { wsUrl: Settings.wsUrl, adminUserExists, diff --git a/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js b/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js index 1f4e53dafc..53d1a628c6 100644 --- a/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js +++ b/services/web/modules/launchpad/test/unit/src/LaunchpadControllerTests.js @@ -31,7 +31,9 @@ describe('LaunchpadController', function () { this.User = {} this.LaunchpadController = SandboxedModule.require(modulePath, { requires: { - '@overleaf/settings': (this.Settings = {}), + '@overleaf/settings': (this.Settings = { + adminPrivilegeAvailable: true, + }), '@overleaf/metrics': (this.Metrics = {}), '../../../../app/src/Features/User/UserRegistrationHandler': (this.UserRegistrationHandler = {}), diff --git a/services/web/test/acceptance/src/AdminOnlyLoginTests.js b/services/web/test/acceptance/src/AdminOnlyLoginTests.js new file mode 100644 index 0000000000..8ad7d77f4f --- /dev/null +++ b/services/web/test/acceptance/src/AdminOnlyLoginTests.js @@ -0,0 +1,64 @@ +const Settings = require('@overleaf/settings') +const { expect } = require('chai') +const User = require('./helpers/User').promises + +describe('AdminOnlyLogin', function () { + let adminUser, regularUser + const flagBefore = Settings.adminOnlyLogin + after(function () { + Settings.adminOnlyLogin = flagBefore + }) + + beforeEach('create admin user', async function () { + adminUser = new User() + await adminUser.ensureUserExists() + await adminUser.ensureAdmin() + }) + + beforeEach('create regular user', async function () { + regularUser = new User() + await regularUser.ensureUserExists() + }) + + async function expectCanLogin(user) { + const response = await user.login() + expect(response.statusCode).to.equal(200) + expect(response.body).to.deep.equal({ redir: '/project' }) + } + + async function expectRejectedLogin(user) { + const response = await user.login() + expect(response.statusCode).to.equal(403) + expect(response.body).to.deep.equal({ + message: { type: 'error', text: 'Admin only panel' }, + }) + } + + describe('adminOnlyLogin=true', function () { + beforeEach(function () { + Settings.adminOnlyLogin = true + }) + + it('should allow the admin user to login', async function () { + await expectCanLogin(adminUser) + }) + + it('should block a regular user from login', async function () { + await expectRejectedLogin(regularUser) + }) + }) + + describe('adminOnlyLogin=false', function () { + beforeEach(function () { + Settings.adminOnlyLogin = false + }) + + it('should allow the admin user to login', async function () { + await expectCanLogin(adminUser) + }) + + it('should allow a regular user to login', async function () { + await expectCanLogin(regularUser) + }) + }) +}) diff --git a/services/web/test/acceptance/src/AdminPrivilegeAvailableTests.js b/services/web/test/acceptance/src/AdminPrivilegeAvailableTests.js new file mode 100644 index 0000000000..6673551fb5 --- /dev/null +++ b/services/web/test/acceptance/src/AdminPrivilegeAvailableTests.js @@ -0,0 +1,62 @@ +const Settings = require('@overleaf/settings') +const { expect } = require('chai') +const User = require('./helpers/User').promises + +describe('AdminPrivilegeAvailable', function () { + let adminUser + const flagBefore = Settings.adminPrivilegeAvailable + after(function () { + Settings.adminPrivilegeAvailable = flagBefore + }) + + beforeEach('create admin user', async function () { + adminUser = new User() + await adminUser.ensureUserExists() + await adminUser.ensureAdmin() + await adminUser.login() + }) + + let projectIdOwned, otherUsersProjectId + beforeEach('create owned project', async function () { + projectIdOwned = await adminUser.createProject('owned project') + }) + + beforeEach('create other user and project', async function () { + const otherUser = new User() + await otherUser.login() + + otherUsersProjectId = await otherUser.createProject('other users project') + }) + + async function hasAccess(projectId) { + const { response } = await adminUser.doRequest( + 'GET', + `/project/${projectId}` + ) + return response.statusCode === 200 + } + + describe('adminPrivilegeAvailable=true', function () { + beforeEach(function () { + Settings.adminPrivilegeAvailable = true + }) + it('should grant the admin access to owned project', async function () { + expect(await hasAccess(projectIdOwned)).to.equal(true) + }) + it('should grant the admin access to non-owned project', async function () { + expect(await hasAccess(otherUsersProjectId)).to.equal(true) + }) + }) + + describe('adminPrivilegeAvailable=false', function () { + beforeEach(function () { + Settings.adminPrivilegeAvailable = false + }) + it('should grant the admin access to owned project', async function () { + expect(await hasAccess(projectIdOwned)).to.equal(true) + }) + it('should block the admin from non-owned project', async function () { + expect(await hasAccess(otherUsersProjectId)).to.equal(false) + }) + }) +}) diff --git a/services/web/test/unit/src/Authentication/AuthenticationControllerTests.js b/services/web/test/unit/src/Authentication/AuthenticationControllerTests.js index 6930231fa1..a3c37c4c48 100644 --- a/services/web/test/unit/src/Authentication/AuthenticationControllerTests.js +++ b/services/web/test/unit/src/Authentication/AuthenticationControllerTests.js @@ -33,6 +33,10 @@ describe('AuthenticationController', function () { this.AuthenticationController = SandboxedModule.require(modulePath, { requires: { + '../Helpers/AdminAuthorizationHelper': (this.AdminAuthorizationHelper = + { + hasAdminAccess: sinon.stub().returns(false), + }), './AuthenticationErrors': AuthenticationErrors, '../User/UserAuditLogHandler': (this.UserAuditLogHandler = { addEntry: sinon.stub().yields(null), @@ -158,6 +162,7 @@ describe('AuthenticationController', function () { this.SessionManager.getSessionUser = sinon .stub() .returns({ isAdmin: true }) + this.AdminAuthorizationHelper.hasAdminAccess.returns(true) this.AuthenticationController.validateAdmin(this.req, this.res, err => { this.SessionManager.getSessionUser.called.should.equal(true) expect(err).to.exist @@ -167,6 +172,7 @@ describe('AuthenticationController', function () { it('should block an admin with a bad domain', function (done) { this.SessionManager.getSessionUser = sinon.stub().returns(this.badAdmin) + this.AdminAuthorizationHelper.hasAdminAccess.returns(true) this.AuthenticationController.validateAdmin(this.req, this.res, err => { this.SessionManager.getSessionUser.called.should.equal(true) expect(err).to.exist diff --git a/services/web/test/unit/src/Authorization/AuthorizationManagerTests.js b/services/web/test/unit/src/Authorization/AuthorizationManagerTests.js index e4849ea7ea..607d4eb949 100644 --- a/services/web/test/unit/src/Authorization/AuthorizationManagerTests.js +++ b/services/web/test/unit/src/Authorization/AuthorizationManagerTests.js @@ -54,7 +54,10 @@ describe('AuthorizationManager', function () { '../Project/ProjectGetter': this.ProjectGetter, '../../models/User': { User: this.User }, '../TokenAccess/TokenAccessHandler': this.TokenAccessHandler, - '@overleaf/settings': { passwordStrengthOptions: {} }, + '@overleaf/settings': { + passwordStrengthOptions: {}, + adminPrivilegeAvailable: true, + }, }, }) }) diff --git a/services/web/test/unit/src/HelperFiles/AuthorizationHelperTests.js b/services/web/test/unit/src/HelperFiles/AuthorizationHelperTests.js index 9c58acfb9b..ac0e84ee71 100644 --- a/services/web/test/unit/src/HelperFiles/AuthorizationHelperTests.js +++ b/services/web/test/unit/src/HelperFiles/AuthorizationHelperTests.js @@ -1,4 +1,5 @@ const SandboxedModule = require('sandboxed-module') +const sinon = require('sinon') const { expect } = require('chai') const modulePath = '../../../../app/src/Features/Helpers/AuthorizationHelper' @@ -6,6 +7,9 @@ describe('AuthorizationHelper', function () { beforeEach(function () { this.AuthorizationHelper = SandboxedModule.require(modulePath, { requires: { + './AdminAuthorizationHelper': (this.AdminAuthorizationHelper = { + hasAdminAccess: sinon.stub().returns(false), + }), '../../models/User': { UserSchema: { obj: { @@ -38,11 +42,13 @@ describe('AuthorizationHelper', function () { it('with admin user', function () { const user = { isAdmin: true } + this.AdminAuthorizationHelper.hasAdminAccess.returns(true) expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.true }) it('with staff user', function () { const user = { staffAccess: { adminMetrics: true, somethingElse: false } } + this.AdminAuthorizationHelper.hasAdminAccess.returns(true) expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.true }) diff --git a/services/web/test/unit/src/Project/ProjectHelperTests.js b/services/web/test/unit/src/Project/ProjectHelperTests.js index 0548610d01..0cd7e4d43f 100644 --- a/services/web/test/unit/src/Project/ProjectHelperTests.js +++ b/services/web/test/unit/src/Project/ProjectHelperTests.js @@ -22,6 +22,7 @@ describe('ProjectHelper', function () { } this.Settings = { + adminPrivilegeAvailable: true, allowedImageNames: [ { imageName: 'texlive-full:2018.1', imageDesc: 'TeX Live 2018' }, { imageName: 'texlive-full:2019.1', imageDesc: 'TeX Live 2019' },