diff --git a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee index 832f01043f..951506891f 100644 --- a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee +++ b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee @@ -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, ()->) diff --git a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee index 8482c28e04..ae8fedc9c8 100644 --- a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee +++ b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee @@ -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 ->