mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Prevent registration of new accounts with existing secondary emails (#1696)
Prevent registration of new accounts with existing secondary emails GitOrigin-RevId: 004cf9d31064fc5b7deb621c95c38f103397ff15
This commit is contained in:
parent
f583ffdbff
commit
5b7974065d
4 changed files with 64 additions and 28 deletions
|
@ -24,11 +24,13 @@ module.exports =
|
||||||
RateLimiter.addCount opts, (err, canContinue)->
|
RateLimiter.addCount opts, (err, canContinue)->
|
||||||
if !canContinue
|
if !canContinue
|
||||||
return res.send 429, { message: req.i18n.translate("rate_limit_hit_wait")}
|
return res.send 429, { message: req.i18n.translate("rate_limit_hit_wait")}
|
||||||
PasswordResetHandler.generateAndEmailResetToken email, (err, exists)->
|
PasswordResetHandler.generateAndEmailResetToken email, (err, status)->
|
||||||
if err?
|
if err?
|
||||||
res.send 500, {message:err?.message}
|
res.send 500, {message:err?.message}
|
||||||
else if exists
|
else if status == 'primary'
|
||||||
res.send 200, {message: {text: req.i18n.translate("password_reset_email_sent")}}
|
res.send 200, {message: {text: req.i18n.translate("password_reset_email_sent")}}
|
||||||
|
else if status == 'secondary'
|
||||||
|
res.send 404, {message: req.i18n.translate("secondary_email_password_reset")}
|
||||||
else
|
else
|
||||||
res.send 404, {message: req.i18n.translate("cant_find_email")}
|
res.send 404, {message: req.i18n.translate("cant_find_email")}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,11 @@ V1Api = require("../V1/V1Api")
|
||||||
|
|
||||||
module.exports = PasswordResetHandler =
|
module.exports = PasswordResetHandler =
|
||||||
|
|
||||||
generateAndEmailResetToken:(email, callback = (error, exists) ->)->
|
generateAndEmailResetToken:(email, callback = (error, status) ->)->
|
||||||
PasswordResetHandler._getPasswordResetData email, (error, exists, data) ->
|
PasswordResetHandler._getPasswordResetData email, (error, exists, data) ->
|
||||||
if error? or !exists
|
if error?
|
||||||
return callback(error, exists)
|
return callback(error, null)
|
||||||
|
else if exists
|
||||||
OneTimeTokenHandler.getNewToken 'password', data, (err, token)->
|
OneTimeTokenHandler.getNewToken 'password', data, (err, token)->
|
||||||
if err then return callback(err)
|
if err then return callback(err)
|
||||||
emailOptions =
|
emailOptions =
|
||||||
|
@ -20,7 +21,13 @@ module.exports = PasswordResetHandler =
|
||||||
setNewPasswordUrl : "#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}&email=#{encodeURIComponent(email)}"
|
setNewPasswordUrl : "#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}&email=#{encodeURIComponent(email)}"
|
||||||
EmailHandler.sendEmail "passwordResetRequested", emailOptions, (error) ->
|
EmailHandler.sendEmail "passwordResetRequested", emailOptions, (error) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
callback null, true
|
callback null, 'primary'
|
||||||
|
else
|
||||||
|
UserGetter.getUserByAnyEmail email, (err, user) ->
|
||||||
|
if !user
|
||||||
|
return callback(error, null)
|
||||||
|
else
|
||||||
|
return callback(error, 'secondary')
|
||||||
|
|
||||||
setNewUserPassword: (token, password, callback = (error, found, user_id) ->)->
|
setNewUserPassword: (token, password, callback = (error, found, user_id) ->)->
|
||||||
OneTimeTokenHandler.getValueFromTokenAndExpire 'password', token, (err, data)->
|
OneTimeTokenHandler.getValueFromTokenAndExpire 'password', token, (err, data)->
|
||||||
|
|
|
@ -54,7 +54,7 @@ describe "PasswordResetController", ->
|
||||||
describe "requestReset", ->
|
describe "requestReset", ->
|
||||||
|
|
||||||
it "should error if the rate limit is hit", (done)->
|
it "should error if the rate limit is hit", (done)->
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
|
||||||
@RateLimiter.addCount.callsArgWith(1, null, false)
|
@RateLimiter.addCount.callsArgWith(1, null, false)
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 429
|
code.should.equal 429
|
||||||
|
@ -65,7 +65,7 @@ describe "PasswordResetController", ->
|
||||||
|
|
||||||
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)
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
|
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
|
||||||
|
@ -82,7 +82,15 @@ describe "PasswordResetController", ->
|
||||||
|
|
||||||
it "should send a 404 if the email doesn't exist", (done)->
|
it "should send a 404 if the email doesn't exist", (done)->
|
||||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, false)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, null)
|
||||||
|
@res.send = (code)=>
|
||||||
|
code.should.equal 404
|
||||||
|
done()
|
||||||
|
@PasswordResetController.requestReset @req, @res
|
||||||
|
|
||||||
|
it "should send a 404 if the email is registered as a secondard email", (done)->
|
||||||
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'secondary')
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 404
|
code.should.equal 404
|
||||||
done()
|
done()
|
||||||
|
@ -92,7 +100,7 @@ describe "PasswordResetController", ->
|
||||||
@email = "UPerCaseEMAIL@example.Com"
|
@email = "UPerCaseEMAIL@example.Com"
|
||||||
@req.body.email = @email
|
@req.body.email = @email
|
||||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, 'primary')
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
|
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
|
||||||
|
|
|
@ -18,6 +18,7 @@ describe "PasswordResetHandler", ->
|
||||||
@UserGetter =
|
@UserGetter =
|
||||||
getUserByMainEmail:sinon.stub()
|
getUserByMainEmail:sinon.stub()
|
||||||
getUser: sinon.stub()
|
getUser: sinon.stub()
|
||||||
|
getUserByAnyEmail: sinon.stub()
|
||||||
@EmailHandler =
|
@EmailHandler =
|
||||||
sendEmail:sinon.stub()
|
sendEmail:sinon.stub()
|
||||||
@AuthenticationManager =
|
@AuthenticationManager =
|
||||||
|
@ -48,29 +49,31 @@ describe "PasswordResetHandler", ->
|
||||||
describe "when in ShareLaTeX", ->
|
describe "when in ShareLaTeX", ->
|
||||||
it "should check the user exists", (done)->
|
it "should check the user exists", (done)->
|
||||||
@UserGetter.getUserByMainEmail.callsArgWith(1)
|
@UserGetter.getUserByMainEmail.callsArgWith(1)
|
||||||
|
@UserGetter.getUserByAnyEmail.callsArgWith(1)
|
||||||
@OneTimeTokenHandler.getNewToken.yields()
|
@OneTimeTokenHandler.getNewToken.yields()
|
||||||
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=>
|
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, status)=>
|
||||||
exists.should.equal false
|
should.equal(status, null)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should send the email with the token", (done)->
|
it "should send the email with the token", (done)->
|
||||||
@UserGetter.getUserByMainEmail.callsArgWith(1, null, @user)
|
@UserGetter.getUserByMainEmail.callsArgWith(1, null, @user)
|
||||||
@OneTimeTokenHandler.getNewToken.yields(null, @token)
|
@OneTimeTokenHandler.getNewToken.yields(null, @token)
|
||||||
@EmailHandler.sendEmail.callsArgWith(2)
|
@EmailHandler.sendEmail.callsArgWith(2)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=>
|
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, status)=>
|
||||||
@EmailHandler.sendEmail.called.should.equal true
|
@EmailHandler.sendEmail.called.should.equal true
|
||||||
exists.should.equal true
|
status.should.equal 'primary'
|
||||||
args = @EmailHandler.sendEmail.args[0]
|
args = @EmailHandler.sendEmail.args[0]
|
||||||
args[0].should.equal "passwordResetRequested"
|
args[0].should.equal "passwordResetRequested"
|
||||||
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}&email=#{encodeURIComponent(@user.email)}"
|
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}&email=#{encodeURIComponent(@user.email)}"
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should return exists = false for a holdingAccount", (done) ->
|
it "should return exists == null for a holdingAccount", (done) ->
|
||||||
@user.holdingAccount = true
|
@user.holdingAccount = true
|
||||||
@UserGetter.getUserByMainEmail.callsArgWith(1, null, @user)
|
@UserGetter.getUserByMainEmail.callsArgWith(1, null, @user)
|
||||||
|
@UserGetter.getUserByAnyEmail.callsArgWith(1)
|
||||||
@OneTimeTokenHandler.getNewToken.yields()
|
@OneTimeTokenHandler.getNewToken.yields()
|
||||||
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=>
|
@PasswordResetHandler.generateAndEmailResetToken @user.email, (err, status)=>
|
||||||
exists.should.equal false
|
should.equal(status, null)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "when in overleaf", ->
|
describe "when in overleaf", ->
|
||||||
|
@ -105,12 +108,13 @@ describe "PasswordResetHandler", ->
|
||||||
args[0].should.equal "passwordResetRequested"
|
args[0].should.equal "passwordResetRequested"
|
||||||
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}&email=#{encodeURIComponent(@user.email)}"
|
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}&email=#{encodeURIComponent(@user.email)}"
|
||||||
|
|
||||||
it 'should return exists == true', ->
|
it 'should return status == true', ->
|
||||||
@callback.calledWith(null, true).should.equal true
|
@callback.calledWith(null, 'primary').should.equal true
|
||||||
|
|
||||||
describe "when the email doesn't exist", ->
|
describe "when the email doesn't exist", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@V1Api.request = sinon.stub().yields(null, { statusCode: 404 }, {})
|
@V1Api.request = sinon.stub().yields(null, { statusCode: 404 }, {})
|
||||||
|
@UserGetter.getUserByAnyEmail.callsArgWith(1)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken @email, @callback
|
@PasswordResetHandler.generateAndEmailResetToken @email, @callback
|
||||||
|
|
||||||
it 'should not set the password token data', ->
|
it 'should not set the password token data', ->
|
||||||
|
@ -120,9 +124,24 @@ describe "PasswordResetHandler", ->
|
||||||
it 'should send an email with the token', ->
|
it 'should send an email with the token', ->
|
||||||
@EmailHandler.sendEmail.called.should.equal false
|
@EmailHandler.sendEmail.called.should.equal false
|
||||||
|
|
||||||
it 'should return exists == false', ->
|
it 'should return status == null', ->
|
||||||
@callback.calledWith(null, false).should.equal true
|
@callback.calledWith(null, null).should.equal true
|
||||||
|
|
||||||
|
describe "when the email is a secondary email", ->
|
||||||
|
beforeEach ->
|
||||||
|
@V1Api.request = sinon.stub().yields(null, { statusCode: 404 }, {})
|
||||||
|
@UserGetter.getUserByAnyEmail.callsArgWith(1, null, @user)
|
||||||
|
@PasswordResetHandler.generateAndEmailResetToken @email, @callback
|
||||||
|
|
||||||
|
it 'should not set the password token data', ->
|
||||||
|
@OneTimeTokenHandler.getNewToken
|
||||||
|
.called.should.equal false
|
||||||
|
|
||||||
|
it 'should not send an email with the token', ->
|
||||||
|
@EmailHandler.sendEmail.called.should.equal false
|
||||||
|
|
||||||
|
it 'should return status == secondary', ->
|
||||||
|
@callback.calledWith(null, 'secondary').should.equal true
|
||||||
|
|
||||||
describe "setNewUserPassword", ->
|
describe "setNewUserPassword", ->
|
||||||
describe "when no data is found", ->
|
describe "when no data is found", ->
|
||||||
|
|
Loading…
Reference in a new issue