AuthenticationManagerTests.coffee 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/Features/Authentication/AuthenticationManager.js"
  6. SandboxedModule = require('sandboxed-module')
  7. events = require "events"
  8. ObjectId = require("mongojs").ObjectId
  9. describe "AuthenticationManager", ->
  10. beforeEach ->
  11. @settings = { security: { bcryptRounds: 12 } }
  12. @AuthenticationManager = SandboxedModule.require modulePath, requires:
  13. "../../models/User": User: @User = {}
  14. "../../infrastructure/mongojs":
  15. db: @db =
  16. users: {}
  17. ObjectId: ObjectId
  18. "bcrypt": @bcrypt = {}
  19. "settings-sharelatex": @settings
  20. @callback = sinon.stub()
  21. describe "authenticate", ->
  22. describe "when the user exists in the database", ->
  23. beforeEach ->
  24. @user =
  25. _id: "user-id"
  26. email: @email = "USER@sharelatex.com"
  27. @unencryptedPassword = "banana"
  28. @User.findOne = sinon.stub().callsArgWith(1, null, @user)
  29. describe "when the hashed password matches", ->
  30. beforeEach (done) ->
  31. @user.hashedPassword = @hashedPassword = "asdfjadflasdf"
  32. @bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
  33. @bcrypt.getRounds = sinon.stub().returns 12
  34. @AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
  35. @callback(error, user)
  36. done()
  37. it "should look up the correct user in the database", ->
  38. @User.findOne.calledWith(email: @email).should.equal true
  39. it "should check that the passwords match", ->
  40. @bcrypt.compare
  41. .calledWith(@unencryptedPassword, @hashedPassword)
  42. .should.equal true
  43. it "should return the user", ->
  44. @callback.calledWith(null, @user).should.equal true
  45. describe "when the encrypted passwords do not match", ->
  46. beforeEach ->
  47. @AuthenticationManager._encryptPassword = sinon.stub().returns("Not the encrypted password")
  48. @AuthenticationManager.authenticate(email: @email, @unencryptedPassword, @callback)
  49. it "should not return the user", ->
  50. @callback.calledWith(null, null).should.equal true
  51. describe "when the hashed password matches but the number of rounds is too low", ->
  52. beforeEach (done) ->
  53. @user.hashedPassword = @hashedPassword = "asdfjadflasdf"
  54. @bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
  55. @bcrypt.getRounds = sinon.stub().returns 7
  56. @AuthenticationManager.setUserPassword = sinon.stub().callsArgWith(2, null)
  57. @AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
  58. @callback(error, user)
  59. done()
  60. it "should look up the correct user in the database", ->
  61. @User.findOne.calledWith(email: @email).should.equal true
  62. it "should check that the passwords match", ->
  63. @bcrypt.compare
  64. .calledWith(@unencryptedPassword, @hashedPassword)
  65. .should.equal true
  66. it "should check the number of rounds", ->
  67. @bcrypt.getRounds.called.should.equal true
  68. it "should set the users password (with a higher number of rounds)", ->
  69. @AuthenticationManager.setUserPassword
  70. .calledWith("user-id", @unencryptedPassword)
  71. .should.equal true
  72. it "should return the user", ->
  73. @callback.calledWith(null, @user).should.equal true
  74. describe "when the user does not exist in the database", ->
  75. beforeEach ->
  76. @User.findOne = sinon.stub().callsArgWith(1, null, null)
  77. @AuthenticationManager.authenticate(email: @email, @unencrpytedPassword, @callback)
  78. it "should not return a user", ->
  79. @callback.calledWith(null, null).should.equal true
  80. describe "validateEmail", ->
  81. describe "valid", ->
  82. it "should return null", ->
  83. result = @AuthenticationManager.validateEmail 'foo@example.com'
  84. expect(result).to.equal null
  85. describe "invalid", ->
  86. it "should return validation error object for no email", ->
  87. result = @AuthenticationManager.validateEmail ''
  88. expect(result).to.not.equal null
  89. expect(result.message).to.equal 'email not valid'
  90. it "should return validation error object for invalid", ->
  91. result = @AuthenticationManager.validateEmail 'notanemail'
  92. expect(result).to.not.equal null
  93. expect(result.message).to.equal 'email not valid'
  94. describe "validatePassword", ->
  95. it "should return null if valid", ->
  96. result = @AuthenticationManager.validatePassword 'banana'
  97. expect(result).to.equal null
  98. describe "invalid", ->
  99. beforeEach ->
  100. @settings.passwordStrengthOptions =
  101. length:
  102. max:10
  103. min:6
  104. it "should return validation error object if not set", ->
  105. result = @AuthenticationManager.validatePassword()
  106. expect(result).to.not.equal null
  107. expect(result.message).to.equal 'password not set'
  108. it "should return validation error object if too short", ->
  109. result = @AuthenticationManager.validatePassword 'dsd'
  110. expect(result).to.not.equal null
  111. expect(result.message).to.equal 'password is too short'
  112. it "should return validation error object if too long", ->
  113. result = @AuthenticationManager.validatePassword 'dsdsadsadsadsadsadkjsadjsadjsadljs'
  114. expect(result).to.not.equal null
  115. expect(result.message).to.equal 'password is too long'
  116. describe "setUserPassword", ->
  117. beforeEach ->
  118. @user_id = ObjectId()
  119. @password = "banana"
  120. @hashedPassword = "asdkjfa;osiuvandf"
  121. @salt = "saltaasdfasdfasdf"
  122. @bcrypt.genSalt = sinon.stub().callsArgWith(1, null, @salt)
  123. @bcrypt.hash = sinon.stub().callsArgWith(2, null, @hashedPassword)
  124. @db.users.update = sinon.stub().callsArg(2)
  125. describe "too long", ->
  126. beforeEach ->
  127. @settings.passwordStrengthOptions =
  128. length:
  129. max:10
  130. @password = "dsdsadsadsadsadsadkjsadjsadjsadljs"
  131. it "should return and error", (done)->
  132. @AuthenticationManager.setUserPassword @user_id, @password, (err)->
  133. expect(err).to.exist
  134. done()
  135. it "should not start the bcrypt process", (done)->
  136. @AuthenticationManager.setUserPassword @user_id, @password, (err)=>
  137. @bcrypt.genSalt.called.should.equal false
  138. @bcrypt.hash.called.should.equal false
  139. done()
  140. describe "too short", ->
  141. beforeEach ->
  142. @settings.passwordStrengthOptions =
  143. length:
  144. max:10
  145. min:6
  146. @password = "dsd"
  147. it "should return and error", (done)->
  148. @AuthenticationManager.setUserPassword @user_id, @password, (err)->
  149. expect(err).to.exist
  150. done()
  151. it "should not start the bcrypt process", (done)->
  152. @AuthenticationManager.setUserPassword @user_id, @password, (err)=>
  153. @bcrypt.genSalt.called.should.equal false
  154. @bcrypt.hash.called.should.equal false
  155. done()
  156. describe "successful set", ->
  157. beforeEach ->
  158. @AuthenticationManager.setUserPassword(@user_id, @password, @callback)
  159. it "should update the user's password in the database", ->
  160. args = @db.users.update.lastCall.args
  161. expect(args[0]).to.deep.equal {_id: ObjectId(@user_id.toString())}
  162. expect(args[1]).to.deep.equal {
  163. $set: {
  164. "hashedPassword": @hashedPassword
  165. }
  166. $unset: password: true
  167. }
  168. it "should hash the password", ->
  169. @bcrypt.genSalt
  170. .calledWith(12)
  171. .should.equal true
  172. @bcrypt.hash
  173. .calledWith(@password, @salt)
  174. .should.equal true
  175. it "should call the callback", ->
  176. @callback.called.should.equal true