Shane Kilkelly 9 лет назад
Родитель
Сommit
574b115022

+ 47 - 24
services/web/app/coffee/Features/Authorization/AuthorizationManager.coffee

@@ -5,34 +5,48 @@ PrivilegeLevels = require("./PrivilegeLevels")
 PublicAccessLevels = require("./PublicAccessLevels")
 Errors = require("../Errors/Errors")
 ObjectId = require("mongojs").ObjectId
+TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
 
 
 module.exports = AuthorizationManager =
+
+
 	# Get the privilege level that the user has for the project
 	# Returns:
 	#	* privilegeLevel: "owner", "readAndWrite", of "readOnly" if the user has
 	#	  access. false if the user does not have access
 	#   * becausePublic: true if the access level is only because the project is public.
-	getPrivilegeLevelForProject: (user_id, project_id, callback = (error, privilegeLevel, becausePublic) ->) ->
-		getPublicAccessLevel = () ->
+	getPrivilegeLevelForProject: (req, user_id, project_id,
+																callback = (error, privilegeLevel, becausePublic) ->) ->
+
+		getPublicAccessLevel = (project_id, cb=(err, level)->) ->
 			if !ObjectId.isValid(project_id)
-				return callback(new Error("invalid project id"))
+				return cb(new Error("invalid project id"))
 			Project.findOne { _id: project_id }, { publicAccesLevel: 1 }, (error, project) ->
-				return callback(error) if error?
+				return cb(error) if error?
 				if !project?
-					return callback new Errors.NotFoundError("no project found with id #{project_id}")
-				if project.publicAccesLevel == PublicAccessLevels.READ_ONLY
-					return callback null, PrivilegeLevels.READ_ONLY, true
-				else if project.publicAccesLevel == PublicAccessLevels.READ_AND_WRITE
-					return callback null, PrivilegeLevels.READ_AND_WRITE, true
-				else if project.publicAccesLevel == PublicAccessLevels.TOKEN_BASED
-					return callback null, PrivilegeLevels.READ_ONLY, false
-				else
-					return callback null, PrivilegeLevels.NONE, false
+					return cb new Errors.NotFoundError("no project found with id #{project_id}")
+				cb null, project.publicAccesLevel
 
 		if !user_id?
-			getPublicAccessLevel()
+			# User is Anonymous, Try Token-based access
+			getPublicAccessLevel project_id, (err, publicAccessLevel) ->
+				return callback(err) if err?
+				if publicAccessLevel == PublicAccessLevels.TOKEN_BASED
+					TokenAccessHandler.requestHasReadOnlyTokenAccess req, project_id, (err, allowed) ->
+						return callback(err) if err?
+						if allowed
+							callback null, PrivilegeLevels.READ_ONLY, false
+						else
+							callback null, PrivilegeLevels.NONE, false
+				else if publicAccessLevel == PublicAccessLevels.READ_ONLY
+					callback null, PrivilegeLevels.READ_ONLY, true
+				else if publicAccessLevel == PublicAccessLevels.READ_AND_WRITE
+					callback null, PrivilegeLevels.READ_AND_WRITE, true
+				else
+					callback null, PrivilegeLevels.NONE, false
 		else
+			# User is present, get their privilege level from database
 			CollaboratorsHandler.getMemberIdPrivilegeLevel user_id, project_id, (error, privilegeLevel) ->
 				return callback(error) if error?
 				if privilegeLevel? and privilegeLevel != PrivilegeLevels.NONE
@@ -44,20 +58,29 @@ module.exports = AuthorizationManager =
 						if isAdmin
 							callback null, PrivilegeLevels.OWNER, false
 						else
-							getPublicAccessLevel()
+							# Legacy public-access system
+							# User is present (not anonymous), but does not have direct access
+							getPublicAccessLevel project_id, (err, publicAccessLevel) ->
+								return callback(err) if err?
+								if publicAccessLevel == PublicAccessLevels.READ_ONLY
+									callback null, PrivilegeLevels.READ_ONLY, true
+								if publicAccessLevel == PublicAccessLevels.READ_AND_WRITE
+									callback null, PrivilegeLevels.READ_AND_WRITE, true
+								else
+									callback null, PrivilegeLevels.NONE, false
 
