Merge pull request #1194 from sharelatex/ew-collabratec-link-project-apis

collabratec link/unlink apis, auth refactor

GitOrigin-RevId: dc901494197334e87d3e6702c789654ccef5e56a
This commit is contained in:
Ersun Warncke 2018-12-04 07:19:48 -04:00 committed by sharelatex
parent f02e3e604b
commit 28c934e8ff
4 changed files with 209 additions and 31 deletions

View file

@ -25,9 +25,10 @@ module.exports = AuthorizationManager =
# * 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.
# * becauseSiteAdmin: true if access level is only because user is admin
getPrivilegeLevelForProject: (
user_id, project_id, token,
callback = (error, privilegeLevel, becausePublic) ->
callback = (error, privilegeLevel, becausePublic, becauseSiteAdmin) ->
) ->
if !user_id?
# User is Anonymous, Try Token-based access
@ -41,48 +42,48 @@ module.exports = AuthorizationManager =
return callback(err) if err?
if isValidReadOnly
# Grant anonymous user read-only access
callback null, PrivilegeLevels.READ_ONLY, false
callback null, PrivilegeLevels.READ_ONLY, false, false
else if (
isValidReadAndWrite and
TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED
)
# Grant anonymous user read-and-write access
callback null, PrivilegeLevels.READ_AND_WRITE, false
callback null, PrivilegeLevels.READ_AND_WRITE, false, false
else
# Deny anonymous access
callback null, PrivilegeLevels.NONE, false
callback null, PrivilegeLevels.NONE, false, false
else if publicAccessLevel == PublicAccessLevels.READ_ONLY
# Legacy public read-only access for anonymous user
callback null, PrivilegeLevels.READ_ONLY, true
callback null, PrivilegeLevels.READ_ONLY, true, false
else if publicAccessLevel == PublicAccessLevels.READ_AND_WRITE
# Legacy public read-write access for anonymous user
callback null, PrivilegeLevels.READ_AND_WRITE, true
callback null, PrivilegeLevels.READ_AND_WRITE, true, false
else
# Deny anonymous user access
callback null, PrivilegeLevels.NONE, false
callback null, PrivilegeLevels.NONE, false, 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
# The user has direct access
callback null, privilegeLevel, false
callback null, privilegeLevel, false, false
else
AuthorizationManager.isUserSiteAdmin user_id, (error, isAdmin) ->
return callback(error) if error?
if isAdmin
callback null, PrivilegeLevels.OWNER, false
callback null, PrivilegeLevels.OWNER, false, true
else
# Legacy public-access system
# User is present (not anonymous), but does not have direct access
AuthorizationManager.getPublicAccessLevel project_id, (err, publicAccessLevel) ->
return callback(err) if err?
if publicAccessLevel == PublicAccessLevels.READ_ONLY
callback null, PrivilegeLevels.READ_ONLY, true
callback null, PrivilegeLevels.READ_ONLY, true, false
else if publicAccessLevel == PublicAccessLevels.READ_AND_WRITE
callback null, PrivilegeLevels.READ_AND_WRITE, true
callback null, PrivilegeLevels.READ_AND_WRITE, true, false
else
callback null, PrivilegeLevels.NONE, false
callback null, PrivilegeLevels.NONE, false, false
canUserReadProject: (user_id, project_id, token, callback = (error, canRead) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel) ->
@ -104,10 +105,10 @@ module.exports = AuthorizationManager =
else
return callback null, false
canUserAdminProject: (user_id, project_id, token, callback = (error, canAdmin) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel) ->
canUserAdminProject: (user_id, project_id, token, callback = (error, canAdmin, becauseSiteAdmin) ->) ->
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel, becausePublic, becauseSiteAdmin) ->
return callback(error) if error?
return callback null, (privilegeLevel == PrivilegeLevels.OWNER)
return callback null, (privilegeLevel == PrivilegeLevels.OWNER), becauseSiteAdmin
isUserSiteAdmin: (user_id, callback = (error, isAdmin) ->) ->
if !user_id?

View file

