Merge pull request #2622 from overleaf/em-promisify-editor-http-controller
Promisify EditorHttpController GitOrigin-RevId: 938350f21a19c10c6bd4261086265a098e71e932
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
let AuthorizationManager
|
||||
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
|
||||
const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
@@ -8,9 +7,9 @@ const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
|
||||
const PublicAccessLevels = require('./PublicAccessLevels')
|
||||
const Errors = require('../Errors/Errors')
|
||||
const { ObjectId } = require('mongojs')
|
||||
const { promisify } = require('util')
|
||||
const { promisifyAll } = require('../../util/promises')
|
||||
|
||||
module.exports = AuthorizationManager = {
|
||||
const AuthorizationManager = {
|
||||
isRestrictedUser(userId, privilegeLevel, isTokenMember) {
|
||||
if (privilegeLevel === PrivilegeLevels.NONE) {
|
||||
return true
|
||||
@@ -21,7 +20,7 @@ module.exports = AuthorizationManager = {
|
||||
},
|
||||
|
||||
isRestrictedUserForProject(userId, projectId, token, callback) {
|
||||
this.getPrivilegeLevelForProject(
|
||||
AuthorizationManager.getPrivilegeLevelForProject(
|
||||
userId,
|
||||
projectId,
|
||||
token,
|
||||
@@ -38,7 +37,11 @@ module.exports = AuthorizationManager = {
|
||||
}
|
||||
callback(
|
||||
null,
|
||||
this.isRestrictedUser(userId, privilegeLevel, isTokenMember)
|
||||
AuthorizationManager.isRestrictedUser(
|
||||
userId,
|
||||
privilegeLevel,
|
||||
isTokenMember
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -279,8 +282,7 @@ module.exports = AuthorizationManager = {
|
||||
}
|
||||
}
|
||||
|
||||
AuthorizationManager.promises = {
|
||||
getPrivilegeLevelForProject: promisify(
|
||||
AuthorizationManager.getPrivilegeLevelForProject
|
||||
)
|
||||
}
|
||||
module.exports = AuthorizationManager
|
||||
module.exports.promises = promisifyAll(AuthorizationManager, {
|
||||
without: 'isRestrictedUser'
|
||||
})
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
let CollaboratorsInviteHandler
|
||||
const { ProjectInvite } = require('../../models/ProjectInvite')
|
||||
const logger = require('logger-sharelatex')
|
||||
const CollaboratorsEmailHandler = require('./CollaboratorsEmailHandler')
|
||||
@@ -23,8 +22,9 @@ const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
|
||||
const Errors = require('../Errors/Errors')
|
||||
const Crypto = require('crypto')
|
||||
const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
|
||||
const { promisifyAll } = require('../../util/promises')
|
||||
|
||||
module.exports = CollaboratorsInviteHandler = {
|
||||
const CollaboratorsInviteHandler = {
|
||||
getAllInvites(projectId, callback) {
|
||||
if (callback == null) {
|
||||
callback = function(err, invites) {}
|
||||
@@ -328,3 +328,6 @@ module.exports = CollaboratorsInviteHandler = {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CollaboratorsInviteHandler
|
||||
module.exports.promises = promisifyAll(CollaboratorsInviteHandler)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
let EditorHttpController
|
||||
const ProjectDeleter = require('../Project/ProjectDeleter')
|
||||
const logger = require('logger-sharelatex')
|
||||
const EditorController = require('./EditorController')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
const AuthorizationManager = require('../Authorization/AuthorizationManager')
|
||||
@@ -13,261 +11,213 @@ const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
|
||||
const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
|
||||
const AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
const Errors = require('../Errors/Errors')
|
||||
const { expressify } = require('../../util/promises')
|
||||
|
||||
module.exports = EditorHttpController = {
|
||||
joinProject(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
let userId = req.query.user_id
|
||||
if (userId === 'anonymous-user') {
|
||||
userId = null
|
||||
}
|
||||
Metrics.inc('editor.join-project')
|
||||
EditorHttpController._buildJoinProjectView(req, projectId, userId, function(
|
||||
error,
|
||||
module.exports = {
|
||||
joinProject: expressify(joinProject),
|
||||
addDoc: expressify(addDoc),
|
||||
addFolder: expressify(addFolder),
|
||||
renameEntity: expressify(renameEntity),
|
||||
moveEntity: expressify(moveEntity),
|
||||
deleteDoc: expressify(deleteDoc),
|
||||
deleteFile: expressify(deleteFile),
|
||||
deleteFolder: expressify(deleteFolder),
|
||||
deleteEntity: expressify(deleteEntity),
|
||||
_nameIsAcceptableLength
|
||||
}
|
||||
|
||||
async function joinProject(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
let userId = req.query.user_id
|
||||
if (userId === 'anonymous-user') {
|
||||
userId = null
|
||||
}
|
||||
Metrics.inc('editor.join-project')
|
||||
const {
|
||||
project,
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
} = await _buildJoinProjectView(req, projectId, userId)
|
||||
if (!project) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
// Hide access tokens if this is not the project owner
|
||||
TokenAccessHandler.protectTokens(project, privilegeLevel)
|
||||
if (isRestrictedUser) {
|
||||
project.owner = { _id: project.owner._id }
|
||||
}
|
||||
// Only show the 'renamed or deleted' message once
|
||||
if (project.deletedByExternalDataSource) {
|
||||
await ProjectDeleter.promises.unmarkAsDeletedByExternalSource(projectId)
|
||||
}
|
||||
res.json({
|
||||
project,
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
})
|
||||
}
|
||||
|
||||
async function _buildJoinProjectView(req, projectId, userId) {
|
||||
const project = await ProjectGetter.promises.getProjectWithoutDocLines(
|
||||
projectId
|
||||
)
|
||||
if (project == null) {
|
||||
throw new Errors.NotFoundError('project not found')
|
||||
}
|
||||
const members = await CollaboratorsGetter.promises.getInvitedMembersWithPrivilegeLevels(
|
||||
projectId
|
||||
)
|
||||
const token = TokenAccessHandler.getRequestToken(req, projectId)
|
||||
const privilegeLevel = await AuthorizationManager.promises.getPrivilegeLevelForProject(
|
||||
userId,
|
||||
projectId,
|
||||
token
|
||||
)
|
||||
if (privilegeLevel == null || privilegeLevel === PrivilegeLevels.NONE) {
|
||||
return { project: null, privilegeLevel: null, isRestrictedUser: false }
|
||||
}
|
||||
const invites = await CollaboratorsInviteHandler.promises.getAllInvites(
|
||||
projectId
|
||||
)
|
||||
const isTokenMember = await CollaboratorsHandler.promises.userIsTokenMember(
|
||||
userId,
|
||||
projectId
|
||||
)
|
||||
const isRestrictedUser = AuthorizationManager.isRestrictedUser(
|
||||
userId,
|
||||
privilegeLevel,
|
||||
isTokenMember
|
||||
)
|
||||
return {
|
||||
project: ProjectEditorHandler.buildProjectModelView(
|
||||
project,
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
if (!project) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
},
|
||||
members,
|
||||
invites
|
||||
),
|
||||
privilegeLevel,
|
||||
isRestrictedUser
|
||||
}
|
||||
}
|
||||
|
||||
_buildJoinProjectView(req, projectId, userId, callback) {
|
||||
if (callback == null) {
|
||||
callback = function() {}
|
||||
}
|
||||
ProjectGetter.getProjectWithoutDocLines(projectId, function(
|
||||
error,
|
||||
project
|
||||
) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
if (project == null) {
|
||||
return callback(new Errors.NotFoundError('project not found'))
|
||||
}
|
||||
CollaboratorsGetter.getInvitedMembersWithPrivilegeLevels(
|
||||
projectId,
|
||||
function(error, members) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
const token = TokenAccessHandler.getRequestToken(req, projectId)
|
||||
AuthorizationManager.getPrivilegeLevelForProject(
|
||||
userId,
|
||||
projectId,
|
||||
token,
|
||||
function(error, privilegeLevel) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
if (
|
||||
privilegeLevel == null ||
|
||||
privilegeLevel === PrivilegeLevels.NONE
|
||||
) {
|
||||
logger.log(
|
||||
{ projectId, userId, privilegeLevel },
|
||||
'not an acceptable privilege level, returning null'
|
||||
)
|
||||
return callback(null, null, false)
|
||||
}
|
||||
CollaboratorsInviteHandler.getAllInvites(projectId, function(
|
||||
error,
|
||||
invites
|
||||
) {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
function _nameIsAcceptableLength(name) {
|
||||
return name != null && name.length < 150 && name.length !== 0
|
||||
}
|
||||
|
||||
_nameIsAcceptableLength(name) {
|
||||
return name != null && name.length < 150 && name.length !== 0
|
||||
},
|
||||
async function addDoc(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
|
||||
addDoc(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
|
||||
if (!EditorHttpController._nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
EditorController.addDoc(
|
||||
if (!_nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
try {
|
||||
const doc = await EditorController.promises.addDoc(
|
||||
projectId,
|
||||
parentFolderId,
|
||||
name,
|
||||
[],
|
||||
'editor',
|
||||
userId,
|
||||
function(error, doc) {
|
||||
if (error && error.message === 'project_has_too_many_files') {
|
||||
return res
|
||||
.status(400)
|
||||
.json(req.i18n.translate('project_has_too_many_files'))
|
||||
} else if (error) {
|
||||
return next(error)
|
||||
} else {
|
||||
return res.json(doc)
|
||||
}
|
||||
}
|
||||
userId
|
||||
)
|
||||
},
|
||||
|
||||
addFolder(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
if (!EditorHttpController._nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
res.json(doc)
|
||||
} catch (err) {
|
||||
if (err.message === 'project_has_too_many_files') {
|
||||
res.status(400).json(req.i18n.translate('project_has_too_many_files'))
|
||||
} else {
|
||||
next(err)
|
||||
}
|
||||
EditorController.addFolder(
|
||||
}
|
||||
}
|
||||
|
||||
async function addFolder(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const { name } = req.body
|
||||
const parentFolderId = req.body.parent_folder_id
|
||||
if (!_nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
try {
|
||||
const doc = await EditorController.promises.addFolder(
|
||||
projectId,
|
||||
parentFolderId,
|
||||
name,
|
||||
'editor',
|
||||
function(error, doc) {
|
||||
if (error && error.message === 'project_has_too_many_files') {
|
||||
return res
|
||||
.status(400)
|
||||
.json(req.i18n.translate('project_has_too_many_files'))
|
||||
} else if (error && error.message === 'invalid element name') {
|
||||
return res.status(400).json(req.i18n.translate('invalid_file_name'))
|
||||
} else if (error) {
|
||||
return next(error)
|
||||
} else {
|
||||
return res.json(doc)
|
||||
}
|
||||
}
|
||||
'editor'
|
||||
)
|
||||
},
|
||||
|
||||
renameEntity(req, res, next) {
|
||||
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)
|
||||
res.json(doc)
|
||||
} catch (err) {
|
||||
if (err.message === 'project_has_too_many_files') {
|
||||
res.status(400).json(req.i18n.translate('project_has_too_many_files'))
|
||||
} else if (err.message === 'invalid element name') {
|
||||
res.status(400).json(req.i18n.translate('invalid_file_name'))
|
||||
} else {
|
||||
next(err)
|
||||
}
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
EditorController.renameEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
entityType,
|
||||
name,
|
||||
userId,
|
||||
function(error) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
return res.sendStatus(204)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
moveEntity(req, res, next) {
|
||||
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) {
|
||||
return next(error)
|
||||
}
|
||||
return res.sendStatus(204)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
deleteDoc(req, res, next) {
|
||||
req.params.entity_type = 'doc'
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteFile(req, res, next) {
|
||||
req.params.entity_type = 'file'
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteFolder(req, res, next) {
|
||||
req.params.entity_type = 'folder'
|
||||
EditorHttpController.deleteEntity(req, res, next)
|
||||
},
|
||||
|
||||
deleteEntity(req, res, next) {
|
||||
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',
|
||||
userId,
|
||||
function(error) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
res.sendStatus(204)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function renameEntity(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const entityId = req.params.entity_id
|
||||
const entityType = req.params.entity_type
|
||||
const { name } = req.body
|
||||
if (!_nameIsAcceptableLength(name)) {
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
await EditorController.promises.renameEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
entityType,
|
||||
name,
|
||||
userId
|
||||
)
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
||||
async function moveEntity(req, res, next) {
|
||||
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)
|
||||
await EditorController.promises.moveEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
folderId,
|
||||
entityType,
|
||||
userId
|
||||
)
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
||||
async function deleteDoc(req, res, next) {
|
||||
req.params.entity_type = 'doc'
|
||||
await deleteEntity(req, res, next)
|
||||
}
|
||||
|
||||
async function deleteFile(req, res, next) {
|
||||
req.params.entity_type = 'file'
|
||||
await deleteEntity(req, res, next)
|
||||
}
|
||||
|
||||
async function deleteFolder(req, res, next) {
|
||||
req.params.entity_type = 'folder'
|
||||
await deleteEntity(req, res, next)
|
||||
}
|
||||
|
||||
async function deleteEntity(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const entityId = req.params.entity_id
|
||||
const entityType = req.params.entity_type
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
await EditorController.promises.deleteEntity(
|
||||
projectId,
|
||||
entityId,
|
||||
entityType,
|
||||
'editor',
|
||||
userId
|
||||
)
|
||||
res.sendStatus(204)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
*/
|
||||
// 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 EditorHttpController = require('./EditorHttpController')
|
||||
const AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
const AuthorizationMiddleware = require('../Authorization/AuthorizationMiddleware')
|
||||
@@ -68,7 +58,7 @@ module.exports = {
|
||||
// Called by the real-time API to load up the current project state.
|
||||
// This is a post request because it's more than just a getting of data. We take actions
|
||||
// whenever a user joins a project, like updating the deleted status.
|
||||
return apiRouter.post(
|
||||
apiRouter.post(
|
||||
'/project/:Project_id/join',
|
||||
AuthenticationController.httpAuth,
|
||||
RateLimiterMiddleware.rateLimit({
|
||||
|
||||
@@ -339,7 +339,10 @@ const promises = {
|
||||
deleteProject: deleteProject,
|
||||
undeleteProject: undeleteProject,
|
||||
expireDeletedProject: expireDeletedProject,
|
||||
deleteUsersProjects: promisify(ProjectDeleter.deleteUsersProjects)
|
||||
deleteUsersProjects: promisify(ProjectDeleter.deleteUsersProjects),
|
||||
unmarkAsDeletedByExternalSource: promisify(
|
||||
ProjectDeleter.unmarkAsDeletedByExternalSource
|
||||
)
|
||||
}
|
||||
|
||||
ProjectDeleter.promises = promises
|
||||
|
||||
Reference in New Issue
Block a user