mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #2206 from overleaf/sk-restricted-users-redux
Update the joinProject api to include isRestrictedUser flag GitOrigin-RevId: 38988b5c886e5355edc2edcd834ae6e334fc9f10
This commit is contained in:
parent
8a46a385f7
commit
2c147f575c
8 changed files with 345 additions and 221 deletions
|
@ -9,6 +9,12 @@ const { ObjectId } = require('mongojs')
|
|||
const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
|
||||
|
||||
module.exports = AuthorizationManager = {
|
||||
isRestrictedUser(userId, privilegeLevel, isTokenMember) {
|
||||
return (
|
||||
privilegeLevel === PrivilegeLevels.READ_ONLY && (isTokenMember || !userId)
|
||||
)
|
||||
},
|
||||
|
||||
getPublicAccessLevel(projectId, callback) {
|
||||
if (!ObjectId.isValid(projectId)) {
|
||||
return callback(new Error('invalid project id'))
|
||||
|
|
|
@ -10,11 +10,13 @@ const CollaboratorsGetter = require('./CollaboratorsGetter')
|
|||
const Errors = require('../Errors/Errors')
|
||||
|
||||
module.exports = {
|
||||
userIsTokenMember: callbackify(userIsTokenMember),
|
||||
removeUserFromProject: callbackify(removeUserFromProject),
|
||||
removeUserFromAllProjects: callbackify(removeUserFromAllProjects),
|
||||
addUserIdToProject: callbackify(addUserIdToProject),
|
||||
transferProjects: callbackify(transferProjects),
|
||||
promises: {
|
||||
userIsTokenMember,
|
||||
removeUserFromProject,
|
||||
removeUserFromAllProjects,
|
||||
addUserIdToProject,
|
||||
|
@ -191,6 +193,32 @@ async function setCollaboratorPrivilegeLevel(
|
|||
}
|
||||
}
|
||||
|
||||
async function userIsTokenMember(userId, projectId) {
|
||||
if (!userId) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const project = await Project.findOne(
|
||||
{
|
||||
_id: projectId,
|
||||
$or: [
|
||||
{ tokenAccessReadOnly_refs: userId },
|
||||
{ tokenAccessReadAndWrite_refs: userId }
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: 1
|
||||
}
|
||||
)
|
||||
return project != null
|
||||
} catch (err) {
|
||||
throw new OError({
|
||||
message: 'problem while checking if user is token member',
|
||||
info: { userId, projectId }
|
||||
}).withCause(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function _flushProjects(projectIds) {
|
||||
for (const projectId of projectIds) {
|
||||
await ProjectEntityHandler.promises.flushProjectToThirdPartyDataStore(
|
||||
|
|
|
@ -1,30 +1,14 @@
|
|||
/* eslint-disable
|
||||
camelcase,
|
||||
handle-callback-err,
|
||||
max-len,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let EditorHttpController
|
||||
const ProjectEntityUpdateHandler = require('../Project/ProjectEntityUpdateHandler')
|
||||
const ProjectDeleter = require('../Project/ProjectDeleter')
|
||||
const logger = require('logger-sharelatex')
|
||||
const EditorRealTimeController = require('./EditorRealTimeController')
|
||||
const EditorController = require('./EditorController')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const AuthorizationManager = require('../Authorization/AuthorizationManager')
|
||||
const ProjectEditorHandler = require('../Project/ProjectEditorHandler')
|
||||
const Metrics = require('metrics-sharelatex')
|
||||
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
|
||||
const CollaboratorsInviteHandler = require('../Collaborators/CollaboratorsInviteHandler')
|
||||
const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
|
||||
const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
|
||||
const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
|
||||
const AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
|
@ -32,63 +16,67 @@ const Errors = require('../Errors/Errors')
|
|||
|
||||
module.exports = EditorHttpController = {
|
||||
joinProject(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
let { user_id } = req.query
|
||||
if (user_id === 'anonymous-user') {
|
||||
user_id = null
|
||||
const projectId = req.params.Project_id
|
||||
let userId = req.query.user_id
|
||||
if (userId === 'anonymous-user') {
|
||||
userId = null
|
||||
}
|
||||
logger.log({ user_id, project_id }, 'join project request')
|
||||
logger.log({ userId, projectId }, 'join project request')
|
||||
Metrics.inc('editor.join-project')
|
||||
return EditorHttpController._buildJoinProjectView(
|
||||
req,
|
||||
project_id,
|
||||
user_id,
|
||||
function(error, project, privilegeLevel) {
|
||||
if (error != null) {
|
||||
return next(error)
|
||||
}
|
||||
// Hide access tokens if this is not the project owner
|
||||
TokenAccessHandler.protectTokens(project, privilegeLevel)
|
||||
res.json({
|
||||
project,
|
||||
privilegeLevel
|
||||
})
|
||||
// Only show the 'renamed or deleted' message once
|
||||
if (project != null ? project.deletedByExternalDataSource : undefined) {
|
||||
return ProjectDeleter.unmarkAsDeletedByExternalSource(project_id)
|
||||
}
|
||||
EditorHttpController._buildJoinProjectView(req, projectId, userId, function(
|
||||
error,
|
||||
project,
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
)
|
||||
// Hide access tokens if this is not the project owner
|
||||
TokenAccessHandler.protectTokens(project, privilegeLevel)
|
||||
if (isRestrictedUser) {
|
||||
project.owner = { _id: project.owner._id }
|
||||
}
|
||||
res.json({
|
||||
project,
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
})
|
||||
// Only show the 'renamed or deleted' message once
|
||||
if (project != null ? project.deletedByExternalDataSource : undefined) {
|
||||
return ProjectDeleter.unmarkAsDeletedByExternalSource(projectId)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
_buildJoinProjectView(req, project_id, user_id, callback) {
|
||||
_buildJoinProjectView(req, projectId, userId, callback) {
|
||||
if (callback == null) {
|
||||
callback = function(error, project, privilegeLevel) {}
|
||||
callback = function() {}
|
||||
}
|
||||
logger.log({ project_id, user_id }, 'building the joinProject view')
|
||||
return ProjectGetter.getProjectWithoutDocLines(project_id, function(
|
||||
logger.log({ projectId, userId }, 'building the joinProject view')
|
||||
ProjectGetter.getProjectWithoutDocLines(projectId, function(
|
||||
error,
|
||||
project
|
||||
) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
if (project == null) {
|
||||
return callback(new Errors.NotFoundError('project not found'))
|
||||
}
|
||||
return CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels(
|
||||
project_id,
|
||||
CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels(
|
||||
projectId,
|
||||
function(error, members) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
const token = TokenAccessHandler.getRequestToken(req, project_id)
|
||||
return AuthorizationManager.getPrivilegeLevelForProject(
|
||||
user_id,
|
||||
project_id,
|
||||
const token = TokenAccessHandler.getRequestToken(req, projectId)
|
||||
AuthorizationManager.getPrivilegeLevelForProject(
|
||||
userId,
|
||||
projectId,
|
||||
token,
|
||||
function(error, privilegeLevel) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
if (
|
||||
|
@ -96,38 +84,53 @@ module.exports = EditorHttpController = {
|
|||
privilegeLevel === PrivilegeLevels.NONE
|
||||
) {
|
||||
logger.log(
|
||||
{ project_id, user_id, privilegeLevel },
|
||||
{ projectId, userId, privilegeLevel },
|
||||
'not an acceptable privilege level, returning null'
|
||||
)
|
||||
return callback(null, null, false)
|
||||
}
|
||||
return CollaboratorsInviteHandler.getAllInvites(
|
||||
project_id,
|
||||
function(error, invites) {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
logger.log(
|
||||
{
|
||||
project_id,
|
||||
user_id,
|
||||
memberCount: members.length,
|
||||
inviteCount: invites.length,
|
||||
privilegeLevel
|
||||
},
|
||||
'returning project model view'
|
||||
)
|
||||
return callback(
|
||||
null,
|
||||
ProjectEditorHandler.buildProjectModelView(
|
||||
project,
|
||||
members,
|
||||
invites
|
||||
),
|
||||
privilegeLevel
|
||||
)
|
||||
CollaboratorsInviteHandler.getAllInvites(projectId, function(
|
||||
error,
|
||||
invites
|
||||
) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
)
|
||||
logger.log(
|
||||
{
|
||||
projectId,
|
||||
userId,
|
||||
memberCount: members.length,
|
||||
inviteCount: invites.length,
|
||||
privilegeLevel
|
||||
},
|
||||
'returning project model view'
|
||||
)
|
||||
CollaboratorsHandler.userIsTokenMember(
|
||||
userId,
|
||||
projectId,
|
||||
(err, isTokenMember) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const isRestrictedUser = AuthorizationManager.isRestrictedUser(
|
||||
userId,
|
||||
privilegeLevel,
|
||||
isTokenMember
|
||||
)
|
||||
callback(
|
||||
null,
|
||||
ProjectEditorHandler.buildProjectModelView(
|
||||
project,
|
||||
members,
|
||||
invites
|
||||
),
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -140,24 +143,24 @@ module.exports = EditorHttpController = {
|
|||
},
|
||||
|
||||
addDoc(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const { parent_folder_id } = req.body
|
||||
const user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log(
|
||||
{ project_id, name, parent_folder_id },
|
||||
{ projectId, name, parentFolderId },
|
||||
'getting request to add doc to project'
|
||||
)
|
||||
if (!EditorHttpController._nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
return EditorController.addDoc(
|
||||
project_id,
|
||||
parent_folder_id,
|
||||
EditorController.addDoc(
|
||||
projectId,
|
||||
parentFolderId,
|
||||
name,
|
||||
[],
|
||||
'editor',
|
||||
user_id,
|
||||
userId,
|
||||
function(error, doc) {
|
||||
if (error && error.message === 'project_has_to_many_files') {
|
||||
return res
|
||||
|
@ -173,15 +176,15 @@ module.exports = EditorHttpController = {
|
|||
},
|
||||
|
||||
addFolder(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const { parent_folder_id } = req.body
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
if (!EditorHttpController._nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
return EditorController.addFolder(
|
||||
project_id,
|
||||
parent_folder_id,
|
||||
EditorController.addFolder(
|
||||
projectId,
|
||||
parentFolderId,
|
||||
name,
|
||||
'editor',
|
||||
function(error, doc) {
|
||||
|
@ -201,22 +204,22 @@ module.exports = EditorHttpController = {
|
|||
},
|
||||
|
||||
renameEntity(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
const { entity_id } = req.params
|
||||
const { entity_type } = req.params
|
||||
const projectId = req.params.Project_id
|
||||
const entityId = req.params.entity_id
|
||||
const entityType = req.params.entity_type
|
||||
const { name } = req.body
|
||||
if (!EditorHttpController._nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
const user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
return EditorController.renameEntity(
|
||||
project_id,
|
||||
entity_id,
|
||||
entity_type,
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
EditorController.renameEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
entityType,
|
||||
name,
|
||||
user_id,
|
||||
userId,
|
||||
function(error) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
return res.sendStatus(204)
|
||||
|
@ -225,19 +228,19 @@ module.exports = EditorHttpController = {
|
|||
},
|
||||
|
||||
moveEntity(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
const { entity_id } = req.params
|
||||
const { entity_type } = req.params
|
||||
const { folder_id } = req.body
|
||||
const user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
return EditorController.moveEntity(
|
||||
project_id,
|
||||
entity_id,
|
||||
folder_id,
|
||||
entity_type,
|
||||
user_id,
|
||||
const projectId = req.params.Project_id
|
||||
const entityId = req.params.entity_id
|
||||
const entityType = req.params.entity_type
|
||||
const folderId = req.body.folder_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
EditorController.moveEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
folderId,
|
||||
entityType,
|
||||
userId,
|
||||
function(error) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
return res.sendStatus(204)
|
||||
|
@ -247,35 +250,35 @@ module.exports = EditorHttpController = {
|
|||
|
||||
deleteDoc(req, res, next) {
|
||||
req.params.entity_type = 'doc'
|
||||
return EditorHttpController.deleteEntity(req, res, next)
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteFile(req, res, next) {
|
||||
req.params.entity_type = 'file'
|
||||
return EditorHttpController.deleteEntity(req, res, next)
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteFolder(req, res, next) {
|
||||
req.params.entity_type = 'folder'
|
||||
return EditorHttpController.deleteEntity(req, res, next)
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteEntity(req, res, next) {
|
||||
const project_id = req.params.Project_id
|
||||
const { entity_id } = req.params
|
||||
const { entity_type } = req.params
|
||||
const user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
return EditorController.deleteEntity(
|
||||
project_id,
|
||||
entity_id,
|
||||
entity_type,
|
||||
const projectId = req.params.Project_id
|
||||
const entityId = req.params.entity_id
|
||||
const entityType = req.params.entity_type
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
EditorController.deleteEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
entityType,
|
||||
'editor',
|
||||
user_id,
|
||||
userId,
|
||||
function(error) {
|
||||
if (error != null) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
return res.sendStatus(204)
|
||||
res.sendStatus(204)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -723,8 +723,11 @@ const ProjectController = {
|
|||
anonymous,
|
||||
anonymousAccessToken: req._anonymousAccessToken,
|
||||
isTokenMember,
|
||||
isRestrictedTokenMember:
|
||||
privilegeLevel === 'readOnly' && (anonymous || isTokenMember),
|
||||
isRestrictedTokenMember: AuthorizationManager.isRestrictedUser(
|
||||
userId,
|
||||
privilegeLevel,
|
||||
isTokenMember
|
||||
),
|
||||
languages: Settings.languages,
|
||||
editorThemes: THEME_LIST,
|
||||
maxDocLength: Settings.max_doc_length,
|
||||
|
|
|
@ -237,6 +237,13 @@ describe('TokenAccess', function() {
|
|||
this.projectId,
|
||||
(response, body) => {
|
||||
expect(body.privilegeLevel).to.equal('readOnly')
|
||||
expect(body.isRestrictedUser).to.equal(true)
|
||||
expect(body.project.owner).to.have.keys('_id')
|
||||
expect(body.project.owner).to.not.have.any.keys(
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name'
|
||||
)
|
||||
},
|
||||
cb
|
||||
)
|
||||
|
@ -344,6 +351,13 @@ describe('TokenAccess', function() {
|
|||
this.tokens.readOnly,
|
||||
(response, body) => {
|
||||
expect(body.privilegeLevel).to.equal('readOnly')
|
||||
expect(body.isRestrictedUser).to.equal(true)
|
||||
expect(body.project.owner).to.have.keys('_id')
|
||||
expect(body.project.owner).to.not.have.any.keys(
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name'
|
||||
)
|
||||
},
|
||||
cb
|
||||
)
|
||||
|
@ -453,6 +467,15 @@ describe('TokenAccess', function() {
|
|||
this.projectId,
|
||||
(response, body) => {
|
||||
expect(body.privilegeLevel).to.equal('readAndWrite')
|
||||
expect(body.isRestrictedUser).to.equal(false)
|
||||
expect(body.project.owner).to.have.all.keys(
|
||||
'_id',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'privileges',
|
||||
'signUpDate'
|
||||
)
|
||||
},
|
||||
cb
|
||||
)
|
||||
|
|
|
@ -45,6 +45,30 @@ describe('AuthorizationManager', function() {
|
|||
return (this.callback = sinon.stub())
|
||||
})
|
||||
|
||||
describe('isRestrictedUser', function() {
|
||||
it('should produce the correct values', function() {
|
||||
const notRestrictedScenarios = [
|
||||
[null, 'readAndWrite', false],
|
||||
['id', 'readAndWrite', true],
|
||||
['id', 'readOnly', false]
|
||||
]
|
||||
const restrictedScenarios = [
|
||||
[null, 'readOnly', false],
|
||||
['id', 'readOnly', true]
|
||||
]
|
||||
for (var notRestrictedArgs of notRestrictedScenarios) {
|
||||
expect(
|
||||
this.AuthorizationManager.isRestrictedUser(...notRestrictedArgs)
|
||||
).to.equal(false)
|
||||
}
|
||||
for (var restrictedArgs of restrictedScenarios) {
|
||||
expect(
|
||||
this.AuthorizationManager.isRestrictedUser(...restrictedArgs)
|
||||
).to.equal(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPrivilegeLevelForProject', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectGetter.getProject = sinon.stub()
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
require('chai').should()
|
||||
|
@ -25,13 +14,10 @@ describe('EditorHttpController', function() {
|
|||
console: console
|
||||
},
|
||||
requires: {
|
||||
'../Project/ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
|
||||
'../Project/ProjectDeleter': (this.ProjectDeleter = {}),
|
||||
'../Project/ProjectGetter': (this.ProjectGetter = {}),
|
||||
'../User/UserGetter': (this.UserGetter = {}),
|
||||
'../Authorization/AuthorizationManager': (this.AuthorizationManager = {}),
|
||||
'../Project/ProjectEditorHandler': (this.ProjectEditorHandler = {}),
|
||||
'./EditorRealTimeController': (this.EditorRealTimeController = {}),
|
||||
'logger-sharelatex': (this.logger = {
|
||||
log: sinon.stub(),
|
||||
error: sinon.stub()
|
||||
|
@ -39,6 +25,7 @@ describe('EditorHttpController', function() {
|
|||
'./EditorController': (this.EditorController = {}),
|
||||
'metrics-sharelatex': (this.Metrics = { inc: sinon.stub() }),
|
||||
'../Collaborators/CollaboratorsGetter': (this.CollaboratorsGetter = {}),
|
||||
'../Collaborators/CollaboratorsHandler': (this.CollaboratorsHandler = {}),
|
||||
'../Collaborators/CollaboratorsInviteHandler': (this.CollaboratorsInviteHandler = {}),
|
||||
'../TokenAccess/TokenAccessHandler': (this.TokenAccessHandler = {}),
|
||||
'../Authentication/AuthenticationController': (this.AuthenticationController = {}),
|
||||
|
@ -64,7 +51,7 @@ describe('EditorHttpController', function() {
|
|||
this.TokenAccessHandler.getRequestToken = sinon
|
||||
.stub()
|
||||
.returns((this.token = null))
|
||||
return (this.TokenAccessHandler.protectTokens = sinon.stub())
|
||||
this.TokenAccessHandler.protectTokens = sinon.stub()
|
||||
})
|
||||
|
||||
describe('joinProject', function() {
|
||||
|
@ -72,71 +59,112 @@ describe('EditorHttpController', function() {
|
|||
this.req.params = { Project_id: this.project_id }
|
||||
this.req.query = { user_id: this.user_id }
|
||||
this.projectView = {
|
||||
_id: this.project_id
|
||||
_id: this.project_id,
|
||||
owner: {
|
||||
_id: 'owner',
|
||||
email: 'owner@example.com',
|
||||
other_property: true
|
||||
}
|
||||
}
|
||||
this.reducedProjectView = {
|
||||
_id: this.project_id,
|
||||
owner: { _id: 'owner' }
|
||||
}
|
||||
this.EditorHttpController._buildJoinProjectView = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, this.projectView, 'owner')
|
||||
return (this.ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub())
|
||||
.callsArgWith(3, null, this.projectView, 'owner', false)
|
||||
this.ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||
})
|
||||
|
||||
describe('successfully', function() {
|
||||
beforeEach(function() {
|
||||
return this.EditorHttpController.joinProject(this.req, this.res)
|
||||
this.AuthorizationManager.isRestrictedUser = sinon.stub().returns(false)
|
||||
this.EditorHttpController.joinProject(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should get the project view', function() {
|
||||
return this.EditorHttpController._buildJoinProjectView
|
||||
this.EditorHttpController._buildJoinProjectView
|
||||
.calledWith(this.req, this.project_id, this.user_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the project and privilege level', function() {
|
||||
return this.res.json
|
||||
this.res.json
|
||||
.calledWith({
|
||||
project: this.projectView,
|
||||
privilegeLevel: 'owner'
|
||||
privilegeLevel: 'owner',
|
||||
isRestrictedUser: false
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should not try to unmark the project as deleted', function() {
|
||||
return this.ProjectDeleter.unmarkAsDeletedByExternalSource.called.should.equal(
|
||||
this.ProjectDeleter.unmarkAsDeletedByExternalSource.called.should.equal(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should send an inc metric', function() {
|
||||
return this.Metrics.inc
|
||||
.calledWith('editor.join-project')
|
||||
.should.equal(true)
|
||||
this.Metrics.inc.calledWith('editor.join-project').should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the project is marked as deleted', function() {
|
||||
beforeEach(function() {
|
||||
this.projectView.deletedByExternalDataSource = true
|
||||
return this.EditorHttpController.joinProject(this.req, this.res)
|
||||
this.EditorHttpController.joinProject(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should unmark the project as deleted', function() {
|
||||
return this.ProjectDeleter.unmarkAsDeletedByExternalSource
|
||||
this.ProjectDeleter.unmarkAsDeletedByExternalSource
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an restricted user', function() {
|
||||
beforeEach(function() {
|
||||
this.EditorHttpController._buildJoinProjectView = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, this.projectView, 'readOnly', true)
|
||||
this.EditorHttpController.joinProject(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should mark the user as restricted, and hide details of owner', function() {
|
||||
this.res.json
|
||||
.calledWith({
|
||||
project: this.reducedProjectView,
|
||||
privilegeLevel: 'readOnly',
|
||||
isRestrictedUser: true
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with an anonymous user', function() {
|
||||
beforeEach(function() {
|
||||
this.req.query = { user_id: 'anonymous-user' }
|
||||
return this.EditorHttpController.joinProject(this.req, this.res)
|
||||
this.EditorHttpController._buildJoinProjectView = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, this.projectView, 'readOnly', true)
|
||||
this.EditorHttpController.joinProject(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should pass the user id as null', function() {
|
||||
return this.EditorHttpController._buildJoinProjectView
|
||||
this.EditorHttpController._buildJoinProjectView
|
||||
.calledWith(this.req, this.project_id, null)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should mark the user as restricted', function() {
|
||||
this.res.json
|
||||
.calledWith({
|
||||
project: this.reducedProjectView,
|
||||
privilegeLevel: 'readOnly',
|
||||
isRestrictedUser: true
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -180,18 +208,19 @@ describe('EditorHttpController', function() {
|
|||
this.CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.members)
|
||||
this.CollaboratorsHandler.userIsTokenMember = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, false)
|
||||
this.AuthorizationManager.isRestrictedUser = sinon.stub().returns(false)
|
||||
this.CollaboratorsInviteHandler.getAllInvites = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.invites)
|
||||
return (this.UserGetter.getUser = sinon
|
||||
.stub()
|
||||
.callsArgWith(2, null, this.user))
|
||||
})
|
||||
|
||||
describe('when project is not found', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectGetter.getProjectWithoutDocLines.yields(null, null)
|
||||
return this.EditorHttpController._buildJoinProjectView(
|
||||
this.EditorHttpController._buildJoinProjectView(
|
||||
this.req,
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
|
@ -211,7 +240,7 @@ describe('EditorHttpController', function() {
|
|||
this.AuthorizationManager.getPrivilegeLevelForProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, 'owner')
|
||||
return this.EditorHttpController._buildJoinProjectView(
|
||||
this.EditorHttpController._buildJoinProjectView(
|
||||
this.req,
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
|
@ -220,32 +249,57 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should find the project without doc lines', function() {
|
||||
return this.ProjectGetter.getProjectWithoutDocLines
|
||||
this.ProjectGetter.getProjectWithoutDocLines
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should get the list of users in the project', function() {
|
||||
return this.CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels
|
||||
this.CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should check the privilege level', function() {
|
||||
return this.AuthorizationManager.getPrivilegeLevelForProject
|
||||
this.AuthorizationManager.getPrivilegeLevelForProject
|
||||
.calledWith(this.user_id, this.project_id, this.token)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should check if user is restricted', function() {
|
||||
this.AuthorizationManager.isRestrictedUser.called.should.equal(true)
|
||||
})
|
||||
|
||||
it('should include the invites', function() {
|
||||
return this.CollaboratorsInviteHandler.getAllInvites
|
||||
this.CollaboratorsInviteHandler.getAllInvites
|
||||
.calledWith(this.project._id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the project model view, privilege level and protocol version', function() {
|
||||
return this.callback
|
||||
.calledWith(null, this.projectModelView, 'owner')
|
||||
this.callback
|
||||
.calledWith(null, this.projectModelView, 'owner', false)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when user is restricted', function() {
|
||||
beforeEach(function() {
|
||||
this.AuthorizationManager.getPrivilegeLevelForProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, 'readOnly')
|
||||
this.AuthorizationManager.isRestrictedUser.returns(true)
|
||||
this.EditorHttpController._buildJoinProjectView(
|
||||
this.req,
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
it('should set the isRestrictedUser flag', function() {
|
||||
this.callback
|
||||
.calledWith(null, this.projectModelView, 'readOnly', true)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
@ -255,7 +309,7 @@ describe('EditorHttpController', function() {
|
|||
this.AuthorizationManager.getPrivilegeLevelForProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, null)
|
||||
return this.EditorHttpController._buildJoinProjectView(
|
||||
this.EditorHttpController._buildJoinProjectView(
|
||||
this.req,
|
||||
this.project_id,
|
||||
this.user_id,
|
||||
|
@ -264,7 +318,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should return false in the callback', function() {
|
||||
return this.callback.calledWith(null, null, false).should.equal(true)
|
||||
this.callback.calledWith(null, null, false).should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -277,18 +331,18 @@ describe('EditorHttpController', function() {
|
|||
name: (this.name = 'doc-name'),
|
||||
parent_folder_id: this.parent_folder_id
|
||||
}
|
||||
return (this.EditorController.addDoc = sinon
|
||||
this.EditorController.addDoc = sinon
|
||||
.stub()
|
||||
.callsArgWith(6, null, this.doc))
|
||||
.callsArgWith(6, null, this.doc)
|
||||
})
|
||||
|
||||
describe('successfully', function() {
|
||||
beforeEach(function() {
|
||||
return this.EditorHttpController.addDoc(this.req, this.res)
|
||||
this.EditorHttpController.addDoc(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should call EditorController.addDoc', function() {
|
||||
return this.EditorController.addDoc
|
||||
this.EditorController.addDoc
|
||||
.calledWith(
|
||||
this.project_id,
|
||||
this.parent_folder_id,
|
||||
|
@ -301,7 +355,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should send the doc back as JSON', function() {
|
||||
return this.res.json.calledWith(this.doc).should.equal(true)
|
||||
this.res.json.calledWith(this.doc).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -339,18 +393,18 @@ describe('EditorHttpController', function() {
|
|||
name: (this.name = 'folder-name'),
|
||||
parent_folder_id: this.parent_folder_id
|
||||
}
|
||||
return (this.EditorController.addFolder = sinon
|
||||
this.EditorController.addFolder = sinon
|
||||
.stub()
|
||||
.callsArgWith(4, null, this.folder))
|
||||
.callsArgWith(4, null, this.folder)
|
||||
})
|
||||
|
||||
describe('successfully', function() {
|
||||
beforeEach(function() {
|
||||
return this.EditorHttpController.addFolder(this.req, this.res)
|
||||
this.EditorHttpController.addFolder(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should call EditorController.addFolder', function() {
|
||||
return this.EditorController.addFolder
|
||||
this.EditorController.addFolder
|
||||
.calledWith(
|
||||
this.project_id,
|
||||
this.parent_folder_id,
|
||||
|
@ -361,7 +415,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should send the folder back as JSON', function() {
|
||||
return this.res.json.calledWith(this.folder).should.equal(true)
|
||||
this.res.json.calledWith(this.folder).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -417,11 +471,11 @@ describe('EditorHttpController', function() {
|
|||
}
|
||||
this.req.body = { name: (this.name = 'new-name') }
|
||||
this.EditorController.renameEntity = sinon.stub().callsArg(5)
|
||||
return this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should call EditorController.renameEntity', function() {
|
||||
return this.EditorController.renameEntity
|
||||
this.EditorController.renameEntity
|
||||
.calledWith(
|
||||
this.project_id,
|
||||
this.entity_id,
|
||||
|
@ -433,7 +487,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should send back a success response', function() {
|
||||
return this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -449,11 +503,11 @@ describe('EditorHttpController', function() {
|
|||
'EDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOTEDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOT')
|
||||
}
|
||||
this.EditorController.renameEntity = sinon.stub().callsArg(4)
|
||||
return this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should send back a bad request status code', function() {
|
||||
return this.res.sendStatus.calledWith(400).should.equal(true)
|
||||
this.res.sendStatus.calledWith(400).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -466,11 +520,11 @@ describe('EditorHttpController', function() {
|
|||
}
|
||||
this.req.body = { name: (this.name = '') }
|
||||
this.EditorController.renameEntity = sinon.stub().callsArg(4)
|
||||
return this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
this.EditorHttpController.renameEntity(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should send back a bad request status code', function() {
|
||||
return this.res.sendStatus.calledWith(400).should.equal(true)
|
||||
this.res.sendStatus.calledWith(400).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -483,11 +537,11 @@ describe('EditorHttpController', function() {
|
|||
}
|
||||
this.req.body = { folder_id: (this.folder_id = 'folder-id-123') }
|
||||
this.EditorController.moveEntity = sinon.stub().callsArg(5)
|
||||
return this.EditorHttpController.moveEntity(this.req, this.res)
|
||||
this.EditorHttpController.moveEntity(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should call EditorController.moveEntity', function() {
|
||||
return this.EditorController.moveEntity
|
||||
this.EditorController.moveEntity
|
||||
.calledWith(
|
||||
this.project_id,
|
||||
this.entity_id,
|
||||
|
@ -499,7 +553,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should send back a success response', function() {
|
||||
return this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -511,11 +565,11 @@ describe('EditorHttpController', function() {
|
|||
entity_type: (this.entity_type = 'entity-type')
|
||||
}
|
||||
this.EditorController.deleteEntity = sinon.stub().callsArg(5)
|
||||
return this.EditorHttpController.deleteEntity(this.req, this.res)
|
||||
this.EditorHttpController.deleteEntity(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should call EditorController.deleteEntity', function() {
|
||||
return this.EditorController.deleteEntity
|
||||
this.EditorController.deleteEntity
|
||||
.calledWith(
|
||||
this.project_id,
|
||||
this.entity_id,
|
||||
|
@ -527,7 +581,7 @@ describe('EditorHttpController', function() {
|
|||
})
|
||||
|
||||
it('should send back a success response', function() {
|
||||
return this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
this.res.sendStatus.calledWith(204).should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -59,7 +59,10 @@ describe('ProjectController', function() {
|
|||
this.TagsHandler = { getAllTags: sinon.stub() }
|
||||
this.NotificationsHandler = { getUserNotifications: sinon.stub() }
|
||||
this.UserModel = { findById: sinon.stub() }
|
||||
this.AuthorizationManager = { getPrivilegeLevelForProject: sinon.stub() }
|
||||
this.AuthorizationManager = {
|
||||
getPrivilegeLevelForProject: sinon.stub(),
|
||||
isRestrictedUser: sinon.stub().returns(false)
|
||||
}
|
||||
this.EditorController = { renameProject: sinon.stub() }
|
||||
this.InactiveProjectManager = { reactivateProjectIfRequired: sinon.stub() }
|
||||
this.ProjectUpdateHandler = { markAsOpened: sinon.stub() }
|
||||
|
@ -804,13 +807,8 @@ describe('ProjectController', function() {
|
|||
return this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should set isRestrictedTokenMember to true when the user is accessing project via read-only token', function(done) {
|
||||
this.CollaboratorsGetter.userIsTokenMember.callsArgWith(2, null, true)
|
||||
this.AuthorizationManager.getPrivilegeLevelForProject.callsArgWith(
|
||||
3,
|
||||
null,
|
||||
'readOnly'
|
||||
)
|
||||
it('should set isRestrictedTokenMember when appropriate', function(done) {
|
||||
this.AuthorizationManager.isRestrictedUser.returns(true)
|
||||
this.res.render = (pageName, opts) => {
|
||||
opts.isRestrictedTokenMember.should.exist
|
||||
opts.isRestrictedTokenMember.should.equal(true)
|
||||
|
@ -819,21 +817,6 @@ describe('ProjectController', function() {
|
|||
return this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should set isRestrictedTokenMember to true when anonymous read-only token access', function(done) {
|
||||
this.CollaboratorsGetter.userIsTokenMember.callsArgWith(2, null, null)
|
||||
this.AuthenticationController.isUserLoggedIn = sinon.stub().returns(false)
|
||||
this.AuthorizationManager.getPrivilegeLevelForProject.callsArgWith(
|
||||
3,
|
||||
null,
|
||||
'readOnly'
|
||||
)
|
||||
this.res.render = (pageName, opts) => {
|
||||
opts.isRestrictedTokenMember.should.exist
|
||||
opts.isRestrictedTokenMember.should.equal(true)
|
||||
return done()
|
||||
}
|
||||
return this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
it('should render the closed page if the editor is closed', function(done) {
|
||||
this.settings.editorIsOpen = false
|
||||
this.res.render = (pageName, opts) => {
|
||||
|
|
Loading…
Reference in a new issue