Implement authorization guards in Authorization{Manager,Controller}

This commit is contained in:
James Allen 2016-03-14 17:06:57 +00:00
parent 1bd8b8d1a3
commit 71ef045728
14 changed files with 753 additions and 28 deletions

View file

@ -99,6 +99,7 @@ module.exports = AuthenticationController =
_redirectToLoginPage: (req, res) -> _redirectToLoginPage: (req, res) ->
logger.log url: req.url, "user not logged in so redirecting to login page" logger.log url: req.url, "user not logged in so redirecting to login page"
console.log req.session
req.query.redir = req.path req.query.redir = req.path
url = "/login?#{querystring.stringify(req.query)}" url = "/login?#{querystring.stringify(req.query)}"
res.redirect url res.redirect url
@ -141,4 +142,5 @@ module.exports = AuthenticationController =
req.session[key] = value req.session[key] = value
req.session.user = lightUser req.session.user = lightUser
console.log "LOGGED IN", req.session
callback() callback()

View file

@ -1,12 +1,61 @@
module.exports = CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
getPrivilegeLevelForProject: (user_id, project_id, callback = (error, canAccess, privilegeLevel) ->) -> Project = require("../../models/Project").Project
return callback(null, true, "readAndWrite") User = require("../../models/User").User
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 = () ->
Project.findOne { _id: project_id }, { publicAccesLevel: 1 }, (error, project) ->
return callback(error) if error?
if project.publicAccesLevel in ["readOnly", "readAndWrite"]
return callback null, project.publicAccesLevel, true
else
return callback null, false, false
if !user_id?
getPublicAccessLevel()
else
CollaboratorsHandler.getMemberIdPrivilegeLevel user_id, project_id, (error, privilegeLevel) ->
return callback(error) if error?
if privilegeLevel? and privilegeLevel
# The user has direct access
callback null, privilegeLevel, false
else
getPublicAccessLevel()
canUserReadProject: (user_id, project_id, callback = (error, canRead) ->) -> canUserReadProject: (user_id, project_id, callback = (error, canRead) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
return callback(error) if error?
return callback null, (privilegeLevel in ["owner", "readAndWrite", "readOnly"])
canUserWriteProjectContent: (user_id, project_id, callback = (error, canWriteContent) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
return callback(error) if error?
return callback null, (privilegeLevel in ["owner", "readAndWrite"])
canUserWriteProjectSettings: (user_id, project_id, callback = (error, canWriteSettings) ->) -> canUserWriteProjectSettings: (user_id, project_id, callback = (error, canWriteSettings) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel, becausePublic) ->
return callback(error) if error?
if privilegeLevel == "owner"
return callback null, true
else if privilegeLevel == "readAndWrite" and !becausePublic
return callback null, true
else
return callback null, false
canUserAdminProject: (user_id, project_id, callback = (error, canAdmin) ->) -> canUserAdminProject: (user_id, project_id, callback = (error, canAdmin) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
return callback(error) if error?
return callback null, (privilegeLevel == "owner")
isUserSiteAdmin: (user_id, callback = (error, isAdmin) ->) -> isUserSiteAdmin: (user_id, callback = (error, isAdmin) ->) ->
if !user_id?
return callback null, false
User.findOne { _id: user_id }, { isAdmin: 1 }, (error, user) ->
return callback(error) if error?
return callback null, (user?.isAdmin == true)

View file

@ -1,21 +1,101 @@
module.exports = AuthorizationManager = require("./AuthorizationManager")
async = require "async"
logger = require "logger-sharelatex"
module.exports = AuthorizationMiddlewear =
ensureUserCanReadMultipleProjects: (req, res, next) -> ensureUserCanReadMultipleProjects: (req, res, next) ->
next() project_ids = (req.query.project_ids or "").split(",")
AuthorizationMiddlewear._getUserId req, (error, user_id) ->
return next(error) if error?
# 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) ->
return next(error) if error?
cb(canRead)
, (unauthorized_project_ids) ->
if unauthorized_project_ids.length > 0
AuthorizationMiddlewear.redirectToRestricted req, res, next
else
next()
ensureUserCanReadProject: (req, res, next) -> ensureUserCanReadProject: (req, res, next) ->
next() AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
return next(error) if error?
AuthorizationManager.canUserReadProject 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"
next()
else
logger.log {user_id, project_id}, "denying user read access to project"
AuthorizationMiddlewear.redirectToRestricted req, res, next
ensureUserCanWriteProjectSettings: (req, res, next) -> ensureUserCanWriteProjectSettings: (req, res, next) ->
next() AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
return next(error) if error?
AuthorizationManager.canUserWriteProjectSettings 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"
next()
else
logger.log {user_id, project_id}, "denying user write access to project settings"
AuthorizationMiddlewear.redirectToRestricted req, res, next
ensureUserCanWriteProjectContent: (req, res, next) -> ensureUserCanWriteProjectContent: (req, res, next) ->
next() AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
return next(error) if error?
AuthorizationManager.canUserWriteProjectContent 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"
next()
else
logger.log {user_id, project_id}, "denying user write access to project settings"
AuthorizationMiddlewear.redirectToRestricted req, res, next
ensureUserCanAdminProject: (req, res, next) -> ensureUserCanAdminProject: (req, res, next) ->
next() AuthorizationMiddlewear._getUserAndProjectId req, (error, user_id, project_id) ->
return next(error) if error?
AuthorizationManager.canUserAdminProject 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"
next()
else
logger.log {user_id, project_id}, "denying user admin access to project"
AuthorizationMiddlewear.redirectToRestricted req, res, next
ensureUserIsSiteAdmin: (req, res, next) -> ensureUserIsSiteAdmin: (req, res, next) ->
next() AuthorizationMiddlewear._getUserId req, (error, user_id) ->
return next(error) if error?
AuthorizationManager.isUserSiteAdmin user_id, (error, isAdmin) ->
return next(error) if error?
if isAdmin
logger.log {user_id}, "allowing user admin access to site"
next()
else
logger.log {user_id}, "denying user admin access to site"
AuthorizationMiddlewear.redirectToRestricted req, res, next
_getUserAndProjectId: (req, callback = (error, user_id, project_id) ->) ->
project_id = req.params?.project_id or req.params?.Project_id
if !project_id?
return callback(new Error("Expected project_id in request parameters"))
AuthorizationMiddlewear._getUserId req, (error, user_id) ->
return callback(error) if error?
callback(null, user_id, project_id)
_getUserId: (req, callback = (error, user_id) ->) ->
if req.session?.user?._id?
user_id = req.session.user._id
else
user_id = null
callback null, user_id
redirectToRestricted: (req, res, next) ->
res.redirect "/restricted"
restricted : (req, res, next)-> restricted : (req, res, next)->
if req.session.user? if req.session.user?

View file

@ -13,11 +13,11 @@ module.exports = CollaboratorsHandler =
return callback(error) if error? return callback(error) if error?
return callback null, null if !project? return callback null, null if !project?
members = [] members = []
members.push { id: project.owner_ref.toString(), privilegeLevel: "admin" } members.push { id: project.owner_ref.toString(), privilegeLevel: "owner" }
for member_id in project.readOnly_refs or [] for member_id in project.readOnly_refs or []
members.push { id: member_id, privilegeLevel: "readOnly" } members.push { id: member_id.toString(), privilegeLevel: "readOnly" }
for member_id in project.collaberator_refs or [] for member_id in project.collaberator_refs or []
members.push { id: member_id, privilegeLevel: "readAndWrite" } members.push { id: member_id.toString(), privilegeLevel: "readAndWrite" }
return callback null, members return callback null, members
getMemberIds: (project_id, callback = (error, member_ids) ->) -> getMemberIds: (project_id, callback = (error, member_ids) ->) ->
@ -33,7 +33,17 @@ module.exports = CollaboratorsHandler =
UserGetter.getUser member.id, (error, user) -> UserGetter.getUser member.id, (error, user) ->
return cb(error) if error? return cb(error) if error?
return cb(null, { user: user, privilegeLevel: member.privilegeLevel }) return cb(null, { user: user, privilegeLevel: member.privilegeLevel })
callback callback
getMemberIdPrivilegeLevel: (user_id, project_id, callback = (error, privilegeLevel) ->) ->
# In future if the schema changes and getting all member ids is more expensive (multiple documents)
# then optimise this.
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) ->
return callback(error) if error?
for member in members or []
if member.id == user_id?.toString()
return callback null, member.privilegeLevel
return callback null, false
getMemberCount: (project_id, callback = (error, count) ->) -> getMemberCount: (project_id, callback = (error, count) ->) ->
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) -> CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) ->