-	canUserReadProject: (user_id, project_id, callback = (error, canRead) ->) ->
-		AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
+	canUserReadProject: (req, user_id, project_id, callback = (error, canRead) ->) ->
+		AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel) ->
 			return callback(error) if error?
 			return callback null, (privilegeLevel in [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE, PrivilegeLevels.READ_ONLY])
 		
-	canUserWriteProjectContent: (user_id, project_id, callback = (error, canWriteContent) ->) ->
-		AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
+	canUserWriteProjectContent: (req, user_id, project_id, callback = (error, canWriteContent) ->) ->
+		AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel) ->
 			return callback(error) if error?
 			return callback null, (privilegeLevel in [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE])
 		
-	canUserWriteProjectSettings: (user_id, project_id, callback = (error, canWriteSettings) ->) ->
-		AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel, becausePublic) ->
+	canUserWriteProjectSettings: (req, user_id, project_id, callback = (error, canWriteSettings) ->) ->
+		AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel, becausePublic) ->
 			return callback(error) if error?
 			if privilegeLevel == PrivilegeLevels.OWNER
 				return callback null, true
@@ -66,8 +89,8 @@ module.exports = AuthorizationManager =
 			else
 				return callback null, false
 	
-	canUserAdminProject: (user_id, project_id, callback = (error, canAdmin) ->) ->
-		AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
+	canUserAdminProject: (req, user_id, project_id, callback = (error, canAdmin) ->) ->
+		AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel) ->
 			return callback(error) if error?
 			return callback null, (privilegeLevel == PrivilegeLevels.OWNER)
 	
@@ -76,4 +99,4 @@ module.exports = AuthorizationManager =
 			return callback null, false
 		User.findOne { _id: user_id }, { isAdmin: 1 }, (error, user) ->
 			return callback(error) if error?
-			return callback null, (user?.isAdmin == true)
+			return callback null, (user?.isAdmin == true)

+ 5 - 5
services/web/app/coffee/Features/Authorization/AuthorizationMiddlewear.coffee

@@ -13,7 +13,7 @@ module.exports = AuthorizationMiddlewear =
 			# Remove the projects we have access to. Note rejectSeries doesn't use
 			# errors in callbacks
 			async.rejectSeries project_ids, (project_id, cb) ->
-				AuthorizationManager.canUserReadProject user_id, project_id, (error, canRead) ->
+				AuthorizationManager.canUserReadProject req, user_id, project_id, (error, canRead) ->
 					return next(error) if error?
 					cb(canRead)
 			, (unauthorized_project_ids) ->
@@ -25,7 +25,7 @@ module.exports = AuthorizationMiddlewear =
 	ensureUserCanReadProject: (req, res, next) ->
 		AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
 			return next(error) if error?
-			AuthorizationManager.canUserReadProject user_id, project_id, (error, canRead) ->
+			AuthorizationManager.canUserReadProject req, user_id, project_id, (error, canRead) ->
 				return next(error) if error?
 				if canRead
 					logger.log {user_id, project_id}, "allowing user read access to project"
@@ -37,7 +37,7 @@ module.exports = AuthorizationMiddlewear =
 	ensureUserCanWriteProjectSettings: (req, res, next) ->
 		AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
 			return next(error) if error?
-			AuthorizationManager.canUserWriteProjectSettings user_id, project_id, (error, canWrite) ->
+			AuthorizationManager.canUserWriteProjectSettings req, user_id, project_id, (error, canWrite) ->
 				return next(error) if error?
 				if canWrite
 					logger.log {user_id, project_id}, "allowing user write access to project settings"
@@ -49,7 +49,7 @@ module.exports = AuthorizationMiddlewear =
 	ensureUserCanWriteProjectContent: (req, res, next) ->
 		AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
 			return next(error) if error?
