| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- const sinon = require('sinon')
- const { expect } = require('chai')
- const SandboxedModule = require('sandboxed-module')
- const { ObjectId } = require('mongodb')
- const Errors = require('../../../../app/src/Features/Errors/Errors')
- const MODULE_PATH =
- '../../../../app/src/Features/Authorization/AuthorizationMiddleware.js'
- describe('AuthorizationMiddleware', function () {
- beforeEach(function () {
- this.userId = new ObjectId().toString()
- this.project_id = new ObjectId().toString()
- this.token = 'some-token'
- this.AuthenticationController = {}
- this.SessionManager = {
- getSessionUser: sinon.stub().returns(null),
- getLoggedInUserId: sinon.stub().returns(this.userId),
- isUserLoggedIn: sinon.stub().returns(true),
- }
- this.AuthorizationManager = {
- promises: {
- canUserReadProject: sinon.stub(),
- canUserWriteProjectSettings: sinon.stub(),
- canUserWriteProjectContent: sinon.stub(),
- canUserAdminProject: sinon.stub(),
- canUserRenameProject: sinon.stub(),
- isUserSiteAdmin: sinon.stub(),
- isRestrictedUserForProject: sinon.stub(),
- },
- }
- this.HttpErrorHandler = {
- forbidden: sinon.stub(),
- }
- this.TokenAccessHandler = {
- getRequestToken: sinon.stub().returns(this.token),
- }
- this.AuthorizationMiddleware = SandboxedModule.require(MODULE_PATH, {
- requires: {
- './AuthorizationManager': this.AuthorizationManager,
- '../Errors/HttpErrorHandler': this.HttpErrorHandler,
- '../Authentication/AuthenticationController':
- this.AuthenticationController,
- '../Authentication/SessionManager': this.SessionManager,
- '../TokenAccess/TokenAccessHandler': this.TokenAccessHandler,
- '../Helpers/AdminAuthorizationHelper': {
- canRedirectToAdminDomain: sinon.stub().returns(false),
- },
- },
- })
- this.req = {
- params: {
- project_id: this.project_id,
- },
- body: {},
- }
- this.res = {
- redirect: sinon.stub(),
- locals: {
- currentUrl: '/current/url',
- },
- }
- this.next = sinon.stub()
- })
- describe('ensureCanReadProject', function () {
- testMiddleware('ensureUserCanReadProject', 'canUserReadProject')
- })
- describe('ensureUserCanWriteProjectContent', function () {
- testMiddleware(
- 'ensureUserCanWriteProjectContent',
- 'canUserWriteProjectContent'
- )
- })
- describe('ensureUserCanWriteProjectSettings', function () {
- describe('when renaming a project', function () {
- beforeEach(function () {
- this.req.body.name = 'new project name'
- })
- testMiddleware(
- 'ensureUserCanWriteProjectSettings',
- 'canUserRenameProject'
- )
- })
- describe('when setting another parameter', function () {
- beforeEach(function () {
- this.req.body.compiler = 'texlive-2017'
- })
- testMiddleware(
- 'ensureUserCanWriteProjectSettings',
- 'canUserWriteProjectSettings'
- )
- })
- })
- describe('ensureUserCanAdminProject', function () {
- testMiddleware('ensureUserCanAdminProject', 'canUserAdminProject')
- })
- describe('ensureUserIsSiteAdmin', function () {
- describe('with logged in user', function () {
- describe('when user has permission', function () {
- setupSiteAdmin(true)
- invokeMiddleware('ensureUserIsSiteAdmin')
- expectNext()
- })
- describe("when user doesn't have permission", function () {
- setupSiteAdmin(false)
- invokeMiddleware('ensureUserIsSiteAdmin')
- expectRedirectToRestricted()
- })
- })
- describe('with oauth user', function () {
- setupOAuthUser()
- describe('when user has permission', function () {
- setupSiteAdmin(true)
- invokeMiddleware('ensureUserIsSiteAdmin')
- expectNext()
- })
- describe("when user doesn't have permission", function () {
- setupSiteAdmin(false)
- invokeMiddleware('ensureUserIsSiteAdmin')
- expectRedirectToRestricted()
- })
- })
- describe('with anonymous user', function () {
- setupAnonymousUser()
- invokeMiddleware('ensureUserIsSiteAdmin')
- expectRedirectToRestricted()
- })
- })
- describe('blockRestrictedUserFromProject', function () {
- describe('for a restricted user', function () {
- setupPermission('isRestrictedUserForProject', true)
- invokeMiddleware('blockRestrictedUserFromProject')
- expectForbidden()
- })
- describe('for a regular user', function (done) {
- setupPermission('isRestrictedUserForProject', false)
- invokeMiddleware('blockRestrictedUserFromProject')
- expectNext()
- })
- })
- describe('ensureUserCanReadMultipleProjects', function () {
- beforeEach(function () {
- this.req.query = { project_ids: 'project1,project2' }
- })
- describe('with logged in user', function () {
- describe('when user has permission to access all projects', function () {
- beforeEach(function () {
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project1', this.token)
- .resolves(true)
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project2', this.token)
- .resolves(true)
- })
- invokeMiddleware('ensureUserCanReadMultipleProjects')
- expectNext()
- })
- describe("when user doesn't have permission to access one of the projects", function () {
- beforeEach(function () {
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project1', this.token)
- .resolves(true)
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project2', this.token)
- .resolves(false)
- })
- invokeMiddleware('ensureUserCanReadMultipleProjects')
- expectRedirectToRestricted()
- })
- })
- describe('with oauth user', function () {
- setupOAuthUser()
- beforeEach(function () {
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project1', this.token)
- .resolves(true)
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(this.userId, 'project2', this.token)
- .resolves(true)
- })
- invokeMiddleware('ensureUserCanReadMultipleProjects')
- expectNext()
- })
- describe('with anonymous user', function () {
- setupAnonymousUser()
- describe('when user has permission', function () {
- describe('when user has permission to access all projects', function () {
- beforeEach(function () {
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(null, 'project1', this.token)
- .resolves(true)
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(null, 'project2', this.token)
- .resolves(true)
- })
- invokeMiddleware('ensureUserCanReadMultipleProjects')
- expectNext()
- })
- describe("when user doesn't have permission to access one of the projects", function () {
- beforeEach(function () {
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(null, 'project1', this.token)
- .resolves(true)
- this.AuthorizationManager.promises.canUserReadProject
- .withArgs(null, 'project2', this.token)
- .resolves(false)
- })
- invokeMiddleware('ensureUserCanReadMultipleProjects')
- expectRedirectToRestricted()
- })
- })
- })
- })
- })
- function testMiddleware(middleware, permission) {
- describe(middleware, function () {
- describe('with missing project_id', function () {
- setupMissingProjectId()
- invokeMiddleware(middleware)
- expectError()
- })
- describe('with logged in user', function () {
- describe('when user has permission', function () {
- setupPermission(permission, true)
- invokeMiddleware(middleware)
- expectNext()
- })
- describe("when user doesn't have permission", function () {
- setupPermission(permission, false)
- invokeMiddleware(middleware)
- expectForbidden()
- })
- })
- describe('with oauth user', function () {
- setupOAuthUser()
- describe('when user has permission', function () {
- setupPermission(permission, true)
- invokeMiddleware(middleware)
- expectNext()
- })
- describe("when user doesn't have permission", function () {
- setupPermission(permission, false)
- invokeMiddleware(middleware)
- expectForbidden()
- })
- })
- describe('with anonymous user', function () {
- setupAnonymousUser()
- describe('when user has permission', function () {
- setupAnonymousPermission(permission, true)
- invokeMiddleware(middleware)
- expectNext()
- })
- describe("when user doesn't have permission", function () {
- setupAnonymousPermission(permission, false)
- invokeMiddleware(middleware)
- expectForbidden()
- })
- })
- describe('with malformed project id', function () {
- setupMalformedProjectId()
- invokeMiddleware(middleware)
- expectNotFound()
- })
- })
- }
- function setupAnonymousUser() {
- beforeEach('set up anonymous user', function () {
- this.SessionManager.getLoggedInUserId.returns(null)
- this.SessionManager.isUserLoggedIn.returns(false)
- })
- }
- function setupOAuthUser() {
- beforeEach('set up oauth user', function () {
- this.SessionManager.getLoggedInUserId.returns(null)
- this.req.oauth_user = { _id: this.userId }
- })
- }
- function setupPermission(permission, value) {
- beforeEach(`set permission ${permission} to ${value}`, function () {
- this.AuthorizationManager.promises[permission]
- .withArgs(this.userId, this.project_id, this.token)
- .resolves(value)
- })
- }
- function setupAnonymousPermission(permission, value) {
- beforeEach(`set anonymous permission ${permission} to ${value}`, function () {
- this.AuthorizationManager.promises[permission]
- .withArgs(null, this.project_id, this.token)
- .resolves(value)
- })
- }
- function setupSiteAdmin(value) {
- beforeEach(`set site admin to ${value}`, function () {
- this.AuthorizationManager.promises.isUserSiteAdmin
- .withArgs(this.userId)
- .resolves(value)
- })
- }
- function setupMissingProjectId() {
- beforeEach('set up missing project id', function () {
- delete this.req.params.project_id
- })
- }
- function setupMalformedProjectId() {
- beforeEach('set up malformed project id', function () {
- this.req.params = { project_id: 'bad-project-id' }
- })
- }
- function invokeMiddleware(method) {
- beforeEach(`invoke ${method}`, function (done) {
- this.next.callsFake(() => done())
- this.HttpErrorHandler.forbidden.callsFake(() => done())
- this.res.redirect.callsFake(() => done())
- this.AuthorizationMiddleware[method](this.req, this.res, this.next)
- })
- }
- function expectNext() {
- it('calls the next middleware', function () {
- expect(this.next).to.have.been.calledWithExactly()
- })
- }
- function expectError() {
- it('calls the error middleware', function () {
- expect(this.next).to.have.been.calledWith(sinon.match.instanceOf(Error))
- })
- }
- function expectNotFound() {
- it('raises a 404', function () {
- expect(this.next).to.have.been.calledWith(
- sinon.match.instanceOf(Errors.NotFoundError)
- )
- })
- }
- function expectForbidden() {
- it('raises a 403', function () {
- expect(this.HttpErrorHandler.forbidden).to.have.been.calledWith(
- this.req,
- this.res
- )
- expect(this.next).not.to.have.been.called
- })
- }
- function expectRedirectToRestricted() {
- it('redirects to restricted', function () {
- expect(this.res.redirect).to.have.been.calledWith(
- '/restricted?from=%2Fcurrent%2Furl'
- )
- expect(this.next).not.to.have.been.called
- })
- }
|