View file

@ -34,9 +34,9 @@ module.exports = EditorHttpController =
return callback(error) if error? return callback(error) if error?
UserGetter.getUser user_id, { isAdmin: true }, (error, user) -> UserGetter.getUser user_id, { isAdmin: true }, (error, user) ->
return callback(error) if error? return callback(error) if error?
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, canAccess, privilegeLevel) -> AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
return callback(error) if error? return callback(error) if error?
if !canAccess if !privilegeLevel
callback null, null, false callback null, null, false
else else
callback(null, callback(null,

View file

@ -225,9 +225,9 @@ module.exports = ProjectController =
daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000 daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000
logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor" logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, canAccess, privilegeLevel)-> AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel)->
return next(error) if error? return next(error) if error?
if !canAccess if !privilegeLevel
return res.sendStatus 401 return res.sendStatus 401
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt? if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?

View file

@ -27,11 +27,12 @@ module.exports = ProjectEditorHandler =
owner = null owner = null
for member in members for member in members
if member.privilegeLevel == "admin" if member.privilegeLevel == "owner"
owner = member.user owner = member.user
else else
result.members.push @buildUserModelView member.user, member.privilegeLevel result.members.push @buildUserModelView member.user, member.privilegeLevel
result.owner = @buildUserModelView owner, "owner" if owner?
result.owner = @buildUserModelView owner, "owner"
if owner?.features? if owner?.features?
if owner.features.collaborators? if owner.features.collaborators?

View file

@ -0,0 +1,340 @@
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/Features/Authorization/AuthorizationManager.js"
SandboxedModule = require('sandboxed-module')
describe "AuthorizationManager", ->
beforeEach ->
@AuthorizationManager = SandboxedModule.require modulePath, requires:
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
"../../models/Project": Project: @Project = {}
"../../models/User": User: @User = {}
@user_id = "user-id-1"
@project_id = "project-id-1"
@callback = sinon.stub()
describe "getPrivilegeLevelForProject", ->
beforeEach ->
@Project.findOne = sinon.stub()
@CollaboratorsHandler.getMemberIdPrivilegeLevel = sinon.stub()
describe "with a private project", ->
beforeEach ->
@Project.findOne
.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
.yields(null, { publicAccesLevel: "private" })
describe "with a user_id with a privilege level", ->
beforeEach ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel
.withArgs(@user_id, @project_id)
.yields(null, "readOnly")
@AuthorizationManager.getPrivilegeLevelForProject @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 ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel
.withArgs(@user_id, @project_id)
.yields(null, false)
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
describe "with no user (anonymous)", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject null, @project_id, @callback
it "should not call CollaboratorsHandler.getMemberIdPrivilegeLevel", ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel.called.should.equal false
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
describe "with a public project", ->
beforeEach ->
@Project.findOne
.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
.yields(null, { publicAccesLevel: "readAndWrite" })
describe "with a user_id with a privilege level", ->
beforeEach ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel
.withArgs(@user_id, @project_id)
.yields(null, "readOnly")
@AuthorizationManager.getPrivilegeLevelForProject @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 ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel
.withArgs(@user_id, @project_id)
.yields(null, false)
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
it "should return the public privilege level", ->
@callback.calledWith(null, "readAndWrite", true).should.equal true
describe "with no user (anonymous)", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject null, @project_id, @callback
it "should not call CollaboratorsHandler.getMemberIdPrivilegeLevel", ->
@CollaboratorsHandler.getMemberIdPrivilegeLevel.called.should.equal false
it "should return the public privilege level", ->
@callback.calledWith(null, "readAndWrite", true).should.equal true
describe "canUserReadProject", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
describe "when user is owner", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject
.withArgs(@user_id, @project_id)
.yields(null, "owner", false)
it "should return true", (done) ->
@AuthorizationManager.canUserReadProject @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)
.yields(null, "readAndWrite", false)
it "should return true", (done) ->
@AuthorizationManager.canUserReadProject @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)
.yields(null, "readOnly", false)
it "should return true", (done) ->
@AuthorizationManager.canUserReadProject @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)
.yields(null, false, false)
it "should return false", (done) ->
@AuthorizationManager.canUserReadProject @user_id, @project_id, (error, canRead) ->
expect(canRead).to.equal false
done()
describe "canUserWriteProjectContent", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
describe "when user is owner", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject
.withArgs(@user_id, @project_id)
.yields(null, "owner", false)
it "should return true", (done) ->
@AuthorizationManager.canUserWriteProjectContent @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)
.yields(null, "readAndWrite", false)
it "should return true", (done) ->
@AuthorizationManager.canUserWriteProjectContent @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)
.yields(null, "readOnly", false)
it "should return false", (done) ->
@AuthorizationManager.canUserWriteProjectContent @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)
.yields(null, false, false)
it "should return false", (done) ->
@AuthorizationManager.canUserWriteProjectContent @user_id, @project_id, (error, canWrite) ->
expect(canWrite).to.equal false
done()
describe "canUserWriteProjectSettings", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
describe "when user is owner", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject
.withArgs(@user_id, @project_id)
.yields(null, "owner", false)
it "should return true", (done) ->
@AuthorizationManager.canUserWriteProjectSettings @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)
.yields(null, "readAndWrite", false)
it "should return true", (done) ->
@AuthorizationManager.canUserWriteProjectSettings @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)
.yields(null, "readAndWrite", true)
it "should return false", (done) ->
@AuthorizationManager.canUserWriteProjectSettings @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)
.yields(null, "readOnly", false)
it "should return false", (done) ->
@AuthorizationManager.canUserWriteProjectSettings @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)
.yields(null, false, false)
it "should return false", (done) ->
@AuthorizationManager.canUserWriteProjectSettings @user_id, @project_id, (error, canWrite) ->
expect(canWrite).to.equal false
done()
describe "canUserAdminProject", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub()
describe "when user is owner", ->
beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject
.withArgs(@user_id, @project_id)
.yields(null, "owner", false)
it "should return true", (done) ->
@AuthorizationManager.canUserAdminProject @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)
.yields(null, "readAndWrite", false)
it "should return false", (done) ->
@AuthorizationManager.canUserAdminProject @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)
.yields(null, "readOnly", false)
it "should return false", (done) ->
@AuthorizationManager.canUserAdminProject @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)
.yields(null, false, false)
it "should return false", (done) ->
@AuthorizationManager.canUserAdminProject @user_id, @project_id, (error, canAdmin) ->
expect(canAdmin).to.equal false
done()
describe "isUserSiteAdmin", ->
beforeEach ->
@User.findOne = sinon.stub()
describe "when user is admin", ->
beforeEach ->
@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) =>
@User.findOne.called.should.equal false
expect(isAdmin).to.equal false
done()