-			AuthorizationManager.canUserWriteProjectContent user_id, project_id, (error, canWrite) ->
+			AuthorizationManager.canUserWriteProjectContent req, user_id, project_id, (error, canWrite) ->
 				return next(error) if error?
 				if canWrite
 					logger.log {user_id, project_id}, "allowing user write access to project content"
@@ -61,7 +61,7 @@ module.exports = AuthorizationMiddlewear =
 	ensureUserCanAdminProject: (req, res, next) ->
 		AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
 			return next(error) if error?
-			AuthorizationManager.canUserAdminProject user_id, project_id, (error, canAdmin) ->
+			AuthorizationManager.canUserAdminProject req, user_id, project_id, (error, canAdmin) ->
 				return next(error) if error?
 				if canAdmin
 					logger.log {user_id, project_id}, "allowing user admin access to project"

+ 3 - 3
services/web/app/coffee/Features/Editor/EditorHttpController.coffee

@@ -20,7 +20,7 @@ module.exports = EditorHttpController =
 			user_id = null
 		logger.log {user_id, project_id}, "join project request"
 		Metrics.inc "editor.join-project"
-		EditorHttpController._buildJoinProjectView project_id, user_id, (error, project, privilegeLevel) ->
+		EditorHttpController._buildJoinProjectView req, project_id, user_id, (error, project, privilegeLevel) ->
 			return next(error) if error?
 			res.json {
 				project: project
@@ -30,7 +30,7 @@ module.exports = EditorHttpController =
 			if project?.deletedByExternalDataSource
 				ProjectDeleter.unmarkAsDeletedByExternalSource project_id
 
-	_buildJoinProjectView: (project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
+	_buildJoinProjectView: (req, project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
 		logger.log {project_id, user_id}, "building the joinProject view"
 		ProjectGetter.getProjectWithoutDocLines project_id, (error, project) ->
 			return callback(error) if error?
@@ -39,7 +39,7 @@ module.exports = EditorHttpController =
 				return callback(error) if error?
 				UserGetter.getUser user_id, { isAdmin: true }, (error, user) ->
 					return callback(error) if error?
-					AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
+					AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel) ->
 						return callback(error) if error?
 						if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
 							logger.log {project_id, user_id, privilegeLevel}, "not an acceptable privilege level, returning null"

+ 1 - 1
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -260,7 +260,7 @@ module.exports = ProjectController =
 			daysSinceLastUpdated =  (new Date() - project.lastUpdated) /86400000
 			logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
 
-			AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel)->
+			AuthorizationManager.getPrivilegeLevelForProject req, user_id, project_id, (error, privilegeLevel)->
 				return next(error) if error?
 				if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
 					return res.sendStatus 401

+ 4 - 4
services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee

@@ -28,7 +28,7 @@ module.exports = TokenAccessController =
 					logger.err {err, token, userId, projectId: project._id},
 						"error adding user to project with readAndWrite token"
 					return next(err)
-				return res.redirect("/project/#{project._id}")
+				return res.redirect(307, "/project/#{project._id}")
 
 	readOnlyToken: (req, res, next) ->
 		userId = AuthenticationController.getLoggedInUserId(req)
@@ -46,8 +46,8 @@ module.exports = TokenAccessController =
 			if !userId?
 				logger.log {userId, projectId: project._id},
 					"adding anonymous user to project with readOnly token"
-				TokenAccessHandler.grantAnonymousUserTokenAccessViaSession(req, project._id)
-				return res.redirect("/project/#{project._id}")
+				TokenAccessHandler.grantSessionReadOnlyTokenAccess(req, project._id, token)
+				return res.redirect(307, "/project/#{project._id}")
 			else
 				logger.log {userId, projectId: project._id},
 					"adding user to project with readOnly token"
@@ -56,6 +56,6 @@ module.exports = TokenAccessController =
 						logger.err {err, token, userId, projectId: project._id},
 							"error adding user to project with readAndWrite token"
 						return next(err)
-					res.redirect("/project/#{project._id}")
+					res.redirect(307, "/project/#{project._id}")
 
 

+ 19 - 7
services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee

@@ -10,7 +10,7 @@ module.exports = TokenAccessHandler =
 		Project.findOne {
 			'tokens.readOnly': token,
 			'publicAccesLevel': PublicAccessLevels.TOKEN_BASED
-		}, {_id: 1}, (err, project) ->
+		}, {_id: 1, publicAccesLevel: 1}, (err, project) ->
 			return callback(err) if err?
 			callback(null, project)
 
