Merge pull request #1356 from sharelatex/spd-password-complexity

Make password validation more consistent between backend and frontend

GitOrigin-RevId: 6ba729da842bf474cf7e9b5e0b2435db0544737c
This commit is contained in:
Simon Detheridge
2019-01-11 14:43:49 +00:00
committed by sharelatex
parent f49523f6bc
commit 4c191953d3
9 changed files with 225 additions and 113 deletions
@@ -115,31 +115,83 @@ describe "AuthenticationManager", ->
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
beforeEach ->
# 73 characters:
@longPassword = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678'
describe "invalid", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
max:10
min:6
describe "with a null password", ->
it "should return an error", ->
expect(@AuthenticationManager.validatePassword()).to.eql { message: 'password not set' }
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'
describe "password length", ->
describe "with the default password length options", ->
it "should reject passwords that are too short", ->
expect(@AuthenticationManager.validatePassword('')).to.eql { message: 'password is too short' }
expect(@AuthenticationManager.validatePassword('foo')).to.eql { message: 'password is too short' }
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 reject passwords that are too long", ->
expect(@AuthenticationManager.validatePassword(@longPassword)).to.eql { message: 'password is too long' }
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'
it "should accept passwords that are a good length", ->
expect(@AuthenticationManager.validatePassword('l337h4x0r')).to.equal null
describe "when the password length is specified in settings", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
min: 10
max: 12
it "should reject passwords that are too short", ->
expect(@AuthenticationManager.validatePassword('012345678')).to.eql { message: 'password is too short' }
it "should accept passwords of exactly minimum length", ->
expect(@AuthenticationManager.validatePassword('0123456789')).to.equal null
it "should reject passwords that are too long", ->
expect(@AuthenticationManager.validatePassword('0123456789abc')).to.eql { message: 'password is too long' }
it "should accept passwords of exactly maximum length", ->
expect(@AuthenticationManager.validatePassword('0123456789ab')).to.equal null
describe "when the maximum password length is set to >72 characters in settings", ->
beforeEach ->
@settings.passwordStrengthOptions =
length:
max: 128
it "should still reject passwords > 72 characters in length", ->
expect(@AuthenticationManager.validatePassword(@longPassword)).to.eql { message: 'password is too long' }
describe "allowed characters", ->
describe "with the default settings for allowed characters", ->
it "should allow passwords with valid characters", ->
expect(@AuthenticationManager.validatePassword("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")).to.equal null
expect(@AuthenticationManager.validatePassword("1234567890@#$%^&*()-_=+[]{};:<>/?!£€.,")).to.equal null
it "should not allow passwords with invalid characters", ->
expect(@AuthenticationManager.validatePassword("correct horse battery staple")).to.eql { message: 'password contains an invalid character' }
describe "when valid characters are overridden in settings", ->
beforeEach ->
@settings.passwordStrengthOptions =
chars:
symbols: " "
it "should allow passwords with valid characters", ->
expect(@AuthenticationManager.validatePassword("correct horse battery staple")).to.equal null
it "should disallow passwords with invalid characters", ->
expect(@AuthenticationManager.validatePassword("1234567890@#$%^&*()-_=+[]{};:<>/?!£€.,")).to.eql { message: 'password contains an invalid character' }
describe "when allowAnyChars is set", ->
beforeEach ->
@settings.passwordStrengthOptions =
allowAnyChars: true
it "should allow any characters", ->
expect(@AuthenticationManager.validatePassword("correct horse battery staple")).to.equal null
expect(@AuthenticationManager.validatePassword("1234567890@#$%^&*()-_=+[]{};:<>/?!£€.,")).to.equal null
describe "setUserPassword", ->
beforeEach ->
@@ -19,12 +19,15 @@ describe "PasswordResetController", ->
addCount: sinon.stub()
@UserSessionsManager =
revokeAllUserSessions: sinon.stub().callsArgWith(2, null)
@AuthenticationManager =
validatePassword: sinon.stub()
@PasswordResetController = SandboxedModule.require modulePath, requires:
"settings-sharelatex":@settings
"./PasswordResetHandler":@PasswordResetHandler
"logger-sharelatex": log:->
"../../infrastructure/RateLimiter":@RateLimiter
"../Authentication/AuthenticationController": @AuthenticationController = {}
"../Authentication/AuthenticationManager": @AuthenticationManager
"../User/UserGetter": @UserGetter = {}
"../User/UserSessionsManager": @UserSessionsManager
@@ -131,6 +134,16 @@ describe "PasswordResetController", ->
done()
@PasswordResetController.setNewUserPassword @req, @res
it "should return 400 (Bad Request) if the password is invalid", (done)->
@req.body.password = "correct horse battery staple"
@AuthenticationManager.validatePassword = sinon.stub().returns { message: 'password contains invalid characters' }
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
@res.sendStatus = (code)=>
code.should.equal 400
@PasswordResetHandler.setNewUserPassword.called.should.equal false
done()
@PasswordResetController.setNewUserPassword @req, @res
it "should clear the session.resetToken", (done) ->
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true, @user_id)
@res.sendStatus = (code)=>
@@ -47,6 +47,7 @@ describe "UserController", ->
@AuthenticationManager =
authenticate: sinon.stub()
setUserPassword: sinon.stub()
validatePassword: sinon.stub()
@ReferalAllocator =
allocate:sinon.stub()
@SubscriptionDomainHandler =
@@ -379,7 +380,6 @@ describe "UserController", ->
done()
@UserController.changePassword @req, @res
it "it should not set the new password if they do not match", (done)->
@AuthenticationManager.authenticate.callsArgWith(2, null, {})
@req.body =
@@ -400,3 +400,14 @@ describe "UserController", ->
@AuthenticationManager.setUserPassword.calledWith(@user._id, "newpass").should.equal true
done()
@UserController.changePassword @req, @res
it "it should not set the new password if it is invalid", (done)->
@AuthenticationManager.validatePassword = sinon.stub().returns { message: 'password contains invalid characters' }
@AuthenticationManager.authenticate.callsArgWith(2, null, {})
@req.body =
newPassword1: "correct horse battery staple"
newPassword2: "correct horse battery staple"
@res.send = =>
@AuthenticationManager.setUserPassword.called.should.equal false
done()
@UserController.changePassword @req, @res