View file

@ -0,0 +1,221 @@
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/Features/Authorization/AuthorizationMiddlewear.js"
SandboxedModule = require('sandboxed-module')
describe "AuthorizationMiddlewear", ->
beforeEach ->
@AuthorizationMiddlewear = SandboxedModule.require modulePath, requires:
"./AuthorizationManager": @AuthorizationManager = {}
"logger-sharelatex": {log: () ->}
@user_id = "user-id-123"
@project_id = "project-id-123"
@req = {}
@res = {}
@next = sinon.stub()
METHODS_TO_TEST = {
"ensureUserCanReadProject": "canUserReadProject"
"ensureUserCanWriteProjectSettings": "canUserWriteProjectSettings"
"ensureUserCanWriteProjectContent": "canUserWriteProjectContent"
"ensureUserCanAdminProject": "canUserAdminProject"
}
for middlewearMethod, managerMethod of METHODS_TO_TEST
do (middlewearMethod, managerMethod) ->
describe middlewearMethod, ->
beforeEach ->
@req.params =
project_id: @project_id
@AuthorizationManager[managerMethod] = sinon.stub()
@AuthorizationMiddlewear.redirectToRestricted = sinon.stub()
describe "with missing project_id", ->
beforeEach ->
@req.params = {}
it "should return an error to next", ->
@AuthorizationMiddlewear[middlewearMethod] @req, @res, @next
@next.calledWith(new Error()).should.equal true
describe "with logged in user", ->
beforeEach ->
@req.session =
user: _id: @user_id
describe "when user has permission", ->
beforeEach ->
@AuthorizationManager[managerMethod]
.withArgs(@user_id, @project_id)
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear[middlewearMethod] @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission", ->
beforeEach ->
@AuthorizationManager[managerMethod]
.withArgs(@user_id, @project_id)
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear[middlewearMethod] @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true
describe "with anonymous user", ->
describe "when user has permission", ->
beforeEach ->
@AuthorizationManager[managerMethod]
.withArgs(null, @project_id)
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear[middlewearMethod] @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission", ->
beforeEach ->
@AuthorizationManager[managerMethod]
.withArgs(null, @project_id)
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear[middlewearMethod] @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true
describe "ensureUserIsSiteAdmin", ->
beforeEach ->
@AuthorizationManager.isUserSiteAdmin = sinon.stub()
@AuthorizationMiddlewear.redirectToRestricted = sinon.stub()
describe "with logged in user", ->
beforeEach ->
@req.session =
user: _id: @user_id
describe "when user has permission", ->
beforeEach ->
@AuthorizationManager.isUserSiteAdmin
.withArgs(@user_id)
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear.ensureUserIsSiteAdmin @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission", ->
beforeEach ->
@AuthorizationManager.isUserSiteAdmin
.withArgs(@user_id)
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear.ensureUserIsSiteAdmin @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true
describe "with anonymous user", ->
describe "when user has permission", ->
beforeEach ->
@AuthorizationManager.isUserSiteAdmin
.withArgs(null)
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear.ensureUserIsSiteAdmin @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission", ->
beforeEach ->
@AuthorizationManager.isUserSiteAdmin
.withArgs(null)
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear.ensureUserIsSiteAdmin @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true
describe "ensureUserCanReadMultipleProjects", ->
beforeEach ->
@AuthorizationManager.canUserReadProject = sinon.stub()
@AuthorizationMiddlewear.redirectToRestricted = sinon.stub()
@req.query =
project_ids: "project1,project2"
describe "with logged in user", ->
beforeEach ->
@req.session =
user: _id: @user_id
describe "when user has permission to access all projects", ->
beforeEach ->
@AuthorizationManager.canUserReadProject
.withArgs(@user_id, "project1")
.yields(null, true)
@AuthorizationManager.canUserReadProject
.withArgs(@user_id, "project2")
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear.ensureUserCanReadMultipleProjects @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission to access one of the projects", ->
beforeEach ->
@AuthorizationManager.canUserReadProject
.withArgs(@user_id, "project1")
.yields(null, true)
@AuthorizationManager.canUserReadProject
.withArgs(@user_id, "project2")
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear.ensureUserCanReadMultipleProjects @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true
describe "with anonymous user", ->
describe "when user has permission", ->
describe "when user has permission to access all projects", ->
beforeEach ->
@AuthorizationManager.canUserReadProject
.withArgs(null, "project1")
.yields(null, true)
@AuthorizationManager.canUserReadProject
.withArgs(null, "project2")
.yields(null, true)
it "should return next", ->
@AuthorizationMiddlewear.ensureUserCanReadMultipleProjects @req, @res, @next
@next.called.should.equal true
describe "when user doesn't have permission to access one of the projects", ->
beforeEach ->
@AuthorizationManager.canUserReadProject
.withArgs(null, "project1")
.yields(null, true)
@AuthorizationManager.canUserReadProject
.withArgs(null, "project2")
.yields(null, false)
it "should redirect to redirectToRestricted", ->
@AuthorizationMiddlewear.ensureUserCanReadMultipleProjects @req, @res, @next
@next.called.should.equal false
@AuthorizationMiddlewear.redirectToRestricted
.calledWith(@req, @res, @next)
.should.equal true