@@ -18,7 +18,7 @@ module.exports = TokenAccessHandler =
 		Project.findOne {
 			'tokens.readAndWrite': token,
 			'publicAccesLevel': PublicAccessLevels.TOKEN_BASED
-		}, {_id: 1}, (err, project) ->
+		}, {_id: 1, publicAccesLevel: 1}, (err, project) ->
 			return callback(err) if err?
 			callback(null, project)
 
@@ -42,13 +42,25 @@ module.exports = TokenAccessHandler =
 		}, (err) ->
 			callback(err)
 
-	grantAnonymousUserTokenAccessViaSession: (req, projectId) ->
+	grantSessionReadOnlyTokenAccess: (req, projectId, token) ->
 		if req.session?
 			if !req.session.anonReadOnlyTokenAccess?
 				req.session.anonReadOnlyTokenAccess = {}
-			req.session.anonReadOnlyTokenAccess[projectId.toString()] = true
-
-	anonymousUserHasTokenAccessViaSession: (req, projectId) ->
-		req?.session?.anonReadOnlyTokenAccess?[projectId.toString()] == true
+			req.session.anonReadOnlyTokenAccess[projectId.toString()] = token.toString()
 
+	requestHasReadOnlyTokenAccess: (req, projectId, callback=(err, allowed)->) ->
+		token = (
+			req?.session?.anonReadOnlyTokenAccess?[projectId.toString()] or
+			req.headers['x-sl-anon-token']
+		)
+		if !token
+			return callback null, false
+		TokenAccessHandler.findProjectWithReadOnlyToken token, (err, project) ->
+			return callback(err) if err?
+			isAllowed = (
+				project? and
+				project.publicAccesLevel == PublicAccessLevels.TOKEN_BASED and
+				project._id.toString() == projectId.toString()
+			)
+			callback null, isAllowed
 

+ 116 - 107
services/web/test/UnitTests/coffee/Authorization/AuthorizationManagerTests.coffee

@@ -5,6 +5,7 @@ expect = chai.expect
 modulePath = "../../../../app/js/Features/Authorization/AuthorizationManager.js"
 SandboxedModule = require('sandboxed-module')
 Errors = require "../../../../app/js/Features/Errors/Errors.js"
+MockRequest = require '../helpers/MockRequest'
 
 describe "AuthorizationManager", ->
 	beforeEach ->
@@ -13,6 +14,9 @@ describe "AuthorizationManager", ->
 			"../../models/Project": Project: @Project = {}
 			"../../models/User": User: @User = {}
 			"../Errors/Errors": Errors
+			"../TokenAccess/TokenAccessHandler": @TokenAccessHandler = {
+				requestHasReadOnlyTokenAccess: sinon.stub().callsArgWith(2, null, false)
+			}
 		@user_id = "user-id-1"
 		@project_id = "project-id-1"
 		@callback = sinon.stub()
@@ -25,53 +29,54 @@ describe "AuthorizationManager", ->
 
 		describe "with a private project", ->
 			beforeEach ->
+				@req = new MockRequest()
 				@Project.findOne
 					.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
 					.yields(null, { publicAccesLevel: "private" })
-			
+
 			describe "with a user_id with a privilege level", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, "readOnly")
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return the user's privilege level", ->
 					@callback.calledWith(null, "readOnly", false).should.equal true
-			
+
 			describe "with a user_id with no privilege level", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, false)
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return false", ->
 					@callback.calledWith(null, false, false).should.equal true
-			
+
 			describe "with a user_id who is an admin", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, true)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, false)
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return the user as an owner", ->
 					@callback.calledWith(null, "owner", false).should.equal true
