mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Implement authorization guards in Authorization{Manager,Controller}
This commit is contained in:
parent
1bd8b8d1a3
commit
71ef045728
14 changed files with 753 additions and 28 deletions
|
@ -99,6 +99,7 @@ module.exports = AuthenticationController =
|
|||
|
||||
_redirectToLoginPage: (req, res) ->
|
||||
logger.log url: req.url, "user not logged in so redirecting to login page"
|
||||
console.log req.session
|
||||
req.query.redir = req.path
|
||||
url = "/login?#{querystring.stringify(req.query)}"
|
||||
res.redirect url
|
||||
|
@ -141,4 +142,5 @@ module.exports = AuthenticationController =
|
|||
req.session[key] = value
|
||||
|
||||
req.session.user = lightUser
|
||||
console.log "LOGGED IN", req.session
|
||||
callback()
|
||||
|
|
|
@ -1,12 +1,61 @@
|
|||
module.exports =
|
||||
getPrivilegeLevelForProject: (user_id, project_id, callback = (error, canAccess, privilegeLevel) ->) ->
|
||||
return callback(null, true, "readAndWrite")
|
||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
Project = require("../../models/Project").Project
|
||||
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) ->) ->
|
||||
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) ->) ->
|
||||
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) ->) ->
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
|
||||
return callback(error) if error?
|
||||
return callback null, (privilegeLevel == "owner")
|
||||
|
||||
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)
|
|
@ -1,21 +1,101 @@
|
|||
module.exports =
|
||||
AuthorizationManager = require("./AuthorizationManager")
|
||||
async = require "async"
|
||||
logger = require "logger-sharelatex"
|
||||
|
||||
module.exports = AuthorizationMiddlewear =
|
||||
ensureUserCanReadMultipleProjects: (req, res, 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) ->
|
||||
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) ->
|
||||
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) ->
|
||||
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) ->
|
||||
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) ->
|
||||
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)->
|
||||
if req.session.user?
|
||||
|
|
|
@ -13,11 +13,11 @@ module.exports = CollaboratorsHandler =
|
|||
return callback(error) if error?
|
||||
return callback null, null if !project?
|
||||
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 []
|
||||
members.push { id: member_id, privilegeLevel: "readOnly" }
|
||||
members.push { id: member_id.toString(), privilegeLevel: "readOnly" }
|
||||
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
|
||||
|
||||
getMemberIds: (project_id, callback = (error, member_ids) ->) ->
|
||||
|
@ -35,6 +35,16 @@ module.exports = CollaboratorsHandler =
|
|||
return cb(null, { user: user, privilegeLevel: member.privilegeLevel })
|
||||
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) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) ->
|
||||
return callback(error) if error?
|
||||
|
|
|
@ -34,9 +34,9 @@ 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, canAccess, privilegeLevel) ->
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
|
||||
return callback(error) if error?
|
||||
if !canAccess
|
||||
if !privilegeLevel
|
||||
callback null, null, false
|
||||
else
|
||||
callback(null,
|
||||
|
|
|
@ -225,9 +225,9 @@ 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, canAccess, privilegeLevel)->
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel)->
|
||||
return next(error) if error?
|
||||
if !canAccess
|
||||
if !privilegeLevel
|
||||
return res.sendStatus 401
|
||||
|
||||
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
||||
|
|
|
@ -27,10 +27,11 @@ module.exports = ProjectEditorHandler =
|
|||
|
||||
owner = null
|
||||
for member in members
|
||||
if member.privilegeLevel == "admin"
|
||||
if member.privilegeLevel == "owner"
|
||||
owner = member.user
|
||||
else
|
||||
result.members.push @buildUserModelView member.user, member.privilegeLevel
|
||||
if owner?
|
||||
result.owner = @buildUserModelView owner, "owner"
|
||||
|
||||
if owner?.features?
|
||||
|
|
|
@ -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()
|
|
@ -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
|
|
@ -36,7 +36,7 @@ describe "CollaboratorsHandler", ->
|
|||
it "should return an array of member ids with their privilege levels", ->
|
||||
@callback
|
||||
.calledWith(null, [
|
||||
{ id: "owner-ref", privilegeLevel: "admin" }
|
||||
{ id: "owner-ref", privilegeLevel: "owner" }
|
||||
{ id: "read-only-ref-1", privilegeLevel: "readOnly" }
|
||||
{ id: "read-only-ref-2", privilegeLevel: "readOnly" }
|
||||
{ id: "read-write-ref-1", privilegeLevel: "readAndWrite" }
|
||||
|
@ -83,6 +83,26 @@ describe "CollaboratorsHandler", ->
|
|||
])
|
||||
.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", ->
|
||||
beforeEach ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub()
|
||||
|
|
|
@ -99,7 +99,7 @@ describe "EditorHttpController", ->
|
|||
describe "when authorized", ->
|
||||
beforeEach ->
|
||||
@AuthorizationManager.getPrivilegeLevelForProject =
|
||||
sinon.stub().callsArgWith(2, null, true, "owner")
|
||||
sinon.stub().callsArgWith(2, null, "owner")
|
||||
@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
|
||||
|
||||
it "should find the project without doc lines", ->
|
||||
|
@ -128,7 +128,7 @@ describe "EditorHttpController", ->
|
|||
describe "when not authorized", ->
|
||||
beforeEach ->
|
||||
@AuthorizationManager.getPrivilegeLevelForProject =
|
||||
sinon.stub().callsArgWith(2, null, false, null)
|
||||
sinon.stub().callsArgWith(2, null, null)
|
||||
@EditorHttpController._buildJoinProjectView(@project_id, @user_id, @callback)
|
||||
|
||||
it "should return false in the callback", ->
|
||||
|
|
|
@ -299,7 +299,7 @@ describe "ProjectController", ->
|
|||
@ProjectModel.findOne.callsArgWith 1, null, @project
|
||||
@UserModel.findById.callsArgWith(1, null, @user)
|
||||
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
||||
@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, true, "owner"
|
||||
@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, "owner"
|
||||
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||
@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
|
||||
@ProjectUpdateHandler.markAsOpened.callsArgWith(1)
|
||||
|
@ -332,7 +332,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, false
|
||||
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 2, null, null
|
||||
@res.sendStatus = (resCode, opts)=>
|
||||
resCode.should.equal 401
|
||||
done()
|
||||
|
|
|
@ -49,7 +49,7 @@ describe "ProjectEditorHandler", ->
|
|||
last_name : "ShareLaTeX"
|
||||
email : "owner@sharelatex.com"
|
||||
},
|
||||
privilegeLevel: "admin"
|
||||
privilegeLevel: "owner"
|
||||
},{
|
||||
user: {
|
||||
_id: "read-only-id"
|
||||
|
|
|
@ -44,6 +44,8 @@ class User
|
|||
projectName: name
|
||||
}, (error, response, body) ->
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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) ->
|
||||
expect_no_settings_write_access @anon, @project_id, redirect_to: "/restricted", done
|
||||
|
|
Loading…
Reference in a new issue