@ -1,6 +1,54 @@
ObjectId = require("mongojs").ObjectId
Project = require("../../models/Project").Project
module.exports = ProjectCollabratecDetailsHandler =
initializeCollabratecProject: (project_id, name, user_id, collabratec_document_id, collabratec_privategroup_id, callback=(err)->) ->
update = $set: { name, collabratecUsers: [ { user_id, collabratec_document_id, collabratec_privategroup_id } ] }
initializeCollabratecProject: (project_id, user_id, collabratec_document_id, collabratec_privategroup_id, callback=(err)->) ->
try
project_id = ObjectId(project_id)
user_id = ObjectId(user_id)
catch err
return callback err
update = $set: { collabratecUsers: [ { user_id, collabratec_document_id, collabratec_privategroup_id } ] }
Project.update { _id: project_id }, update, callback
isLinkedCollabratecUserProject: (project_id, user_id, callback=(err, isLinked)->) ->
try
project_id = ObjectId(project_id)
user_id = ObjectId(user_id)
catch err
return callback err
query =
_id: project_id
collabratecUsers: $elemMatch:
user_id: user_id
Project.findOne query, {_id: 1}, (err, project) ->
callback err if err?
callback null, project?
linkCollabratecUserProject: (project_id, user_id, collabratec_document_id, callback=(err)->) ->
try
project_id = ObjectId(project_id)
user_id = ObjectId(user_id)
catch err
return callback err
query =
_id: project_id
collabratecUsers: $not: $elemMatch:
collabratec_document_id: collabratec_document_id
user_id: user_id
update = $push: collabratecUsers:
collabratec_document_id: collabratec_document_id
user_id: user_id
Project.update query, update, callback
unlinkCollabratecUserProject: (project_id, user_id, callback=(err)->) ->
try
project_id = ObjectId(project_id)
user_id = ObjectId(user_id)
catch err
return callback err
query =
_id: project_id
update = $pull: collabratecUsers:
user_id: user_id
Project.update query, update, callback

View file

@ -42,7 +42,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return the user's privilege level", ->
@callback.calledWith(null, "readOnly", false).should.equal true
@callback.calledWith(null, "readOnly", false, false).should.equal true
describe "with a user_id with no privilege level", ->
beforeEach ->
@ -53,7 +53,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
@callback.calledWith(null, false, false, false).should.equal true
describe "with a user_id who is an admin", ->
beforeEach ->
@ -64,7 +64,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return the user as an owner", ->
@callback.calledWith(null, "owner", false).should.equal true
@callback.calledWith(null, "owner", false, true).should.equal true
describe "with no user (anonymous)", ->
@ -86,7 +86,7 @@ describe "AuthorizationManager", ->
@TokenAccessHandler.isValidToken.calledWith(@project_id, @token).should.equal true
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
@callback.calledWith(null, false, false, false).should.equal true
describe 'when the token is valid for read-and-write', ->
@ -108,7 +108,7 @@ describe "AuthorizationManager", ->
@TokenAccessHandler.isValidToken.calledWith(@project_id, @token).should.equal true
it "should deny access", ->
@callback.calledWith(null, false, false).should.equal true
@callback.calledWith(null, false, false, false).should.equal true
describe 'when read-write-sharing is enabled', ->
beforeEach ->
@ -165,7 +165,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return the user's privilege level", ->
@callback.calledWith(null, "readOnly", false).should.equal true
@callback.calledWith(null, "readOnly", false, false).should.equal true
describe "with a user_id with no privilege level", ->
beforeEach ->
@ -176,7 +176,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
@callback.calledWith(null, false, false, false).should.equal true
describe "with a user_id who is an admin", ->
beforeEach ->
@ -187,7 +187,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @token, @callback
it "should return the user as an owner", ->
@callback.calledWith(null, "owner", false).should.equal true
@callback.calledWith(null, "owner", false, true).should.equal true
describe "with no user (anonymous)", ->
beforeEach ->
@ -200,7 +200,7 @@ describe "AuthorizationManager", ->
@AuthorizationManager.isUserSiteAdmin.called.should.equal false
it "should return false", ->
@callback.calledWith(null, false, false).should.equal true
@callback.calledWith(null, false, false, false).should.equal true
describe "with a public project", ->
beforeEach ->

View file