-			
+
 			describe "with no user (anonymous)", ->
 				beforeEach ->
-					@AuthorizationManager.getPrivilegeLevelForProject null, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, null, @project_id, @callback
+
 				it "should not call CollaboratorsHandler.getMemberIdPrivilegeLevel", ->
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel.called.should.equal false
-				
+
 				it "should not call AuthorizationManager.isUserSiteAdmin", ->
 					@AuthorizationManager.isUserSiteAdmin.called.should.equal false
-				
+
 				it "should return false", ->
 					@callback.calledWith(null, false, false).should.equal true
 
@@ -80,61 +85,61 @@ describe "AuthorizationManager", ->
 				@Project.findOne
 					.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
 					.yields(null, { publicAccesLevel: "readAndWrite" })
-			
+
 			describe "with a user_id with a privilege level", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, "readOnly")
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return the user's privilege level", ->
 					@callback.calledWith(null, "readOnly", false).should.equal true
-			
+
 			describe "with a user_id with no privilege level", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, false)
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return the public privilege level", ->
 					@callback.calledWith(null, "readAndWrite", true).should.equal true
-			
+
 			describe "with a user_id who is an admin", ->
 				beforeEach ->
 					@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, true)
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel
 						.withArgs(@user_id, @project_id)
 						.yields(null, false)
-					@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, @callback
+
 				it "should return the user as an owner", ->
 					@callback.calledWith(null, "owner", false).should.equal true
-			
+
 			describe "with no user (anonymous)", ->
 				beforeEach ->
-					@AuthorizationManager.getPrivilegeLevelForProject null, @project_id, @callback
-				
+					@AuthorizationManager.getPrivilegeLevelForProject @req, null, @project_id, @callback
+
 				it "should not call CollaboratorsHandler.getMemberIdPrivilegeLevel", ->
 					@CollaboratorsHandler.getMemberIdPrivilegeLevel.called.should.equal false
-				
+
 				it "should not call AuthorizationManager.isUserSiteAdmin", ->
 					@AuthorizationManager.isUserSiteAdmin.called.should.equal false
-				
+
 				it "should return the public privilege level", ->
 					@callback.calledWith(null, "readAndWrite", true).should.equal true
-		
+
 		describe "when the project doesn't exist", ->
 			beforeEach ->
 				@Project.findOne
 					.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
 					.yields(null, null)
-				
+
 			it "should return a NotFoundError", ->
-				@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, (error) ->
+				@AuthorizationManager.getPrivilegeLevelForProject @req, @user_id, @project_id, (error) ->
 					error.should.be.instanceof Errors.NotFoundError
 
 		describe "when the project id is not valid", ->
@@ -145,214 +150,218 @@ describe "AuthorizationManager", ->
 					.yields(null, "readOnly")
 
 			it "should return a error", (done)->
-				@AuthorizationManager.getPrivilegeLevelForProject undefined, "not project id", (err) =>
+				@AuthorizationManager.getPrivilegeLevelForProject @req, undefined, "not project id", (err) =>
 					@Project.findOne.called.should.equal false
 					expect(err).to.exist
 					done()
 
 	describe "canUserReadProject", ->
 		beforeEach ->
+			@req = new MockRequest()
 			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
-		
+
 		describe "when user is owner", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "owner", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserReadProject @user_id, @project_id, (error, canRead) ->
+				@AuthorizationManager.canUserReadProject @req, @user_id, @project_id, (error, canRead) ->
 					expect(canRead).to.equal true
 					done()
-		
+
 		describe "when user has read-write access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readAndWrite", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserReadProject @user_id, @project_id, (error, canRead) ->
+				@AuthorizationManager.canUserReadProject @req, @user_id, @project_id, (error, canRead) ->
 					expect(canRead).to.equal true
 					done()
-		
+
 		describe "when user has read-only access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readOnly", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserReadProject @user_id, @project_id, (error, canRead) ->
+				@AuthorizationManager.canUserReadProject @req, @user_id, @project_id, (error, canRead) ->
 					expect(canRead).to.equal true
 					done()
