Refactor registration so it can be called from modules

This commit is contained in:
James Allen 2015-12-11 17:11:13 +00:00
parent 58604d3c91
commit e8f21986dd
5 changed files with 108 additions and 83 deletions

View file

@ -8,10 +8,7 @@ metrics = require("../../infrastructure/Metrics")
Url = require("url")
AuthenticationManager = require("../Authentication/AuthenticationManager")
UserUpdater = require("./UserUpdater")
EmailHandler = require("../Email/EmailHandler")
OneTimeTokenHandler = require "../Security/OneTimeTokenHandler"
settings = require "settings-sharelatex"
crypto = require "crypto"
module.exports =
@ -85,28 +82,8 @@ module.exports =
if !email? or email == ""
res.sendStatus 422 # Unprocessable Entity
return
logger.log {email}, "registering new user"
UserRegistrationHandler.registerNewUser {
email: email
password: crypto.randomBytes(32).toString("hex")
}, (err, user)->
if err? and err?.message != "EmailAlreadyRegistered"
return next(err)
if err?.message == "EmailAlreadyRegistered"
logger.log {email}, "user already exists, resending welcome email"
ONE_WEEK = 7 * 24 * 60 * 60 # seconds
OneTimeTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)->
return next(err) if err?
setNewPasswordUrl = "#{settings.siteUrl}/user/activate?token=#{token}&user_id=#{user._id}"
EmailHandler.sendEmail "registered", {
to: user.email
setNewPasswordUrl: setNewPasswordUrl
}, () ->
UserRegistrationHandler.registerNewUserAndSendActivationEmail email, (error, user, setNewPasswordUrl) ->
return next(error) if error?
res.json {
email: user.email
setNewPasswordUrl: setNewPasswordUrl

View file

@ -5,8 +5,12 @@ AuthenticationManager = require("../Authentication/AuthenticationManager")
NewsLetterManager = require("../Newsletter/NewsletterManager")
async = require("async")
logger = require("logger-sharelatex")
crypto = require("crypto")
EmailHandler = require("../Email/EmailHandler")
OneTimeTokenHandler = require "../Security/OneTimeTokenHandler"
settings = require "settings-sharelatex"
module.exports =
module.exports = UserRegistrationHandler =
validateEmail : (email) ->
re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\ ".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA -Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email)
@ -59,6 +63,31 @@ module.exports =
logger.log user: user, "registered"
callback(err, user)
registerNewUserAndSendActivationEmail: (email, callback = (error, user, setNewPasswordUrl) ->) ->
logger.log {email}, "registering new user"
UserRegistrationHandler.registerNewUser {
email: email
password: crypto.randomBytes(32).toString("hex")
}, (err, user)->
if err? and err?.message != "EmailAlreadyRegistered"
return next(err)
if err?.message == "EmailAlreadyRegistered"
logger.log {email}, "user already exists, resending welcome email"
ONE_WEEK = 7 * 24 * 60 * 60 # seconds
OneTimeTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)->
return next(err) if err?
setNewPasswordUrl = "#{settings.siteUrl}/user/activate?token=#{token}&user_id=#{user._id}"
EmailHandler.sendEmail "registered", {
to: user.email
setNewPasswordUrl: setNewPasswordUrl
}, () ->
callback null, user, setNewPasswordUrl

View file

@ -71,7 +71,7 @@ describe "AuthenticationManager", ->
@bcrypt.genSalt = sinon.stub().callsArgWith(1, null, @salt)
@bcrypt.hash = sinon.stub().callsArgWith(2, null, @hashedPassword)
@db.users.update = sinon.stub().callsArg(2)
@AuthenticationManager.setUserPassword(@user_id, @unencryptedPassword, @callback)
@AuthenticationManager.setUserPassword(@user_id, @password, @callback)
it "should update the user's password in the database", ->
@db.users.update

View file

