mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-06 18:29:07 +00:00
Merge branch 'master' into sk-pdf-error-display
This commit is contained in:
commit
45077cfba9
68 changed files with 1891 additions and 1141 deletions
1
services/web/.gitignore
vendored
1
services/web/.gitignore
vendored
|
@ -40,6 +40,7 @@ app.js
|
|||
app/js/*
|
||||
test/UnitTests/js/*
|
||||
test/smoke/js/*
|
||||
test/acceptance/js/*
|
||||
cookies.txt
|
||||
requestQueueWorker.js
|
||||
TpdsWorker.js
|
||||
|
|
|
@ -71,6 +71,14 @@ module.exports = (grunt) ->
|
|||
dest: 'test/UnitTests/js/',
|
||||
ext: '.js'
|
||||
|
||||
acceptance_tests:
|
||||
expand: true,
|
||||
flatten: false,
|
||||
cwd: 'test/acceptance/coffee',
|
||||
src: ['**/*.coffee'],
|
||||
dest: 'test/acceptance/js/',
|
||||
ext: '.js'
|
||||
|
||||
less:
|
||||
app:
|
||||
files:
|
||||
|
@ -119,6 +127,7 @@ module.exports = (grunt) ->
|
|||
clean:
|
||||
app: ["app/js"]
|
||||
unit_tests: ["test/UnitTests/js"]
|
||||
acceptance_tests: ["test/acceptance/js"]
|
||||
|
||||
mochaTest:
|
||||
unit:
|
||||
|
@ -131,6 +140,12 @@ module.exports = (grunt) ->
|
|||
options:
|
||||
reporter: grunt.option('reporter') or 'spec'
|
||||
grep: grunt.option("grep")
|
||||
acceptance:
|
||||
src: ["test/acceptance/js/#{grunt.option('feature') or '**'}/*.js"]
|
||||
options:
|
||||
timeout: 10000
|
||||
reporter: grunt.option('reporter') or 'spec'
|
||||
grep: grunt.option("grep")
|
||||
|
||||
"git-rev-parse":
|
||||
version:
|
||||
|
@ -184,6 +199,7 @@ module.exports = (grunt) ->
|
|||
]
|
||||
"Test tasks": [
|
||||
"test:unit"
|
||||
"test:acceptance"
|
||||
]
|
||||
"Run tasks": [
|
||||
"run"
|
||||
|
@ -290,6 +306,7 @@ module.exports = (grunt) ->
|
|||
grunt.registerTask 'compile:css', 'Compile the less files to css', ['less']
|
||||
grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs', "file_append"]
|
||||
grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests']
|
||||
grunt.registerTask 'compile:acceptance_tests', 'Compile the acceptance tests', ['clean:acceptance_tests', 'coffee:acceptance_tests']
|
||||
grunt.registerTask 'compile:smoke_tests', 'Compile the smoke tests', ['coffee:smoke_tests']
|
||||
grunt.registerTask 'compile:tests', 'Compile all the tests', ['compile:smoke_tests', 'compile:unit_tests']
|
||||
grunt.registerTask 'compile', 'Compiles everything need to run web-sharelatex', ['compile:server', 'compile:client', 'compile:css']
|
||||
|
@ -297,6 +314,7 @@ module.exports = (grunt) ->
|
|||
grunt.registerTask 'install', "Compile everything when installing as an npm module", ['compile']
|
||||
|
||||
grunt.registerTask 'test:unit', 'Run the unit tests (use --grep=<regex> or --feature=<feature> for individual tests)', ['compile:server', 'compile:modules:server', 'compile:unit_tests', 'compile:modules:unit_tests', 'mochaTest:unit'].concat(moduleUnitTestTasks)
|
||||
grunt.registerTask 'test:acceptance', 'Run the acceptance tests (use --grep=<regex> or --feature=<feature> for individual tests)', ['compile:acceptance_tests', 'mochaTest:acceptance']
|
||||
grunt.registerTask 'test:smoke', 'Run the smoke tests', ['compile:smoke_tests', 'mochaTest:smoke']
|
||||
|
||||
grunt.registerTask 'test:modules:unit', 'Run the unit tests for the modules', ['compile:modules:server', 'compile:modules:unit_tests'].concat(moduleUnitTestTasks)
|
||||
|
|
|
@ -18,18 +18,6 @@ argv = require("optimist")
|
|||
.usage("Usage: $0")
|
||||
.argv
|
||||
|
||||
Server.app.use (error, req, res, next) ->
|
||||
if error?.code is 'EBADCSRFTOKEN'
|
||||
logger.log err: error,url:req.url, method:req.method, user:req?.sesson?.user, "invalid csrf"
|
||||
res.sendStatus(403)
|
||||
return
|
||||
logger.error err: error, url:req.url, method:req.method, user:req?.sesson?.user, "error passed to top level next middlewear"
|
||||
res.statusCode = error.status or 500
|
||||
if res.statusCode == 500
|
||||
res.end("Oops, something went wrong with your request, sorry. If this continues, please contact us at #{Settings.adminEmail}")
|
||||
else
|
||||
res.end()
|
||||
|
||||
if Settings.catchErrors
|
||||
process.removeAllListeners "uncaughtException"
|
||||
process.on "uncaughtException", (error) ->
|
||||
|
|
|
@ -44,50 +44,27 @@ module.exports = AuthenticationController =
|
|||
text: req.i18n.translate("email_or_password_wrong_try_again"),
|
||||
type: 'error'
|
||||
|
||||
getAuthToken: (req, res, next = (error) ->) ->
|
||||
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
|
||||
return next(error) if error?
|
||||
AuthenticationManager.getAuthToken user_id, (error, auth_token) ->
|
||||
return next(error) if error?
|
||||
res.send(auth_token)
|
||||
|
||||
getLoggedInUserId: (req, callback = (error, user_id) ->) ->
|
||||
if req?.session?.user?._id?
|
||||
callback null, req.session.user._id.toString()
|
||||
else
|
||||
callback null, null
|
||||
|
||||
getLoggedInUser: (req, options = {allow_auth_token: false}, callback = (error, user) ->) ->
|
||||
if typeof(options) == "function"
|
||||
callback = options
|
||||
options = {allow_auth_token: false}
|
||||
|
||||
getLoggedInUser: (req, callback = (error, user) ->) ->
|
||||
if req.session?.user?._id?
|
||||
query = req.session.user._id
|
||||
else if req.query?.auth_token? and options.allow_auth_token
|
||||
query = { auth_token: req.query.auth_token }
|
||||
else
|
||||
return callback null, null
|
||||
|
||||
UserGetter.getUser query, callback
|
||||
|
||||
requireLogin: (options = {allow_auth_token: false, load_from_db: false}) ->
|
||||
requireLogin: () ->
|
||||
doRequest = (req, res, next = (error) ->) ->
|
||||
load_from_db = options.load_from_db
|
||||
if req.query?.auth_token? and options.allow_auth_token
|
||||
load_from_db = true
|
||||
if load_from_db
|
||||
AuthenticationController.getLoggedInUser req, { allow_auth_token: options.allow_auth_token }, (error, user) ->
|
||||
return next(error) if error?
|
||||
return AuthenticationController._redirectToLoginOrRegisterPage(req, res) if !user?
|
||||
req.user = user
|
||||
return next()
|
||||
if !req.session.user?
|
||||
AuthenticationController._redirectToLoginOrRegisterPage(req, res)
|
||||
else
|
||||
if !req.session.user?
|
||||
AuthenticationController._redirectToLoginOrRegisterPage(req, res)
|
||||
else
|
||||
req.user = req.session.user
|
||||
return next()
|
||||
req.user = req.session.user
|
||||
return next()
|
||||
|
||||
return doRequest
|
||||
|
||||
|
|
|
@ -35,19 +35,3 @@ module.exports = AuthenticationManager =
|
|||
$unset: password: true
|
||||
}, callback)
|
||||
|
||||
getAuthToken: (user_id, callback = (error, auth_token) ->) ->
|
||||
db.users.findOne { _id: ObjectId(user_id.toString()) }, { auth_token : true }, (error, user) =>
|
||||
return callback(error) if error?
|
||||
return callback(new Error("user could not be found: #{user_id}")) if !user?
|
||||
if user.auth_token?
|
||||
callback null, user.auth_token
|
||||
else
|
||||
@_createSecureToken (error, auth_token) ->
|
||||
db.users.update { _id: ObjectId(user_id.toString()) }, { $set : auth_token: auth_token }, (error) ->
|
||||
return callback(error) if error?
|
||||
callback null, auth_token
|
||||
|
||||
_createSecureToken: (callback = (error, token) ->) ->
|
||||
crypto.randomBytes 48, (error, buffer) ->
|
||||
return callback(error) if error?
|
||||
callback null, buffer.toString("hex")
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
Project = require("../../models/Project").Project
|
||||
User = require("../../models/User").User
|
||||
PrivilegeLevels = require("./PrivilegeLevels")
|
||||
PublicAccessLevels = require("./PublicAccessLevels")
|
||||
Errors = require("../Errors/Errors")
|
||||
|
||||
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?
|
||||
return callback new Errors.NotFoundError("no project found with id #{project_id}")
|
||||
if project.publicAccesLevel == PublicAccessLevels.READ_ONLY
|
||||
return callback null, PrivilegeLevels.READ_ONLY, true
|
||||
else if project.publicAccesLevel == PublicAccessLevels.READ_AND_WRITE
|
||||
return callback null, PrivilegeLevels.READ_AND_WRITE, true
|
||||
else
|
||||
return callback null, PrivilegeLevels.NONE, false
|
||||
|
||||
if !user_id?
|
||||
getPublicAccessLevel()
|
||||
else
|
||||
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
|
||||
else
|
||||
AuthorizationManager.isUserSiteAdmin user_id, (error, isAdmin) ->
|
||||
return callback(error) if error?
|
||||
if isAdmin
|
||||
callback null, PrivilegeLevels.OWNER, 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 [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE, PrivilegeLevels.READ_ONLY])
|
||||
|
||||
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 [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE])
|
||||
|
||||
canUserWriteProjectSettings: (user_id, project_id, callback = (error, canWriteSettings) ->) ->
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel, becausePublic) ->
|
||||
return callback(error) if error?
|
||||
if privilegeLevel == PrivilegeLevels.OWNER
|
||||
return callback null, true
|
||||
else if privilegeLevel == PrivilegeLevels.READ_AND_WRITE 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 == PrivilegeLevels.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)
|
|
@ -0,0 +1,111 @@
|
|||
AuthorizationManager = require("./AuthorizationManager")
|
||||
async = require "async"
|
||||
logger = require "logger-sharelatex"
|
||||
ObjectId = require("mongojs").ObjectId
|
||||
Errors = require "../Errors/Errors"
|
||||
|
||||
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"))
|
||||
if !ObjectId.isValid(project_id)
|
||||
return callback(new Errors.NotFoundError("invalid project_id: #{project_id}"))
|
||||
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?
|
||||
res.render 'user/restricted',
|
||||
title:'restricted'
|
||||
else
|
||||
logger.log "user not logged in and trying to access #{req.url}, being redirected to login"
|
||||
res.redirect '/register'
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module.exports =
|
||||
NONE: false
|
||||
READ_ONLY: "readOnly"
|
||||
READ_AND_WRITE: "readAndWrite"
|
||||
OWNER: "owner"
|
|
@ -0,0 +1,4 @@
|
|||
module.exports =
|
||||
READ_ONLY: "readOnly"
|
||||
READ_AND_WRITE: "readAndWrite"
|
||||
PRIVATE: "private"
|
|
@ -7,15 +7,6 @@ UserGetter = require "../User/UserGetter"
|
|||
mimelib = require("mimelib")
|
||||
|
||||
module.exports = CollaboratorsController =
|
||||
getCollaborators: (req, res, next = (error) ->) ->
|
||||
ProjectGetter.getProject req.params.Project_id, { owner_ref: true, collaberator_refs: true, readOnly_refs: true}, (error, project) ->
|
||||
return next(error) if error?
|
||||
ProjectGetter.populateProjectWithUsers project, (error, project) ->
|
||||
return next(error) if error?
|
||||
CollaboratorsController._formatCollaborators project, (error, collaborators) ->
|
||||
return next(error) if error?
|
||||
res.send(JSON.stringify(collaborators))
|
||||
|
||||
addUserToProject: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
LimitationsManager.canAddXCollaborators project_id, 1, (error, allowed) =>
|
||||
|
@ -59,29 +50,3 @@ module.exports = CollaboratorsController =
|
|||
EditorRealTimeController.emitToRoom(project_id, 'userRemovedFromProject', user_id)
|
||||
callback()
|
||||
|
||||
_formatCollaborators: (project, callback = (error, collaborators) ->) ->
|
||||
collaborators = []
|
||||
|
||||
pushCollaborator = (user, permissions, owner) ->
|
||||
collaborators.push {
|
||||
id: user._id.toString()
|
||||
first_name: user.first_name
|
||||
last_name: user.last_name
|
||||
email: user.email
|
||||
permissions: permissions
|
||||
owner: owner
|
||||
}
|
||||
|
||||
if project.owner_ref?
|
||||
pushCollaborator(project.owner_ref, ["read", "write", "admin"], true)
|
||||
|
||||
if project.collaberator_refs? and project.collaberator_refs.length > 0
|
||||
for user in project.collaberator_refs
|
||||
pushCollaborator(user, ["read", "write"], false)
|
||||
|
||||
if project.readOnly_refs? and project.readOnly_refs.length > 0
|
||||
for user in project.readOnly_refs
|
||||
pushCollaborator(user, ["read"], false)
|
||||
|
||||
callback null, collaborators
|
||||
|
||||
|
|
|
@ -1,13 +1,77 @@
|
|||
UserCreator = require('../User/UserCreator')
|
||||
Project = require("../../models/Project").Project
|
||||
ProjectEntityHandler = require("../Project/ProjectEntityHandler")
|
||||
mimelib = require("mimelib")
|
||||
logger = require('logger-sharelatex')
|
||||
UserGetter = require "../User/UserGetter"
|
||||
ContactManager = require "../Contacts/ContactManager"
|
||||
CollaboratorsEmailHandler = require "./CollaboratorsEmailHandler"
|
||||
async = require "async"
|
||||
PrivilegeLevels = require "../Authorization/PrivilegeLevels"
|
||||
Errors = require "../Errors/Errors"
|
||||
|
||||
module.exports = CollaboratorsHandler =
|
||||
getMemberIdsWithPrivilegeLevels: (project_id, callback = (error, members) ->) ->
|
||||
Project.findOne { _id: project_id }, { owner_ref: 1, collaberator_refs: 1, readOnly_refs: 1 }, (error, project) ->
|
||||
return callback(error) if error?
|
||||
return callback new Errors.NotFoundError("no project found with id #{project_id}") if !project?
|
||||
members = []
|
||||
members.push { id: project.owner_ref.toString(), privilegeLevel: PrivilegeLevels.OWNER }
|
||||
for member_id in project.readOnly_refs or []
|
||||
members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_ONLY }
|
||||
for member_id in project.collaberator_refs or []
|
||||
members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_AND_WRITE }
|
||||
return callback null, members
|
||||
|
||||
getMemberIds: (project_id, callback = (error, member_ids) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) ->
|
||||
return callback(error) if error?
|
||||
return callback null, members.map (m) -> m.id
|
||||
|
||||
getMembersWithPrivilegeLevels: (project_id, callback = (error, members) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members = []) ->
|
||||
return callback(error) if error?
|
||||
async.mapLimit members, 3,
|
||||
(member, cb) ->
|
||||
UserGetter.getUser member.id, (error, user) ->
|
||||
return cb(error) if error?
|
||||
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
|
||||
if member.id == user_id?.toString()
|
||||
return callback null, member.privilegeLevel
|
||||
return callback null, PrivilegeLevels.NONE
|
||||
|
||||
getMemberCount: (project_id, callback = (error, count) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members) ->
|
||||
return callback(error) if error?
|
||||
return callback null, (members or []).length
|
||||
|
||||
getCollaboratorCount: (project_id, callback = (error, count) ->) ->
|
||||
CollaboratorsHandler.getMemberCount project_id, (error, count) ->
|
||||
return callback(error) if error?
|
||||
return callback null, count - 1 # Don't count project owner
|
||||
|
||||
isUserMemberOfProject: (user_id, project_id, callback = (error, isMember, privilegeLevel) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members = []) ->
|
||||
return callback(error) if error?
|
||||
for member in members
|
||||
if member.id.toString() == user_id.toString()
|
||||
return callback null, true, member.privilegeLevel
|
||||
return callback null, false, null
|
||||
|
||||
getProjectsUserIsCollaboratorOf: (user_id, fields, callback = (error, readAndWriteProjects, readOnlyProjects) ->) ->
|
||||
Project.find {collaberator_refs:user_id}, fields, (err, readAndWriteProjects)=>
|
||||
return callback(err) if err?
|
||||
Project.find {readOnly_refs:user_id}, fields, (err, readOnlyProjects)=>
|
||||
return callback(err) if err?
|
||||
callback(null, readAndWriteProjects, readOnlyProjects)
|
||||
|
||||
removeUserFromProject: (project_id, user_id, callback = (error) ->)->
|
||||
logger.log user_id: user_id, project_id: project_id, "removing user"
|
||||
conditions = _id:project_id
|
||||
|
@ -38,10 +102,10 @@ module.exports = CollaboratorsHandler =
|
|||
if existing_users.indexOf(user_id.toString()) > -1
|
||||
return callback null # User already in Project
|
||||
|
||||
if privilegeLevel == 'readAndWrite'
|
||||
if privilegeLevel == PrivilegeLevels.READ_AND_WRITE
|
||||
level = {"collaberator_refs":user_id}
|
||||
logger.log {privileges: "readAndWrite", user_id, project_id}, "adding user"
|
||||
else if privilegeLevel == 'readOnly'
|
||||
else if privilegeLevel == PrivilegeLevels.READ_ONLY
|
||||
level = {"readOnly_refs":user_id}
|
||||
logger.log {privileges: "readOnly", user_id, project_id}, "adding user"
|
||||
else
|
||||
|
@ -57,6 +121,7 @@ module.exports = CollaboratorsHandler =
|
|||
Project.update { _id: project_id }, { $addToSet: level }, (error) ->
|
||||
return callback(error) if error?
|
||||
# Flush to TPDS in background to add files to collaborator's Dropbox
|
||||
ProjectEntityHandler = require("../Project/ProjectEntityHandler")
|
||||
ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, (error) ->
|
||||
if error?
|
||||
logger.error {err: error, project_id, user_id}, "error flushing to TPDS after adding collaborator"
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
CollaboratorsController = require('./CollaboratorsController')
|
||||
SecurityManager = require('../../managers/SecurityManager')
|
||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
AuthorizationMiddlewear = require('../Authorization/AuthorizationMiddlewear')
|
||||
|
||||
module.exports =
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.post '/project/:Project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
||||
apiRouter.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
||||
|
||||
webRouter.post '/project/:Project_id/users', SecurityManager.requestIsOwner, CollaboratorsController.addUserToProject
|
||||
webRouter.delete '/project/:Project_id/users/:user_id', SecurityManager.requestIsOwner, CollaboratorsController.removeUserFromProject
|
||||
webRouter.post '/project/:Project_id/users', AuthorizationMiddlewear.ensureUserCanAdminProject, CollaboratorsController.addUserToProject
|
||||
webRouter.delete '/project/:Project_id/users/:user_id', AuthorizationMiddlewear.ensureUserCanAdminProject, CollaboratorsController.removeUserFromProject
|
||||
|
|
|
@ -86,6 +86,9 @@ module.exports = ClsiManager =
|
|||
rootResourcePathOverride = path
|
||||
|
||||
rootResourcePath = rootResourcePathOverride if rootResourcePathOverride?
|
||||
if !rootResourcePath?
|
||||
logger.warn {project_id}, "no root document found, setting to main.tex"
|
||||
rootResourcePath = "main.tex"
|
||||
|
||||
for path, file of files
|
||||
path = path.replace(/^\//, "") # Remove leading /
|
||||
|
@ -94,19 +97,16 @@ module.exports = ClsiManager =
|
|||
url: "#{Settings.apis.filestore.url}/project/#{project._id}/file/#{file._id}"
|
||||
modified: file.created?.getTime()
|
||||
|
||||
if !rootResourcePath?
|
||||
callback new Error("no root document exists")
|
||||
else
|
||||
callback null, {
|
||||
compile:
|
||||
options:
|
||||
compiler: project.compiler
|
||||
timeout: options.timeout
|
||||
imageName: project.imageName
|
||||
draft: !!options.draft
|
||||
rootResourcePath: rootResourcePath
|
||||
resources: resources
|
||||
}
|
||||
callback null, {
|
||||
compile:
|
||||
options:
|
||||
compiler: project.compiler
|
||||
timeout: options.timeout
|
||||
imageName: project.imageName
|
||||
draft: !!options.draft
|
||||
rootResourcePath: rootResourcePath
|
||||
resources: resources
|
||||
}
|
||||
|
||||
wordCount: (project_id, file, options, callback = (error, response) ->) ->
|
||||
ClsiManager._buildRequest project_id, options, (error, req) ->
|
||||
|
|
|
@ -26,7 +26,8 @@ module.exports = CompileManager =
|
|||
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
|
||||
return callback(error) if error?
|
||||
if recentlyCompiled
|
||||
return callback new Error("project was recently compiled so not continuing")
|
||||
logger.warn {project_id, user_id}, "project was recently compiled so not continuing"
|
||||
return callback null, "too-recently-compiled", []
|
||||
|
||||
CompileManager._ensureRootDocumentIsSet project_id, (error) ->
|
||||
return callback(error) if error?
|
||||
|
|
|
@ -5,14 +5,18 @@ EditorRealTimeController = require "./EditorRealTimeController"
|
|||
EditorController = require "./EditorController"
|
||||
ProjectGetter = require('../Project/ProjectGetter')
|
||||
UserGetter = require('../User/UserGetter')
|
||||
AuthorizationManager = require("../Security/AuthorizationManager")
|
||||
AuthorizationManager = require("../Authorization/AuthorizationManager")
|
||||
ProjectEditorHandler = require('../Project/ProjectEditorHandler')
|
||||
Metrics = require('../../infrastructure/Metrics')
|
||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
PrivilegeLevels = require "../Authorization/PrivilegeLevels"
|
||||
|
||||
module.exports = EditorHttpController =
|
||||
joinProject: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
user_id = req.query.user_id
|
||||
if user_id == "anonymous-user"
|
||||
user_id = null
|
||||
logger.log {user_id, project_id}, "join project request"
|
||||
Metrics.inc "editor.join-project"
|
||||
EditorHttpController._buildJoinProjectView project_id, user_id, (error, project, privilegeLevel) ->
|
||||
|
@ -29,17 +33,17 @@ module.exports = EditorHttpController =
|
|||
ProjectGetter.getProjectWithoutDocLines project_id, (error, project) ->
|
||||
return callback(error) if error?
|
||||
return callback(new Error("not found")) if !project?
|
||||
ProjectGetter.populateProjectWithUsers project, (error, project) ->
|
||||
CollaboratorsHandler.getMembersWithPrivilegeLevels project, (error, members) ->
|
||||
return callback(error) if error?
|
||||
UserGetter.getUser user_id, { isAdmin: true }, (error, user) ->
|
||||
return callback(error) if error?
|
||||
AuthorizationManager.getPrivilegeLevelForProject project, user, (error, canAccess, privilegeLevel) ->
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel) ->
|
||||
return callback(error) if error?
|
||||
if !canAccess
|
||||
if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
|
||||
callback null, null, false
|
||||
else
|
||||
callback(null,
|
||||
ProjectEditorHandler.buildProjectModelView(project),
|
||||
ProjectEditorHandler.buildProjectModelView(project, members),
|
||||
privilegeLevel
|
||||
)
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
EditorHttpController = require('./EditorHttpController')
|
||||
SecurityManager = require('../../managers/SecurityManager')
|
||||
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||
AuthorizationMiddlewear = require('../Authorization/AuthorizationMiddlewear')
|
||||
|
||||
module.exports =
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
||||
webRouter.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
|
||||
webRouter.post '/project/:Project_id/doc', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.addDoc
|
||||
webRouter.post '/project/:Project_id/folder', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.addFolder
|
||||
|
||||
webRouter.post '/project/:Project_id/:entity_type/:entity_id/rename', SecurityManager.requestCanModifyProject, EditorHttpController.renameEntity
|
||||
webRouter.post '/project/:Project_id/:entity_type/:entity_id/move', SecurityManager.requestCanModifyProject, EditorHttpController.moveEntity
|
||||
webRouter.post '/project/:Project_id/:entity_type/:entity_id/rename', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.renameEntity
|
||||
webRouter.post '/project/:Project_id/:entity_type/:entity_id/move', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.moveEntity
|
||||
|
||||
webRouter.delete '/project/:Project_id/file/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFile
|
||||
webRouter.delete '/project/:Project_id/doc/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteDoc
|
||||
webRouter.delete '/project/:Project_id/folder/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFolder
|
||||
webRouter.delete '/project/:Project_id/file/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteFile
|
||||
webRouter.delete '/project/:Project_id/doc/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteDoc
|
||||
webRouter.delete '/project/:Project_id/folder/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteFolder
|
||||
|
||||
webRouter.post '/project/:Project_id/doc/:doc_id/restore', SecurityManager.requestCanModifyProject, EditorHttpController.restoreDoc
|
||||
webRouter.post '/project/:Project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.restoreDoc
|
||||
|
||||
# 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
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
Errors = require "./Errors"
|
||||
logger = require "logger-sharelatex"
|
||||
|
||||
module.exports = ErrorController =
|
||||
notFound: (req, res)->
|
||||
res.statusCode = 404
|
||||
res.status(404)
|
||||
res.render 'general/404',
|
||||
title: "page_not_found"
|
||||
title: "page_not_found"
|
||||
|
||||
serverError: (req, res)->
|
||||
res.status(500)
|
||||
res.render 'general/500',
|
||||
title: "Server Error"
|
||||
|
||||
handleError: (error, req, res, next) ->
|
||||
if error?.code is 'EBADCSRFTOKEN'
|
||||
logger.log err: error,url:req.url, method:req.method, user:req?.sesson?.user, "invalid csrf"
|
||||
res.sendStatus(403)
|
||||
return
|
||||
if error instanceof Errors.NotFoundError
|
||||
logger.warn {err: error, url: req.url}, "not found error"
|
||||
ErrorController.notFound req, res
|
||||
else
|
||||
logger.error err: error, url:req.url, method:req.method, user:req?.sesson?.user, "error passed to top level next middlewear"
|
||||
ErrorController.serverError req, res
|
9
services/web/app/coffee/Features/Errors/Errors.coffee
Normal file
9
services/web/app/coffee/Features/Errors/Errors.coffee
Normal file
|
@ -0,0 +1,9 @@
|
|||
NotFoundError = (message) ->
|
||||
error = new Error(message)
|
||||
error.name = "NotFoundError"
|
||||
error.__proto__ = NotFoundError.prototype
|
||||
return error
|
||||
NotFoundError.prototype.__proto__ = Error.prototype
|
||||
|
||||
module.exports = Errors =
|
||||
NotFoundError: NotFoundError
|
|
@ -5,7 +5,6 @@ projectDuplicator = require("./ProjectDuplicator")
|
|||
projectCreationHandler = require("./ProjectCreationHandler")
|
||||
editorController = require("../Editor/EditorController")
|
||||
metrics = require('../../infrastructure/Metrics')
|
||||
Project = require('../../models/Project').Project
|
||||
User = require('../../models/User').User
|
||||
TagsHandler = require("../Tags/TagsHandler")
|
||||
SubscriptionLocator = require("../Subscription/SubscriptionLocator")
|
||||
|
@ -13,10 +12,12 @@ NotificationsHandler = require("../Notifications/NotificationsHandler")
|
|||
LimitationsManager = require("../Subscription/LimitationsManager")
|
||||
_ = require("underscore")
|
||||
Settings = require("settings-sharelatex")
|
||||
SecurityManager = require("../../managers/SecurityManager")
|
||||
AuthorizationManager = require("../Authorization/AuthorizationManager")
|
||||
fs = require "fs"
|
||||
InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
|
||||
ProjectUpdateHandler = require("./ProjectUpdateHandler")
|
||||
ProjectGetter = require("./ProjectGetter")
|
||||
PrivilegeLevels = require("../Authorization/PrivilegeLevels")
|
||||
|
||||
module.exports = ProjectController =
|
||||
|
||||
|
@ -41,6 +42,14 @@ module.exports = ProjectController =
|
|||
jobs.push (callback) ->
|
||||
editorController.setRootDoc project_id, req.body.rootDocId, callback
|
||||
|
||||
async.series jobs, (error) ->
|
||||
return next(error) if error?
|
||||
res.sendStatus(204)
|
||||
|
||||
updateProjectAdminSettings: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
|
||||
jobs = []
|
||||
if req.body.publicAccessLevel?
|
||||
jobs.push (callback) ->
|
||||
editorController.setPublicAccessLevel project_id, req.body.publicAccessLevel, callback
|
||||
|
@ -129,7 +138,7 @@ module.exports = ProjectController =
|
|||
notifications: (cb)->
|
||||
NotificationsHandler.getUserNotifications user_id, cb
|
||||
projects: (cb)->
|
||||
Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
|
||||
ProjectGetter.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
|
||||
hasSubscription: (cb)->
|
||||
LimitationsManager.userHasSubscriptionOrIsGroupMember req.session.user, cb
|
||||
user: (cb) ->
|
||||
|
@ -179,23 +188,23 @@ module.exports = ProjectController =
|
|||
anonymous = false
|
||||
else
|
||||
anonymous = true
|
||||
user_id = 'openUser'
|
||||
user_id = null
|
||||
|
||||
project_id = req.params.Project_id
|
||||
logger.log project_id:project_id, "loading editor"
|
||||
|
||||
async.parallel {
|
||||
project: (cb)->
|
||||
Project.findPopulatedById project_id, cb
|
||||
ProjectGetter.getProject project_id, { name: 1, lastUpdated: 1}, cb
|
||||
user: (cb)->
|
||||
if user_id == 'openUser'
|
||||
if !user_id?
|
||||
cb null, defaultSettingsForAnonymousUser(user_id)
|
||||
else
|
||||
User.findById user_id, (err, user)->
|
||||
logger.log project_id:project_id, user_id:user_id, "got user"
|
||||
cb err, user
|
||||
subscription: (cb)->
|
||||
if user_id == 'openUser'
|
||||
if !user_id?
|
||||
return cb()
|
||||
SubscriptionLocator.getUsersSubscription user_id, cb
|
||||
activate: (cb)->
|
||||
|
@ -216,8 +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"
|
||||
|
||||
SecurityManager.userCanAccessProject user, project, (canAccess, privilegeLevel)->
|
||||
if !canAccess
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel)->
|
||||
return next(error) if error?
|
||||
if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
|
||||
return res.sendStatus 401
|
||||
|
||||
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
||||
|
@ -228,7 +238,6 @@ module.exports = ProjectController =
|
|||
title: project.name
|
||||
priority_title: true
|
||||
bodyClasses: ["editor"]
|
||||
project : project
|
||||
project_id : project._id
|
||||
user : {
|
||||
id : user.id
|
||||
|
|
|
@ -5,6 +5,7 @@ documentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
|||
tagsHandler = require("../Tags/TagsHandler")
|
||||
async = require("async")
|
||||
FileStoreHandler = require("../FileStore/FileStoreHandler")
|
||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
|
||||
module.exports = ProjectDeleter =
|
||||
|
||||
|
@ -44,16 +45,10 @@ module.exports = ProjectDeleter =
|
|||
(cb)->
|
||||
documentUpdaterHandler.flushProjectToMongoAndDelete project_id, cb
|
||||
(cb)->
|
||||
tagsHandler.removeProjectFromAllTags project.owner_ref, project_id, (err)->
|
||||
CollaboratorsHandler.getMemberIds project_id, (error, member_ids = []) ->
|
||||
for member_id in member_ids
|
||||
tagsHandler.removeProjectFromAllTags member_id, project_id, (err)->
|
||||
cb() #doesn't matter if this fails or the order it happens in
|
||||
(cb)->
|
||||
project.collaberator_refs.forEach (collaberator_ref)->
|
||||
tagsHandler.removeProjectFromAllTags collaberator_ref, project_id, ->
|
||||
cb()
|
||||
(cb)->
|
||||
project.readOnly_refs.forEach (readOnly_ref)->
|
||||
tagsHandler.removeProjectFromAllTags readOnly_ref, project_id, ->
|
||||
cb()
|
||||
(cb)->
|
||||
Project.update {_id:project_id}, { $set: { archived: true }}, cb
|
||||
], (err)->
|
||||
|
|
|
@ -4,6 +4,7 @@ Project = require('../../models/Project').Project
|
|||
logger = require("logger-sharelatex")
|
||||
tpdsUpdateSender = require '../ThirdPartyDataStore/TpdsUpdateSender'
|
||||
_ = require("underscore")
|
||||
PublicAccessLevels = require("../Authorization/PublicAccessLevels")
|
||||
|
||||
module.exports =
|
||||
|
||||
|
@ -49,6 +50,6 @@ module.exports =
|
|||
|
||||
setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
|
||||
logger.log project_id: project_id, level: newAccessLevel, "set public access level"
|
||||
if project_id? && newAccessLevel? and _.include ['readOnly', 'readAndWrite', 'private'], newAccessLevel
|
||||
if project_id? && newAccessLevel? and _.include [PublicAccessLevels.READ_ONLY, PublicAccessLevels.READ_AND_WRITE, PublicAccessLevels.PRIVATE], newAccessLevel
|
||||
Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
|
||||
callback()
|
|
@ -1,11 +1,7 @@
|
|||
_ = require("underscore")
|
||||
|
||||
module.exports = ProjectEditorHandler =
|
||||
buildProjectModelView: (project, options) ->
|
||||
options ||= {}
|
||||
if !options.includeUsers?
|
||||
options.includeUsers = true
|
||||
|
||||
buildProjectModelView: (project, members) ->
|
||||
result =
|
||||
_id : project._id
|
||||
name : project.name
|
||||
|
@ -18,40 +14,27 @@ module.exports = ProjectEditorHandler =
|
|||
spellCheckLanguage: project.spellCheckLanguage
|
||||
deletedByExternalDataSource : project.deletedByExternalDataSource || false
|
||||
deletedDocs: project.deletedDocs
|
||||
members: []
|
||||
|
||||
owner = null
|
||||
for member in members
|
||||
if member.privilegeLevel == "owner"
|
||||
owner = member.user
|
||||
else
|
||||
result.members.push @buildUserModelView member.user, member.privilegeLevel
|
||||
if owner?
|
||||
result.owner = @buildUserModelView owner, "owner"
|
||||
|
||||
if options.includeUsers
|
||||
result.features =
|
||||
collaborators: -1 # Infinite
|
||||
versioning: false
|
||||
dropbox:false
|
||||
compileTimeout: 60
|
||||
compileGroup:"standard"
|
||||
templates: false
|
||||
references: false
|
||||
result.features = _.defaults(owner?.features or {}, {
|
||||
collaborators: -1 # Infinite
|
||||
versioning: false
|
||||
dropbox:false
|
||||
compileTimeout: 60
|
||||
compileGroup:"standard"
|
||||
templates: false
|
||||
references: false
|
||||
})
|
||||
|
||||
if project.owner_ref.features?
|
||||
if project.owner_ref.features.collaborators?
|
||||
result.features.collaborators = project.owner_ref.features.collaborators
|
||||
if project.owner_ref.features.versioning?
|
||||
result.features.versioning = project.owner_ref.features.versioning
|
||||
if project.owner_ref.features.dropbox?
|
||||
result.features.dropbox = project.owner_ref.features.dropbox
|
||||
if project.owner_ref.features.compileTimeout?
|
||||
result.features.compileTimeout = project.owner_ref.features.compileTimeout
|
||||
if project.owner_ref.features.compileGroup?
|
||||
result.features.compileGroup = project.owner_ref.features.compileGroup
|
||||
if project.owner_ref.features.templates?
|
||||
result.features.templates = project.owner_ref.features.templates
|
||||
if project.owner_ref.features.references?
|
||||
result.features.references = project.owner_ref.features.references
|
||||
|
||||
|
||||
result.owner = @buildUserModelView project.owner_ref, "owner"
|
||||
result.members = []
|
||||
for ref in project.readOnly_refs
|
||||
result.members.push @buildUserModelView ref, "readOnly"
|
||||
for ref in project.collaberator_refs
|
||||
result.members.push @buildUserModelView ref, "readAndWrite"
|
||||
return result
|
||||
|
||||
buildUserModelView: (user, privileges) ->
|
||||
|
|
|
@ -2,11 +2,10 @@ mongojs = require("../../infrastructure/mongojs")
|
|||
db = mongojs.db
|
||||
ObjectId = mongojs.ObjectId
|
||||
async = require "async"
|
||||
Project = require("../../models/Project").Project
|
||||
Errors = require("../../errors")
|
||||
logger = require("logger-sharelatex")
|
||||
|
||||
|
||||
|
||||
module.exports = ProjectGetter =
|
||||
EXCLUDE_DEPTH: 8
|
||||
|
||||
|
@ -51,42 +50,11 @@ module.exports = ProjectGetter =
|
|||
return callback(err)
|
||||
callback(null, project?[0])
|
||||
|
||||
populateProjectWithUsers: (project, callback=(error, project) ->) ->
|
||||
# eventually this should be in a UserGetter.getUser module
|
||||
getUser = (user_id, callback=(error, user) ->) ->
|
||||
unless user_id instanceof ObjectId
|
||||
user_id = ObjectId(user_id)
|
||||
db.users.find _id: user_id, (error, users = []) ->
|
||||
callback error, users[0]
|
||||
|
||||
jobs = []
|
||||
jobs.push (callback) ->
|
||||
getUser project.owner_ref, (error, user) ->
|
||||
|
||||
findAllUsersProjects: (user_id, fields, callback = (error, ownedProjects, readAndWriteProjects, readOnlyProjects) ->) ->
|
||||
CollaboratorsHandler = require "../Collaborators/CollaboratorsHandler"
|
||||
Project.find {owner_ref: user_id}, fields, (error, projects) ->
|
||||
return callback(error) if error?
|
||||
CollaboratorsHandler.getProjectsUserIsCollaboratorOf user_id, fields, (error, readAndWriteProjects, readOnlyProjects) ->
|
||||
return callback(error) if error?
|
||||
if user?
|
||||
project.owner_ref = user
|
||||
callback null, project
|
||||
|
||||
readOnly_refs = project.readOnly_refs
|
||||
project.readOnly_refs = []
|
||||
for readOnly_ref in readOnly_refs
|
||||
do (readOnly_ref) ->
|
||||
jobs.push (callback) ->
|
||||
getUser readOnly_ref, (error, user) ->
|
||||
return callback(error) if error?
|
||||
if user?
|
||||
project.readOnly_refs.push user
|
||||
callback null, project
|
||||
|
||||
collaberator_refs = project.collaberator_refs
|
||||
project.collaberator_refs = []
|
||||
for collaberator_ref in collaberator_refs
|
||||
do (collaberator_ref) ->
|
||||
jobs.push (callback) ->
|
||||
getUser collaberator_ref, (error, user) ->
|
||||
return callback(error) if error?
|
||||
if user?
|
||||
project.collaberator_refs.push user
|
||||
callback null, project
|
||||
|
||||
async.parallelLimit jobs, 3, (error) -> callback error, project
|
||||
callback null, projects, readAndWriteProjects, readOnlyProjects
|
||||
|
|
|
@ -4,6 +4,7 @@ Errors = require "../../errors"
|
|||
_ = require('underscore')
|
||||
logger = require('logger-sharelatex')
|
||||
async = require('async')
|
||||
ProjectGetter = require "./ProjectGetter"
|
||||
|
||||
module.exports = ProjectLocator =
|
||||
findElement: (options, _callback = (err, element, path, parentFolder)->)->
|
||||
|
@ -131,7 +132,8 @@ module.exports = ProjectLocator =
|
|||
async.waterfall jobs, callback
|
||||
|
||||
findUsersProjectByName: (user_id, projectName, callback)->
|
||||
Project.findAllUsersProjects user_id, 'name archived', (err, projects, collabertions=[])->
|
||||
ProjectGetter.findAllUsersProjects user_id, 'name archived', (err, projects, collabertions=[])->
|
||||
return callback(error) if error?
|
||||
projects = projects.concat(collabertions)
|
||||
projectName = projectName.toLowerCase()
|
||||
project = _.find projects, (project)->
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
logger = require("logger-sharelatex")
|
||||
request = require("request")
|
||||
settings = require("settings-sharelatex")
|
||||
Project = require("../../models/Project").Project
|
||||
ProjectGetter = require "../Project/ProjectGetter"
|
||||
UserGetter = require "../User/UserGetter"
|
||||
DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||
U = require('underscore')
|
||||
Async = require('async')
|
||||
|
@ -31,11 +32,12 @@ module.exports = ReferencesHandler =
|
|||
return ids
|
||||
|
||||
_isFullIndex: (project, callback = (err, result) ->) ->
|
||||
owner = project.owner_ref
|
||||
callback(null, owner.features.references == true)
|
||||
UserGetter.getUser project.owner_ref, { features: true }, (err, owner) ->
|
||||
return callback(err) if err?
|
||||
callback(null, owner?.features?.references == true)
|
||||
|
||||
indexAll: (projectId, callback=(err, data)->) ->
|
||||
Project.findPopulatedById projectId, (err, project) ->
|
||||
ProjectGetter.getProject projectId, {rootFolder: true, owner_ref: 1}, (err, project) ->
|
||||
if err
|
||||
logger.err {err, projectId}, "error finding project"
|
||||
return callback(err)
|
||||
|
@ -44,7 +46,7 @@ module.exports = ReferencesHandler =
|
|||
ReferencesHandler._doIndexOperation(projectId, project, docIds, callback)
|
||||
|
||||
index: (projectId, docIds, callback=(err, data)->) ->
|
||||
Project.findPopulatedById projectId, (err, project) ->
|
||||
ProjectGetter.getProject projectId, {rootFolder: true, owner_ref: 1}, (err, project) ->
|
||||
if err
|
||||
logger.err {err, projectId}, "error finding project"
|
||||
return callback(err)
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
SecurityManager = require '../../managers/SecurityManager'
|
||||
|
||||
module.exports = AuthorizationManager =
|
||||
getPrivilegeLevelForProject: (
|
||||
project, user,
|
||||
callback = (error, canAccess, privilegeLevel)->
|
||||
) ->
|
||||
# This is not tested because eventually this function should be brought into
|
||||
# this module.
|
||||
SecurityManager.userCanAccessProject user, project, (canAccess, privilegeLevel) ->
|
||||
if canAccess
|
||||
callback null, true, privilegeLevel
|
||||
else
|
||||
callback null, false
|
||||
|
||||
setPrivilegeLevelOnClient: (client, privilegeLevel) ->
|
||||
client.set("privilege_level", privilegeLevel)
|
||||
|
||||
ensureClientCanViewProject: (client, callback = (error, project_id)->) ->
|
||||
@ensureClientHasPrivilegeLevelForProject client, ["owner", "readAndWrite", "readOnly"], callback
|
||||
|
||||
ensureClientCanEditProject: (client, callback = (error, project_id)->) ->
|
||||
@ensureClientHasPrivilegeLevelForProject client, ["owner", "readAndWrite"], callback
|
||||
|
||||
ensureClientCanAdminProject: (client, callback = (error, project_id)->) ->
|
||||
@ensureClientHasPrivilegeLevelForProject client, ["owner"], callback
|
||||
|
||||
ensureClientHasPrivilegeLevelForProject: (client, levels, callback = (error, project_id)->) ->
|
||||
client.get "privilege_level", (error, level) ->
|
||||
return callback(error) if error?
|
||||
if level?
|
||||
client.get "project_id", (error, project_id) ->
|
||||
return callback(error) if error?
|
||||
if project_id?
|
||||
if levels.indexOf(level) > -1
|
||||
callback null, project_id
|
||||
|
||||
|
|
@ -3,6 +3,7 @@ Project = require("../../models/Project").Project
|
|||
User = require("../../models/User").User
|
||||
SubscriptionLocator = require("./SubscriptionLocator")
|
||||
Settings = require("settings-sharelatex")
|
||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
|
||||
module.exports =
|
||||
|
||||
|
@ -13,16 +14,11 @@ module.exports =
|
|||
callback null, owner.features.collaborators
|
||||
else
|
||||
callback null, Settings.defaultPlanCode.collaborators
|
||||
|
||||
currentNumberOfCollaboratorsInProject: (project_id, callback) ->
|
||||
Project.findById project_id, 'collaberator_refs readOnly_refs', (error, project) ->
|
||||
return callback(error) if error?
|
||||
callback null, (project.collaberator_refs.length + project.readOnly_refs.length)
|
||||
|
||||
canAddXCollaborators: (project_id, x_collaborators, callback = (error, allowed)->) ->
|
||||
@allowedNumberOfCollaboratorsInProject project_id, (error, allowed_number) =>
|
||||
return callback(error) if error?
|
||||
@currentNumberOfCollaboratorsInProject project_id, (error, current_number) =>
|
||||
CollaboratorsHandler.getCollaboratorCount project_id, (error, current_number) =>
|
||||
return callback(error) if error?
|
||||
if current_number + x_collaborators <= allowed_number or allowed_number < 0
|
||||
callback null, true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SecurityManager = require '../../managers/SecurityManager'
|
||||
AuthenticationController = require '../Authentication/AuthenticationController'
|
||||
SubscriptionHandler = require './SubscriptionHandler'
|
||||
PlansLocator = require("./PlansLocator")
|
||||
SubscriptionViewModelBuilder = require('./SubscriptionViewModelBuilder')
|
||||
|
@ -31,7 +31,7 @@ module.exports = SubscriptionController =
|
|||
|
||||
#get to show the recurly.js page
|
||||
paymentPage: (req, res, next) ->
|
||||
SecurityManager.getCurrentUser req, (error, user) =>
|
||||
AuthenticationController.getLoggedInUser req, (error, user) =>
|
||||
return next(error) if error?
|
||||
plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
|
||||
LimitationsManager.userHasSubscription user, (err, hasSubscription)->
|
||||
|
@ -80,7 +80,7 @@ module.exports = SubscriptionController =
|
|||
|
||||
|
||||
userSubscriptionPage: (req, res, next) ->
|
||||
SecurityManager.getCurrentUser req, (error, user) =>
|
||||
AuthenticationController.getLoggedInUser req, (error, user) =>
|
||||
return next(error) if error?
|
||||
LimitationsManager.userHasSubscriptionOrIsGroupMember user, (err, hasSubOrIsGroupMember, subscription)->
|
||||
groupLicenceInviteUrl = SubscriptionDomainHandler.getDomainLicencePage(user)
|
||||
|
@ -109,7 +109,7 @@ module.exports = SubscriptionController =
|
|||
|
||||
|
||||
userCustomSubscriptionPage: (req, res, next)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
LimitationsManager.userHasSubscriptionOrIsGroupMember user, (err, hasSubOrIsGroupMember, subscription)->
|
||||
res.render "subscriptions/custom_account",
|
||||
title: "your_subscription"
|
||||
|
@ -117,7 +117,7 @@ module.exports = SubscriptionController =
|
|||
|
||||
|
||||
editBillingDetailsPage: (req, res, next) ->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
return next(error) if error?
|
||||
LimitationsManager.userHasSubscription user, (err, hasSubscription)->
|
||||
if !hasSubscription
|
||||
|
@ -138,7 +138,7 @@ module.exports = SubscriptionController =
|
|||
id : user.id
|
||||
|
||||
createSubscription: (req, res, next)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
return callback(error) if error?
|
||||
recurly_token_id = req.body.recurly_token_id
|
||||
subscriptionDetails = req.body.subscriptionDetails
|
||||
|
@ -150,14 +150,14 @@ module.exports = SubscriptionController =
|
|||
res.sendStatus 201
|
||||
|
||||
successful_subscription: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) =>
|
||||
AuthenticationController.getLoggedInUser req, (error, user) =>
|
||||
SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel user, (error, subscription) ->
|
||||
res.render "subscriptions/successful_subscription",
|
||||
title: "thank_you"
|
||||
subscription:subscription
|
||||
|
||||
cancelSubscription: (req, res, next) ->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
logger.log user_id:user._id, "canceling subscription"
|
||||
return next(error) if error?
|
||||
SubscriptionHandler.cancelSubscription user, (err)->
|
||||
|
@ -166,7 +166,7 @@ module.exports = SubscriptionController =
|
|||
res.redirect "/user/subscription"
|
||||
|
||||
updateSubscription: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
return next(error) if error?
|
||||
planCode = req.body.plan_code
|
||||
logger.log planCode: planCode, user_id:user._id, "updating subscription"
|
||||
|
@ -176,7 +176,7 @@ module.exports = SubscriptionController =
|
|||
res.redirect "/user/subscription"
|
||||
|
||||
reactivateSubscription: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
logger.log user_id:user._id, "reactivating subscription"
|
||||
return next(error) if error?
|
||||
SubscriptionHandler.reactivateSubscription user, (err)->
|
||||
|
@ -195,7 +195,7 @@ module.exports = SubscriptionController =
|
|||
res.sendStatus 200
|
||||
|
||||
renderUpgradeToAnnualPlanPage: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
LimitationsManager.userHasSubscription user, (err, hasSubscription, subscription)->
|
||||
planCode = subscription?.planCode.toLowerCase()
|
||||
if planCode?.indexOf("annual") != -1
|
||||
|
@ -212,7 +212,7 @@ module.exports = SubscriptionController =
|
|||
planName: planName
|
||||
|
||||
processUpgradeToAnnualPlan: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
{planName} = req.body
|
||||
coupon_code = Settings.coupon_codes.upgradeToAnnualPromo[planName]
|
||||
annualPlanName = "#{planName}-annual"
|
||||
|
@ -225,7 +225,7 @@ module.exports = SubscriptionController =
|
|||
res.sendStatus 200
|
||||
|
||||
extendTrial: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
AuthenticationController.getLoggedInUser req, (error, user) ->
|
||||
LimitationsManager.userHasSubscription user, (err, hasSubscription, subscription)->
|
||||
SubscriptionHandler.extendTrial subscription, 14, (err)->
|
||||
if err?
|
||||
|
|
|
@ -5,6 +5,7 @@ Project = require('../../models/Project').Project
|
|||
keys = require('../../infrastructure/Keys')
|
||||
metrics = require("../../infrastructure/Metrics")
|
||||
request = require("request")
|
||||
CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
|
||||
|
||||
buildPath = (user_id, project_name, filePath)->
|
||||
projectPath = path.join(project_name, "/", filePath)
|
||||
|
@ -122,9 +123,11 @@ module.exports = TpdsUpdateSender =
|
|||
TpdsUpdateSender._enqueue "poll-dropbox:#{user_id}", "standardHttpRequest", options, callback
|
||||
|
||||
getProjectsUsersIds = (project_id, callback = (err, owner_id, allUserIds)->)->
|
||||
Project.findById project_id, "_id owner_ref readOnly_refs collaberator_refs", (err, project)->
|
||||
allUserIds = [].concat(project.collaberator_refs).concat(project.readOnly_refs).concat(project.owner_ref)
|
||||
callback err, project.owner_ref, allUserIds
|
||||
Project.findById project_id, "_id owner_ref", (err, project) ->
|
||||
return callback(err) if err?
|
||||
CollaboratorsHandler.getMemberIds project_id, (err, member_ids) ->
|
||||
return callback(err) if err?
|
||||
callback err, project?.owner_ref, member_ids
|
||||
|
||||
mergeProjectNameAndPath = (project_name, path)->
|
||||
if(path.indexOf('/') == 0)
|
||||
|
|
|
@ -25,33 +25,31 @@ module.exports = ArchiveManager =
|
|||
error += chunk
|
||||
|
||||
unzip.on "error", (err) ->
|
||||
logger.error {err, source, destination}, "unzip failed"
|
||||
logger.error {err, source}, "unzip failed"
|
||||
if err.code == "ENOENT"
|
||||
logger.error "unzip command not found. Please check the unzip command is installed"
|
||||
callback(err)
|
||||
|
||||
unzip.on "exit", () ->
|
||||
unzip.on "close", (exitCode) ->
|
||||
if error?
|
||||
error = new Error(error)
|
||||
logger.error err:error, source: source, destination: destination, "error checking zip size"
|
||||
logger.error err:error, source: source, "error checking zip size"
|
||||
|
||||
lines = output.split("\n")
|
||||
lastLine = lines[lines.length - 2]?.trim()
|
||||
totalSizeInBytes = lastLine?.split(" ")?[0]
|
||||
|
||||
totalSizeInBytes = parseInt(totalSizeInBytes)
|
||||
totalSizeInBytesAsInt = parseInt(totalSizeInBytes)
|
||||
|
||||
if !totalSizeInBytes? or isNaN(totalSizeInBytes)
|
||||
logger.err source:source, "error getting bytes of zip"
|
||||
return callback(new Error("something went wrong"))
|
||||
if !totalSizeInBytesAsInt? or isNaN(totalSizeInBytesAsInt)
|
||||
logger.err source:source, totalSizeInBytes:totalSizeInBytes, totalSizeInBytesAsInt:totalSizeInBytesAsInt, lastLine:lastLine, exitCode:exitCode, "error getting bytes of zip"
|
||||
return callback(new Error("error getting bytes of zip"))
|
||||
|
||||
isTooLarge = totalSizeInBytes > (ONE_MEG * 300)
|
||||
|
||||
callback(error, isTooLarge)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extractZipArchive: (source, destination, _callback = (err) ->) ->
|
||||
callback = (args...) ->
|
||||
|
@ -87,7 +85,7 @@ module.exports = ArchiveManager =
|
|||
logger.error "unzip command not found. Please check the unzip command is installed"
|
||||
callback(err)
|
||||
|
||||
unzip.on "exit", () ->
|
||||
unzip.on "close", () ->
|
||||
timer.done()
|
||||
if error?
|
||||
error = new Error(error)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SecurityManager = require('../../managers/SecurityManager')
|
||||
AuthorizationMiddlewear = require('../Authorization/AuthorizationMiddlewear')
|
||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
ProjectUploadController = require "./ProjectUploadController"
|
||||
RateLimiterMiddlewear = require('../Security/RateLimiterMiddlewear')
|
||||
|
@ -16,6 +16,7 @@ module.exports =
|
|||
maxRequests: 200
|
||||
timeInterval: 60 * 30
|
||||
}),
|
||||
SecurityManager.requestCanModifyProject,
|
||||
AuthenticationController.requireLogin(),
|
||||
AuthorizationMiddlewear.ensureUserCanWriteProjectContent,
|
||||
ProjectUploadController.uploadFile
|
||||
|
||||
|
|
|
@ -6,10 +6,6 @@ sanitize = require('sanitizer')
|
|||
|
||||
module.exports = UserController =
|
||||
getLoggedInUsersPersonalInfo: (req, res, next = (error) ->) ->
|
||||
# this is funcky as hell, we don't use the current session to get the user
|
||||
# we use the auth token, actually destroying session from the chat api request
|
||||
if req.query?.auth_token?
|
||||
req.session?.destroy()
|
||||
logger.log user: req.user, "reciving request for getting logged in users personal info"
|
||||
return next(new Error("User is not logged in")) if !req.user?
|
||||
UserGetter.getUser req.user._id, {
|
||||
|
|
|
@ -30,6 +30,8 @@ OldAssetProxy = require("./OldAssetProxy")
|
|||
translations = require("translations-sharelatex").setup(Settings.i18n)
|
||||
Modules = require "./Modules"
|
||||
|
||||
ErrorController = require "../Features/Errors/ErrorController"
|
||||
|
||||
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongojs/node_modules/mongodb"), logger)
|
||||
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongoose/node_modules/mongodb"), logger)
|
||||
|
||||
|
@ -136,6 +138,8 @@ app.use(webRouter)
|
|||
|
||||
router = new Router(webRouter, apiRouter)
|
||||
|
||||
app.use ErrorController.handleError
|
||||
|
||||
module.exports =
|
||||
app: app
|
||||
server: server
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
logger = require('logger-sharelatex')
|
||||
crypto = require 'crypto'
|
||||
Assert = require 'assert'
|
||||
Settings = require 'settings-sharelatex'
|
||||
User = require('../models/User').User
|
||||
Project = require('../models/Project').Project
|
||||
ErrorController = require("../Features/Errors/ErrorController")
|
||||
AuthenticationController = require("../Features/Authentication/AuthenticationController")
|
||||
_ = require('underscore')
|
||||
metrics = require('../infrastructure/Metrics')
|
||||
querystring = require('querystring')
|
||||
async = require "async"
|
||||
|
||||
module.exports = SecurityManager =
|
||||
restricted : (req, res, next)->
|
||||
if req.session.user?
|
||||
res.render 'user/restricted',
|
||||
title:'restricted'
|
||||
else
|
||||
logger.log "user not logged in and trying to access #{req.url}, being redirected to login"
|
||||
res.redirect '/register'
|
||||
|
||||
getCurrentUser: (req, callback) ->
|
||||
if req.session.user?
|
||||
User.findById req.session.user._id, callback
|
||||
else
|
||||
callback null, null
|
||||
|
||||
requestCanAccessMultipleProjects: (req, res, next) ->
|
||||
project_ids = req.query.project_ids?.split(",")
|
||||
jobs = []
|
||||
for project_id in project_ids or []
|
||||
do (project_id) ->
|
||||
jobs.push (callback) ->
|
||||
# This is a bit hacky - better to have an abstracted method
|
||||
# that we can pass project_id to, but this whole file needs
|
||||
# a serious refactor ATM.
|
||||
req.params.Project_id = project_id
|
||||
SecurityManager.requestCanAccessProject req, res, (error) ->
|
||||
delete req.params.Project_id
|
||||
callback(error)
|
||||
async.series jobs, next
|
||||
|
||||
requestCanAccessProject : (req, res, next)->
|
||||
doRequest = (req, res, next) ->
|
||||
getRequestUserAndProject req, res, {allow_auth_token: options?.allow_auth_token}, (err, user, project)->
|
||||
if !project? or project.archived
|
||||
return ErrorController.notFound(req, res, next)
|
||||
userCanAccessProject user, project, (canAccess, permissionLevel)->
|
||||
if canAccess
|
||||
next()
|
||||
else if user?
|
||||
logger.log "user_id: #{user._id} email: #{user.email} trying to access restricted page #{req.path}"
|
||||
res.redirect('/restricted')
|
||||
else
|
||||
logger.log "user not logged in and trying to access #{req.url}, being redirected to login"
|
||||
AuthenticationController._redirectToLoginOrRegisterPage(req, res)
|
||||
if arguments.length > 1
|
||||
options =
|
||||
allow_auth_token: false
|
||||
doRequest.apply(this, arguments)
|
||||
else
|
||||
options = req
|
||||
return doRequest
|
||||
|
||||
requestCanModifyProject : (req, res, next)->
|
||||
getRequestUserAndProject req, res, {}, (err, user, project)=>
|
||||
userCanModifyProject user, project, (canModify)->
|
||||
if canModify
|
||||
next()
|
||||
else
|
||||
logger.log "user_id: #{user?._id} email: #{user?.email} can not modify project redirecting to restricted page"
|
||||
res.redirect('/restricted')
|
||||
|
||||
userCanModifyProject : userCanModifyProject = (user, project, callback)->
|
||||
if !user? or !project?
|
||||
callback false
|
||||
else if userIsOwner user, project
|
||||
callback true
|
||||
else if userIsCollaberator user, project
|
||||
callback true
|
||||
else if project.publicAccesLevel == "readAndWrite"
|
||||
callback true
|
||||
else if user.isAdmin
|
||||
callback true
|
||||
else
|
||||
callback false
|
||||
|
||||
|
||||
requestIsOwner : (req, res, next)->
|
||||
getRequestUserAndProject req, res, {}, (err, user, project)->
|
||||
if !user?
|
||||
return res.redirect('/restricted')
|
||||
else if userIsOwner user, project || user.isAdmin
|
||||
next()
|
||||
else
|
||||
logger.log user_id: user?._id, email: user?.email, "user is not owner of project redirecting to restricted page"
|
||||
res.redirect('/restricted')
|
||||
|
||||
requestIsAdmin : isAdmin = (req, res, next)->
|
||||
logger.log "checking if user is admin"
|
||||
user = req.session.user
|
||||
if(user? && user.isAdmin)
|
||||
logger.log user: user, "User is admin"
|
||||
next()
|
||||
else
|
||||
res.redirect('/restricted')
|
||||
logger.log user:user, "is not admin redirecting to restricted page"
|
||||
|
||||
userCanAccessProject : userCanAccessProject = (user, project, callback)=>
|
||||
if !user?
|
||||
user = {_id:'anonymous-user'}
|
||||
if !project?
|
||||
callback false
|
||||
logger.log user:user, project:project, "Checking if can access"
|
||||
if userIsOwner user, project
|
||||
callback true, "owner"
|
||||
else if userIsCollaberator user, project
|
||||
callback true, "readAndWrite"
|
||||
else if userIsReadOnly user, project
|
||||
callback true, "readOnly"
|
||||
else if user.isAdmin
|
||||
logger.log user:user, project:project, "user is admin and can access project"
|
||||
callback true, "owner"
|
||||
else if project.publicAccesLevel == "readAndWrite"
|
||||
logger.log user:user, project:project, "project is a public read and write project"
|
||||
callback true, "readAndWrite"
|
||||
else if project.publicAccesLevel == "readOnly"
|
||||
logger.log user:user, project:project, "project is a public read only project"
|
||||
callback true, "readOnly"
|
||||
else
|
||||
metrics.inc "security.denied"
|
||||
logger.log user:user, project:project, "Security denied - user can not enter project"
|
||||
callback false
|
||||
|
||||
userIsOwner : userIsOwner = (user, project)->
|
||||
if !user?
|
||||
return false
|
||||
else
|
||||
userId = user._id+''
|
||||
ownerRef = getProjectIdFromRef(project.owner_ref)
|
||||
if userId == ownerRef
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
userIsCollaberator : userIsCollaberator = (user, project)->
|
||||
if !user?
|
||||
return false
|
||||
else
|
||||
userId = user._id+''
|
||||
result = false
|
||||
_.each project.collaberator_refs, (colabRef)->
|
||||
colabRef = getProjectIdFromRef(colabRef)
|
||||
if colabRef == userId
|
||||
result = true
|
||||
return result
|
||||
|
||||
userIsReadOnly : userIsReadOnly = (user, project)->
|
||||
if !user?
|
||||
return false
|
||||
else
|
||||
userId = user._id+''
|
||||
result = false
|
||||
_.each project.readOnly_refs, (readOnlyRef)->
|
||||
readOnlyRef = getProjectIdFromRef(readOnlyRef)
|
||||
|
||||
if readOnlyRef == userId
|
||||
result = true
|
||||
return result
|
||||
|
||||
getRequestUserAndProject = (req, res, options, callback)->
|
||||
project_id = req.params.Project_id
|
||||
if !project_id?
|
||||
logger.log project_id:project_id, options:options, url:req?.url, "no project_id trying to getRequestUserAndProject"
|
||||
return res.send 422
|
||||
Project.findById project_id, 'name owner_ref readOnly_refs collaberator_refs publicAccesLevel archived', (err, project)=>
|
||||
if err?
|
||||
logger.err err:err, "error getting project for security check"
|
||||
return callback err
|
||||
AuthenticationController.getLoggedInUser req, options, (err, user)=>
|
||||
if err?
|
||||
logger.err err:err, "error getting last logged in user for security check"
|
||||
callback err, user, project
|
||||
|
||||
getProjectIdFromRef = (ref)->
|
||||
if !ref?
|
||||
return null
|
||||
else if ref._id?
|
||||
return ref._id+''
|
||||
else
|
||||
return ref+''
|
||||
|
||||
|
|
@ -43,33 +43,6 @@ ProjectSchema.statics.getProject = (project_or_id, fields, callback)->
|
|||
return callback(new Errors.NotFoundError(e.message))
|
||||
this.findById project_or_id, fields, callback
|
||||
|
||||
ProjectSchema.statics.findPopulatedById = (project_id, callback)->
|
||||
logger.log project_id:project_id, "findPopulatedById"
|
||||
this.find(_id: project_id )
|
||||
.populate('collaberator_refs')
|
||||
.populate('readOnly_refs')
|
||||
.populate('owner_ref')
|
||||
.exec (err, projects)->
|
||||
if err?
|
||||
logger.err err:err, project_id:project_id, "something went wrong looking for project findPopulatedById"
|
||||
callback(err)
|
||||
else if !projects? || projects.length == 0
|
||||
logger.err project_id:project_id, "something went wrong looking for project findPopulatedById, no project could be found"
|
||||
callback "not found"
|
||||
else
|
||||
logger.log project_id:project_id, "finished findPopulatedById"
|
||||
callback(null, projects[0])
|
||||
|
||||
ProjectSchema.statics.findAllUsersProjects = (user_id, requiredFields, callback)->
|
||||
this.find {owner_ref:user_id}, requiredFields, (err, projects)=>
|
||||
this.find {collaberator_refs:user_id}, requiredFields, (err, collabertions)=>
|
||||
this.find {readOnly_refs:user_id}, requiredFields, (err, readOnlyProjects)=>
|
||||
callback(err, projects, collabertions, readOnlyProjects)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
applyToAllFilesRecursivly = ProjectSchema.statics.applyToAllFilesRecursivly = (folder, fun)->
|
||||
_.each folder.fileRefs, (file)->
|
||||
fun(file)
|
||||
|
|
23
services/web/app/coffee/models/ProjectInvite.coffee
Normal file
23
services/web/app/coffee/models/ProjectInvite.coffee
Normal file
|
@ -0,0 +1,23 @@
|
|||
mongoose = require 'mongoose'
|
||||
Settings = require 'settings-sharelatex'
|
||||
|
||||
Schema = mongoose.Schema
|
||||
ObjectId = Schema.ObjectId
|
||||
|
||||
ProjectInviteSchema = new Schema
|
||||
project_id: ObjectId
|
||||
from_user_id: ObjectId
|
||||
privilegeLevel: String
|
||||
# For existing users
|
||||
to_user_id: ObjectId
|
||||
# For non-existant users
|
||||
hashed_token: String
|
||||
email: String
|
||||
|
||||
conn = mongoose.createConnection(Settings.mongo.url, server: poolSize: Settings.mongo.poolSize || 10)
|
||||
|
||||
ProjectInvite = conn.model('ProjectInvite', ProjectInviteSchema)
|
||||
|
||||
mongoose.model 'ProjectInvite', ProjectInviteSchema
|
||||
exports.ProjectInvite = ProjectInvite
|
||||
exports.ProjectInviteSchema = ProjectInviteSchema
|
|
@ -55,32 +55,6 @@ UserSchema = new Schema
|
|||
# has this set to true, despite never having had a free trial
|
||||
hadFreeTrial: {type: Boolean, default: false}
|
||||
|
||||
UserSchema.statics.getAllIds = (callback)->
|
||||
this.find {}, ["first_name"], callback
|
||||
|
||||
|
||||
UserSchema.statics.findReadOnlyProjects = (user_id, callback)->
|
||||
@find({'projects.readOnly_refs':user_id}).populate('projects.readOnly_refs').run (err, users)->
|
||||
projects = []
|
||||
_.each users, (user)->
|
||||
_.each user.projects, (project)->
|
||||
_.each project.readOnly_refs, (subUser)->
|
||||
if(subUser._id == user_id)
|
||||
projects.push(project)
|
||||
callback(projects)
|
||||
|
||||
UserSchema.statics.findCollaborationProjects = (user_id, callback)->
|
||||
@find({'projects.collaberator_refs':user_id}).populate('projects.collaberator_refs').run (err, users)->
|
||||
projects = []
|
||||
_.each users, (user)->
|
||||
_.each user.projects, (project)->
|
||||
_.each project.collaberator_refs, (subUser)->
|
||||
if(subUser._id == user_id)
|
||||
projects.push(project)
|
||||
callback(projects)
|
||||
|
||||
|
||||
|
||||
conn = mongoose.createConnection(Settings.mongo.url, server: poolSize: 10)
|
||||
|
||||
User = conn.model('User', UserSchema)
|
||||
|
|
|
@ -3,8 +3,6 @@ ErrorController = require('./Features/Errors/ErrorController')
|
|||
ProjectController = require("./Features/Project/ProjectController")
|
||||
ProjectApiController = require("./Features/Project/ProjectApiController")
|
||||
SpellingController = require('./Features/Spelling/SpellingController')
|
||||
SecurityManager = require('./managers/SecurityManager')
|
||||
AuthorizationManager = require('./Features/Security/AuthorizationManager')
|
||||
EditorController = require("./Features/Editor/EditorController")
|
||||
EditorRouter = require("./Features/Editor/EditorRouter")
|
||||
Settings = require('settings-sharelatex')
|
||||
|
@ -39,6 +37,7 @@ RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
|||
InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
|
||||
ContactRouter = require("./Features/Contacts/ContactRouter")
|
||||
ReferencesController = require('./Features/References/ReferencesController')
|
||||
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
|
||||
|
||||
logger = require("logger-sharelatex")
|
||||
_ = require("underscore")
|
||||
|
@ -54,7 +53,7 @@ module.exports = class Router
|
|||
|
||||
webRouter.post '/login', AuthenticationController.login
|
||||
webRouter.get '/logout', UserController.logout
|
||||
webRouter.get '/restricted', SecurityManager.restricted
|
||||
webRouter.get '/restricted', AuthorizationMiddlewear.restricted
|
||||
|
||||
# Left as a placeholder for implementing a public register page
|
||||
webRouter.get '/register', UserPagesController.registerPage
|
||||
|
@ -88,8 +87,7 @@ module.exports = class Router
|
|||
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||
webRouter.delete '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
||||
|
||||
webRouter.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
||||
webRouter.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
|
||||
webRouter.get '/user/personal_info', AuthenticationController.requireLogin(), UserInfoController.getLoggedInUsersPersonalInfo
|
||||
apiRouter.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
||||
|
||||
webRouter.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
||||
|
@ -100,12 +98,13 @@ module.exports = class Router
|
|||
params: ["Project_id"]
|
||||
maxRequests: 10
|
||||
timeInterval: 60
|
||||
}), SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
||||
webRouter.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
||||
webRouter.post '/project/:Project_id/settings', SecurityManager.requestCanModifyProject, ProjectController.updateProjectSettings
|
||||
}), AuthorizationMiddlewear.ensureUserCanReadProject, ProjectController.loadEditor
|
||||
webRouter.get '/Project/:Project_id/file/:File_id', AuthorizationMiddlewear.ensureUserCanReadProject, FileStoreController.getFile
|
||||
webRouter.post '/project/:Project_id/settings', AuthorizationMiddlewear.ensureUserCanWriteProjectSettings, ProjectController.updateProjectSettings
|
||||
webRouter.post '/project/:Project_id/settings/admin', AuthorizationMiddlewear.ensureUserCanAdminProject, ProjectController.updateProjectAdminSettings
|
||||
|
||||
webRouter.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||
webRouter.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||
webRouter.post '/project/:Project_id/compile', AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.compile
|
||||
webRouter.get '/Project/:Project_id/output/output.pdf', AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.downloadPdf
|
||||
webRouter.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||
((req, res, next) ->
|
||||
params =
|
||||
|
@ -113,24 +112,24 @@ module.exports = class Router
|
|||
"file": req.params[1]
|
||||
req.params = params
|
||||
next()
|
||||
), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi
|
||||
webRouter.delete "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
|
||||
webRouter.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
webRouter.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
webRouter.get "/project/:Project_id/wordcount", SecurityManager.requestCanAccessProject, CompileController.wordCount
|
||||
), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
|
||||
webRouter.delete "/project/:Project_id/output", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.deleteAuxFiles
|
||||
webRouter.get "/project/:Project_id/sync/code", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySync
|
||||
webRouter.get "/project/:Project_id/sync/pdf", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySync
|
||||
webRouter.get "/project/:Project_id/wordcount", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.wordCount
|
||||
|
||||
webRouter.delete '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
|
||||
webRouter.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
|
||||
webRouter.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
|
||||
webRouter.delete '/Project/:Project_id', AuthorizationMiddlewear.ensureUserCanAdminProject, ProjectController.deleteProject
|
||||
webRouter.post '/Project/:Project_id/restore', AuthorizationMiddlewear.ensureUserCanAdminProject, ProjectController.restoreProject
|
||||
webRouter.post '/Project/:Project_id/clone', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectController.cloneProject
|
||||
|
||||
webRouter.post '/project/:Project_id/rename', SecurityManager.requestIsOwner, ProjectController.renameProject
|
||||
webRouter.post '/project/:Project_id/rename', AuthorizationMiddlewear.ensureUserCanAdminProject, ProjectController.renameProject
|
||||
|
||||
webRouter.get "/project/:Project_id/updates", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.get "/project/:Project_id/doc/:doc_id/diff", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.get "/project/:Project_id/updates", AuthorizationMiddlewear.ensureUserCanReadProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.get "/project/:Project_id/doc/:doc_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", AuthorizationMiddlewear.ensureUserCanReadProject, TrackChangesController.proxyToTrackChangesApi
|
||||
|
||||
webRouter.get '/Project/:Project_id/download/zip', SecurityManager.requestCanAccessProject, ProjectDownloadsController.downloadProject
|
||||
webRouter.get '/project/download/zip', SecurityManager.requestCanAccessMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||
webRouter.get '/Project/:Project_id/download/zip', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectDownloadsController.downloadProject
|
||||
webRouter.get '/project/download/zip', AuthorizationMiddlewear.ensureUserCanReadMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||
|
||||
webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
|
||||
webRouter.post '/tag', AuthenticationController.requireLogin(), TagsController.createTag
|
||||
|
@ -174,26 +173,26 @@ module.exports = class Router
|
|||
webRouter.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
webRouter.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
|
||||
webRouter.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
|
||||
webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
|
||||
webRouter.get "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.getMessages
|
||||
webRouter.post "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.sendMessage
|
||||
|
||||
webRouter.get /learn(\/.*)?/, WikiController.getPage
|
||||
|
||||
webRouter.post "/project/:Project_id/references/index", SecurityManager.requestCanAccessProject, ReferencesController.index
|
||||
webRouter.post "/project/:Project_id/references/indexAll", SecurityManager.requestCanAccessProject, ReferencesController.indexAll
|
||||
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
|
||||
webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
||||
|
||||
#Admin Stuff
|
||||
webRouter.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
||||
webRouter.get '/admin/user', SecurityManager.requestIsAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
|
||||
webRouter.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
||||
webRouter.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
||||
webRouter.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
||||
webRouter.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
||||
webRouter.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
||||
webRouter.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds
|
||||
webRouter.post '/admin/pollDropboxForUser', SecurityManager.requestIsAdmin, AdminController.pollDropboxForUser
|
||||
webRouter.post '/admin/messages', SecurityManager.requestIsAdmin, AdminController.createMessage
|
||||
webRouter.post '/admin/messages/clear', SecurityManager.requestIsAdmin, AdminController.clearMessages
|
||||
webRouter.get '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index
|
||||
webRouter.get '/admin/user', AuthorizationMiddlewear.ensureUserIsSiteAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
|
||||
webRouter.get '/admin/register', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.registerNewUser
|
||||
webRouter.post '/admin/register', AuthorizationMiddlewear.ensureUserIsSiteAdmin, UserController.register
|
||||
webRouter.post '/admin/closeEditor', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.closeEditor
|
||||
webRouter.post '/admin/dissconectAllUsers', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.dissconectAllUsers
|
||||
webRouter.post '/admin/syncUserToSubscription', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.syncUserToSubscription
|
||||
webRouter.post '/admin/flushProjectToTpds', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.flushProjectToTpds
|
||||
webRouter.post '/admin/pollDropboxForUser', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.pollDropboxForUser
|
||||
webRouter.post '/admin/messages', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.createMessage
|
||||
webRouter.post '/admin/messages/clear', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.clearMessages
|
||||
|
||||
apiRouter.get '/perfTest', (req,res)->
|
||||
res.send("hello")
|
||||
|
@ -205,7 +204,7 @@ module.exports = class Router
|
|||
webRouter.get '/health_check', HealthCheckController.check
|
||||
webRouter.get '/health_check/redis', HealthCheckController.checkRedis
|
||||
|
||||
apiRouter.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) ->
|
||||
apiRouter.get "/status/compiler/:Project_id", AuthorizationMiddlewear.ensureUserCanReadProject, (req, res) ->
|
||||
sendRes = _.once (statusCode, message)->
|
||||
res.writeHead statusCode
|
||||
res.end message
|
||||
|
@ -222,9 +221,9 @@ module.exports = class Router
|
|||
headers: req.headers
|
||||
})
|
||||
|
||||
apiRouter.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
||||
apiRouter.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
||||
apiRouter.get '/oops-mongo', (req, res, next) ->
|
||||
webRouter.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
||||
webRouter.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
||||
webRouter.get '/oops-mongo', (req, res, next) ->
|
||||
require("./models/Project").Project.findOne {}, () ->
|
||||
throw new Error("Test error")
|
||||
|
||||
|
@ -233,7 +232,7 @@ module.exports = class Router
|
|||
res.send()
|
||||
|
||||
webRouter.post '/error/client', (req, res, next) ->
|
||||
logger.error err: req.body.error, meta: req.body.meta, "client side error"
|
||||
logger.warn err: req.body.error, meta: req.body.meta, "client side error"
|
||||
res.sendStatus(204)
|
||||
|
||||
webRouter.get '*', ErrorController.notFound
|
21
services/web/app/views/general/500.jade
Normal file
21
services/web/app/views/general/500.jade
Normal file
|
@ -0,0 +1,21 @@
|
|||
doctype html
|
||||
html(itemscope, itemtype='http://schema.org/Product')
|
||||
head
|
||||
title Something went wrong
|
||||
link(rel="icon", href="/favicon.ico")
|
||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||
link(href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css",rel="stylesheet")
|
||||
body
|
||||
.content
|
||||
.container
|
||||
.row
|
||||
.col-md-8.col-md-offset-2.text-center
|
||||
.page-header
|
||||
h2 Oh dear, something went wrong.
|
||||
p: img(src="/img/lion-sad-128.png", alt="Sad Lion")
|
||||
p
|
||||
| Something went wrong with your request, sorry. Our staff are probably looking into this, but if it continues, please contact us at #{settings.adminEmail}
|
||||
p
|
||||
a(href="/")
|
||||
i.fa.fa-arrow-circle-o-left
|
||||
| Take me home
|
|
@ -259,7 +259,7 @@ module.exports =
|
|||
|
||||
# Should we allow access to any page without logging in? This includes
|
||||
# public projects, /learn, /templates, about pages, etc.
|
||||
allowPublicAccess: false
|
||||
allowPublicAccess: if process.env["SHARELATEX_ALLOW_PUBLIC_ACCESS"] == 'true' then true else false
|
||||
|
||||
# Maximum size of text documents in the real-time editing system.
|
||||
max_doc_length: 2 * 1024 * 1024 # 2mb
|
||||
|
|
|
@ -44,7 +44,8 @@
|
|||
"redback": "0.4.0",
|
||||
"redis": "0.10.1",
|
||||
"redis-sharelatex": "0.0.9",
|
||||
"request": "2.34.0",
|
||||
"request": "^2.69.0",
|
||||
"requests": "^0.1.7",
|
||||
"rimraf": "2.2.6",
|
||||
"sanitizer": "0.1.1",
|
||||
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
||||
|
|
|
@ -10,5 +10,10 @@ define [
|
|||
saveProjectSettings: (data) ->
|
||||
data._csrf = window.csrfToken
|
||||
ide.$http.post "/project/#{ide.project_id}/settings", data
|
||||
|
||||
saveProjectAdminSettings: (data) ->
|
||||
data._csrf = window.csrfToken
|
||||
ide.$http.post "/project/#{ide.project_id}/settings/admin", data
|
||||
|
||||
}
|
||||
]
|
|
@ -143,7 +143,7 @@ define [
|
|||
|
||||
$scope.makePublic = () ->
|
||||
$scope.project.publicAccesLevel = $scope.inputs.privileges
|
||||
settings.saveProjectSettings({publicAccessLevel: $scope.inputs.privileges})
|
||||
settings.saveProjectAdminSettings({publicAccessLevel: $scope.inputs.privileges})
|
||||
$modalInstance.close()
|
||||
|
||||
$scope.cancel = () ->
|
||||
|
@ -153,7 +153,7 @@ define [
|
|||
App.controller "MakePrivateModalController", ["$scope", "$modalInstance", "settings", ($scope, $modalInstance, settings) ->
|
||||
$scope.makePrivate = () ->
|
||||
$scope.project.publicAccesLevel = "private"
|
||||
settings.saveProjectSettings({publicAccessLevel: "private"})
|
||||
settings.saveProjectAdminSettings({publicAccessLevel: "private"})
|
||||
$modalInstance.close()
|
||||
|
||||
$scope.cancel = () ->
|
||||
|
|
BIN
services/web/public/img/lion-sad-128.png
Normal file
BIN
services/web/public/img/lion-sad-128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -173,7 +173,7 @@ describe "AuthenticationController", ->
|
|||
beforeEach ->
|
||||
@req.session =
|
||||
user: @user
|
||||
@AuthenticationController.getLoggedInUser(@req, {}, @callback)
|
||||
@AuthenticationController.getLoggedInUser(@req, @callback)
|
||||
|
||||
it "should look up the user in the database", ->
|
||||
@UserGetter.getUser
|
||||
|
@ -183,105 +183,37 @@ describe "AuthenticationController", ->
|
|||
it "should return the user", ->
|
||||
@callback.calledWith(null, @user).should.equal true
|
||||
|
||||
describe "with an auth token, but without auth_token_allowed set to true", ->
|
||||
beforeEach ->
|
||||
@req.query =
|
||||
auth_token: "auth-token"
|
||||
@AuthenticationController.getLoggedInUser(@req, {}, @callback)
|
||||
|
||||
it "should not look up the user in the database", ->
|
||||
@UserGetter.getUser.called.should.equal false
|
||||
|
||||
it "should return null in the callback", ->
|
||||
@callback.calledWith(null, null).should.equal true
|
||||
|
||||
describe "with an auth token and auth_token_allowed set to true", ->
|
||||
beforeEach ->
|
||||
@req.query =
|
||||
auth_token: "auth-token"
|
||||
@AuthenticationController.getLoggedInUser(@req, {allow_auth_token: true}, @callback)
|
||||
|
||||
it "should look up the user in the database", ->
|
||||
@UserGetter.getUser
|
||||
.calledWith(auth_token: @req.query.auth_token)
|
||||
.should.equal true
|
||||
|
||||
it "should return the user", ->
|
||||
@callback.calledWith(null, @user).should.equal true
|
||||
|
||||
describe "requireLogin", ->
|
||||
beforeEach ->
|
||||
@user =
|
||||
_id: "user-id-123"
|
||||
email: "user@sharelatex.com"
|
||||
@middleware = @AuthenticationController.requireLogin()
|
||||
|
||||
describe "when loading from the database", ->
|
||||
describe "when the user is logged in", ->
|
||||
beforeEach ->
|
||||
@middleware = @AuthenticationController.requireLogin(@options = { allow_auth_token: true, load_from_db: true })
|
||||
|
||||
describe "when the user is logged in", ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUser = sinon.stub().callsArgWith(2, null, @user)
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should call getLoggedInUser with the passed options", ->
|
||||
@AuthenticationController.getLoggedInUser.calledWith(@req, { allow_auth_token: true }).should.equal true
|
||||
|
||||
it "should set the user property on the request", ->
|
||||
@req.user.should.deep.equal @user
|
||||
|
||||
it "should call the next method in the chain", ->
|
||||
@next.called.should.equal true
|
||||
|
||||
describe "when the user is not logged in", ->
|
||||
beforeEach ->
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage = sinon.stub()
|
||||
@AuthenticationController.getLoggedInUser = sinon.stub().callsArgWith(2, null, null)
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should redirect to the register page", ->
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage.calledWith(@req, @res).should.equal true
|
||||
|
||||
describe "when not loading from the database", ->
|
||||
beforeEach ->
|
||||
@middleware = @AuthenticationController.requireLogin(@options = { load_from_db: false })
|
||||
|
||||
describe "when the user is logged in", ->
|
||||
beforeEach ->
|
||||
@req.session =
|
||||
user: @user = {
|
||||
_id: "user-id-123"
|
||||
email: "user@sharelatex.com"
|
||||
}
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should set the user property on the request", ->
|
||||
@req.user.should.deep.equal @user
|
||||
|
||||
it "should call the next method in the chain", ->
|
||||
@next.called.should.equal true
|
||||
|
||||
describe "when the user is not logged in", ->
|
||||
beforeEach ->
|
||||
@req.session = {}
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage = sinon.stub()
|
||||
@req.query = {}
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should redirect to the register or login page", ->
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage.calledWith(@req, @res).should.equal true
|
||||
|
||||
describe "when not loading from the database but an auth_token is provided", ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUser = sinon.stub().callsArgWith(2, null, @user)
|
||||
@middleware = @AuthenticationController.requireLogin(@options = { load_from_db: false, allow_auth_token: true })
|
||||
@req.query = auth_token: @auth_token = "auth-token-provided"
|
||||
@req.session =
|
||||
user: @user = {
|
||||
_id: "user-id-123"
|
||||
email: "user@sharelatex.com"
|
||||
}
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should try to load the user from the database anyway", ->
|
||||
@AuthenticationController.getLoggedInUser
|
||||
.calledWith(@req, {allow_auth_token: true})
|
||||
.should.equal true
|
||||
it "should set the user property on the request", ->
|
||||
@req.user.should.deep.equal @user
|
||||
|
||||
it "should call the next method in the chain", ->
|
||||
@next.called.should.equal true
|
||||
|
||||
describe "when the user is not logged in", ->
|
||||
beforeEach ->
|
||||
@req.session = {}
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage = sinon.stub()
|
||||
@req.query = {}
|
||||
@middleware(@req, @res, @next)
|
||||
|
||||
it "should redirect to the register or login page", ->
|
||||
@AuthenticationController._redirectToLoginOrRegisterPage.calledWith(@req, @res).should.equal true
|
||||
|
||||
describe "requireGlobalLogin", ->
|
||||
beforeEach ->
|
||||
|
|
|
@ -95,49 +95,3 @@ describe "AuthenticationManager", ->
|
|||
|
||||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
|
||||
describe "getAuthToken", ->
|
||||
beforeEach ->
|
||||
@auth_token = "auth-token"
|
||||
|
||||
describe "when the user has an auth token set", ->
|
||||
beforeEach ->
|
||||
@db.users.findOne = sinon.stub().callsArgWith(2, null, auth_token: @auth_token)
|
||||
@AuthenticationManager.getAuthToken(@user_id, @callback)
|
||||
|
||||
it "should look up the auth token in the db", ->
|
||||
@db.users.findOne
|
||||
.calledWith({
|
||||
_id: ObjectId(@user_id.toString())
|
||||
}, {
|
||||
auth_token: true
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should return the auth token", ->
|
||||
@callback.calledWith(null, @auth_token).should.equal true
|
||||
|
||||
describe "when the user does not have an auth token set", ->
|
||||
beforeEach ->
|
||||
@db.users.findOne = sinon.stub().callsArgWith(2, null, auth_token: null)
|
||||
@db.users.update = sinon.stub().callsArgWith(2, null)
|
||||
@AuthenticationManager._createSecureToken = sinon.stub().callsArgWith(0, null, @auth_token)
|
||||
@AuthenticationManager.getAuthToken(@user_id, @callback)
|
||||
|
||||
it "should generate a new auth token", ->
|
||||
@AuthenticationManager._createSecureToken.called.should.equal true
|
||||
|
||||
it "should set the auth token on the user document in the db", ->
|
||||
@db.users.update
|
||||
.calledWith({
|
||||
_id: ObjectId(@user_id.toString())
|
||||
}, {
|
||||
$set: auth_token: @auth_token
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should return the auth token", ->
|
||||
@callback.calledWith(null, @auth_token).should.equal true
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,385 @@
|
|||
sinon = require('sinon')
|
||||
chai = require('chai')
|
||||
should = chai.should()
|
||||
expect = chai.expect
|
||||
modulePath = "../../../../app/js/Features/Authorization/AuthorizationManager.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
Errors = require "../../../../app/js/Features/Errors/Errors.js"
|
||||
|
||||
describe "AuthorizationManager", ->
|
||||
beforeEach ->
|
||||
@AuthorizationManager = SandboxedModule.require modulePath, requires:
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
"../../models/Project": Project: @Project = {}
|
||||
"../../models/User": User: @User = {}
|
||||
"../Errors/Errors": Errors
|
||||
@user_id = "user-id-1"
|
||||
@project_id = "project-id-1"
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe "getPrivilegeLevelForProject", ->
|
||||
beforeEach ->
|
||||
@Project.findOne = sinon.stub()
|
||||
@AuthorizationManager.isUserSiteAdmin = 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 ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
|
||||
@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 ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
|
||||
@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 a user_id who is an admin", ->
|
||||
beforeEach ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, true)
|
||||
@CollaboratorsHandler.getMemberIdPrivilegeLevel
|
||||
.withArgs(@user_id, @project_id)
|
||||
.yields(null, false)
|
||||
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
|
||||
|
||||
it "should return the user as an owner", ->
|
||||
@callback.calledWith(null, "owner", 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 not call AuthorizationManager.isUserSiteAdmin", ->
|
||||
@AuthorizationManager.isUserSiteAdmin.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 ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
|
||||
@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 ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, false)
|
||||
@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 a user_id who is an admin", ->
|
||||
beforeEach ->
|
||||
@AuthorizationManager.isUserSiteAdmin.withArgs(@user_id).yields(null, true)
|
||||
@CollaboratorsHandler.getMemberIdPrivilegeLevel
|
||||
.withArgs(@user_id, @project_id)
|
||||
.yields(null, false)
|
||||
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, @callback
|
||||
|
||||
it "should return the user as an owner", ->
|
||||
@callback.calledWith(null, "owner", 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 not call AuthorizationManager.isUserSiteAdmin", ->
|
||||
@AuthorizationManager.isUserSiteAdmin.called.should.equal false
|
||||
|
||||
it "should return the public privilege level", ->
|
||||
@callback.calledWith(null, "readAndWrite", true).should.equal true
|
||||
|
||||
describe "when the project doesn't exist", ->
|
||||
beforeEach ->
|
||||
@Project.findOne
|
||||
.withArgs({ _id: @project_id }, { publicAccesLevel: 1 })
|
||||
.yields(null, null)
|
||||
|
||||
it "should return a NotFoundError", ->
|
||||
@AuthorizationManager.getPrivilegeLevelForProject @user_id, @project_id, (error) ->
|
||||
error.should.be.instanceof Errors.NotFoundError
|
||||
|
||||
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,237 @@
|
|||
sinon = require('sinon')
|
||||
chai = require('chai')
|
||||
should = chai.should()
|
||||
expect = chai.expect
|
||||
modulePath = "../../../../app/js/Features/Authorization/AuthorizationMiddlewear.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
Errors = require "../../../../app/js/Features/Errors/Errors.js"
|
||||
|
||||
describe "AuthorizationMiddlewear", ->
|
||||
beforeEach ->
|
||||
@AuthorizationMiddlewear = SandboxedModule.require modulePath, requires:
|
||||
"./AuthorizationManager": @AuthorizationManager = {}
|
||||
"logger-sharelatex": {log: () ->}
|
||||
"mongojs": ObjectId: @ObjectId = {}
|
||||
"../Errors/Errors": Errors
|
||||
@user_id = "user-id-123"
|
||||
@project_id = "project-id-123"
|
||||
@req = {}
|
||||
@res = {}
|
||||
@ObjectId.isValid = sinon.stub()
|
||||
@ObjectId.isValid.withArgs(@project_id).returns true
|
||||
@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 "with malformed project id", ->
|
||||
beforeEach ->
|
||||
@req.params =
|
||||
project_id: "blah"
|
||||
@ObjectId.isValid = sinon.stub().returns false
|
||||
|
||||
it "should return a not found error", (done) ->
|
||||
@AuthorizationMiddlewear[middlewearMethod] @req, @res, (error) ->
|
||||
error.should.be.instanceof Errors.NotFoundError
|
||||
done()
|
||||
|
||||
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
|
|
@ -24,35 +24,6 @@ describe "CollaboratorsController", ->
|
|||
@project_id = "project-id-123"
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe "getCollaborators", ->
|
||||
beforeEach ->
|
||||
@project =
|
||||
_id: @project_id = "project-id-123"
|
||||
@collaborators = ["array of collaborators"]
|
||||
@req.params = Project_id: @project_id
|
||||
@ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, @project)
|
||||
@ProjectGetter.populateProjectWithUsers = sinon.stub().callsArgWith(1, null, @project)
|
||||
@CollaboratorsController._formatCollaborators = sinon.stub().callsArgWith(1, null, @collaborators)
|
||||
@CollaboratorsController.getCollaborators(@req, @res)
|
||||
|
||||
it "should get the project", ->
|
||||
@ProjectGetter.getProject
|
||||
.calledWith(@project_id, { owner_ref: true, collaberator_refs: true, readOnly_refs: true })
|
||||
.should.equal true
|
||||
|
||||
it "should populate the users in the project", ->
|
||||
@ProjectGetter.populateProjectWithUsers
|
||||
.calledWith(@project)
|
||||
.should.equal true
|
||||
|
||||
it "should format the collaborators", ->
|
||||
@CollaboratorsController._formatCollaborators
|
||||
.calledWith(@project)
|
||||
.should.equal true
|
||||
|
||||
it "should return the formatted collaborators", ->
|
||||
@res.body.should.equal JSON.stringify(@collaborators)
|
||||
|
||||
describe "addUserToProject", ->
|
||||
beforeEach ->
|
||||
@req.params =
|
||||
|
@ -179,85 +150,3 @@ describe "CollaboratorsController", ->
|
|||
it "should return a success code", ->
|
||||
@res.sendStatus.calledWith(204).should.equal true
|
||||
|
||||
describe "_formatCollaborators", ->
|
||||
beforeEach ->
|
||||
@owner =
|
||||
_id: ObjectId()
|
||||
first_name: "Lenny"
|
||||
last_name: "Lion"
|
||||
email: "test@sharelatex.com"
|
||||
hashed_password: "password" # should not be included
|
||||
|
||||
describe "formatting the owner", ->
|
||||
beforeEach ->
|
||||
@project =
|
||||
owner_ref: @owner
|
||||
collaberator_refs: []
|
||||
@CollaboratorsController._formatCollaborators(@project, @callback)
|
||||
|
||||
it "should return the owner with read, write and admin permissions", ->
|
||||
@formattedOwner = @callback.args[0][1][0]
|
||||
expect(@formattedOwner).to.deep.equal {
|
||||
id: @owner._id.toString()
|
||||
first_name: @owner.first_name
|
||||
last_name: @owner.last_name
|
||||
email: @owner.email
|
||||
permissions: ["read", "write", "admin"]
|
||||
owner: true
|
||||
}
|
||||
|
||||
describe "formatting a collaborator with write access", ->
|
||||
beforeEach ->
|
||||
@collaborator =
|
||||
_id: ObjectId()
|
||||
first_name: "Douglas"
|
||||
last_name: "Adams"
|
||||
email: "doug@sharelatex.com"
|
||||
hashed_password: "password" # should not be included
|
||||
|
||||
@project =
|
||||
owner_ref: @owner
|
||||
collaberator_refs: [ @collaborator ]
|
||||
@CollaboratorsController._formatCollaborators(@project, @callback)
|
||||
|
||||
it "should return the collaborator with read and write permissions", ->
|
||||
@formattedCollaborator = @callback.args[0][1][1]
|
||||
expect(@formattedCollaborator).to.deep.equal {
|
||||
id: @collaborator._id.toString()
|
||||
first_name: @collaborator.first_name
|
||||
last_name: @collaborator.last_name
|
||||
email: @collaborator.email
|
||||
permissions: ["read", "write"]
|
||||
owner: false
|
||||
}
|
||||
|
||||
describe "formatting a collaborator with read only access", ->
|
||||
beforeEach ->
|
||||
@collaborator =
|
||||
_id: ObjectId()
|
||||
first_name: "Douglas"
|
||||
last_name: "Adams"
|
||||
email: "doug@sharelatex.com"
|
||||
hashed_password: "password" # should not be included
|
||||
|
||||
@project =
|
||||
owner_ref: @owner
|
||||
collaberator_refs: []
|
||||
readOnly_refs: [ @collaborator ]
|
||||
@CollaboratorsController._formatCollaborators(@project, @callback)
|
||||
|
||||
it "should return the collaborator with read permissions", ->
|
||||
@formattedCollaborator = @callback.args[0][1][1]
|
||||
expect(@formattedCollaborator).to.deep.equal {
|
||||
id: @collaborator._id.toString()
|
||||
first_name: @collaborator.first_name
|
||||
last_name: @collaborator.last_name
|
||||
email: @collaborator.email
|
||||
permissions: ["read"]
|
||||
owner: false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ path = require('path')
|
|||
sinon = require('sinon')
|
||||
modulePath = path.join __dirname, "../../../../app/js/Features/Collaborators/CollaboratorsHandler"
|
||||
expect = require("chai").expect
|
||||
Errors = require "../../../../app/js/Features/Errors/Errors.js"
|
||||
|
||||
describe "CollaboratorsHandler", ->
|
||||
beforeEach ->
|
||||
|
@ -16,12 +17,145 @@ describe "CollaboratorsHandler", ->
|
|||
"../../models/Project": Project: @Project = {}
|
||||
"../Project/ProjectEntityHandler": @ProjectEntityHandler = {}
|
||||
"./CollaboratorsEmailHandler": @CollaboratorsEmailHandler = {}
|
||||
"../Errors/Errors": Errors
|
||||
|
||||
@project_id = "mock-project-id"
|
||||
@user_id = "mock-user-id"
|
||||
@adding_user_id = "adding-user-id"
|
||||
@email = "joe@sharelatex.com"
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe "getMemberIdsWithPrivilegeLevels", ->
|
||||
describe "with project", ->
|
||||
beforeEach ->
|
||||
@Project.findOne = sinon.stub()
|
||||
@Project.findOne.withArgs({_id: @project_id}, {owner_ref: 1, collaberator_refs: 1, readOnly_refs: 1}).yields(null, @project = {
|
||||
owner_ref: [ "owner-ref" ]
|
||||
readOnly_refs: [ "read-only-ref-1", "read-only-ref-2" ]
|
||||
collaberator_refs: [ "read-write-ref-1", "read-write-ref-2" ]
|
||||
})
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels @project_id, @callback
|
||||
|
||||
it "should return an array of member ids with their privilege levels", ->
|
||||
@callback
|
||||
.calledWith(null, [
|
||||
{ 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" }
|
||||
{ id: "read-write-ref-2", privilegeLevel: "readAndWrite" }
|
||||
])
|
||||
.should.equal true
|
||||
|
||||
describe "with a missing project", ->
|
||||
beforeEach ->
|
||||
@Project.findOne = sinon.stub().yields(null, null)
|
||||
|
||||
it "should return a NotFoundError", (done) ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels @project_id, (error) ->
|
||||
error.should.be.instanceof Errors.NotFoundError
|
||||
done()
|
||||
|
||||
describe "getMemberIds", ->
|
||||
beforeEach ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub()
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels
|
||||
.withArgs(@project_id)
|
||||
.yields(null, [{id: "member-id-1"}, {id: "member-id-2"}])
|
||||
@CollaboratorHandler.getMemberIds @project_id, @callback
|
||||
|
||||
it "should return the ids", ->
|
||||
@callback
|
||||
.calledWith(null, ["member-id-1", "member-id-2"])
|
||||
.should.equal true
|
||||
|
||||
describe "getMembersWithPrivilegeLevels", ->
|
||||
beforeEach ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels = sinon.stub()
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels.withArgs(@project_id).yields(null, [
|
||||
{ id: "read-only-ref-1", privilegeLevel: "readOnly" }
|
||||
{ id: "read-only-ref-2", privilegeLevel: "readOnly" }
|
||||
{ id: "read-write-ref-1", privilegeLevel: "readAndWrite" }
|
||||
{ id: "read-write-ref-2", privilegeLevel: "readAndWrite" }
|
||||
])
|
||||
@UserGetter.getUser = sinon.stub()
|
||||
@UserGetter.getUser.withArgs("read-only-ref-1").yields(null, { _id: "read-only-ref-1" })
|
||||
@UserGetter.getUser.withArgs("read-only-ref-2").yields(null, { _id: "read-only-ref-2" })
|
||||
@UserGetter.getUser.withArgs("read-write-ref-1").yields(null, { _id: "read-write-ref-1" })
|
||||
@UserGetter.getUser.withArgs("read-write-ref-2").yields(null, { _id: "read-write-ref-2" })
|
||||
@CollaboratorHandler.getMembersWithPrivilegeLevels @project_id, @callback
|
||||
|
||||
it "should return an array of members with their privilege levels", ->
|
||||
@callback
|
||||
.calledWith(undefined, [
|
||||
{ user: { _id: "read-only-ref-1" }, privilegeLevel: "readOnly" }
|
||||
{ user: { _id: "read-only-ref-2" }, privilegeLevel: "readOnly" }
|
||||
{ user: { _id: "read-write-ref-1" }, privilegeLevel: "readAndWrite" }
|
||||
{ user: { _id: "read-write-ref-2" }, privilegeLevel: "readAndWrite" }
|
||||
])
|
||||
.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()
|
||||
|
||||
describe "when user is a member of the project", ->
|
||||
beforeEach ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels.withArgs(@project_id).yields(null, [
|
||||
{ id: "not-the-user", privilegeLevel: "readOnly" }
|
||||
{ id: @user_id, privilegeLevel: "readAndWrite" }
|
||||
])
|
||||
@CollaboratorHandler.isUserMemberOfProject @user_id, @project_id, @callback
|
||||
|
||||
it "should return true and the privilegeLevel", ->
|
||||
@callback
|
||||
.calledWith(null, true, "readAndWrite")
|
||||
.should.equal true
|
||||
|
||||
describe "when user is not a member of the project", ->
|
||||
beforeEach ->
|
||||
@CollaboratorHandler.getMemberIdsWithPrivilegeLevels.withArgs(@project_id).yields(null, [
|
||||
{ id: "not-the-user", privilegeLevel: "readOnly" }
|
||||
])
|
||||
@CollaboratorHandler.isUserMemberOfProject @user_id, @project_id, @callback
|
||||
|
||||
it "should return false", ->
|
||||
@callback
|
||||
.calledWith(null, false, null)
|
||||
.should.equal true
|
||||
|
||||
describe "getProjectsUserIsCollaboratorOf", ->
|
||||
beforeEach ->
|
||||
@fields = "mock fields"
|
||||
@Project.find = sinon.stub()
|
||||
@Project.find.withArgs({collaberator_refs:@user_id}, @fields).yields(null, ["mock-read-write-project-1", "mock-read-write-project-2"])
|
||||
@Project.find.withArgs({readOnly_refs:@user_id}, @fields).yields(null, ["mock-read-only-project-1", "mock-read-only-project-2"])
|
||||
@CollaboratorHandler.getProjectsUserIsCollaboratorOf @user_id, @fields, @callback
|
||||
|
||||
it "should call the callback with the projects", ->
|
||||
@callback
|
||||
.calledWith(null, ["mock-read-write-project-1", "mock-read-write-project-2"], ["mock-read-only-project-1", "mock-read-only-project-2"])
|
||||
.should.equal true
|
||||
|
||||
describe "removeUserFromProject", ->
|
||||
beforeEach ->
|
||||
|
|
|
@ -19,7 +19,7 @@ describe "ClsiManager", ->
|
|||
url: "https://clsipremium.example.com"
|
||||
"../../models/Project": Project: @Project = {}
|
||||
"../Project/ProjectEntityHandler": @ProjectEntityHandler = {}
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }
|
||||
"request": @request = {}
|
||||
@project_id = "project-id"
|
||||
@callback = sinon.stub()
|
||||
|
@ -218,9 +218,9 @@ describe "ClsiManager", ->
|
|||
@project.rootDoc_id = "not-valid"
|
||||
@ClsiManager._buildRequest @project, null, (@error, @request) =>
|
||||
done()
|
||||
|
||||
it "should return an error", ->
|
||||
expect(@error).to.exist
|
||||
|
||||
it "should set to main.tex", ->
|
||||
@request.compile.rootResourcePath.should.equal "main.tex"
|
||||
|
||||
describe "with the draft option", ->
|
||||
it "should add the draft option into the request", (done) ->
|
||||
|
|
|
@ -27,7 +27,7 @@ describe "CompileManager", ->
|
|||
Timer: class Timer
|
||||
done: sinon.stub()
|
||||
inc: sinon.stub()
|
||||
"logger-sharelatex": @logger = { log: sinon.stub() }
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), warn: sinon.stub() }
|
||||
@project_id = "mock-project-id-123"
|
||||
@user_id = "mock-user-id-123"
|
||||
@callback = sinon.stub()
|
||||
|
@ -90,15 +90,12 @@ describe "CompileManager", ->
|
|||
.should.equal true
|
||||
|
||||
describe "when the project has been recently compiled", ->
|
||||
beforeEach ->
|
||||
it "should return", (done)->
|
||||
@CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true)
|
||||
@CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, true)
|
||||
@CompileManager.compile @project_id, @user_id, {}, @callback
|
||||
|
||||
it "should return the callback with an error", ->
|
||||
@callback
|
||||
.calledWith(new Error("project was recently compiled so not continuing"))
|
||||
.should.equal true
|
||||
@CompileManager.compile @project_id, @user_id, {}, (err, status)->
|
||||
status.should.equal "too-recently-compiled"
|
||||
done()
|
||||
|
||||
describe "should check the rate limit", ->
|
||||
it "should return", (done)->
|
||||
|
|
|
@ -10,12 +10,13 @@ describe "EditorHttpController", ->
|
|||
'../Project/ProjectDeleter' : @ProjectDeleter = {}
|
||||
'../Project/ProjectGetter' : @ProjectGetter = {}
|
||||
'../User/UserGetter' : @UserGetter = {}
|
||||
"../Security/AuthorizationManager": @AuthorizationManager = {}
|
||||
"../Authorization/AuthorizationManager": @AuthorizationManager = {}
|
||||
'../Project/ProjectEditorHandler': @ProjectEditorHandler = {}
|
||||
"./EditorRealTimeController": @EditorRealTimeController = {}
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
"./EditorController": @EditorController = {}
|
||||
'../../infrastructure/Metrics': @Metrics = {inc: sinon.stub()}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
|
||||
@project_id = "mock-project-id"
|
||||
@doc_id = "mock-doc-id"
|
||||
|
@ -76,6 +77,17 @@ describe "EditorHttpController", ->
|
|||
@ProjectDeleter.unmarkAsDeletedByExternalSource
|
||||
.calledWith(@project_id)
|
||||
.should.equal true
|
||||
|
||||
describe "with an anonymous user", ->
|
||||
beforeEach ->
|
||||
@req.query =
|
||||
user_id: "anonymous-user"
|
||||
@EditorHttpController.joinProject @req, @res
|
||||
|
||||
it "should pass the user id as null", ->
|
||||
@EditorHttpController._buildJoinProjectView
|
||||
.calledWith(@project_id, null)
|
||||
.should.equal true
|
||||
|
||||
describe "_buildJoinProjectView", ->
|
||||
beforeEach ->
|
||||
|
@ -85,19 +97,20 @@ describe "EditorHttpController", ->
|
|||
@user =
|
||||
_id: @user_id = "user-id"
|
||||
projects: {}
|
||||
@members = ["members", "mock"]
|
||||
@projectModelView =
|
||||
_id: @project_id
|
||||
owner:{_id:"something"}
|
||||
view: true
|
||||
@ProjectEditorHandler.buildProjectModelView = sinon.stub().returns(@projectModelView)
|
||||
@ProjectGetter.getProjectWithoutDocLines = sinon.stub().callsArgWith(1, null, @project)
|
||||
@ProjectGetter.populateProjectWithUsers = sinon.stub().callsArgWith(1, null, @project)
|
||||
@CollaboratorsHandler.getMembersWithPrivilegeLevels = sinon.stub().callsArgWith(1, null, @members)
|
||||
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user)
|
||||
|
||||
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", ->
|
||||
|
@ -105,8 +118,8 @@ describe "EditorHttpController", ->
|
|||
.calledWith(@project_id)
|
||||
.should.equal true
|
||||
|
||||
it "should populate the user references in the project", ->
|
||||
@ProjectGetter.populateProjectWithUsers
|
||||
it "should get the list of users in the project", ->
|
||||
@CollaboratorsHandler.getMembersWithPrivilegeLevels
|
||||
.calledWith(@project)
|
||||
.should.equal true
|
||||
|
||||
|
@ -117,7 +130,7 @@ describe "EditorHttpController", ->
|
|||
|
||||
it "should check the privilege level", ->
|
||||
@AuthorizationManager.getPrivilegeLevelForProject
|
||||
.calledWith(@project, @user)
|
||||
.calledWith(@user_id, @project_id)
|
||||
.should.equal true
|
||||
|
||||
it "should return the project model view, privilege level and protocol version", ->
|
||||
|
@ -126,7 +139,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", ->
|
||||
|
|
|
@ -35,13 +35,10 @@ describe "ProjectController", ->
|
|||
getAllTags: sinon.stub()
|
||||
@NotificationsHandler =
|
||||
getUserNotifications: sinon.stub()
|
||||
@ProjectModel =
|
||||
findAllUsersProjects: sinon.stub()
|
||||
findPopulatedById: sinon.stub()
|
||||
@UserModel =
|
||||
findById: sinon.stub()
|
||||
@SecurityManager =
|
||||
userCanAccessProject:sinon.stub()
|
||||
@AuthorizationManager =
|
||||
getPrivilegeLevelForProject:sinon.stub()
|
||||
@EditorController =
|
||||
renameProject:sinon.stub()
|
||||
@InactiveProjectManager =
|
||||
|
@ -50,6 +47,9 @@ describe "ProjectController", ->
|
|||
markAsOpened: sinon.stub()
|
||||
@ReferencesSearchHandler =
|
||||
indexProjectReferences: sinon.stub()
|
||||
@ProjectGetter =
|
||||
findAllUsersProjects: sinon.stub()
|
||||
getProject: sinon.stub()
|
||||
@ProjectController = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex":@settings
|
||||
"logger-sharelatex":
|
||||
|
@ -67,12 +67,12 @@ describe "ProjectController", ->
|
|||
"../Subscription/LimitationsManager": @LimitationsManager
|
||||
"../Tags/TagsHandler":@TagsHandler
|
||||
"../Notifications/NotificationsHandler":@NotificationsHandler
|
||||
'../../models/Project': Project:@ProjectModel
|
||||
"../../models/User":User:@UserModel
|
||||
"../../managers/SecurityManager":@SecurityManager
|
||||
"../Authorization/AuthorizationManager":@AuthorizationManager
|
||||
"../InactiveData/InactiveProjectManager":@InactiveProjectManager
|
||||
"./ProjectUpdateHandler":@ProjectUpdateHandler
|
||||
"../ReferencesSearch/ReferencesSearchHandler": @ReferencesSearchHandler
|
||||
"./ProjectGetter": @ProjectGetter
|
||||
|
||||
@user =
|
||||
_id:"!£123213kjljkl"
|
||||
|
@ -128,18 +128,6 @@ describe "ProjectController", ->
|
|||
done()
|
||||
@ProjectController.updateProjectSettings @req, @res
|
||||
|
||||
it "should update the public access level", (done) ->
|
||||
@EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
publicAccessLevel: @publicAccessLevel = "readonly"
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setPublicAccessLevel
|
||||
.calledWith(@project_id, @publicAccessLevel)
|
||||
.should.equal true
|
||||
code.should.equal 204
|
||||
done()
|
||||
@ProjectController.updateProjectSettings @req, @res
|
||||
|
||||
it "should update the root doc", (done) ->
|
||||
@EditorController.setRootDoc = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
|
@ -151,6 +139,19 @@ describe "ProjectController", ->
|
|||
code.should.equal 204
|
||||
done()
|
||||
@ProjectController.updateProjectSettings @req, @res
|
||||
|
||||
describe "updateProjectAdminSettings", ->
|
||||
it "should update the public access level", (done) ->
|
||||
@EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
publicAccessLevel: @publicAccessLevel = "readonly"
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setPublicAccessLevel
|
||||
.calledWith(@project_id, @publicAccessLevel)
|
||||
.should.equal true
|
||||
code.should.equal 204
|
||||
done()
|
||||
@ProjectController.updateProjectAdminSettings @req, @res
|
||||
|
||||
describe "deleteProject", ->
|
||||
it "should tell the project deleter to archive when forever=false", (done)->
|
||||
|
@ -224,7 +225,7 @@ describe "ProjectController", ->
|
|||
@LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, false)
|
||||
@TagsHandler.getAllTags.callsArgWith(1, null, @tags, {})
|
||||
@NotificationsHandler.getUserNotifications = sinon.stub().callsArgWith(1, null, @notifications, {})
|
||||
@ProjectModel.findAllUsersProjects.callsArgWith(2, null, @projects, @collabertions, @readOnly)
|
||||
@ProjectGetter.findAllUsersProjects.callsArgWith(2, null, @projects, @collabertions, @readOnly)
|
||||
|
||||
it "should render the project/list page", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
|
@ -297,10 +298,10 @@ describe "ProjectController", ->
|
|||
fontSize:"massive"
|
||||
theme:"sexy"
|
||||
email: "bob@bob.com"
|
||||
@ProjectModel.findPopulatedById.callsArgWith 1, null, @project
|
||||
@ProjectGetter.getProject.callsArgWith 2, null, @project
|
||||
@UserModel.findById.callsArgWith(1, null, @user)
|
||||
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
||||
@SecurityManager.userCanAccessProject.callsArgWith 2, true, "owner"
|
||||
@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, "owner"
|
||||
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||
@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
|
||||
@ProjectUpdateHandler.markAsOpened.callsArgWith(1)
|
||||
|
@ -312,12 +313,6 @@ describe "ProjectController", ->
|
|||
done()
|
||||
@ProjectController.loadEditor @req, @res
|
||||
|
||||
it "should add the project onto the opts", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
opts.project.should.equal @project
|
||||
done()
|
||||
@ProjectController.loadEditor @req, @res
|
||||
|
||||
it "should add user", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
opts.user.email.should.equal @user.email
|
||||
|
@ -339,7 +334,7 @@ describe "ProjectController", ->
|
|||
@ProjectController.loadEditor @req, @res
|
||||
|
||||
it "should not render the page if the project can not be accessed", (done)->
|
||||
@SecurityManager.userCanAccessProject = sinon.stub().callsArgWith 2, false
|
||||
@AuthorizationManager.getPrivilegeLevelForProject = sinon.stub().callsArgWith 2, null, null
|
||||
@res.sendStatus = (resCode, opts)=>
|
||||
resCode.should.equal 401
|
||||
done()
|
||||
|
|
|
@ -33,6 +33,7 @@ describe 'ProjectDeleter', ->
|
|||
'../DocumentUpdater/DocumentUpdaterHandler': @documentUpdaterHandler
|
||||
"../Tags/TagsHandler":@TagsHandler
|
||||
"../FileStore/FileStoreHandler": @FileStoreHandler = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
"./ProjectGetter": @ProjectGetter
|
||||
'logger-sharelatex':
|
||||
log:->
|
||||
|
@ -92,6 +93,8 @@ describe 'ProjectDeleter', ->
|
|||
|
||||
describe "archiveProject", ->
|
||||
beforeEach ->
|
||||
@CollaboratorsHandler.getMemberIds = sinon.stub()
|
||||
@CollaboratorsHandler.getMemberIds.withArgs(@project_id).yields(null, ["member-id-1", "member-id-2"])
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @project)
|
||||
@Project.update.callsArgWith(2)
|
||||
|
||||
|
@ -111,12 +114,8 @@ describe 'ProjectDeleter', ->
|
|||
|
||||
it "should removeProjectFromAllTags", (done)->
|
||||
@deleter.archiveProject @project_id, =>
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith(@project.owner_ref, @project_id).should.equal true
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith(@project.collaberator_refs[0], @project_id).should.equal true
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith(@project.collaberator_refs[1], @project_id).should.equal true
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith(@project.readOnly_refs[0], @project_id).should.equal true
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith(@project.readOnly_refs[1], @project_id).should.equal true
|
||||
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith("member-id-1", @project_id).should.equal true
|
||||
@TagsHandler.removeProjectFromAllTags.calledWith("member-id-2", @project_id).should.equal true
|
||||
done()
|
||||
|
||||
describe "restoreProject", ->
|
||||
|
|
|
@ -38,33 +38,41 @@ describe "ProjectEditorHandler", ->
|
|||
folders : []
|
||||
}]
|
||||
}]
|
||||
owner_ref :
|
||||
_id: "owner-id"
|
||||
first_name : "Owner"
|
||||
last_name : "ShareLaTeX"
|
||||
email : "owner@sharelatex.com"
|
||||
readOnly_refs: [{
|
||||
_id: "read-only-id"
|
||||
first_name : "Read"
|
||||
last_name : "Only"
|
||||
email : "read-only@sharelatex.com"
|
||||
}]
|
||||
collaberator_refs: [{
|
||||
_id: "read-write-id"
|
||||
first_name : "Read"
|
||||
last_name : "Write"
|
||||
email : "read-write@sharelatex.com"
|
||||
}]
|
||||
deletedDocs: [{
|
||||
_id: "deleted-doc-id"
|
||||
name: "main.tex"
|
||||
}]
|
||||
@members = [{
|
||||
user: @owner = {
|
||||
_id: "owner-id"
|
||||
first_name : "Owner"
|
||||
last_name : "ShareLaTeX"
|
||||
email : "owner@sharelatex.com"
|
||||
},
|
||||
privilegeLevel: "owner"
|
||||
},{
|
||||
user: {
|
||||
_id: "read-only-id"
|
||||
first_name : "Read"
|
||||
last_name : "Only"
|
||||
email : "read-only@sharelatex.com"
|
||||
},
|
||||
privilegeLevel: "readOnly"
|
||||
},{
|
||||
user: {
|
||||
_id: "read-write-id"
|
||||
first_name : "Read"
|
||||
last_name : "Write"
|
||||
email : "read-write@sharelatex.com"
|
||||
},
|
||||
privilegeLevel: "readAndWrite"
|
||||
}]
|
||||
@handler = SandboxedModule.require modulePath
|
||||
|
||||
describe "buildProjectModelView", ->
|
||||
describe "with owner and members included", ->
|
||||
beforeEach ->
|
||||
@result = @handler.buildProjectModelView @project
|
||||
@result = @handler.buildProjectModelView @project, @members
|
||||
|
||||
it "should include the id", ->
|
||||
should.exist @result._id
|
||||
|
@ -140,41 +148,30 @@ describe "ProjectEditorHandler", ->
|
|||
|
||||
it "should set the deletedByExternalDataSource flag to false when it is not there", ->
|
||||
delete @project.deletedByExternalDataSource
|
||||
result = @handler.buildProjectModelView @project
|
||||
result = @handler.buildProjectModelView @project, @members
|
||||
result.deletedByExternalDataSource.should.equal false
|
||||
|
||||
it "should set the deletedByExternalDataSource flag to false when it is false", ->
|
||||
result = @handler.buildProjectModelView @project
|
||||
result = @handler.buildProjectModelView @project, @members
|
||||
result.deletedByExternalDataSource.should.equal false
|
||||
|
||||
it "should set the deletedByExternalDataSource flag to true when it is true", ->
|
||||
@project.deletedByExternalDataSource = true
|
||||
result = @handler.buildProjectModelView @project
|
||||
result = @handler.buildProjectModelView @project, @members
|
||||
result.deletedByExternalDataSource.should.equal true
|
||||
|
||||
describe "features", ->
|
||||
beforeEach ->
|
||||
@project.owner_ref.features =
|
||||
@owner.features =
|
||||
versioning: true
|
||||
collaborators: 3
|
||||
compileGroup:"priority"
|
||||
compileTimeout: 96
|
||||
@result = @handler.buildProjectModelView @project
|
||||
@result = @handler.buildProjectModelView @project, @members
|
||||
|
||||
it "should copy the owner features to the project", ->
|
||||
@result.features.versioning.should.equal @project.owner_ref.features.versioning
|
||||
@result.features.collaborators.should.equal @project.owner_ref.features.collaborators
|
||||
@result.features.compileGroup.should.equal @project.owner_ref.features.compileGroup
|
||||
@result.features.compileTimeout.should.equal @project.owner_ref.features.compileTimeout
|
||||
@result.features.versioning.should.equal @owner.features.versioning
|
||||
@result.features.collaborators.should.equal @owner.features.collaborators
|
||||
@result.features.compileGroup.should.equal @owner.features.compileGroup
|
||||
@result.features.compileTimeout.should.equal @owner.features.compileTimeout
|
||||
|
||||
|
||||
describe "without owners and members", ->
|
||||
beforeEach ->
|
||||
@result = @handler.buildProjectModelView @project, includeUsers: false
|
||||
|
||||
it "should not include the owner", ->
|
||||
should.not.exist @result.owner
|
||||
|
||||
it "should not include the members", ->
|
||||
should.not.exist @result.members
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ describe "ProjectGetter", ->
|
|||
projects: {}
|
||||
users: {}
|
||||
ObjectId: ObjectId
|
||||
"../../models/Project": Project: @Project = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
"logger-sharelatex":
|
||||
err:->
|
||||
log:->
|
||||
|
@ -134,56 +136,16 @@ describe "ProjectGetter", ->
|
|||
@db.projects.find = sinon.stub().callsArgWith(2, null, [@project])
|
||||
|
||||
|
||||
it "should call find with the project id when string id is passed", (done)->
|
||||
@ProjectGetter.getProject @project_id, (err, project)=>
|
||||
@db.projects.find.calledWith(_id: ObjectId(@project_id)).should.equal true
|
||||
assert.deepEqual @project, project
|
||||
done()
|
||||
|
||||
it "should call find with the project id when object id is passed", (done)->
|
||||
@ProjectGetter.getProject ObjectId(@project_id), (err, project)=>
|
||||
@db.projects.find.calledWith(_id: ObjectId(@project_id)).should.equal true
|
||||
assert.deepEqual @project, project
|
||||
done()
|
||||
|
||||
it "should call the db when a mongoose objectid is used", (done)->
|
||||
mongooseID = require('mongoose').Types.ObjectId(@project_id)
|
||||
@ProjectGetter.getProject mongooseID, (err, project)=>
|
||||
@db.projects.find.calledWith(_id: ObjectId(@project_id)).should.equal true
|
||||
assert.deepEqual @project, project
|
||||
done()
|
||||
|
||||
describe "populateProjectWithUsers", ->
|
||||
describe "findAllUsersProjects", ->
|
||||
beforeEach ->
|
||||
@users = []
|
||||
@user_lookup = {}
|
||||
for i in [0..4]
|
||||
@users[i] = _id: ObjectId.createPk()
|
||||
@user_lookup[@users[i]._id.toString()] = @users[i]
|
||||
@project =
|
||||
_id: ObjectId.createPk()
|
||||
owner_ref: @users[0]._id
|
||||
readOnly_refs: [@users[1]._id, @users[2]._id]
|
||||
collaberator_refs: [@users[3]._id, @users[4]._id]
|
||||
@db.users.find = (query, callback) =>
|
||||
callback null, [@user_lookup[query._id.toString()]]
|
||||
sinon.spy @db.users, "find"
|
||||
@ProjectGetter.populateProjectWithUsers @project, (err, project)=>
|
||||
@callback err, project
|
||||
|
||||
it "should look up each user", ->
|
||||
for user in @users
|
||||
@db.users.find.calledWith(_id: user._id).should.equal true
|
||||
|
||||
it "should set the owner_ref to the owner", ->
|
||||
@project.owner_ref.should.equal @users[0]
|
||||
|
||||
it "should set the readOnly_refs to the read only users", ->
|
||||
expect(@project.readOnly_refs).to.deep.equal [@users[1], @users[2]]
|
||||
|
||||
it "should set the collaberator_refs to the collaborators", ->
|
||||
expect(@project.collaberator_refs).to.deep.equal [@users[3], @users[4]]
|
||||
|
||||
it "should call the callback", ->
|
||||
assert.deepEqual @callback.args[0][1], @project
|
||||
|
||||
@fields = {"mock": "fields"}
|
||||
@Project.find = sinon.stub()
|
||||
@Project.find.withArgs({owner_ref: @user_id}, @fields).yields(null, ["mock-owned-projects"])
|
||||
@CollaboratorsHandler.getProjectsUserIsCollaboratorOf = sinon.stub()
|
||||
@CollaboratorsHandler.getProjectsUserIsCollaboratorOf.withArgs(@user_id, @fields).yields(null, ["mock-rw-projects"], ["mock-ro-projects"])
|
||||
@ProjectGetter.findAllUsersProjects @user_id, @fields, @callback
|
||||
|
||||
it "should call the callback with all the projects", ->
|
||||
@callback
|
||||
.calledWith(null, ["mock-owned-projects"], ["mock-rw-projects"], ["mock-ro-projects"])
|
||||
.should.equal true
|
||||
|
|
|
@ -30,7 +30,7 @@ project.rootFolder[0] = rootFolder
|
|||
project.rootDoc_id = rootDoc._id
|
||||
|
||||
|
||||
describe 'ProjectLocatorTests', ->
|
||||
describe 'ProjectLocator', ->
|
||||
|
||||
beforeEach ->
|
||||
Project.getProject = (project_id, fields, callback)=>
|
||||
|
@ -301,7 +301,7 @@ describe 'ProjectLocatorTests', ->
|
|||
user_id = "123jojoidns"
|
||||
stubbedProject = {name:"findThis"}
|
||||
projects = [{name:"notThis"}, {name:"wellll"}, stubbedProject, {name:"Noooo"}]
|
||||
Project.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects)
|
||||
@ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects)
|
||||
@locator.findUsersProjectByName user_id, stubbedProject.name.toLowerCase(), (err, project)->
|
||||
project.should.equal stubbedProject
|
||||
done()
|
||||
|
@ -310,7 +310,7 @@ describe 'ProjectLocatorTests', ->
|
|||
user_id = "123jojoidns"
|
||||
stubbedProject = {name:"findThis", _id:12331321}
|
||||
projects = [{name:"notThis"}, {name:"wellll"}, {name:"findThis",archived:true}, stubbedProject, {name:"findThis",archived:true}, {name:"Noooo"}]
|
||||
Project.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects)
|
||||
@ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects)
|
||||
@locator.findUsersProjectByName user_id, stubbedProject.name.toLowerCase(), (err, project)->
|
||||
project._id.should.equal stubbedProject._id
|
||||
done()
|
||||
|
@ -319,7 +319,7 @@ describe 'ProjectLocatorTests', ->
|
|||
user_id = "123jojoidns"
|
||||
stubbedProject = {name:"findThis"}
|
||||
projects = [{name:"notThis"}, {name:"wellll"}, {name:"Noooo"}]
|
||||
Project.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects, [stubbedProject])
|
||||
@ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, projects, [stubbedProject])
|
||||
@locator.findUsersProjectByName user_id, stubbedProject.name.toLowerCase(), (err, project)->
|
||||
project.should.equal stubbedProject
|
||||
done()
|
||||
|
|
|
@ -39,10 +39,11 @@ describe 'ReferencesHandler', ->
|
|||
get: sinon.stub()
|
||||
post: sinon.stub()
|
||||
}
|
||||
'../../models/Project': {
|
||||
Project: @Project = {
|
||||
findPopulatedById: sinon.stub().callsArgWith(1, null, @fakeProject)
|
||||
}
|
||||
'../Project/ProjectGetter': @ProjectGetter = {
|
||||
getProject: sinon.stub().callsArgWith(2, null, @fakeProject)
|
||||
}
|
||||
'../User/UserGetter': @UserGetter = {
|
||||
getUser: sinon.stub()
|
||||
}
|
||||
'../DocumentUpdater/DocumentUpdaterHandler': @DocumentUpdaterHandler = {
|
||||
flushDocToMongo: sinon.stub().callsArgWith(2, null)
|
||||
|
@ -70,10 +71,10 @@ describe 'ReferencesHandler', ->
|
|||
@handler._findBibDocIds.callCount.should.equal 0
|
||||
done()
|
||||
|
||||
it 'should call Project.findPopulatedById', (done) ->
|
||||
it 'should call ProjectGetter.getProject', (done) ->
|
||||
@call (err, data) =>
|
||||
@Project.findPopulatedById.callCount.should.equal 1
|
||||
@Project.findPopulatedById.calledWith(@projectId).should.equal true
|
||||
@ProjectGetter.getProject.callCount.should.equal 1
|
||||
@ProjectGetter.getProject.calledWith(@projectId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should not call _findBibDocIds', (done) ->
|
||||
|
@ -109,10 +110,10 @@ describe 'ReferencesHandler', ->
|
|||
expect(data).to.equal @fakeResponseData
|
||||
done()
|
||||
|
||||
describe 'when Project.findPopulatedById produces an error', ->
|
||||
describe 'when ProjectGetter.getProject produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, new Error('woops'))
|
||||
@ProjectGetter.getProject.callsArgWith(2, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err, data) =>
|
||||
|
@ -129,7 +130,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when _isFullIndex produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
|
@ -147,7 +148,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when flushDocToMongo produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, false)
|
||||
@DocumentUpdaterHandler.flushDocToMongo.callsArgWith(2, new Error('woops'))
|
||||
|
||||
|
@ -167,7 +168,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when request produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, null, false)
|
||||
@DocumentUpdaterHandler.flushDocToMongo.callsArgWith(2, null)
|
||||
@request.post.callsArgWith(1, new Error('woops'))
|
||||
|
@ -182,7 +183,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when request responds with error status', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, null, false)
|
||||
@request.post.callsArgWith(1, null, {statusCode: 500}, null)
|
||||
|
||||
|
@ -234,10 +235,10 @@ describe 'ReferencesHandler', ->
|
|||
expect(data).to.equal @fakeResponseData
|
||||
done()
|
||||
|
||||
describe 'when Project.findPopulatedById produces an error', ->
|
||||
describe 'when ProjectGetter.getProject produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, new Error('woops'))
|
||||
@ProjectGetter.getProject.callsArgWith(2, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err, data) =>
|
||||
|
@ -254,7 +255,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when _isFullIndex produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
|
@ -272,7 +273,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'when flushDocToMongo produces an error', ->
|
||||
|
||||
beforeEach ->
|
||||
@Project.findPopulatedById.callsArgWith(1, null, @fakeProject)
|
||||
@ProjectGetter.getProject.callsArgWith(2, null, @fakeProject)
|
||||
@handler._isFullIndex.callsArgWith(1, false)
|
||||
@DocumentUpdaterHandler.flushDocToMongo.callsArgWith(2, new Error('woops'))
|
||||
|
||||
|
@ -312,16 +313,19 @@ describe 'ReferencesHandler', ->
|
|||
|
||||
beforeEach ->
|
||||
@fakeProject =
|
||||
owner_ref:
|
||||
features:
|
||||
references: false
|
||||
owner_ref: @owner_ref = "owner-ref-123"
|
||||
@owner =
|
||||
features:
|
||||
references: false
|
||||
@UserGetter.getUser = sinon.stub()
|
||||
@UserGetter.getUser.withArgs(@owner_ref, {features: true}).yields(null, @owner)
|
||||
@call = (callback) =>
|
||||
@handler._isFullIndex @fakeProject, callback
|
||||
|
||||
describe 'with references feature on', ->
|
||||
|
||||
beforeEach ->
|
||||
@fakeProject.owner_ref.features.references = true
|
||||
@owner.features.references = true
|
||||
|
||||
it 'should return true', ->
|
||||
@call (err, isFullIndex) =>
|
||||
|
@ -331,7 +335,7 @@ describe 'ReferencesHandler', ->
|
|||
describe 'with references feature off', ->
|
||||
|
||||
beforeEach ->
|
||||
@fakeProject.owner_ref.features.references = false
|
||||
@owner.features.references = false
|
||||
|
||||
it 'should return false', ->
|
||||
@call (err, isFullIndex) =>
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
SandboxedModule = require('sandboxed-module')
|
||||
sinon = require('sinon')
|
||||
require('chai').should()
|
||||
modulePath = require('path').join __dirname, '../../../../app/js/Features/Security/AuthorizationManager'
|
||||
MockClient = require "../helpers/MockClient"
|
||||
|
||||
describe "AuthorizationManager", ->
|
||||
beforeEach ->
|
||||
@client = new MockClient()
|
||||
@AuthorizationManager = SandboxedModule.require modulePath, requires:
|
||||
'../../managers/SecurityManager':{}
|
||||
|
||||
describe "ensureClientCanViewProject", ->
|
||||
beforeEach ->
|
||||
@client.set("project_id", "project-id")
|
||||
|
||||
it "should let the request through for a readOnly privilege", (done) ->
|
||||
@client.set("privilege_level", "readOnly")
|
||||
@AuthorizationManager.ensureClientCanViewProject @client, done
|
||||
|
||||
it "should let the request through for a readAndWrite privilege", (done) ->
|
||||
@client.set("privilege_level", "readAndWrite")
|
||||
@AuthorizationManager.ensureClientCanViewProject @client, done
|
||||
|
||||
it "should let the request through for a owner privilege", (done) ->
|
||||
@client.set("privilege_level", "owner")
|
||||
@AuthorizationManager.ensureClientCanViewProject @client, done
|
||||
|
||||
it "should ignore an empty privilege", ->
|
||||
@AuthorizationManager.ensureClientCanViewProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
describe "ensureClientCanEditProject", ->
|
||||
beforeEach ->
|
||||
@client.set("project_id", "project-id")
|
||||
|
||||
it "should ignore a readOnly privilege", ->
|
||||
@client.set("privilege_level", "readOnly")
|
||||
@AuthorizationManager.ensureClientCanEditProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
it "should let the request through for a readAndWrite privilege", (done) ->
|
||||
@client.set("privilege_level", "readAndWrite")
|
||||
@AuthorizationManager.ensureClientCanEditProject @client, done
|
||||
|
||||
it "should let the request through for a owner privilege", (done) ->
|
||||
@client.set("privilege_level", "owner")
|
||||
@AuthorizationManager.ensureClientCanEditProject @client, done
|
||||
|
||||
it "should ignore an empty privilege", ->
|
||||
@AuthorizationManager.ensureClientCanEditProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
describe "ensureClientCanAdminProject", ->
|
||||
beforeEach ->
|
||||
@client.set("project_id", "project-id")
|
||||
|
||||
it "should ignore a readOnly privilege", ->
|
||||
@client.set("privilege_level", "readOnly")
|
||||
@AuthorizationManager.ensureClientCanAdminProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
it "should ignore a readAndWrite privilege", ->
|
||||
@client.set("privilege_level", "readAndWrite")
|
||||
@AuthorizationManager.ensureClientCanAdminProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
it "should let the request through for a owner privilege", (done) ->
|
||||
@client.set("privilege_level", "owner")
|
||||
@AuthorizationManager.ensureClientCanAdminProject @client, done
|
||||
|
||||
it "should ignore an empty privilege", ->
|
||||
@AuthorizationManager.ensureClientCanAdminProject @client, () ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
describe "ensureClientHasPrivilegeLevelForProject", ->
|
||||
it "should ignore callback if privilege_level is not set", ->
|
||||
@client.set("project_id", "project-id")
|
||||
@AuthorizationManager.ensureClientHasPrivilegeLevelForProject @client,
|
||||
["owner"], (error, project_id) ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
it "should ignore callback if project_id is not set", ->
|
||||
@client.set("privilege_level", "owner")
|
||||
@AuthorizationManager.ensureClientHasPrivilegeLevelForProject @client,
|
||||
["owner"], (error, project_id) ->
|
||||
throw new Error("Should not be called")
|
||||
|
||||
it "should return the project_id", (done) ->
|
||||
@client.set("privilege_level", "owner")
|
||||
@client.set("project_id", "project-id-123")
|
||||
@AuthorizationManager.ensureClientHasPrivilegeLevelForProject @client,
|
||||
["owner"], (error, project_id) ->
|
||||
project_id.should.equal "project-id-123"
|
||||
done()
|
||||
|
|
@ -29,6 +29,7 @@ describe "LimitationsManager", ->
|
|||
'../../models/User' : User: @User
|
||||
'./SubscriptionLocator':@SubscriptionLocator
|
||||
'settings-sharelatex' : @Settings = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
'logger-sharelatex':log:->
|
||||
|
||||
describe "allowedNumberOfCollaboratorsInProject", ->
|
||||
|
@ -51,22 +52,10 @@ describe "LimitationsManager", ->
|
|||
|
||||
it "should return the number of collaborators the user is allowed", ->
|
||||
@callback.calledWith(null, @user.features.collaborators).should.equal true
|
||||
|
||||
describe "currentNumberOfCollaboratorsInProject", ->
|
||||
beforeEach ->
|
||||
@project.collaberator_refs = ["one", "two"]
|
||||
@project.readOnly_refs = ["three"]
|
||||
@callback = sinon.stub()
|
||||
@LimitationsManager.currentNumberOfCollaboratorsInProject(@project_id, @callback)
|
||||
|
||||
it "should return the total number of collaborators", ->
|
||||
@callback.calledWith(null, 3).should.equal true
|
||||
|
||||
describe "canAddXCollaborators", ->
|
||||
beforeEach ->
|
||||
sinon.stub @LimitationsManager,
|
||||
"currentNumberOfCollaboratorsInProject",
|
||||
(project_id, callback) => callback(null, @current_number)
|
||||
@CollaboratorsHandler.getCollaboratorCount = (project_id, callback) => callback(null, @current_number)
|
||||
sinon.stub @LimitationsManager,
|
||||
"allowedNumberOfCollaboratorsInProject",
|
||||
(project_id, callback) => callback(null, @allowed_number)
|
||||
|
|
|
@ -23,8 +23,8 @@ describe "SubscriptionController sanboxed", ->
|
|||
@user = {email:"tom@yahoo.com"}
|
||||
@activeRecurlySubscription = mockSubscriptions["subscription-123-active"]
|
||||
|
||||
@SecurityManager =
|
||||
getCurrentUser: sinon.stub().callsArgWith(1, null, @user)
|
||||
@AuthenticationController =
|
||||
getLoggedInUser: sinon.stub().callsArgWith(1, null, @user)
|
||||
@SubscriptionHandler =
|
||||
createSubscription: sinon.stub().callsArgWith(3)
|
||||
updateSubscription: sinon.stub().callsArgWith(3)
|
||||
|
@ -61,7 +61,7 @@ describe "SubscriptionController sanboxed", ->
|
|||
@SubscriptionDomainHandler =
|
||||
getDomainLicencePage:sinon.stub()
|
||||
@SubscriptionController = SandboxedModule.require modulePath, requires:
|
||||
'../../managers/SecurityManager': @SecurityManager
|
||||
'../Authentication/AuthenticationController': @AuthenticationController
|
||||
'./SubscriptionHandler': @SubscriptionHandler
|
||||
"./PlansLocator": @PlansLocator
|
||||
'./SubscriptionViewModelBuilder': @SubscriptionViewModelBuilder
|
||||
|
|
|
@ -20,7 +20,10 @@ filestoreUrl = "filestore.sharelatex.com"
|
|||
describe 'TpdsUpdateSender', ->
|
||||
beforeEach ->
|
||||
@requestQueuer = (queue, meth, opts, callback)->
|
||||
project = {owner_ref:user_id,readOnly_refs:[read_only_ref_1], collaberator_refs:[collaberator_ref_1]}
|
||||
project = {owner_ref:user_id}
|
||||
member_ids = [collaberator_ref_1, read_only_ref_1, user_id]
|
||||
@CollaboratorsHandler =
|
||||
getMemberIds: sinon.stub().yields(null, member_ids)
|
||||
@Project = findById:sinon.stub().callsArgWith(2, null, project)
|
||||
@docstoreUrl = "docstore.sharelatex.env"
|
||||
@request = sinon.stub().returns(pipe:->)
|
||||
|
@ -38,6 +41,7 @@ describe 'TpdsUpdateSender', ->
|
|||
"logger-sharelatex":{log:->}
|
||||
'../../models/Project': Project:@Project
|
||||
'request':@request
|
||||
'../Collaborators/CollaboratorsHandler': @CollaboratorsHandler
|
||||
"../../infrastructure/Metrics":
|
||||
inc:->
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ describe "ArchiveManager", ->
|
|||
describe "successfully", ->
|
||||
beforeEach (done) ->
|
||||
@ArchiveManager.extractZipArchive @source, @destination, done
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should run unzip", ->
|
||||
@child.spawn.calledWithExactly("unzip", [@source, "-d", @destination]).should.equal true
|
||||
|
@ -56,7 +56,7 @@ describe "ArchiveManager", ->
|
|||
@callback(error)
|
||||
done()
|
||||
@process.stderr.emit "data", "Something went wrong"
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should return the callback with an error", ->
|
||||
@callback.calledWithExactly(new Error("Something went wrong")).should.equal true
|
||||
|
@ -99,35 +99,35 @@ describe "ArchiveManager", ->
|
|||
isTooLarge.should.equal false
|
||||
done()
|
||||
@process.stdout.emit "data", @output("109042")
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should return true with large bytes", (done)->
|
||||
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
|
||||
isTooLarge.should.equal true
|
||||
done()
|
||||
@process.stdout.emit "data", @output("1090000000000000042")
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should return error on no data", (done)->
|
||||
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
|
||||
expect(error).to.exist
|
||||
done()
|
||||
@process.stdout.emit "data", ""
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should return error if it didn't get a number", (done)->
|
||||
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
|
||||
expect(error).to.exist
|
||||
done()
|
||||
@process.stdout.emit "data", @output("total_size_string")
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
it "should return error if the is only a bit of data", (done)->
|
||||
@ArchiveManager._isZipTooLarge @source, (error, isTooLarge) =>
|
||||
expect(error).to.exist
|
||||
done()
|
||||
@process.stdout.emit "data", " Length Date Time Name \n--------"
|
||||
@process.emit "exit"
|
||||
@process.emit "close"
|
||||
|
||||
describe "findTopLevelDirectory", ->
|
||||
beforeEach ->
|
||||
|
|
306
services/web/test/acceptance/coffee/AuthorizationTests.coffee
Normal file
306
services/web/test/acceptance/coffee/AuthorizationTests.coffee
Normal file
|
@ -0,0 +1,306 @@
|
|||
expect = require("chai").expect
|
||||
async = require("async")
|
||||
User = require "./helpers/User"
|
||||
request = require "./helpers/request"
|
||||
settings = require "settings-sharelatex"
|
||||
|
||||
try_read_access = (user, project_id, test, callback) ->
|
||||
async.series [
|
||||
(cb) ->
|
||||
user.request.get "/project/#{project_id}", (error, response, body) ->
|
||||
return cb(error) if error?
|
||||
test(response, body)
|
||||
cb()
|
||||
(cb) ->
|
||||
user.request.get "/project/#{project_id}/download/zip", (error, response, body) ->
|
||||
return cb(error) if error?
|
||||
test(response, body)
|
||||
cb()
|
||||
], callback
|
||||
|
||||
try_settings_write_access = (user, project_id, test, callback) ->
|
||||
async.series [
|
||||
(cb) ->
|
||||
user.request.post {
|
||||
uri: "/project/#{project_id}/settings"
|
||||
json:
|
||||
compiler: "latex"
|
||||
}, (error, response, body) ->
|
||||
return cb(error) if error?
|
||||
test(response, body)
|
||||
cb()
|
||||
], callback
|
||||
|
||||
try_admin_access = (user, project_id, test, callback) ->
|
||||
async.series [
|
||||
(cb) ->
|
||||
user.request.post {
|
||||
uri: "/project/#{project_id}/rename"
|
||||
json:
|
||||
newProjectName: "new-name"
|
||||
}, (error, response, body) ->
|
||||
return cb(error) if error?
|
||||
test(response, body)
|
||||
cb()
|
||||
(cb) ->
|
||||
user.request.post {
|
||||
uri: "/project/#{project_id}/settings/admin"
|
||||
json:
|
||||
publicAccessLevel: "private"
|
||||
}, (error, response, body) ->
|
||||
return cb(error) if error?
|
||||
test(response, body)
|
||||
cb()
|
||||
], callback
|
||||
|
||||
try_content_access = (user, project_id, test, callback) ->
|
||||
# The real-time service calls this end point to determine the user's
|
||||
# permissions.
|
||||
if user.id?
|
||||
user_id = user.id
|
||||
else
|
||||
user_id = "anonymous-user"
|
||||
request.post {
|
||||
url: "/project/#{project_id}/join"
|
||||
qs: {user_id}
|
||||
auth:
|
||||
user: settings.apis.web.user
|
||||
pass: settings.apis.web.pass
|
||||
sendImmediately: true
|
||||
json: true
|
||||
jar: false
|
||||
}, (error, response, body) ->
|
||||
return callback(error) if error?
|
||||
test(response, body)
|
||||
callback()
|
||||
|
||||
expect_read_access = (user, project_id, callback) ->
|
||||
async.series [
|
||||
(cb) ->
|
||||
try_read_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.be.oneOf [200, 204]
|
||||
, cb)
|
||||
(cb) ->
|
||||
try_content_access(user, project_id, (response, body) ->
|
||||
expect(body.privilegeLevel).to.be.oneOf ["owner", "readAndWrite", "readOnly"]
|
||||
, cb)
|
||||
], callback
|
||||
|
||||
expect_content_write_access = (user, project_id, callback) ->
|
||||
try_content_access(user, project_id, (response, body) ->
|
||||
expect(body.privilegeLevel).to.be.oneOf ["owner", "readAndWrite"]
|
||||
, callback)
|
||||
|
||||
expect_settings_write_access = (user, project_id, callback) ->
|
||||
try_settings_write_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.be.oneOf [200, 204]
|
||||
, callback)
|
||||
|
||||
expect_admin_access = (user, project_id, callback) ->
|
||||
try_admin_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.be.oneOf [200, 204]
|
||||
, callback)
|
||||
|
||||
expect_no_read_access = (user, project_id, options, callback) ->
|
||||
async.series [
|
||||
(cb) ->
|
||||
try_read_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.equal 302
|
||||
expect(response.headers.location).to.match new RegExp(options.redirect_to)
|
||||
, cb)
|
||||
(cb) ->
|
||||
try_content_access(user, project_id, (response, body) ->
|
||||
expect(body.privilegeLevel).to.be.equal false
|
||||
, cb)
|
||||
], callback
|
||||
|
||||
expect_no_content_write_access = (user, project_id, callback) ->
|
||||
try_content_access(user, project_id, (response, body) ->
|
||||
expect(body.privilegeLevel).to.be.oneOf [false, "readOnly"]
|
||||
, callback)
|
||||
|
||||
expect_no_settings_write_access = (user, project_id, options, callback) ->
|
||||
try_settings_write_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.equal 302
|
||||
expect(response.headers.location).to.match new RegExp(options.redirect_to)
|
||||
, callback)
|
||||
|
||||
expect_no_admin_access = (user, project_id, options, callback) ->
|
||||
try_admin_access(user, project_id, (response, body) ->
|
||||
expect(response.statusCode).to.equal 302
|
||||
expect(response.headers.location).to.match new RegExp(options.redirect_to)
|
||||
, callback)
|
||||
|
||||
describe "Authorization", ->
|
||||
before (done) ->
|
||||
@timeout(10000)
|
||||
@owner = new User()
|
||||
@other1 = new User()
|
||||
@other2 = new User()
|
||||
@anon = new User()
|
||||
@site_admin = new User({email: "admin@example.com"})
|
||||
async.parallel [
|
||||
(cb) => @owner.login cb
|
||||
(cb) => @other1.login cb
|
||||
(cb) => @other2.login cb
|
||||
(cb) => @anon.getCsrfToken cb
|
||||
(cb) =>
|
||||
@site_admin.login (err) =>
|
||||
return cb(err) if error?
|
||||
@site_admin.ensure_admin cb
|
||||
], done
|
||||
|
||||
describe "private project", ->
|
||||
before (done) ->
|
||||
@owner.createProject "private-project", (error, project_id) =>
|
||||
return done(error) if error?
|
||||
@project_id = project_id
|
||||
done()
|
||||
|
||||
it "should allow the owner read access to it", (done) ->
|
||||
expect_read_access @owner, @project_id, done
|
||||
|
||||
it "should allow the owner write access to its content", (done) ->
|
||||
expect_content_write_access @owner, @project_id, done
|
||||
|
||||
it "should allow the owner write access to its settings", (done) ->
|
||||
expect_settings_write_access @owner, @project_id, done
|
||||
|
||||
it "should allow the owner admin access to it", (done) ->
|
||||
expect_admin_access @owner, @project_id, done
|
||||
|
||||
it "should not allow another user read access to the project", (done) ->
|
||||
expect_no_read_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow another user write access to its content", (done) ->
|
||||
expect_no_content_write_access @other1, @project_id, done
|
||||
|
||||
it "should not allow another user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow another user admin access to it", (done) ->
|
||||
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: "/restricted", done
|
||||
|
||||
it "should not allow anonymous user write access to its content", (done) ->
|
||||
expect_no_content_write_access @anon, @project_id, 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
|
||||
|
||||
it "should not allow anonymous user admin access to it", (done) ->
|
||||
expect_no_admin_access @anon, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should allow site admin users read access to it", (done) ->
|
||||
expect_read_access @site_admin, @project_id, done
|
||||
|
||||
it "should allow site admin users write access to its content", (done) ->
|
||||
expect_content_write_access @site_admin, @project_id, done
|
||||
|
||||
it "should allow site admin users write access to its settings", (done) ->
|
||||
expect_settings_write_access @site_admin, @project_id, done
|
||||
|
||||
it "should allow site admin users admin access to it", (done) ->
|
||||
expect_admin_access @site_admin, @project_id, done
|
||||
|
||||
|
||||
describe "shared project", ->
|
||||
before (done) ->
|
||||
@rw_user = @other1
|
||||
@ro_user = @other2
|
||||
@owner.createProject "private-project", (error, project_id) =>
|
||||
return done(error) if error?
|
||||
@project_id = project_id
|
||||
@owner.addUserToProject @project_id, @ro_user.email, "readOnly", (error) =>
|
||||
return done(error) if error?
|
||||
@owner.addUserToProject @project_id, @rw_user.email, "readAndWrite", (error) =>
|
||||
return done(error) if error?
|
||||
done()
|
||||
|
||||
it "should allow the read-only user read access to it", (done) ->
|
||||
expect_read_access @ro_user, @project_id, done
|
||||
|
||||
it "should not allow the read-only user write access to its content", (done) ->
|
||||
expect_no_content_write_access @ro_user, @project_id, done
|
||||
|
||||
it "should not allow the read-only user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @ro_user, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow the read-only user admin access to it", (done) ->
|
||||
expect_no_admin_access @ro_user, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should allow the read-write user read access to it", (done) ->
|
||||
expect_read_access @rw_user, @project_id, done
|
||||
|
||||
it "should allow the read-write user write access to its content", (done) ->
|
||||
expect_content_write_access @rw_user, @project_id, done
|
||||
|
||||
it "should allow the read-write user write access to its settings", (done) ->
|
||||
expect_settings_write_access @rw_user, @project_id, done
|
||||
|
||||
it "should not allow the read-write user admin access to it", (done) ->
|
||||
expect_no_admin_access @rw_user, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
describe "public read-write project", ->
|
||||
before (done) ->
|
||||
@owner.createProject "public-rw-project", (error, project_id) =>
|
||||
return done(error) if error?
|
||||
@project_id = project_id
|
||||
@owner.makePublic @project_id, "readAndWrite", done
|
||||
|
||||
it "should allow a user read access to it", (done) ->
|
||||
expect_read_access @other1, @project_id, done
|
||||
|
||||
it "should allow a user write access to its content", (done) ->
|
||||
expect_content_write_access @other1, @project_id, done
|
||||
|
||||
it "should not allow a user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow a user admin access to it", (done) ->
|
||||
expect_no_admin_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should allow an anonymous user read access to it", (done) ->
|
||||
expect_read_access @anon, @project_id, done
|
||||
|
||||
it "should allow an anonymous user write access to its content", (done) ->
|
||||
expect_content_write_access @anon, @project_id, done
|
||||
|
||||
it "should not allow an anonymous user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @anon, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow an anonymous user admin access to it", (done) ->
|
||||
expect_no_admin_access @anon, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
describe "public read-only project", ->
|
||||
before (done) ->
|
||||
@owner.createProject "public-ro-project", (error, project_id) =>
|
||||
return done(error) if error?
|
||||
@project_id = project_id
|
||||
@owner.makePublic @project_id, "readOnly", done
|
||||
|
||||
it "should allow a user read access to it", (done) ->
|
||||
expect_read_access @other1, @project_id, done
|
||||
|
||||
it "should not allow a user write access to its content", (done) ->
|
||||
expect_no_content_write_access @other1, @project_id, done
|
||||
|
||||
it "should not allow a user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow a user admin access to it", (done) ->
|
||||
expect_no_admin_access @other1, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should allow an anonymous user read access to it", (done) ->
|
||||
expect_read_access @anon, @project_id, done
|
||||
|
||||
it "should not allow an anonymous user write access to its content", (done) ->
|
||||
expect_no_content_write_access @anon, @project_id, done
|
||||
|
||||
it "should not allow an anonymous user write access to its settings", (done) ->
|
||||
expect_no_settings_write_access @anon, @project_id, redirect_to: "/restricted", done
|
||||
|
||||
it "should not allow an anonymous user admin access to it", (done) ->
|
||||
expect_no_admin_access @anon, @project_id, redirect_to: "/restricted", done
|
21
services/web/test/acceptance/coffee/ProjectCRUDTests.coffee
Normal file
21
services/web/test/acceptance/coffee/ProjectCRUDTests.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
expect = require("chai").expect
|
||||
async = require("async")
|
||||
User = require "./helpers/User"
|
||||
|
||||
describe "Project CRUD", ->
|
||||
before (done) ->
|
||||
@user = new User()
|
||||
@user.login done
|
||||
|
||||
describe "when project doesn't exist", ->
|
||||
it "should return 404", (done) ->
|
||||
@user.request.get "/project/aaaaaaaaaaaaaaaaaaaaaaaa", (err, res, body) ->
|
||||
expect(res.statusCode).to.equal 404
|
||||
done()
|
||||
|
||||
describe "when project has malformed id", ->
|
||||
it "should return 404", (done) ->
|
||||
@user.request.get "/project/blah", (err, res, body) ->
|
||||
expect(res.statusCode).to.equal 404
|
||||
done()
|
||||
|
77
services/web/test/acceptance/coffee/helpers/User.coffee
Normal file
77
services/web/test/acceptance/coffee/helpers/User.coffee
Normal file
|
@ -0,0 +1,77 @@
|
|||
request = require("./request")
|
||||
settings = require("settings-sharelatex")
|
||||
{db, ObjectId} = require("../../../../app/js/infrastructure/mongojs")
|
||||
|
||||
count = 0
|
||||
|
||||
class User
|
||||
constructor: (options = {}) ->
|
||||
@email = "acceptance-test-#{count}@example.com"
|
||||
@password = "acceptance-test-#{count}-password"
|
||||
count++
|
||||
@jar = request.jar()
|
||||
@request = request.defaults({
|
||||
jar: @jar
|
||||
})
|
||||
|
||||
login: (callback = (error) ->) ->
|
||||
@getCsrfToken (error) =>
|
||||
return callback(error) if error?
|
||||
@request.post {
|
||||
url: "/register" # Register will log in, but also ensure user exists
|
||||
json:
|
||||
email: @email
|
||||
password: @password
|
||||
}, (error, response, body) =>
|
||||
return callback(error) if error?
|
||||
db.users.findOne {email: @email}, (error, user) =>
|
||||
return callback(error) if error?
|
||||
@id = user?._id?.toString()
|
||||
callback()
|
||||
|
||||
ensure_admin: (callback = (error) ->) ->
|
||||
db.users.update {_id: ObjectId(@id)}, { $set: { isAdmin: true }}, callback
|
||||
|
||||
createProject: (name, callback = (error, project_id) ->) ->
|
||||
@request.post {
|
||||
url: "/project/new",
|
||||
json:
|
||||
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) ->) ->
|
||||
@request.post {
|
||||
url: "/project/#{project_id}/users",
|
||||
json: {email, privileges}
|
||||
}, (error, response, body) ->
|
||||
return callback(error) if error?
|
||||
callback(null, body.user)
|
||||
|
||||
makePublic: (project_id, level, callback = (error) ->) ->
|
||||
@request.post {
|
||||
url: "/project/#{project_id}/settings/admin",
|
||||
json:
|
||||
publicAccessLevel: level
|
||||
}, (error, response, body) ->
|
||||
return callback(error) if error?
|
||||
callback(null)
|
||||
|
||||
getCsrfToken: (callback = (error) ->) ->
|
||||
@request.get {
|
||||
url: "/register"
|
||||
}, (err, response, body) =>
|
||||
return callback(error) if error?
|
||||
csrfMatches = body.match("window.csrfToken = \"(.*?)\";")
|
||||
if !csrfMatches?
|
||||
return callback(new Error("no csrf token found"))
|
||||
@request = @request.defaults({
|
||||
headers:
|
||||
"x-csrf-token": csrfMatches[1]
|
||||
})
|
||||
callback()
|
||||
|
||||
module.exports = User
|
|
@ -0,0 +1,5 @@
|
|||
BASE_URL = "http://localhost:3000"
|
||||
module.exports = require("request").defaults({
|
||||
baseUrl: BASE_URL,
|
||||
followRedirect: false
|
||||
})
|
Loading…
Add table
Reference in a new issue