-		
+
 		describe "when user has no access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, false, false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserReadProject @user_id, @project_id, (error, canRead) ->
+				@AuthorizationManager.canUserReadProject @req, @user_id, @project_id, (error, canRead) ->
 					expect(canRead).to.equal false
 					done()
 
 	describe "canUserWriteProjectContent", ->
 		beforeEach ->
+			@req = new MockRequest()
 			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
-		
+
 		describe "when user is owner", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "owner", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserWriteProjectContent @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectContent @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal true
 					done()
-		
+
 		describe "when user has read-write access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readAndWrite", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserWriteProjectContent @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectContent @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal true
 					done()
-		
+
 		describe "when user has read-only access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readOnly", false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserWriteProjectContent @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectContent @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal false
 					done()
-		
+
 		describe "when user has no access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, false, false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserWriteProjectContent @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectContent @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal false
 					done()
 
 	describe "canUserWriteProjectSettings", ->
 		beforeEach ->
+			@req = new MockRequest()
 			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
-		
+
 		describe "when user is owner", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "owner", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectSettings @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal true
 					done()
-		
+
 		describe "when user has read-write access as a collaborator", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readAndWrite", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectSettings @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal true
 					done()
-		
+
 		describe "when user has read-write access as the public", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readAndWrite", true)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectSettings @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal false
 					done()
-		
+
 		describe "when user has read-only access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readOnly", false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectSettings @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal false
 					done()
-		
+
 		describe "when user has no access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, false, false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
+				@AuthorizationManager.canUserWriteProjectSettings @req, @user_id, @project_id, (error, canWrite) ->
 					expect(canWrite).to.equal false
 					done()
 
 	describe "canUserAdminProject", ->
 		beforeEach ->
+			@req = new MockRequest()
 			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
-		
+
 		describe "when user is owner", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "owner", false)
-			
+
 			it "should return true", (done) ->
-				@AuthorizationManager.canUserAdminProject @user_id, @project_id, (error, canAdmin) ->
+				@AuthorizationManager.canUserAdminProject @req, @user_id, @project_id, (error, canAdmin) ->
 					expect(canAdmin).to.equal true
 					done()
-		
+
 		describe "when user has read-write access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readAndWrite", false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserAdminProject @user_id, @project_id, (error, canAdmin) ->
+				@AuthorizationManager.canUserAdminProject @req, @user_id, @project_id, (error, canAdmin) ->
 					expect(canAdmin).to.equal false
 					done()
-		
+
 		describe "when user has read-only access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, "readOnly", false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserAdminProject @user_id, @project_id, (error, canAdmin) ->
+				@AuthorizationManager.canUserAdminProject @req, @user_id, @project_id, (error, canAdmin) ->
 					expect(canAdmin).to.equal false
 					done()
-		
+
 		describe "when user has no access", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.withArgs(@user_id, @project_id)
+					.withArgs(@req, @user_id, @project_id)
 					.yields(null, false, false)
-			
+
 			it "should return false", (done) ->
-				@AuthorizationManager.canUserAdminProject @user_id, @project_id, (error, canAdmin) ->
+				@AuthorizationManager.canUserAdminProject @req, @user_id, @project_id, (error, canAdmin) ->
 					expect(canAdmin).to.equal false
 					done()
-	
+
 	describe "isUserSiteAdmin", ->
 		beforeEach ->
 			@User.findOne = sinon.stub()
@@ -362,34 +371,34 @@ describe "AuthorizationManager", ->
 				@User.findOne
 					.withArgs({ _id: @user_id }, { isAdmin: 1 })
 					.yields(null, { isAdmin: true })
-			
+
 			it "should return true", (done) ->
 				@AuthorizationManager.isUserSiteAdmin @user_id, (error, isAdmin) ->
 					expect(isAdmin).to.equal true
 					done()
-		
+
 		describe "when user is not admin", ->
 			beforeEach ->
 				@User.findOne
 					.withArgs({ _id: @user_id }, { isAdmin: 1 })
 					.yields(null, { isAdmin: false })
