Merge pull request #1072 from sharelatex/spd-open-with-overleaf

Implement v1 open-with-overleaf API in v2 (part 1)

GitOrigin-RevId: 488f4eeefc29086a72295ccbc7c63d2f927add12
This commit is contained in:
Paulo Jorge Reis 2018-11-15 09:40:33 +00:00 committed by sharelatex
parent 9d4c3039e7
commit af2d959504
16 changed files with 338 additions and 87 deletions

View file

@ -0,0 +1,13 @@
module.exports = DocumentHelper =
getTitleFromTexContent: (content, maxContentToScan = 30000) ->
TITLE_WITH_CURLY_BRACES = /\\[tT]itle\*?\s*{([^}]+)}/
TITLE_WITH_SQUARE_BRACES = /\\[tT]itle\s*\[([^\]]+)\]/
ESCAPED_BRACES = /\\([{}\[\]])/g
content = content.substring(0, maxContentToScan).split("\n") if typeof content is 'string'
title = null
for line in content
match = line.match(TITLE_WITH_SQUARE_BRACES) || line.match(TITLE_WITH_CURLY_BRACES)
if match?
title = match[1].replace(ESCAPED_BRACES, (br)->br[1])
break
return title

View file

@ -0,0 +1,14 @@
Settings = require 'settings-sharelatex'
module.exports = UrlHelper =
wrapUrlWithProxy: (url) ->
# TODO: Consider what to do for Community and Enterprise edition?
if !Settings.apis?.linkedUrlProxy?.url?
throw new Error('no linked url proxy configured')
return "#{Settings.apis.linkedUrlProxy.url}?url=#{encodeURIComponent(url)}"
prependHttpIfNeeded: (url) ->
if !url.match('://')
url = 'http://' + url
return url

View file