View file

@ -36,7 +36,7 @@ describe "CollaboratorsHandler", ->
it "should return an array of member ids with their privilege levels", -> it "should return an array of member ids with their privilege levels", ->
@callback @callback
.calledWith(null, [ .calledWith(null, [
{ id: "owner-ref", privilegeLevel: "admin" } { id: "owner-ref", privilegeLevel: "owner" }
{ id: "read-only-ref-1", privilegeLevel: "readOnly" } { id: "read-only-ref-1", privilegeLevel: "readOnly" }
{ id: "read-only-ref-2", privilegeLevel: "readOnly" } { id: "read-only-ref-2", privilegeLevel: "readOnly" }
{ id: "read-write-ref-1", privilegeLevel: "readAndWrite" } { id: "read-write-ref-1", privilegeLevel: "readAndWrite" }
@ -83,6 +83,26 @@ describe "CollaboratorsHandler", ->
]) ])
.should.equal true .should.equal true
describe "getMemberIdPrivilegeLevel", ->
beforeEach ->
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub()
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels
.withArgs(@project_id)
.yields(null, [
{id: "member-id-1", privilegeLevel: "readAndWrite"}
{id: "member-id-2", privilegeLevel: "readOnly"}
])
it "should return the privilege level if it exists", (done) ->
@CollaboratorHandler.getMemberIdPrivilegeLevel "member-id-2", @project_id, (error, level) ->
expect(level).to.equal "readOnly"
done()
it "should return false if the member has no privilege level", (done) ->
@CollaboratorHandler.getMemberIdPrivilegeLevel "member-id-3", @project_id, (error, level) ->
expect(level).to.equal false
done()
describe "isUserMemberOfProject", -> describe "isUserMemberOfProject", ->
beforeEach -> beforeEach ->
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub() @CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub()