@ -13,6 +13,8 @@ modulePath = Path.join __dirname, "../../../../app/js/Features/Project/ProjectCo
describe "ProjectCollabratecDetailsHandler", ->
beforeEach ->
@projectId = ObjectId("5bea8747c7bba6012fcaceb3")
@userId = ObjectId("5be316a9c7f6aa03802ea8fb")
@ProjectModel = {}
@ProjectCollabratecDetailsHandler = SandboxedModule.require modulePath, requires:
"../../models/Project": { Project: @ProjectModel }
@ -23,23 +25,150 @@ describe "ProjectCollabratecDetailsHandler", ->
describe "when update succeeds", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields()
@ProjectCollabratecDetailsHandler.initializeCollabratecProject "project-id", "name", "user-id", "collabratec-document-id", "collabratec-private-group-id", @callback
@ProjectCollabratecDetailsHandler.initializeCollabratecProject @projectId, @userId, "collabratec-document-id", "collabratec-private-group-id", @callback
it "should update project model", ->
update = $set: {
name: "name",
collabratecUsers: [ {
user_id: "user-id",
user_id: @userId,
collabratec_document_id: "collabratec-document-id",
collabratec_privategroup_id: "collabratec-private-group-id"
} ]
}
expect(@ProjectModel.update).to.have.been.calledWith { _id: "project-id" }, update, @callback
expect(@ProjectModel.update).to.have.been.calledWith { _id: @projectId }, update, @callback
describe "when update has error", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields("error")
@ProjectCollabratecDetailsHandler.initializeCollabratecProject "project-id", "name", "user-id", "collabratec-document-id", "collabratec-private-group-id", @callback
@ProjectCollabratecDetailsHandler.initializeCollabratecProject @projectId, @userId, "collabratec-document-id", "collabratec-private-group-id", @callback
it "should callback with error", ->
expect(@callback).to.have.been.calledWith("error")
describe "with invalid args", ->
beforeEach ->
@ProjectModel.update = sinon.stub()
@ProjectCollabratecDetailsHandler.initializeCollabratecProject "bad-project-id", "bad-user-id", "collabratec-document-id", "collabratec-private-group-id", @callback
it "should not update", ->
expect(@ProjectModel.update).not.to.have.beenCalled
it "should callback with error", ->
expect(@callback.firstCall.args[0]).to.be.instanceOf Error
describe "isLinkedCollabratecUserProject", ->
beforeEach ->
@ProjectModel.findOne = sinon.stub().yields()
describe "when find succeeds", ->
describe "when user project found", ->
beforeEach ->
@ProjectModel.findOne = sinon.stub().yields(null, "project")
@ProjectCollabratecDetailsHandler.isLinkedCollabratecUserProject @projectId, @userId, @callback
it "should call find with project and user id", ->
expect(@ProjectModel.findOne).to.have.been.calledWithMatch {
_id: ObjectId(@projectId)
collabratecUsers: $elemMatch:
user_id: ObjectId(@userId)
}
it "should callback with true", ->
expect(@callback).to.have.been.calledWith null, true
describe "when user project found", ->
beforeEach ->
@ProjectModel.findOne = sinon.stub().yields(null, null)
@ProjectCollabratecDetailsHandler.isLinkedCollabratecUserProject @projectId, @userId, @callback
it "should callback with false", ->
expect(@callback).to.have.been.calledWith null, false
describe "when find has error", ->
beforeEach ->
@ProjectModel.findOne = sinon.stub().yields("error")
@ProjectCollabratecDetailsHandler.isLinkedCollabratecUserProject @projectId, @userId, @callback
it "should callback with error", ->
expect(@callback).to.have.been.calledWith "error"
describe "with invalid args", ->
beforeEach ->
@ProjectModel.findOne = sinon.stub()
@ProjectCollabratecDetailsHandler.isLinkedCollabratecUserProject "bad-project-id", "bad-user-id", @callback
it "should not update", ->
expect(@ProjectModel.findOne).not.to.have.beenCalled
it "should callback with error", ->
expect(@callback.firstCall.args[0]).to.be.instanceOf Error
describe "linkCollabratecUserProject", ->
describe "when update succeeds", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields()
@ProjectCollabratecDetailsHandler.linkCollabratecUserProject @projectId, @userId, "collabratec-document-id", @callback
it "should update project model", ->
query =
_id: @projectId
collabratecUsers: $not: $elemMatch:
collabratec_document_id: "collabratec-document-id"
user_id: @userId
update = $push: collabratecUsers:
collabratec_document_id: "collabratec-document-id"
user_id: @userId
expect(@ProjectModel.update).to.have.been.calledWith query, update, @callback
describe "when update has error", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields("error")
@ProjectCollabratecDetailsHandler.linkCollabratecUserProject @projectId, @userId, "collabratec-document-id", @callback
it "should callback with error", ->
expect(@callback).to.have.been.calledWith("error")
describe "with invalid args", ->
beforeEach ->
@ProjectModel.update = sinon.stub()
@ProjectCollabratecDetailsHandler.linkCollabratecUserProject "bad-project-id", "bad-user-id", "collabratec-document-id", @callback
it "should not update", ->
expect(@ProjectModel.update).not.to.have.beenCalled
it "should callback with error", ->
expect(@callback.firstCall.args[0]).to.be.instanceOf Error
describe "unlinkCollabratecUserProject", ->
describe "when update succeeds", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields()
@ProjectCollabratecDetailsHandler.unlinkCollabratecUserProject @projectId, @userId, @callback
it "should update project model", ->
query =
_id: @projectId
update = $pull: collabratecUsers:
user_id: @userId
expect(@ProjectModel.update).to.have.been.calledWith query, update, @callback
describe "when update has error", ->
beforeEach ->
@ProjectModel.update = sinon.stub().yields("error")
@ProjectCollabratecDetailsHandler.unlinkCollabratecUserProject @projectId, @userId, @callback
it "should callback with error", ->
expect(@callback).to.have.been.calledWith("error")
describe "with invalid args", ->
beforeEach ->
@ProjectModel.update = sinon.stub()
@ProjectCollabratecDetailsHandler.unlinkCollabratecUserProject "bad-project-id", "bad-user-id", @callback
it "should not update", ->
expect(@ProjectModel.update).not.to.have.beenCalled
it "should callback with error", ->
expect(@callback.firstCall.args[0]).to.be.instanceOf Error