token based reset works

This commit is contained in:
Henry Oswald 2014-05-15 17:58:25 +01:00
parent 1ffd19099b
commit 96d98329f1
8 changed files with 50 additions and 24 deletions

View file

@ -37,16 +37,30 @@ Henry <br>
ShareLaTeX Co-founder
'''
templates.passwordReset =
templates.passwordResetRequested =
subject: _.template "Password Reset - ShareLatex.com"
layout: NotificationEmailLayout
type:"notification"
compiledTemplate: _.template '''
<h1 class="h1">Password Reset</h1>
<p>
Your password has been reset, the new password is <p> <%= newPassword %>
We got a request to reset your ShareLaTeX password.
<p>
please <a href="https://www.sharelatex.com/login">login here</a> and then change your password <a href=#{Settings.siteUrl}/user/settings> in your user settings</a>
<center>
<div style="width:200px;background-color:#0069CC;border:1px solid #02A9D6;border-radius:4px;padding:15px; margin:10px 5px">
<div style="padding-right:10px;padding-left:10px">
<a href="<%= setNewPasswordUrl %>" style="text-decoration:none" target="_blank">
<span style= "font-size:16px;font-family:Arial;font-weight:bold;color:#fff;white-space:nowrap;display:block; text-align:center">
Reset password
</span>
</a>
</div>
</div>
</center>
If you ignore this message, your password won't be changed.
<p>
If you didn't request a password reset, let us know.
</p>
<p>Thank you</p>
@ -79,6 +93,7 @@ module.exports =
buildEmail: (templateName, opts)->
template = templates[templateName]
console.log opts
opts.body = template.compiledTemplate(opts)
return {
subject : template.subject(opts)

View file

@ -16,7 +16,7 @@ module.exports =
if err then return callback(err)
emailOptions =
to : email
setNewPasswordUrl : "#{settings.siteUrl}/user/password/set?resetToken=#{token}"
setNewPasswordUrl : "#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}"
EmailHandler.sendEmail "passwordResetRequested", emailOptions, callback
setNewUserPassword: (token, password, callback)->

View file

@ -4,23 +4,24 @@ rclient = redis.createClient(Settings.redis.web.port, Settings.redis.web.host)
rclient.auth(Settings.redis.web.password)
uuid = require("node-uuid")
ONE_MIN = 60 * 1000
ONE_HOUR_IN_MS = ONE_MIN * 60
ONE_HOUR_IN_S = 60 * 60
buildKey = (token)-> return "password_token:#{token}"
module.exports =
getNewToken: (user_id, callback)->
token = uuid.v4()
multi = rclient.multi()
multi.set token, user_id
multi.expire token, ONE_HOUR_IN_MS
multi.set buildKey(token), user_id
multi.expire buildKey(token), ONE_HOUR_IN_S
multi.exec (err)->
callback(err, token)
getUserIdFromToken: (token, callback)->
multi = rclient.multi()
multi.get token
multi.del token
multi.get buildKey(token)
multi.del buildKey(token)
multi.exec (err, results)->
callback err, results[0]

View file

@ -20,5 +20,5 @@ block content
input.span4#password.required(type='password', name='password', placeholder='********')
.actions
button.btn-primary.btn.btn-large#login(type='submit') Login
a#passwordReset(href='/user/passwordreset') forgot password?
a#passwordReset(href='/user/password/reset') forgot password?
include ../general/small-footer

View file

@ -7,7 +7,7 @@ block content
.page-header
h1 Set Password
.messageArea
form.validate#passwordReset(method='post')
form.validate#setPasswordReset(method='post')
input(type="hidden", name="_csrf", value=csrfToken)
.clearfix
label(for='xlInput') Password

View file

@ -93,16 +93,26 @@ require [
event.preventDefault()
formData = $(this).serialize()
$.ajax
url: "/user/passwordReset"
url: "/user/password/reset"
type:'POST'
data: formData
success: (data)->
if data.message
new Message data.message
else if data.redir
window.location.href = data.redir
else
window.location.href = '/'
new Message text:"You have been sent an email to complete your password reset."
error:(data)->
new Message type:"error", text:"something went wrong processing your request."
$('form#setPasswordReset').submit (event)->
event.preventDefault()
formData = $(this).serialize()
$.ajax
url: "/user/password/set"
type:'POST'
data: formData
success: (data)->
new Message text:"Your password has been reset"
error:(data)->
new Message type:"error", text:"something went wrong processing your request."
$('a#deleteUserAccount').click (e)->

View file

@ -54,7 +54,7 @@ describe "PasswordResetHandler", ->
@EmailHandler.sendEmail.called.should.equal true
args = @EmailHandler.sendEmail.args[0]
args[0].should.equal "passwordResetRequested"
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?resetToken=#{@token}"
args[1].setNewPasswordUrl.should.equal "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}"
done()

View file

@ -39,8 +39,8 @@ describe "TokenGenerator", ->
it "should set a new token into redis with a ttl", (done)->
@redisMulti.exec.callsArgWith(0)
@TokenGenerator.getNewToken @user_id, (err, token)=>
@redisMulti.set @stubbedToken, @user_id
@redisMulti.expire @stubbedToken, (60*1000)*60
@redisMulti.set "password_token:#{@stubbedToken}", @user_id
@redisMulti.expire "password_token:#{@stubbedToken}", 60 * 60
done()
it "should return if there was an error", (done)->
@ -56,8 +56,8 @@ describe "TokenGenerator", ->
@redisMulti.exec.callsArgWith(0, null, [@user_id])
@TokenGenerator.getUserIdFromToken @stubbedToken, (err, user_id)=>
user_id.should.equal @user_id
@redisMulti.get.calledWith(@stubbedToken).should.equal true
@redisMulti.del.calledWith(@stubbedToken).should.equal true
@redisMulti.get.calledWith("password_token:#{@stubbedToken}").should.equal true
@redisMulti.del.calledWith("password_token:#{@stubbedToken}").should.equal true
done()