@ -40,10 +40,6 @@ describe "UserController", ->
autoAllocate:sinon.stub()
@UserUpdater =
changeEmailAddress:sinon.stub()
@EmailHandler =
sendEmail:sinon.stub().callsArgWith(2)
@OneTimeTokenHandler =
getNewToken: sinon.stub()
@settings =
siteUrl: "sharelatex.example.com"
@UserController = SandboxedModule.require modulePath, requires:
@ -57,9 +53,6 @@ describe "UserController", ->
"../Authentication/AuthenticationManager": @AuthenticationManager
"../Referal/ReferalAllocator":@ReferalAllocator
"../Subscription/SubscriptionDomainHandler":@SubscriptionDomainHandler
"../Email/EmailHandler": @EmailHandler
"../Security/OneTimeTokenHandler": @OneTimeTokenHandler
"crypto": @crypto = {}
"settings-sharelatex": @settings
"logger-sharelatex": {log:->}
@ -175,52 +168,24 @@ describe "UserController", ->
describe "register", ->
beforeEach ->
@req.body.email = @user.email = "email@example.com"
@crypto.randomBytes = sinon.stub().returns({toString: () => @password = "mock-password"})
@OneTimeTokenHandler.getNewToken.callsArgWith(2, null, @token = "mock-token")
describe "with a new user", ->
beforeEach ->
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
@UserRegistrationHandler.registerNewUserAndSendActivationEmail = sinon.stub().callsArgWith(1, null, @user, @url = "mock/url")
@req.body.email = @user.email = @email = "email@example.com"
@UserController.register @req, @res
it "should ask the UserRegistrationHandler to register user", ->
@UserRegistrationHandler.registerNewUser
.calledWith({
email: @req.body.email
password: @password
}).should.equal true
it "should generate a new password reset token", ->
@OneTimeTokenHandler.getNewToken
.calledWith(@user_id, expiresIn: 7 * 24 * 60 * 60)
it "should register the user and send them an email", ->
@UserRegistrationHandler.registerNewUserAndSendActivationEmail
.calledWith(@email)
.should.equal true
it "should send a registered email", ->
@EmailHandler.sendEmail
.calledWith("registered", {
to: @user.email
setNewPasswordUrl: "#{@settings.siteUrl}/user/activate?token=#{@token}&user_id=#{@user_id}"
})
.should.equal true
it "should return the user", ->
it "should return the user and activation url", ->
console.log @res.json.args
@res.json
.calledWith({
email: @user.email
setNewPasswordUrl: "#{@settings.siteUrl}/user/activate?token=#{@token}&user_id=#{@user_id}"
email: @email,
setNewPasswordUrl: @url
})
.should.equal true
describe "with a user that already exists", ->
beforeEach ->
@UserRegistrationHandler.registerNewUser.callsArgWith(1, new Error("EmailAlreadyRegistered"), @user)
@UserController.register @req, @res
it "should still generate a new password token and email", ->
@OneTimeTokenHandler.getNewToken.called.should.equal true
@EmailHandler.sendEmail.called.should.equal true
describe "changePassword", ->
it "should check the old password is the current one at the moment", (done)->

View file

@ -10,7 +10,7 @@ describe "UserRegistrationHandler", ->
beforeEach ->
@user =
_id: "31j2lk21kjl"
_id: @user_id = "31j2lk21kjl"
@User =
findOne:sinon.stub()
update: sinon.stub().callsArgWith(2)
@ -20,12 +20,20 @@ describe "UserRegistrationHandler", ->
setUserPassword: sinon.stub().callsArgWith(2)
@NewsLetterManager =
subscribe: sinon.stub().callsArgWith(1)
@EmailHandler =
sendEmail:sinon.stub().callsArgWith(2)
@OneTimeTokenHandler =
getNewToken: sinon.stub()
@handler = SandboxedModule.require modulePath, requires:
"../../models/User": {User:@User}
"./UserCreator": @UserCreator
"../Authentication/AuthenticationManager":@AuthenticationManager
"../Newsletter/NewsletterManager":@NewsLetterManager
"logger-sharelatex": @logger = { log: sinon.stub() }
"crypto": @crypto = {}
"../Email/EmailHandler": @EmailHandler
"../Security/OneTimeTokenHandler": @OneTimeTokenHandler
"settings-sharelatex": @settings = {siteUrl: "http://sl.example.com"}
@passingRequest = {email:"something@email.com", password:"123"}
@ -128,4 +136,50 @@ describe "UserRegistrationHandler", ->
it "should call the ReferalAllocator", (done)->
done()
describe "registerNewUserAndSendActivationEmail", ->
beforeEach ->
@email = "email@example.com"
@crypto.randomBytes = sinon.stub().returns({toString: () => @password = "mock-password"})
@OneTimeTokenHandler.getNewToken.callsArgWith(2, null, @token = "mock-token")
@handler.registerNewUser = sinon.stub()
@callback = sinon.stub()
describe "with a new user", ->
beforeEach ->
@handler.registerNewUser.callsArgWith(1, null, @user)
@handler.registerNewUserAndSendActivationEmail @email, @callback
it "should ask the UserRegistrationHandler to register user", ->
@handler.registerNewUser
.calledWith({
email: @email
password: @password
}).should.equal true
it "should generate a new password reset token", ->
@OneTimeTokenHandler.getNewToken
.calledWith(@user_id, expiresIn: 7 * 24 * 60 * 60)
.should.equal true
it "should send a registered email", ->
@EmailHandler.sendEmail
.calledWith("registered", {
to: @user.email
setNewPasswordUrl: "#{@settings.siteUrl}/user/activate?token=#{@token}&user_id=#{@user_id}"
})
.should.equal true
it "should return the user", ->
@callback
.calledWith(null, @user, "#{@settings.siteUrl}/user/activate?token=#{@token}&user_id=#{@user_id}")
.should.equal true
describe "with a user that already exists", ->
beforeEach ->
@handler.registerNewUser.callsArgWith(1, new Error("EmailAlreadyRegistered"), @user)
@handler.registerNewUserAndSendActivationEmail @email, @callback
it "should still generate a new password token and email", ->
@OneTimeTokenHandler.getNewToken.called.should.equal true
@EmailHandler.sendEmail.called.should.equal true