PasswordResetControllerTests.coffee 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. should = require('chai').should()
  2. expect = require("chai").expect
  3. SandboxedModule = require('sandboxed-module')
  4. assert = require('assert')
  5. path = require('path')
  6. sinon = require('sinon')
  7. modulePath = path.join __dirname, "../../../../app/js/Features/PasswordReset/PasswordResetController"
  8. expect = require("chai").expect
  9. describe "PasswordResetController", ->
  10. beforeEach ->
  11. @settings = {}
  12. @PasswordResetHandler =
  13. generateAndEmailResetToken:sinon.stub()
  14. setNewUserPassword:sinon.stub()
  15. @RateLimiter =
  16. addCount: sinon.stub()
  17. @UserSessionsManager =
  18. revokeAllUserSessions: sinon.stub().callsArgWith(2, null)
  19. @AuthenticationManager =
  20. validatePassword: sinon.stub()
  21. @UserUpdater =
  22. removeReconfirmFlag: sinon.stub().callsArgWith(1, null)
  23. @PasswordResetController = SandboxedModule.require modulePath, requires:
  24. "settings-sharelatex":@settings
  25. "./PasswordResetHandler":@PasswordResetHandler
  26. "logger-sharelatex": log:->
  27. "../../infrastructure/RateLimiter":@RateLimiter
  28. "../Authentication/AuthenticationController": @AuthenticationController = {}
  29. "../Authentication/AuthenticationManager": @AuthenticationManager
  30. "../User/UserGetter": @UserGetter = {}
  31. "../User/UserSessionsManager": @UserSessionsManager
  32. "../User/UserUpdater": @UserUpdater
  33. @email = "bob@bob.com "
  34. @user_id = 'mock-user-id'
  35. @token = "my security token that was emailed to me"
  36. @password = "my new password"
  37. @req =
  38. body:
  39. email:@email
  40. passwordResetToken:@token
  41. password:@password
  42. i18n:
  43. translate:->
  44. session: {}
  45. query: {}
  46. @res = {}
  47. describe "requestReset", ->
  48. it "should error if the rate limit is hit", (done)->
  49. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
  50. @RateLimiter.addCount.callsArgWith(1, null, false)
  51. @res.send = (code)=>
  52. code.should.equal 429
  53. @PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal false
  54. done()
  55. @PasswordResetController.requestReset @req, @res
  56. it "should tell the handler to process that email", (done)->
  57. @RateLimiter.addCount.callsArgWith(1, null, true)
  58. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
  59. @res.send = (code)=>
  60. code.should.equal 200
  61. @PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
  62. done()
  63. @PasswordResetController.requestReset @req, @res
  64. it "should send a 500 if there is an error", (done)->
  65. @RateLimiter.addCount.callsArgWith(1, null, true)
  66. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, "error")
  67. @res.send = (code)=>
  68. code.should.equal 500
  69. done()
  70. @PasswordResetController.requestReset @req, @res
  71. it "should send a 404 if the email doesn't exist", (done)->
  72. @RateLimiter.addCount.callsArgWith(1, null, true)
  73. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, null)
  74. @res.send = (code)=>
  75. code.should.equal 404
  76. done()
  77. @PasswordResetController.requestReset @req, @res
  78. it "should send a 404 if the email is registered as a secondard email", (done)->
  79. @RateLimiter.addCount.callsArgWith(1, null, true)
  80. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'secondary')
  81. @res.send = (code)=>
  82. code.should.equal 404
  83. done()
  84. @PasswordResetController.requestReset @req, @res
  85. it "should lowercase the email address", (done)->
  86. @email = "UPerCaseEMAIL@example.Com"
  87. @req.body.email = @email
  88. @RateLimiter.addCount.callsArgWith(1, null, true)
  89. @PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
  90. @res.send = (code)=>
  91. code.should.equal 200
  92. @PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
  93. done()
  94. @PasswordResetController.requestReset @req, @res
  95. describe "setNewUserPassword", ->
  96. beforeEach ->
  97. @req.session.resetToken = @token
  98. it "should tell the user handler to reset the password", (done)->
  99. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id)
  100. @res.sendStatus = (code)=>
  101. code.should.equal 200
  102. @PasswordResetHandler.setNewUserPassword.calledWith(@token, @password).should.equal true
  103. done()
  104. @PasswordResetController.setNewUserPassword @req, @res
  105. it "should send 404 if the token didn't work", (done)->
  106. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, false, @user_id)
  107. @res.sendStatus = (code)=>
  108. code.should.equal 404
  109. done()
  110. @PasswordResetController.setNewUserPassword @req, @res
  111. it "should return 400 (Bad Request) if there is no password", (done)->
  112. @req.body.password = ""
  113. @PasswordResetHandler.setNewUserPassword.callsArgWith(2)
  114. @res.sendStatus = (code)=>
  115. code.should.equal 400
  116. @PasswordResetHandler.setNewUserPassword.called.should.equal false
  117. done()
  118. @PasswordResetController.setNewUserPassword @req, @res
  119. it "should return 400 (Bad Request) if there is no passwordResetToken", (done)->
  120. @req.body.passwordResetToken = ""
  121. @PasswordResetHandler.setNewUserPassword.callsArgWith(2)
  122. @res.sendStatus = (code)=>
  123. code.should.equal 400
  124. @PasswordResetHandler.setNewUserPassword.called.should.equal false
  125. done()
  126. @PasswordResetController.setNewUserPassword @req, @res
  127. it "should return 400 (Bad Request) if the password is invalid", (done)->
  128. @req.body.password = "correct horse battery staple"
  129. @AuthenticationManager.validatePassword = sinon.stub().returns { message: 'password contains invalid characters' }
  130. @PasswordResetHandler.setNewUserPassword.callsArgWith(2)
  131. @res.sendStatus = (code)=>
  132. code.should.equal 400
  133. @PasswordResetHandler.setNewUserPassword.called.should.equal false
  134. done()
  135. @PasswordResetController.setNewUserPassword @req, @res
  136. it "should clear the session.resetToken", (done) ->
  137. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id)
  138. @res.sendStatus = (code)=>
  139. code.should.equal 200
  140. @req.session.should.not.have.property 'resetToken'
  141. done()
  142. @PasswordResetController.setNewUserPassword @req, @res
  143. it 'should clear sessions', (done) ->
  144. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id)
  145. @res.sendStatus = (code)=>
  146. @UserSessionsManager.revokeAllUserSessions.callCount.should.equal 1
  147. done()
  148. @PasswordResetController.setNewUserPassword @req, @res
  149. it 'should call removeReconfirmFlag', (done) ->
  150. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id)
  151. @res.sendStatus = (code)=>
  152. @UserUpdater.removeReconfirmFlag.callCount.should.equal 1
  153. done()
  154. @PasswordResetController.setNewUserPassword @req, @res
  155. describe 'when login_after is set', ->
  156. beforeEach ->
  157. @UserGetter.getUser = sinon.stub().callsArgWith(2, null, { email: "joe@example.com" })
  158. @PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id = "user-id-123")
  159. @req.body.login_after = "true"
  160. @res.json = sinon.stub()
  161. @AuthenticationController.afterLoginSessionSetup = sinon.stub().callsArgWith(2, null)
  162. @AuthenticationController._getRedirectFromSession = sinon.stub().returns('/some/path')
  163. it "should login user if login_after is set", (done) ->
  164. @PasswordResetController.setNewUserPassword @req, @res
  165. @AuthenticationController.afterLoginSessionSetup.callCount.should.equal 1
  166. @AuthenticationController.afterLoginSessionSetup.calledWith(
  167. @req,
  168. {email: 'joe@example.com'}
  169. ).should.equal true
  170. @AuthenticationController._getRedirectFromSession.callCount.should.equal 1
  171. @res.json.callCount.should.equal 1
  172. @res.json.calledWith({redir: '/some/path'}).should.equal true
  173. done()
  174. describe "renderSetPasswordForm", ->
  175. describe "with token in query-string", ->
  176. beforeEach ->
  177. @req.query.passwordResetToken = @token
  178. it "should set session.resetToken and redirect", (done) ->
  179. @req.session.should.not.have.property 'resetToken'
  180. @res.redirect = (path) =>
  181. path.should.equal '/user/password/set'
  182. @req.session.resetToken.should.equal @token
  183. done()
  184. @PasswordResetController.renderSetPasswordForm(@req, @res)
  185. describe "without a token in query-string", ->
  186. describe "with token in session", ->
  187. beforeEach ->
  188. @req.session.resetToken = @token
  189. it "should render the page, passing the reset token", (done) ->
  190. @res.render = (template_path, options) =>
  191. options.passwordResetToken.should.equal @req.session.resetToken
  192. done()
  193. @PasswordResetController.renderSetPasswordForm(@req, @res)
  194. describe "without a token in session", ->
  195. it "should redirect to the reset request page", (done) ->
  196. @res.redirect = (path) =>
  197. path.should.equal "/user/password/reset"
  198. @req.session.should.not.have.property 'resetToken'
  199. done()
  200. @PasswordResetController.renderSetPasswordForm(@req, @res)