-			
+
 			it "should return false", (done) ->
 				@AuthorizationManager.isUserSiteAdmin @user_id, (error, isAdmin) ->
 					expect(isAdmin).to.equal false
 					done()
-		
+
 		describe "when user is not found", ->
 			beforeEach ->
 				@User.findOne
 					.withArgs({ _id: @user_id }, { isAdmin: 1 })
 					.yields(null, null)
-			
+
 			it "should return false", (done) ->
 				@AuthorizationManager.isUserSiteAdmin @user_id, (error, isAdmin) ->
 					expect(isAdmin).to.equal false
 					done()
-		
+
 		describe "when no user is passed", ->
 			it "should return false", (done) ->
 				@AuthorizationManager.isUserSiteAdmin null, (error, isAdmin) =>

+ 12 - 12
services/web/test/UnitTests/coffee/Authorization/AuthorizationMiddlewearTests.coffee

@@ -55,7 +55,7 @@ describe "AuthorizationMiddlewear", ->
 					describe "when user has permission", ->
 						beforeEach ->
 							@AuthorizationManager[managerMethod]
-								.withArgs(@user_id, @project_id)
+								.withArgs(sinon.match.any, @user_id, @project_id)
 								.yields(null, true)
 
 						it "should return next", ->
@@ -65,7 +65,7 @@ describe "AuthorizationMiddlewear", ->
 					describe "when user doesn't have permission", ->
 						beforeEach ->
 							@AuthorizationManager[managerMethod]
-								.withArgs(@user_id, @project_id)
+								.withArgs(sinon.match.any, @user_id, @project_id)
 								.yields(null, false)
 
 						it "should redirect to redirectToRestricted", ->
@@ -80,7 +80,7 @@ describe "AuthorizationMiddlewear", ->
 						beforeEach ->
 							@AuthenticationController.getLoggedInUserId.returns(null)
 							@AuthorizationManager[managerMethod]
-								.withArgs(null, @project_id)
+								.withArgs(@req, null, @project_id)
 								.yields(null, true)
 
 						it "should return next", ->
@@ -91,7 +91,7 @@ describe "AuthorizationMiddlewear", ->
 						beforeEach ->
 							@AuthenticationController.getLoggedInUserId.returns(null)
 							@AuthorizationManager[managerMethod]
-								.withArgs(null, @project_id)
+								.withArgs(@req, null, @project_id)
 								.yields(null, false)
 
 						it "should redirect to redirectToRestricted", ->
@@ -184,10 +184,10 @@ describe "AuthorizationMiddlewear", ->
 			describe "when user has permission to access all projects", ->
 				beforeEach ->
 					@AuthorizationManager.canUserReadProject
-						.withArgs(@user_id, "project1")
+						.withArgs(sinon.match.any, @user_id, "project1")
 						.yields(null, true)
 					@AuthorizationManager.canUserReadProject
-						.withArgs(@user_id, "project2")
+						.withArgs(sinon.match.any, @user_id, "project2")
 						.yields(null, true)
 
 				it "should return next", ->
@@ -197,10 +197,10 @@ describe "AuthorizationMiddlewear", ->
 			describe "when user doesn't have permission to access one of the projects", ->
 				beforeEach ->
 					@AuthorizationManager.canUserReadProject
-						.withArgs(@user_id, "project1")
+						.withArgs(sinon.match.any, @user_id, "project1")
 						.yields(null, true)
 					@AuthorizationManager.canUserReadProject
-						.withArgs(@user_id, "project2")
+						.withArgs(sinon.match.any, @user_id, "project2")
 						.yields(null, false)
 
 				it "should redirect to redirectToRestricted", ->
@@ -216,10 +216,10 @@ describe "AuthorizationMiddlewear", ->
 					beforeEach ->
 						@AuthenticationController.getLoggedInUserId.returns(null)
 						@AuthorizationManager.canUserReadProject
-							.withArgs(null, "project1")
+							.withArgs(sinon.match.any, null, "project1")
 							.yields(null, true)
 						@AuthorizationManager.canUserReadProject