View file

@ -99,7 +99,7 @@ describe "EditorHttpController", ->
describe "when authorized", -> describe "when authorized", ->
beforeEach -> beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = @AuthorizationManager.getPrivilegeLevelForProject =
sinon.stub().callsArgWith(2, null, true, "owner") sinon.stub().callsArgWith(2, null, "owner")
@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback) @EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
it "should find the project without doc lines", -> it "should find the project without doc lines", ->
@ -128,7 +128,7 @@ describe "EditorHttpController", ->
describe "when not authorized", -> describe "when not authorized", ->
beforeEach -> beforeEach ->
@AuthorizationManager.getPrivilegeLevelForProject = @AuthorizationManager.getPrivilegeLevelForProject =
sinon.stub().callsArgWith(2, null, false, null) sinon.stub().callsArgWith(2, null, null)
@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback) @EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
it "should return false in the callback", -> it "should return false in the callback", ->

View file

@ -299,7 +299,7 @@ describe "ProjectController", ->
@ProjectModel.findOne.callsArgWith 1, null, @project @ProjectModel.findOne.callsArgWith 1, null, @project
@UserModel.findById.callsArgWith(1, null, @user) @UserModel.findById.callsArgWith(1, null, @user)
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {}) @SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, true, "owner" @AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, "owner"
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub() @ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1) @InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
@ProjectUpdateHandler.markAsOpened.callsArgWith(1) @ProjectUpdateHandler.markAsOpened.callsArgWith(1)
@ -332,7 +332,7 @@ describe "ProjectController", ->
@ProjectController.loadEditor @req, @res @ProjectController.loadEditor @req, @res
it "should not render the page if the project can not be accessed", (done)-> it "should not render the page if the project can not be accessed", (done)->
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 2, null, false @AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 2, null, null
@res.sendStatus = (resCode, opts)=> @res.sendStatus = (resCode, opts)=>
resCode.should.equal 401 resCode.should.equal 401
done() done()

