From bf1bb22afd850f43f1d4cd59785eb7db96add015 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 May 2014 10:31:33 +0100 Subject: [PATCH] added rate limiting to password reset endpoint --- .../PasswordResetController.coffee | 20 ++++++++++++++----- .../PasswordResetControllerTests.coffee | 20 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee b/services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee index b30b3d01fe..fab67bdb16 100644 --- a/services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee +++ b/services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee @@ -1,4 +1,6 @@ PasswordResetHandler = require("./PasswordResetHandler") +RateLimiter = require("../../infrastructure/RateLimiter") + module.exports = @@ -8,11 +10,19 @@ module.exports = requestReset: (req, res)-> email = req.body.email.trim() - PasswordResetHandler.generateAndEmailResetToken email, (err)-> - if err? - res.send 500 - else - res.send 200 + opts = + endpointName:"auto_compile" + timeInterval:60 + subjectName:email + throttle: 3 + RateLimiter.addCount opts, (err, canCompile)-> + if !canCompile + return res.send 500 + PasswordResetHandler.generateAndEmailResetToken email, (err)-> + if err? + res.send 500 + else + res.send 200 renderSetPasswordForm: (req, res)-> res.render "user/setPassword", diff --git a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee b/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee index 26417906d8..4dc3af38dc 100644 --- a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee @@ -14,10 +14,13 @@ describe "PasswordResetController", -> @PasswordResetHandler = generateAndEmailResetToken:sinon.stub() setNewUserPassword:sinon.stub() + @RateLimiter = + addCount: sinon.stub() @PasswordResetController = SandboxedModule.require modulePath, requires: "settings-sharelatex":@settings "./PasswordResetHandler":@PasswordResetHandler "logger-sharelatex": log:-> + "../../infrastructure/RateLimiter":@RateLimiter @email = "bob@bob.com " @token = "my security token that was emailed to me" @@ -28,12 +31,23 @@ describe "PasswordResetController", -> passwordResetToken:@token password:@password - @res ={} + @res = {} describe "requestReset", -> + it "should error if the rate limit is hit", (done)-> + @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1) + @RateLimiter.addCount.callsArgWith(1, null, false) + @res.send = (code)=> + code.should.equal 500 + @PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal false + done() + @PasswordResetController.requestReset @req, @res + + it "should tell the handler to process that email", (done)-> + @RateLimiter.addCount.callsArgWith(1, null, true) @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1) @res.send = (code)=> code.should.equal 200 @@ -41,8 +55,8 @@ describe "PasswordResetController", -> done() @PasswordResetController.requestReset @req, @res - it "should send a 500 if there is an error", (done)-> + @RateLimiter.addCount.callsArgWith(1, null, true) @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, "error") @res.send = (code)=> code.should.equal 500 @@ -75,8 +89,6 @@ describe "PasswordResetController", -> done() @PasswordResetController.setNewUserPassword @req, @res - - it "should error if there is no password", (done)-> @req.body.passwordResetToken = "" @PasswordResetHandler.setNewUserPassword.callsArgWith(2)