overleaf/services/web/test/unit/coffee/Authentication/AuthenticationManagerTests.coffee
Jessica Lawshe 7666c8a481 Merge pull request #1236 from sharelatex/jel-password-reset
Reset password via API request to v1

GitOrigin-RevId: 00b0306ca77df650595a762382a8a63b05a945f6
2018-12-14 16:02:14 +00:00

249 lines
8.7 KiB
CoffeeScript

sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/Features/Authentication/AuthenticationManager.js"
SandboxedModule = require('sandboxed-module')
events = require "events"
ObjectId = require("mongojs").ObjectId
Errors = require "../../../../app/js/Features/Errors/Errors"
describe "AuthenticationManager", ->
beforeEach ->
@settings = { security: { bcryptRounds: 12 } }
@AuthenticationManager = SandboxedModule.require modulePath, requires:
"../../models/User": User: @User = {}
"../../infrastructure/mongojs":
db: @db =
users: {}
ObjectId: ObjectId
"bcrypt": @bcrypt = {}
"settings-sharelatex": @settings
"../V1/V1Handler": @V1Handler = {}
"../User/UserGetter": @UserGetter = {}
@callback = sinon.stub()
describe "authenticate", ->
describe "when the user exists in the database", ->
beforeEach ->
@user =
_id: "user-id"
email: @email = "USER@sharelatex.com"
@unencryptedPassword = "banana"
@User.findOne = sinon.stub().callsArgWith(1, null, @user)
describe "when the hashed password matches", ->
beforeEach (done) ->
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
@bcrypt.getRounds = sinon.stub().returns 12
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
@callback(error, user)
done()
it "should look up the correct user in the database", ->
@User.findOne.calledWith(email: @email).should.equal true
it "should check that the passwords match", ->
@bcrypt.compare
.calledWith(@unencryptedPassword, @hashedPassword)
.should.equal true
it "should return the user", ->
@callback.calledWith(null, @user).should.equal true
describe "when the encrypted passwords do not match", ->
beforeEach ->
@AuthenticationManager._encryptPassword = sinon.stub().returns("Not the encrypted password")
@AuthenticationManager.authenticate(email: @email, @unencryptedPassword, @callback)
it "should not return the user", ->
@callback.calledWith(null, null).should.equal true
describe "when the hashed password matches but the number of rounds is too low", ->
beforeEach (done) ->
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
@bcrypt.getRounds = sinon.stub().returns 7
@AuthenticationManager.setUserPassword = sinon.stub().callsArgWith(2, null)
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
@callback(error, user)
done()
it "should look up the correct user in the database", ->
@User.findOne.calledWith(email: @email).should.equal true
it "should check that the passwords match", ->
@bcrypt.compare
.calledWith(@unencryptedPassword, @hashedPassword)
.should.equal true
it "should check the number of rounds", ->
@bcrypt.getRounds.called.should.equal true
it "should set the users password (with a higher number of rounds)", ->
@AuthenticationManager.setUserPassword
.calledWith("user-id", @unencryptedPassword)
.should.equal true
it "should return the user", ->
@callback.calledWith(null, @user).should.equal true
describe "when the user does not exist in the database", ->
beforeEach ->
@User.findOne = sinon.stub().callsArgWith(1, null, null)
@AuthenticationManager.authenticate(email: @email, @unencrpytedPassword, @callback)
it "should not return a user", ->
@callback.calledWith(null, null).should.equal true
describe "validateEmail", ->
describe "valid", ->
it "should return null", ->
result = @AuthenticationManager.validateEmail 'foo@example.com'
expect(result).to.equal null
describe "invalid", ->
it "should return validation error object for no email", ->
result = @AuthenticationManager.validateEmail ''
expect(result).to.not.equal null
expect(result.message).to.equal 'email not valid'
it "should return validation error object for invalid", ->
result = @AuthenticationManager.validateEmail 'notanemail'
expect(result).to.not.equal null
expect(result.message).to.equal 'email not valid'
describe "validatePassword", ->
it "should return null if valid", ->
result = @AuthenticationManager.validatePassword 'banana'
expect(result).to.equal null
describe "invalid", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
max:10
min:6
it "should return validation error object if not set", ->
result = @AuthenticationManager.validatePassword()
expect(result).to.not.equal null
expect(result.message).to.equal 'password not set'
it "should return validation error object if too short", ->
result = @AuthenticationManager.validatePassword 'dsd'
expect(result).to.not.equal null
expect(result.message).to.equal 'password is too short'
it "should return validation error object if too long", ->
result = @AuthenticationManager.validatePassword 'dsdsadsadsadsadsadkjsadjsadjsadljs'
expect(result).to.not.equal null
expect(result.message).to.equal 'password is too long'
describe "setUserPassword", ->
beforeEach ->
@user_id = ObjectId()
@password = "banana"
@hashedPassword = "asdkjfa;osiuvandf"
@salt = "saltaasdfasdfasdf"
@bcrypt.genSalt = sinon.stub().callsArgWith(1, null, @salt)
@bcrypt.hash = sinon.stub().callsArgWith(2, null, @hashedPassword)
@db.users.update = sinon.stub().callsArg(2)
describe "too long", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
max:10
@password = "dsdsadsadsadsadsadkjsadjsadjsadljs"
it "should return and error", (done)->
@AuthenticationManager.setUserPassword @user_id, @password, (err)->
expect(err).to.exist
done()
it "should not start the bcrypt process", (done)->
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
@bcrypt.genSalt.called.should.equal false
@bcrypt.hash.called.should.equal false
done()
describe "too short", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
max:10
min:6
@password = "dsd"
it "should return and error", (done)->
@AuthenticationManager.setUserPassword @user_id, @password, (err)->
expect(err).to.exist
done()
it "should not start the bcrypt process", (done)->
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
@bcrypt.genSalt.called.should.equal false
@bcrypt.hash.called.should.equal false
done()
describe "password set attempt", ->
describe "with SL user in SL", ->
beforeEach ->
@UserGetter.getUser = sinon.stub().yields(null, { overleaf: null })
@AuthenticationManager.setUserPassword(@user_id, @password, @callback)
it 'should look up the user', ->
@UserGetter.getUser.calledWith(@user_id).should.equal true
it "should update the user's password in the database", ->
args = @db.users.update.lastCall.args
expect(args[0]).to.deep.equal {_id: ObjectId(@user_id.toString())}
expect(args[1]).to.deep.equal {
$set: {
"hashedPassword": @hashedPassword
}
$unset: password: true
}
it "should hash the password", ->
@bcrypt.genSalt
.calledWith(12)
.should.equal true
@bcrypt.hash
.calledWith(@password, @salt)
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true
describe "with SL user in v2", ->
beforeEach (done) ->
@settings.overleaf = true
@UserGetter.getUser = sinon.stub().yields(null, { overleaf: null })
@AuthenticationManager.setUserPassword @user_id, @password, (err, changed) =>
@callback(err, changed)
done()
it "should error", ->
@callback.calledWith(new Errors.SLInV2Error("Password Reset Attempt")).should.equal true
describe "with v2 user in SL", ->
beforeEach (done) ->
@UserGetter.getUser = sinon.stub().yields(null, { overleaf: {id: 1} })
@AuthenticationManager.setUserPassword @user_id, @password, (err, changed) =>
@callback(err, changed)
done()
it "should error", ->
@callback.calledWith(new Errors.NotInV2Error("Password Reset Attempt")).should.equal true
describe "with v2 user in v2", ->
beforeEach (done) ->
@settings.overleaf = true
@UserGetter.getUser = sinon.stub().yields(null, { overleaf: {id: 1} })
@V1Handler.doPasswordReset = sinon.stub().yields(null, true)
@AuthenticationManager.setUserPassword @user_id, @password, (err, changed) =>
@callback(err, changed)
done()
it "should set the password in v2", ->
@callback.calledWith(null, true).should.equal true