View file

@ -49,7 +49,7 @@ describe "ProjectEditorHandler", ->
last_name : "ShareLaTeX" last_name : "ShareLaTeX"
email : "owner@sharelatex.com" email : "owner@sharelatex.com"
}, },
privilegeLevel: "admin" privilegeLevel: "owner"
},{ },{
user: { user: {
_id: "read-only-id" _id: "read-only-id"

View file

@ -44,6 +44,8 @@ class User
projectName: name projectName: name
}, (error, response, body) -> }, (error, response, body) ->
return callback(error) if error? return callback(error) if error?
if !body?.project_id?
console.error "SOMETHING WENT WRONG CREATING PROJECT", response.statusCode, response.headers["location"], body
callback(null, body.project_id) callback(null, body.project_id)
addUserToProject: (project_id, email, privileges, callback = (error, user) ->) -> addUserToProject: (project_id, email, privileges, callback = (error, user) ->) ->
@ -240,7 +242,7 @@ describe "Authorization", ->
expect_no_admin_access @other1, @project_id, redirect_to: "/restricted", done expect_no_admin_access @other1, @project_id, redirect_to: "/restricted", done
it "should not allow anonymous user read access to it", (done) -> it "should not allow anonymous user read access to it", (done) ->
expect_no_read_access @anon, @project_id, redirect_to: "/login", done expect_no_read_access @anon, @project_id, redirect_to: "/restricted", done
it "should not allow anonymous user write access to its settings", (done) -> it "should not allow anonymous user write access to its settings", (done) ->
expect_no_settings_write_access @anon, @project_id, redirect_to: "/restricted", done expect_no_settings_write_access @anon, @project_id, redirect_to: "/restricted", done