@ -1,10 +1,9 @@
request = require 'request'
_ = require "underscore"
urlValidator = require 'valid-url'
Settings = require 'settings-sharelatex'
{ InvalidUrlError, UrlFetchFailedError } = require './LinkedFilesErrors'
LinkedFilesHandler = require './LinkedFilesHandler'
UrlHelper = require '../Helpers/UrlHelper'
module.exports = UrlAgent = {
@ -36,7 +35,7 @@ module.exports = UrlAgent = {
_sanitizeData: (data) ->
return {
provider: data.provider
url: @._prependHttpIfNeeded(data.url)
url: UrlHelper.prependHttpIfNeeded(data.url)
}
_getUrlStream: (project_id, data, current_user_id, callback = (error, fsPath) ->) ->
@ -44,19 +43,9 @@ module.exports = UrlAgent = {
url = data.url
if !urlValidator.isWebUri(url)
return callback(new InvalidUrlError("invalid url: #{url}"))
url = @_wrapWithProxy(url)
url = UrlHelper.wrapUrlWithProxy(url)
readStream = request.get(url)
readStream.pause()
callback(null, readStream)
_prependHttpIfNeeded: (url) ->
if !url.match('://')
url = 'http://' + url
return url
_wrapWithProxy: (url) ->
# TODO: Consider what to do for Community and Enterprise edition?
if !Settings.apis?.linkedUrlProxy?.url?
throw new Error('no linked url proxy configured')
return "#{Settings.apis.linkedUrlProxy.url}?url=#{encodeURIComponent(url)}"
}

View file

@ -64,18 +64,18 @@ module.exports = ProjectCreationHandler =
return callback(err) if err?
callback err, project
createProjectFromSnippet : (owner_id, projectName, docLines, callback = (error, project) ->)->
@createBlankProject owner_id, projectName, (error, project)->
return callback(error) if error?
ProjectCreationHandler._createRootDoc project, owner_id, docLines, callback
createBasicProject : (owner_id, projectName, callback = (error, project) ->)->
self = @
@createBlankProject owner_id, projectName, (error, project)->
return callback(error) if error?
self._buildTemplate "mainbasic.tex", owner_id, projectName, (error, docLines)->
return callback(error) if error?
ProjectEntityUpdateHandler.addDoc project._id, project.rootFolder[0]._id, "main.tex", docLines, owner_id, (error, doc)->
if error?
logger.err err:error, "error adding doc when creating basic project"
return callback(error)
ProjectEntityUpdateHandler.setRootDoc project._id, doc._id, (error) ->
callback(error, project)
ProjectCreationHandler._createRootDoc project, owner_id, docLines, callback
createExampleProject: (owner_id, projectName, callback = (error, project) ->)->
self = @
@ -85,9 +85,7 @@ module.exports = ProjectCreationHandler =
(callback) ->
self._buildTemplate "main.tex", owner_id, projectName, (error, docLines)->
return callback(error) if error?
ProjectEntityUpdateHandler.addDoc project._id, project.rootFolder[0]._id, "main.tex", docLines, owner_id, (error, doc)->
return callback(error) if error?
ProjectEntityUpdateHandler.setRootDoc project._id, doc._id, callback
ProjectCreationHandler._createRootDoc project, owner_id, docLines, callback
(callback) ->
self._buildTemplate "references.bib", owner_id, projectName, (error, docLines)->
return callback(error) if error?
@ -99,6 +97,14 @@ module.exports = ProjectCreationHandler =
], (error) ->
callback(error, project)
_createRootDoc: (project, owner_id, docLines, callback = (error, project) ->)->
ProjectEntityUpdateHandler.addDoc project._id, project.rootFolder[0]._id, "main.tex", docLines, owner_id, (error, doc)->
if error?
logger.err err:error, "error adding root doc when creating project"
return callback(error)
ProjectEntityUpdateHandler.setRootDoc project._id, doc._id, (error) ->
callback(error, project)
_buildTemplate: (template_name, user_id, project_name, callback = (error, output) ->)->
User.findById user_id, "first_name last_name", (error, user)->
return callback(error) if error?

View file

@ -0,0 +1,10 @@
ENGINE_TO_COMPILER_MAP = {
latex_dvipdf: "latex"
pdflatex: "pdflatex"
xelatex: "xelatex"
lualatex: "lualatex"
}
module.exports =
compilerFromV1Engine: (engine) ->
return ENGINE_TO_COMPILER_MAP[engine]

View file

@ -1,15 +1,9 @@
path = require('path')
AuthenticationController = require('../../../js/Features/Authentication/AuthenticationController')
TemplatesManager = require('./TemplatesManager')
ProjectHelper = require('../../../js/Features/Project/ProjectHelper')
logger = require('logger-sharelatex')
ENGINE_TO_COMPILER_MAP = {
latex_dvipdf: "latex"
pdflatex: "pdflatex"
xelatex: "xelatex"
lualatex: "lualatex"
}
module.exports = TemplatesController =
getV1Template: (req, res)->
@ -22,7 +16,7 @@ module.exports = TemplatesController =
data.templateVersionId = templateVersionId
data.templateId = templateId
data.name = req.query.templateName
data.compiler = ENGINE_TO_COMPILER_MAP[req.query.latexEngine]
data.compiler = ProjectHelper.compilerFromV1Engine(req.query.latexEngine)
data.mainFile = req.query.mainFile
data.brandVariationId = req.query.brandVariationId
res.render path.resolve(__dirname, "../../../views/project/editor/new_from_template"), data

View file

@ -0,0 +1,44 @@
csurf = require('csurf')
csrf = csurf()
# Wrapper for `csurf` middleware that provides a list of routes that can be excluded from csrf checks.
#
# Include with `Csrf = require('./Csrf')`
#
# Add the middleware to the router with:
# myRouter.csrf = new Csrf()
# myRouter.use webRouter.csrf.middleware
# When building routes, specify a route to exclude from csrf checks with:
# myRouter.csrf.disableDefaultCsrfProtection "/path" "METHOD"
#
# To validate the csrf token in a request to ensure that it's valid, you can use `validateRequest`, which takes a
# request object and calls a callback with either true or false.
module.exports = class Csrf
constructor: ->
@excluded_routes = {}
disableDefaultCsrfProtection: (route, method) ->
@excluded_routes[route] = {} unless @excluded_routes[route]
@excluded_routes[route][method] = 1
middleware: (req, res, next) =>
# We want to call the middleware for all routes, even if excluded, because csurf sets up a csrfToken() method on
# the request, to get a new csrf token for any rendered forms. For excluded routes we'll then ignore a 'bad csrf
# token' error from csurf and continue on...
# check whether the request method is excluded for the specified route
if @excluded_routes[req.path]?[req.method] == 1
# ignore the error if it's due to a bad csrf token, and continue
csrf req, res, (err) =>
if (err && err.code != 'EBADCSRFTOKEN')
next(err)
else
next()
else
csrf req, res, next
@validateRequest: (req, cb = (valid)->) ->
# run a dummy csrf check to see if it returns an error
csrf req, null, (err) ->
cb(!err?)

View file

@ -9,6 +9,7 @@ Router = require('../router')
helmet = require "helmet"
metrics.inc("startup")
UserSessionsRedis = require('../Features/User/UserSessionsRedis')
Csrf = require('./Csrf')
sessionsRedisClient = UserSessionsRedis.client()
@ -17,8 +18,6 @@ RedisStore = require('connect-redis')(session)
bodyParser = require('body-parser')
multer = require('multer')
methodOverride = require('method-override')
csrf = require('csurf')
csrfProtection = csrf()
cookieParser = require('cookie-parser')
bearerToken = require('express-bearer-token')
@ -114,7 +113,8 @@ Modules.hooks.fire 'passportSetup', passport, (err) ->
Modules.applyNonCsrfRouter(webRouter, privateApiRouter, publicApiRouter)
webRouter.use csrfProtection
webRouter.csrf = new Csrf()
webRouter.use webRouter.csrf.middleware
webRouter.use translations.expressMiddlewear
webRouter.use translations.setLangBasedOnDomainMiddlewear

View file

@ -618,7 +618,7 @@
"babel-loader": {
"version": "7.1.4",
"from": "babel-loader@>=7.1.2 <8.0.0",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz",
"resolved": "http://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz",
"dev": true
},
"babel-messages": {
@ -1525,7 +1525,7 @@
"capture-stack-trace": {
"version": "1.0.0",
"from": "capture-stack-trace@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
"resolved": "http://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
"dev": true
},
"caseless": {
@ -1606,13 +1606,13 @@
"readable-stream": {
"version": "2.3.6",
"from": "readable-stream@^2.0.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"dev": true
},
"readdirp": {
"version": "2.1.0",
"from": "readdirp@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"resolved": "http://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"dev": true
},
"string_decoder": {
@ -1631,7 +1631,7 @@
"ci-info": {
"version": "1.1.3",
"from": "ci-info@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz",
"resolved": "http://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz",
"dev": true
},
"cipher-base": {
@ -1822,7 +1822,7 @@
"compressible": {
"version": "2.0.13",
"from": "compressible@>=2.0.13 <2.1.0",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz",
"resolved": "http://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz",
"dev": true,
"dependencies": {
"mime-db": {
@ -2039,7 +2039,7 @@
"contentful-sdk-core": {
"version": "6.0.0-beta1",
"from": "contentful-sdk-core@>=6.0.0-beta0 <7.0.0",
"resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-6.0.0-beta1.tgz"
"resolved": "http://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-6.0.0-beta1.tgz"
},
"continuable-cache": {
"version": "0.3.1",
@ -2493,7 +2493,7 @@
"detect-node": {
"version": "2.0.3",
"from": "detect-node@>=2.0.3 <3.0.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz",
"resolved": "http://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz",
"dev": true
},
"di": {
@ -2836,7 +2836,7 @@
"es-to-primitive": {
"version": "1.1.1",
"from": "es-to-primitive@>=1.1.1 <2.0.0",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
"resolved": "http://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
"dev": true
},
"es5-ext": {
@ -3104,7 +3104,7 @@
"eslint-plugin-import": {
"version": "2.11.0",
"from": "eslint-plugin-import@>=2.9.0 <3.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.11.0.tgz",
"resolved": "http://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.11.0.tgz",
"dev": true,
"dependencies": {
"debug": {
@ -3188,7 +3188,7 @@
"eslint-plugin-promise": {
"version": "3.7.0",
"from": "eslint-plugin-promise@>=3.6.0 <4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz",
"resolved": "http://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz",
"dev": true
},
"eslint-plugin-react": {
@ -3694,7 +3694,7 @@
"follow-redirects": {
"version": "1.0.0",
"from": "follow-redirects@1.0.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
"resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
"dev": true,
"optional": true,
"dependencies": {
@ -3878,7 +3878,7 @@
"generate-function": {
"version": "2.0.0",
"from": "generate-function@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
"resolved": "http://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
"dev": true,
"optional": true
},
@ -3948,7 +3948,7 @@
"readable-stream": {
"version": "2.3.6",
"from": "readable-stream@2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"dev": true,
"optional": true
},
@ -4538,7 +4538,7 @@
"async": {
"version": "1.5.2",
"from": "async@>=1.4.0 <2.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz"
"resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz"
},
"source-map": {
"version": "0.4.4",
@ -4562,7 +4562,7 @@
"yargs": {
"version": "3.10.0",
"from": "yargs@>=3.10.0 <3.11.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"optional": true
}
}
@ -5432,7 +5432,7 @@
"is-symbol": {
"version": "1.0.1",
"from": "is-symbol@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
"dev": true
},
"is-typedarray": {
@ -5849,7 +5849,7 @@
"killable": {
"version": "1.0.0",
"from": "killable@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
"resolved": "http://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
"dev": true
},
"kind-of": {
@ -6200,7 +6200,7 @@
"lodash": {
"version": "4.17.4",
"from": "lodash@>=4.13.1 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz"
"resolved": "http://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz"
},
"lodash.assign": {
"version": "4.2.0",
@ -6782,7 +6782,7 @@
"commander": {
"version": "2.15.1",
"from": "commander@>=2.9.0 <3.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"dev": true,
"optional": true
},
@ -7031,7 +7031,7 @@
"map-stream": {
"version": "0.1.0",
"from": "map-stream@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
"resolved": "http://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
"dev": true
},
"map-visit": {
@ -7121,7 +7121,7 @@
"minimist": {
"version": "1.2.0",
"from": "minimist@>=1.1.3 <2.0.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"dev": true
},
"path-exists": {
@ -7340,10 +7340,22 @@
}
}
},
"mmmagic": {
"version": "0.5.2",
"from": "mmmagic@latest",
"resolved": "https://registry.npmjs.org/mmmagic/-/mmmagic-0.5.2.tgz",
"dependencies": {
"nan": {
"version": "2.11.1",
"from": "nan@>=2.11.0 <3.0.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz"
}
}
},
"mocha": {
"version": "5.0.1",
"from": "mocha@5.0.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz",
"resolved": "http://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz",
"dependencies": {
"commander": {
"version": "2.11.0",
@ -7900,7 +7912,7 @@
"chokidar": {
"version": "2.0.3",
"from": "chokidar@>=2.0.2 <3.0.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
"resolved": "http://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
"dev": true
},
"debug": {
@ -8112,13 +8124,13 @@
"readable-stream": {
"version": "2.3.6",
"from": "readable-stream@^2.0.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"dev": true
},
"readdirp": {
"version": "2.1.0",
"from": "readdirp@^2.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"resolved": "http://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"dev": true
},
"semver": {
@ -8481,7 +8493,7 @@
"opn": {
"version": "5.3.0",
"from": "opn@>=5.1.0 <6.0.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
"resolved": "http://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
"dev": true
},
"optimist": {
@ -9201,7 +9213,7 @@
"proxy-agent": {
"version": "3.0.0",
"from": "proxy-agent@>=3.0.0 <3.1.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz",
"resolved": "http://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz",
"dev": true,
"optional": true,
"dependencies": {
@ -9254,7 +9266,7 @@
"event-stream": {
"version": "3.3.4",
"from": "event-stream@>=3.3.0 <3.4.0",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
"resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
"dev": true
},
"ps-tree": {
@ -9424,7 +9436,7 @@
"querystringify": {
"version": "0.0.4",
"from": "querystringify@>=0.0.0 <0.1.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz",
"resolved": "http://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz",
"dev": true
},
"quick-lru": {
@ -10006,7 +10018,7 @@
"sanitize-html": {
"version": "1.18.2",
"from": "sanitize-html@>=1.14.1 <2.0.0",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.18.2.tgz",
"resolved": "http://registry.npmjs.org/sanitize-html/-/sanitize-html-1.18.2.tgz",
"dev": true,
"dependencies": {
"ansi-styles": {
@ -10145,7 +10157,7 @@
"lodash": {
"version": "4.12.0",
"from": "lodash@4.12.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz"
"resolved": "http://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz"
}
}
},
@ -10677,7 +10689,7 @@
"split": {
"version": "0.3.3",
"from": "split@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
"resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz",
"dev": true
},
"split-string": {
@ -10767,7 +10779,7 @@
"stream-combiner": {
"version": "0.0.4",
"from": "stream-combiner@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
"resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
"dev": true
},
"stream-http": {
@ -12297,7 +12309,7 @@
"webpack-dev-server": {
"version": "2.11.2",
"from": "webpack-dev-server@>=2.11.1 <3.0.0",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz",
"resolved": "http://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz",
"dev": true,
"dependencies": {
"accepts": {
@ -12353,7 +12365,7 @@
"chokidar": {
"version": "2.0.3",
"from": "chokidar@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
"resolved": "http://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
"dev": true
},
"cliui": {
@ -12521,7 +12533,7 @@
"finalhandler": {
"version": "1.1.1",
"from": "finalhandler@1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"dev": true,
"dependencies": {
"debug": {
@ -12773,13 +12785,13 @@
"readable-stream": {
"version": "2.3.6",
"from": "readable-stream@^2.0.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"dev": true
},
"readdirp": {
"version": "2.1.0",
"from": "readdirp@^2.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"resolved": "http://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
"dev": true
},
"rimraf": {

View file

@ -66,6 +66,7 @@
"method-override": "^2.3.3",
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.8.0",
"minimist": "1.2.0",
"mmmagic": "^0.5.2",
"mocha": "^5.0.1",
"mongojs": "2.4.0",
"mongoose": "4.11.4",

View file

@ -36,8 +36,8 @@ describe "RedirectUrls", ->
it 'redirects to /sign_in_to_v1 with authWithV1 setting', (done) ->
assertRedirect(
'get',
'/docs?zip_uri=http%3A%2F%2Foverleaf.test%2Ffoo%3Fbar%3Dbaz%26qux%3Dthing&bar=baz',
'/docs_v1?zip_uri=http%3A%2F%2Foverleaf.test%2Ffoo%3Fbar%3Dbaz%26qux%3Dthing&bar=baz',
302,
'/sign_in_to_v1?return_to=%2Fdocs%3Fzip_uri%3Dhttp%253A%252F%252Foverleaf.test%252Ffoo%253Fbar%253Dbaz%2526qux%253Dthing%26bar%3Dbaz',
done
)
)

View file

@ -137,7 +137,7 @@ module.exports =
url: (params) -> "/destination/#{params.id}/params"
},
'/redirect/qs': '/destination/qs'
'/docs': {
'/docs_v1': {
authWithV1: true
url: '/docs'
}

View file

@ -0,0 +1,28 @@
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/Features/Documents/DocumentHelper.js"
SandboxedModule = require('sandboxed-module')
describe "DocumentHelper", ->
beforeEach ->
@DocumentHelper = SandboxedModule.require modulePath
describe "getTitleFromTexContent", ->
it "should return the title", ->
document = "\\begin{document}\n\\title{foo}\n\\end{document}"
expect(@DocumentHelper.getTitleFromTexContent(document)).to.equal "foo"
it "should return the title if surrounded by space", ->
document = "\\begin{document}\n \\title{foo} \n\\end{document}"
expect(@DocumentHelper.getTitleFromTexContent(document)).to.equal "foo"
it "should return null if there is no title", ->
document = "\\begin{document}\n\\end{document}"
expect(@DocumentHelper.getTitleFromTexContent(document)).to.eql null
it "should accept an array", ->
document = ["\\begin{document}","\\title{foo}","\\end{document}"]
expect(@DocumentHelper.getTitleFromTexContent(document)).to.equal "foo"

View file

@ -189,24 +189,38 @@ describe 'ProjectCreationHandler', ->
throw new Error("unknown template: #{template_name}")
sinon.spy @handler, "_buildTemplate"
@handler.createBlankProject = sinon.stub().callsArgWith(2, null, @project)
@handler._createRootDoc = sinon.stub().callsArgWith(3, null, @project)
@handler.createBasicProject(ownerId, projectName, @callback)
it "should create a blank project first", ->
@handler.createBlankProject.calledWith(ownerId, projectName)
.should.equal true
it 'should insert main.tex', ->
@ProjectEntityUpdateHandler.addDoc.calledWith(project_id, rootFolderId, "main.tex", ["mainbasic.tex", "lines"], ownerId)
it 'should create the root document', ->
@handler._createRootDoc
.calledWith(@project, ownerId, ["mainbasic.tex", "lines"])
.should.equal true
it 'should set the main doc id', ->
@ProjectEntityUpdateHandler.setRootDoc.calledWith(project_id, docId).should.equal true
it 'should build the mainbasic.tex template', ->
@handler._buildTemplate
.calledWith("mainbasic.tex", ownerId, projectName)
.should.equal true
describe 'Creating a project from a snippet', ->
beforeEach ->
@project = new @ProjectModel
@handler.createBlankProject = sinon.stub().callsArgWith(2, null, @project)
@handler._createRootDoc = sinon.stub().callsArgWith(3, null, @project)
@handler.createProjectFromSnippet(ownerId, projectName, ["snippet line 1", "snippet line 2"], @callback)
it "should create a blank project first", ->
@handler.createBlankProject.calledWith(ownerId, projectName)
.should.equal true
it 'should create the root document', ->
@handler._createRootDoc
.calledWith(@project, ownerId, ["snippet line 1", "snippet line 2"])
.should.equal true
describe 'Creating an example project', ->
beforeEach ->
@ -219,15 +233,16 @@ describe 'ProjectCreationHandler', ->
throw new Error("unknown template: #{template_name}")
sinon.spy @handler, "_buildTemplate"
@handler.createBlankProject = sinon.stub().callsArgWith(2, null, @project)
@handler._createRootDoc = sinon.stub().callsArgWith(3, null, @project)
@handler.createExampleProject(ownerId, projectName, @callback)
it "should create a blank project first", ->
@handler.createBlankProject.calledWith(ownerId, projectName)
.should.equal true
it 'should insert main.tex', ->
@ProjectEntityUpdateHandler.addDoc
.calledWith(project_id, rootFolderId, "main.tex", ["main.tex", "lines"], ownerId)
it 'should create the root document', ->
@handler._createRootDoc
.calledWith(@project, ownerId, ["main.tex", "lines"])
.should.equal true
it 'should insert references.bib', ->
@ -245,9 +260,6 @@ describe 'ProjectCreationHandler', ->
)
.should.equal true
it 'should set the main doc id', ->
@ProjectEntityUpdateHandler.setRootDoc.calledWith(project_id, docId).should.equal true
it 'should build the main.tex template', ->
@handler._buildTemplate
.calledWith("main.tex", ownerId, projectName)
@ -287,3 +299,17 @@ describe 'ProjectCreationHandler', ->
it "should put the year in", (done)->
@template.indexOf(new Date().getUTCFullYear()).should.not.equal -1
done()
describe "_createRootDoc", ->
beforeEach (done)->
@project = new @ProjectModel()
@handler._createRootDoc @project, ownerId, ["line 1", "line 2"], done
it 'should insert main.tex', ->
@ProjectEntityUpdateHandler.addDoc
.calledWith(project_id, rootFolderId, "main.tex", ["line 1", "line 2"], ownerId)
.should.equal true
it 'should set the main doc id', ->
@ProjectEntityUpdateHandler.setRootDoc.calledWith(project_id, docId).should.equal true

View file

@ -0,0 +1,23 @@
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/Features/Project/ProjectHelper.js"
SandboxedModule = require('sandboxed-module')
describe "ProjectHelper", ->
beforeEach ->
@ProjectHelper = SandboxedModule.require modulePath
describe "compilerFromV1Engine", ->
it "returns the correct engine for latex_dvipdf", ->
expect(@ProjectHelper.compilerFromV1Engine('latex_dvipdf')).to.equal 'latex'
it "returns the correct engine for pdflatex", ->
expect(@ProjectHelper.compilerFromV1Engine('pdflatex')).to.equal 'pdflatex'
it "returns the correct engine for xelatex", ->
expect(@ProjectHelper.compilerFromV1Engine('xelatex')).to.equal 'xelatex'
it "returns the correct engine for lualatex", ->
expect(@ProjectHelper.compilerFromV1Engine('lualatex')).to.equal 'lualatex'

View file

@ -0,0 +1,91 @@
assert = require("chai").assert
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/infrastructure/Csrf.js"
SandboxedModule = require('sandboxed-module')
describe "Csrf", ->
beforeEach ->
@csurf_csrf = sinon.stub().callsArgWith(2, @err = {code: 'EBADCSRFTOKEN'})
@Csrf = SandboxedModule.require modulePath, requires:
csurf: sinon.stub().returns(@csurf_csrf)
@csrf = new @Csrf()
@next = sinon.stub()
@path = '/foo/bar'
@req =
path: @path
method: 'POST'
@res = {}
describe 'the middleware', ->
describe 'when there are no excluded routes', ->
it 'passes the csrf error on', ->
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal true
describe 'when the route is excluded', ->
it 'does not pass the csrf error on', ->
@csrf.disableDefaultCsrfProtection(@path, 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal false
describe 'when there is a partial route match', ->
it 'passes the csrf error on when the match is too short', ->
@csrf.disableDefaultCsrfProtection('/foo', 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal true
it 'passes the csrf error on when the match is too long', ->
@csrf.disableDefaultCsrfProtection('/foo/bar/baz', 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal true
describe 'when there are multiple exclusions', ->
it 'does not pass the csrf error on when the match is present', ->
@csrf.disableDefaultCsrfProtection(@path, 'POST')
@csrf.disableDefaultCsrfProtection('/test', 'POST')
@csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal false
it 'passes the csrf error on when the match is not present', ->
@csrf.disableDefaultCsrfProtection('/url', 'POST')
@csrf.disableDefaultCsrfProtection('/test', 'POST')
@csrf.disableDefaultCsrfProtection('/a/b/c', 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal true
describe 'when the method does not match', ->
it 'passes the csrf error on', ->
@csrf.disableDefaultCsrfProtection(@path, 'POST')
@req.method = 'GET'
@csrf.middleware @req, @res, @next
expect(@next.calledWith(@err)).to.equal true
describe 'when the route is excluded, but the error is not a bad-csrf-token error', ->
it 'passes the error on', ->
@Csrf = SandboxedModule.require modulePath, requires:
csurf: @csurf = sinon.stub().returns(@csurf_csrf = sinon.stub().callsArgWith(2, err = {code: 'EOTHER'}))
@csrf = new @Csrf()
@csrf.disableDefaultCsrfProtection(@path, 'POST')
@csrf.middleware @req, @res, @next
expect(@next.calledWith(err)).to.equal true
expect(@next.calledWith(@err)).to.equal false
describe 'validateRequest', ->
describe 'when the request is invalid', ->
it 'calls the callback with `false`', ->
@cb = sinon.stub()
@Csrf.validateRequest(@req, @cb)
expect(@cb.calledWith(false)).to.equal true
describe 'when the request is valid', ->
it 'calls the callback with `true`', ->
@Csrf = SandboxedModule.require modulePath, requires:
csurf: @csurf = sinon.stub().returns(@csurf_csrf = sinon.stub().callsArg(2))
@cb = sinon.stub()
@Csrf.validateRequest(@req, @cb)
expect(@cb.calledWith(true)).to.equal true