Parcourir la source

added rate limiting to password reset endpoint

Henry Oswald il y a 12 ans
Parent
commit
bf1bb22afd

+ 15 - 5
services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee

@@ -1,4 +1,6 @@
 PasswordResetHandler = require("./PasswordResetHandler")
 PasswordResetHandler = require("./PasswordResetHandler")
+RateLimiter = require("../../infrastructure/RateLimiter")
+
 
 
 module.exports =
 module.exports =
 
 
@@ -8,11 +10,19 @@ module.exports =
 
 
 	requestReset: (req, res)->
 	requestReset: (req, res)->
 		email = req.body.email.trim()
 		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)->
 	renderSetPasswordForm: (req, res)->
 		res.render "user/setPassword", 
 		res.render "user/setPassword", 

+ 16 - 4
services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee

@@ -14,10 +14,13 @@ describe "PasswordResetController", ->
 		@PasswordResetHandler =
 		@PasswordResetHandler =
 			generateAndEmailResetToken:sinon.stub()
 			generateAndEmailResetToken:sinon.stub()
 			setNewUserPassword:sinon.stub()
 			setNewUserPassword:sinon.stub()
+		@RateLimiter = 
+			addCount: sinon.stub()
 		@PasswordResetController = SandboxedModule.require modulePath, requires:
 		@PasswordResetController = SandboxedModule.require modulePath, requires:
 			"settings-sharelatex":@settings
 			"settings-sharelatex":@settings
 			"./PasswordResetHandler":@PasswordResetHandler
 			"./PasswordResetHandler":@PasswordResetHandler
 			"logger-sharelatex": log:->
 			"logger-sharelatex": log:->
+			"../../infrastructure/RateLimiter":@RateLimiter
 
 
 		@email = "bob@bob.com "
 		@email = "bob@bob.com "
 		@token = "my security token that was emailed to me"
 		@token = "my security token that was emailed to me"
@@ -28,12 +31,23 @@ describe "PasswordResetController", ->
 				passwordResetToken:@token
 				passwordResetToken:@token
 				password:@password
 				password:@password
 
 
-		@res ={}
+		@res = {}
 
 
 
 
 	describe "requestReset", ->
 	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)->
 		it "should tell the handler to process that email", (done)->
+			@RateLimiter.addCount.callsArgWith(1, null, true)
 			@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1)
 			@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1)
 			@res.send = (code)=>
 			@res.send = (code)=>
 				code.should.equal 200
 				code.should.equal 200
@@ -41,8 +55,8 @@ describe "PasswordResetController", ->
 				done()
 				done()
 			@PasswordResetController.requestReset @req, @res
 			@PasswordResetController.requestReset @req, @res
 
 
-
 		it "should send a 500 if there is an error", (done)->
 		it "should send a 500 if there is an error", (done)->
+			@RateLimiter.addCount.callsArgWith(1, null, true)
 			@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, "error")
 			@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, "error")
 			@res.send = (code)=>
 			@res.send = (code)=>
 				code.should.equal 500
 				code.should.equal 500
@@ -75,8 +89,6 @@ describe "PasswordResetController", ->
 				done()
 				done()
 			@PasswordResetController.setNewUserPassword @req, @res
 			@PasswordResetController.setNewUserPassword @req, @res
 
 
-
-
 		it "should error if there is no password", (done)->
 		it "should error if there is no password", (done)->
 			@req.body.passwordResetToken = ""
 			@req.body.passwordResetToken = ""
 			@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
 			@PasswordResetHandler.setNewUserPassword.callsArgWith(2)