mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into pr-history-labels-part-2
This commit is contained in:
commit
a5c6f81573
12 changed files with 152 additions and 27 deletions
|
@ -1,6 +1,5 @@
|
|||
AuthenticationManager = require ("./AuthenticationManager")
|
||||
LoginRateLimiter = require("../Security/LoginRateLimiter")
|
||||
UserGetter = require "../User/UserGetter"
|
||||
UserUpdater = require "../User/UserUpdater"
|
||||
Metrics = require('metrics-sharelatex')
|
||||
logger = require("logger-sharelatex")
|
||||
|
@ -64,7 +63,10 @@ module.exports = AuthenticationController =
|
|||
if user # `user` is either a user object or false
|
||||
AuthenticationController.finishLogin(user, req, res, next)
|
||||
else
|
||||
res.json message: info
|
||||
if info.redir?
|
||||
res.json {redir: info.redir}
|
||||
else
|
||||
res.json message: info
|
||||
)(req, res, next)
|
||||
|
||||
finishLogin: (user, req, res, next) ->
|
||||
|
@ -81,20 +83,30 @@ module.exports = AuthenticationController =
|
|||
|
||||
doPassportLogin: (req, username, password, done) ->
|
||||
email = username.toLowerCase()
|
||||
LoginRateLimiter.processLoginRequest email, (err, isAllowed)->
|
||||
return done(err) if err?
|
||||
if !isAllowed
|
||||
logger.log email:email, "too many login requests"
|
||||
return done(null, null, {text: req.i18n.translate("to_many_login_requests_2_mins"), type: 'error'})
|
||||
AuthenticationManager.authenticate email: email, password, (error, user) ->
|
||||
return done(error) if error?
|
||||
if user?
|
||||
# async actions
|
||||
return done(null, user)
|
||||
else
|
||||
AuthenticationController._recordFailedLogin()
|
||||
logger.log email: email, "failed log in"
|
||||
return done(null, false, {text: req.i18n.translate("email_or_password_wrong_try_again"), type: 'error'})
|
||||
Modules = require "../../infrastructure/Modules"
|
||||
Modules.hooks.fire 'preDoPassportLogin', email, (err, infoList) ->
|
||||
return next(err) if err?
|
||||
info = infoList.find((i) => i?)
|
||||
if info?
|
||||
return done(null, false, info)
|
||||
LoginRateLimiter.processLoginRequest email, (err, isAllowed)->
|
||||
return done(err) if err?
|
||||
if !isAllowed
|
||||
logger.log email:email, "too many login requests"
|
||||
return done(null, null, {text: req.i18n.translate("to_many_login_requests_2_mins"), type: 'error'})
|
||||
AuthenticationManager.authenticate email: email, password, (error, user) ->
|
||||
return done(error) if error?
|
||||
if user?
|
||||
# async actions
|
||||
return done(null, user)
|
||||
else
|
||||
AuthenticationController._recordFailedLogin()
|
||||
logger.log email: email, "failed log in"
|
||||
return done(
|
||||
null,
|
||||
false,
|
||||
{text: req.i18n.translate("email_or_password_wrong_try_again"), type: 'error'}
|
||||
)
|
||||
|
||||
_loginAsyncHandlers: (req, user) ->
|
||||
UserHandler.setupLoginData(user, ()->)
|
||||
|
|
|
@ -347,7 +347,6 @@ module.exports = ProjectController =
|
|||
useV2History: !!project.overleaf?.history?.display
|
||||
richTextEnabled: Features.hasFeature('rich-text')
|
||||
showTestControls: req.query?.tc == 'true' || user.isAdmin
|
||||
showPublishModal: req.query?.pm == 'true'
|
||||
timer.done()
|
||||
|
||||
_buildProjectList: (allProjects, v1Projects = [])->
|
||||
|
|
|
@ -55,7 +55,8 @@ module.exports = ProjectCreationHandler =
|
|||
if Settings.apis?.project_history?.displayHistoryForNewProjects
|
||||
project.overleaf.history.display = true
|
||||
if Settings.currentImageName?
|
||||
project.imageName = Settings.currentImageName
|
||||
# avoid clobbering any imageName already set in attributes (e.g. importedImageName)
|
||||
project.imageName ?= Settings.currentImageName
|
||||
project.rootFolder[0] = rootFolder
|
||||
User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
|
||||
project.spellCheckLanguage = user.ace.spellCheckLanguage
|
||||
|
|
|
@ -68,7 +68,7 @@ Modules.loadViewIncludes app
|
|||
|
||||
app.use bodyParser.urlencoded({ extended: true, limit: "2mb"})
|
||||
# Make sure we can process the max doc length plus some overhead for JSON encoding
|
||||
app.use bodyParser.json({limit: Settings.max_doc_length + 16 * 1024}) # 16kb overhead
|
||||
app.use bodyParser.json({limit: Settings.max_doc_length + 64 * 1024}) # 64kb overhead
|
||||
app.use multer(dest: Settings.path.uploadFolder)
|
||||
app.use methodOverride()
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ script(type="text/ng-template", id="historyLabelTooltipTpl")
|
|||
i.fa.fa-tag
|
||||
| {{ $ctrl.labelText }}
|
||||
p.history-label-tooltip-owner #{translate("history_label_created_by")} {{ $ctrl.labelOwnerName }}
|
||||
time.history-label-tooltip-datetime {{ labelCreationDateTime | formatDate }}
|
||||
time.history-label-tooltip-datetime {{ $ctrl.labelCreationDateTime | formatDate }}
|
||||
|
||||
|
||||
script(type="text/ng-template", id="historyV2DeleteLabelModalTemplate")
|
||||
|
|
|
@ -129,7 +129,6 @@ script(type="text/ng-template", id="historyEntriesListTpl")
|
|||
users="$ctrl.users"
|
||||
on-select="$ctrl.onEntrySelect({ selectedEntry: selectedEntry })"
|
||||
on-label-delete="$ctrl.onLabelDelete({ label: label })"
|
||||
ng-show="!$ctrl.isLoading"
|
||||
)
|
||||
.loading(ng-show="$ctrl.isLoading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
|
|
|
@ -162,7 +162,7 @@ define [
|
|||
cursorPosition = @editor.getCursorPosition()
|
||||
end = change.end
|
||||
{lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end)
|
||||
if lineUpToCursor.match(/.*%.*/)
|
||||
if (i = lineUpToCursor.indexOf('%') > -1 and lineUpToCursor[i-1] != '\\')
|
||||
return
|
||||
lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\"
|
||||
lastTwoChars = lineUpToCursor.slice(-2)
|
||||
|
|
|
@ -6,13 +6,17 @@ define [
|
|||
App.controller "HistoryListController", ["$scope", "$modal", "ide", ($scope, $modal, ide) ->
|
||||
$scope.hoveringOverListSelectors = false
|
||||
|
||||
projectUsers = $scope.project.members.concat $scope.project.owner
|
||||
$scope.projectUsers = []
|
||||
|
||||
$scope.$watch "project.members", (newVal) ->
|
||||
if newVal?
|
||||
$scope.projectUsers = newVal.concat $scope.project.owner
|
||||
|
||||
# This method (and maybe the one below) will be removed soon. User details data will be
|
||||
# injected into the history API responses, so we won't need to fetch user data from other
|
||||
# local data structures.
|
||||
_getUserById = (id) ->
|
||||
_.find projectUsers, (user) ->
|
||||
_.find $scope.projectUsers, (user) ->
|
||||
curUserId = user?._id or user?.id
|
||||
curUserId == id
|
||||
|
||||
|
|
|
@ -7,8 +7,13 @@ define [
|
|||
$scope.hoveringOverListSelectors = false
|
||||
$scope.listConfig =
|
||||
showOnlyLabelled: false
|
||||
$scope.projectUsers = $scope.project.members.concat $scope.project.owner
|
||||
|
||||
$scope.projectUsers = []
|
||||
|
||||
$scope.$watch "project.members", (newVal) ->
|
||||
if newVal?
|
||||
$scope.projectUsers = newVal.concat $scope.project.owner
|
||||
|
||||
$scope.loadMore = () =>
|
||||
ide.historyManager.fetchNextBatchOfUpdates()
|
||||
|
||||
|
|
85
services/web/scripts/increase_compile_timeouts.js
Normal file
85
services/web/scripts/increase_compile_timeouts.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
const mongojs = require('../app/js/infrastructure/mongojs')
|
||||
const { db } = mongojs
|
||||
const async = require('async')
|
||||
const minilist = require('minimist')
|
||||
|
||||
const newTimeout = 240
|
||||
const oldTimeoutLimits = {$gt: 60, $lt: 240}
|
||||
|
||||
const updateUser = function (user, callback) {
|
||||
console.log(`Updating user ${user._id}`)
|
||||
const update = {
|
||||
$set: {
|
||||
'features.compileTimeout': newTimeout
|
||||
}
|
||||
}
|
||||
db.users.update({
|
||||
_id: user._id,
|
||||
'features.compileTimeout': oldTimeoutLimits
|
||||
}, update, callback)
|
||||
}
|
||||
|
||||
const updateUsers = (users, callback) =>
|
||||
async.eachLimit(users, ASYNC_LIMIT, updateUser, function (error) {
|
||||
if (error) {
|
||||
callback(error)
|
||||
return
|
||||
}
|
||||
counter += users.length
|
||||
console.log(`${counter} users updated`)
|
||||
if (DO_ALL) {
|
||||
return loopForUsers(callback)
|
||||
} else {
|
||||
console.log('*** run again to continue updating ***')
|
||||
return callback()
|
||||
}
|
||||
})
|
||||
|
||||
var loopForUsers = callback =>
|
||||
db.users.find(
|
||||
{ 'features.compileTimeout': oldTimeoutLimits },
|
||||
{ 'features.compileTimeout': 1 }
|
||||
).limit(FETCH_LIMIT, function (error, users) {
|
||||
if (error) {
|
||||
callback(error)
|
||||
return
|
||||
}
|
||||
if (users.length === 0) {
|
||||
console.log(`DONE (${counter} users updated)`)
|
||||
return callback()
|
||||
}
|
||||
updateUsers(users, callback)
|
||||
})
|
||||
|
||||
var counter = 0
|
||||
var run = () =>
|
||||
loopForUsers(function (error) {
|
||||
if (error) { throw error }
|
||||
process.exit()
|
||||
})
|
||||
|
||||
let FETCH_LIMIT, ASYNC_LIMIT, DO_ALL
|
||||
var setup = function () {
|
||||
let args = minilist(process.argv.slice(2))
|
||||
// --fetch N get N users each time
|
||||
FETCH_LIMIT = (args.fetch) ? args.fetch : 100
|
||||
// --async M run M updates in parallel
|
||||
ASYNC_LIMIT = (args.async) ? args.async : 10
|
||||
// --all means run to completion
|
||||
if (args.all) {
|
||||
if (args.fetch) {
|
||||
console.error('error: do not use --fetch with --all')
|
||||
process.exit(1)
|
||||
} else {
|
||||
DO_ALL = true
|
||||
// if we are updating for all users then ignore the fetch limit.
|
||||
FETCH_LIMIT = 0
|
||||
// A limit() value of 0 (i.e. .limit(0)) is equivalent to setting
|
||||
// no limit.
|
||||
// https://docs.mongodb.com/manual/reference/method/cursor.limit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup()
|
||||
run()
|
|
@ -15,7 +15,6 @@ describe "AuthenticationController", ->
|
|||
tk.freeze(Date.now())
|
||||
@AuthenticationController = SandboxedModule.require modulePath, requires:
|
||||
"./AuthenticationManager": @AuthenticationManager = {}
|
||||
"../User/UserGetter" : @UserGetter = {}
|
||||
"../User/UserUpdater" : @UserUpdater = {}
|
||||
"metrics-sharelatex": @Metrics = { inc: sinon.stub() }
|
||||
"../Security/LoginRateLimiter": @LoginRateLimiter = { processLoginRequest:sinon.stub(), recordSuccessfulLogin:sinon.stub() }
|
||||
|
@ -29,6 +28,7 @@ describe "AuthenticationController", ->
|
|||
trackSession: sinon.stub()
|
||||
untrackSession: sinon.stub()
|
||||
revokeAllUserSessions: sinon.stub().callsArgWith(1, null)
|
||||
"../../infrastructure/Modules": @Modules = {hooks: {fire: sinon.stub().callsArgWith(2, null, [])}}
|
||||
@user =
|
||||
_id: ObjectId()
|
||||
email: @email = "USER@example.com"
|
||||
|
@ -214,6 +214,7 @@ describe "AuthenticationController", ->
|
|||
beforeEach ->
|
||||
@AuthenticationController._recordFailedLogin = sinon.stub()
|
||||
@AuthenticationController._recordSuccessfulLogin = sinon.stub()
|
||||
@Modules.hooks.fire = sinon.stub().callsArgWith(2, null, [])
|
||||
# @AuthenticationController.establishUserSession = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
email: @email
|
||||
|
@ -222,6 +223,17 @@ describe "AuthenticationController", ->
|
|||
postLoginRedirect: "/path/to/redir/to"
|
||||
@cb = sinon.stub()
|
||||
|
||||
describe "when the preDoPassportLogin hooks produce an info object", ->
|
||||
beforeEach ->
|
||||
@Modules.hooks.fire = sinon.stub().callsArgWith(2, null, [null, {redir: '/somewhere'}, null])
|
||||
|
||||
it "should stop early and call done with this info object", (done) ->
|
||||
@AuthenticationController.doPassportLogin(@req, @req.body.email, @req.body.password, @cb)
|
||||
@cb.callCount.should.equal 1
|
||||
@cb.calledWith(null, false, {redir: '/somewhere'}).should.equal true
|
||||
@LoginRateLimiter.processLoginRequest.callCount.should.equal 0
|
||||
done()
|
||||
|
||||
describe "when the users rate limit", ->
|
||||
|
||||
beforeEach ->
|
||||
|
|
|
@ -111,7 +111,7 @@ describe 'ProjectCreationHandler', ->
|
|||
project.spellCheckLanguage.should.equal "de"
|
||||
done()
|
||||
|
||||
it "should set the imageName to currentImageName if set", (done) ->
|
||||
it "should set the imageName to currentImageName if set and no imageName attribute", (done) ->
|
||||
@Settings.currentImageName = "mock-image-name"
|
||||
@handler.createBlankProject ownerId, projectName, (err, project)=>
|
||||
project.imageName.should.equal @Settings.currentImageName
|
||||
|
@ -123,6 +123,14 @@ describe 'ProjectCreationHandler', ->
|
|||
expect(project.imageName).to.not.exist
|
||||
done()
|
||||
|
||||
it "should set the imageName to the attribute value if set and not overwrite it with the currentImageName", (done) ->
|
||||
@Settings.currentImageName = "mock-image-name"
|
||||
attributes =
|
||||
imageName: "attribute-image-name"
|
||||
@handler.createBlankProject ownerId, projectName, attributes, (err, project)=>
|
||||
project.imageName.should.equal attributes.imageName
|
||||
done()
|
||||
|
||||
it "should not set the overleaf.history.display if not configured in settings", (done) ->
|
||||
@Settings.apis.project_history.displayHistoryForNewProjects = false
|
||||
@handler.createBlankProject ownerId, projectName, (err, project)=>
|
||||
|
|
Loading…
Reference in a new issue