-							.withArgs(null, "project2")
+							.withArgs(sinon.match.any, null, "project2")
 							.yields(null, true)
 
 					it "should return next", ->
@@ -230,10 +230,10 @@ describe "AuthorizationMiddlewear", ->
 					beforeEach ->
 						@AuthenticationController.getLoggedInUserId.returns(null)
 						@AuthorizationManager.canUserReadProject
-							.withArgs(null, "project1")
+							.withArgs(sinon.match.any, null, "project1")
 							.yields(null, true)
 						@AuthorizationManager.canUserReadProject
-							.withArgs(null, "project2")
+							.withArgs(sinon.match.any, null, "project2")
 							.yields(null, false)
 
 					it "should redirect to redirectToRestricted", ->

+ 8 - 8
services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee

@@ -39,7 +39,7 @@ describe "EditorHttpController", ->
 			@projectView = {
 				_id: @project_id
 			}
-			@EditorHttpController._buildJoinProjectView = sinon.stub().callsArgWith(2, null, @projectView, "owner")
+			@EditorHttpController._buildJoinProjectView = sinon.stub().callsArgWith(3, null, @projectView, "owner")
 			@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
 			
 		describe "successfully", ->
@@ -48,7 +48,7 @@ describe "EditorHttpController", ->
 				
 			it "should get the project view", ->
 				@EditorHttpController._buildJoinProjectView
-					.calledWith(@project_id, @user_id)
+					.calledWith(@req, @project_id, @user_id)
 					.should.equal true
 					
 			it "should return the project and privilege level", ->
@@ -87,7 +87,7 @@ describe "EditorHttpController", ->
 			
 			it "should pass the user id as null", ->
 				@EditorHttpController._buildJoinProjectView
-					.calledWith(@project_id, null)
+					.calledWith(@req, @project_id, null)
 					.should.equal true
 
 	describe "_buildJoinProjectView", ->
@@ -116,8 +116,8 @@ describe "EditorHttpController", ->
 		describe "when authorized", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject =
-					sinon.stub().callsArgWith(2, null, "owner")
-				@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
+					sinon.stub().callsArgWith(3, null, "owner")
+				@EditorHttpController._buildJoinProjectView(@req, @project_id, @user_id, @callback)
 				
 			it "should find the project without doc lines", ->
 				@ProjectGetter.getProjectWithoutDocLines
@@ -136,7 +136,7 @@ describe "EditorHttpController", ->
 					
 			it "should check the privilege level", ->
 				@AuthorizationManager.getPrivilegeLevelForProject
-					.calledWith(@user_id, @project_id)
+					.calledWith(@req, @user_id, @project_id)
 					.should.equal true
 
 			it 'should include the invites', ->
@@ -150,8 +150,8 @@ describe "EditorHttpController", ->
 		describe "when not authorized", ->
 			beforeEach ->
 				@AuthorizationManager.getPrivilegeLevelForProject =
-					sinon.stub().callsArgWith(2, null, null)
-				@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
+					sinon.stub().callsArgWith(3, null, null)
+				@EditorHttpController._buildJoinProjectView(@req, @project_id, @user_id, @callback)
 				
 			it "should return false in the callback", ->
 				@callback.calledWith(null, null, false).should.equal true

+ 2 - 2
services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee

@@ -302,7 +302,7 @@ describe "ProjectController", ->
 			@ProjectGetter.getProject.callsArgWith 2, null, @project
 			@UserModel.findById.callsArgWith(1, null, @user)
 			@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
-			@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, "owner"
+			@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 3, null, "owner"
 			@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
 			@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
 			@AnalyticsManager.getLastOccurance.yields(null, {"mock": "event"})
@@ -335,7 +335,7 @@ describe "ProjectController", ->
 			@ProjectController.loadEditor @req, @res
 
 		it "should not render the page if the project can not be accessed", (done)->
-			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 2, null, null
+			@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 3, null, null
 			@res.sendStatus = (resCode, opts)=>
 				resCode.should.equal 401
 				done()