diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index f1b78c50a6..4efe88299c 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -299,7 +299,7 @@ module.exports = (grunt) -> settings = require "settings-sharelatex" UserRegistrationHandler = require "./app/js/Features/User/UserRegistrationHandler" - PasswordResetTokenHandler = require "./app/js/Features/PasswordReset/PasswordResetTokenHandler" + OneTimeTokenHandler = require "./app/js/Features/Security/OneTimeTokenHandler" UserRegistrationHandler.registerNewUser { email: email password: require("crypto").randomBytes(32).toString("hex") @@ -310,7 +310,7 @@ module.exports = (grunt) -> user.save (error) -> throw error if error? ONE_WEEK = 7 * 24 * 60 * 60 # seconds - PasswordResetTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)-> + OneTimeTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)-> return next(err) if err? console.log "" diff --git a/services/web/app/coffee/Features/PasswordReset/PasswordResetHandler.coffee b/services/web/app/coffee/Features/PasswordReset/PasswordResetHandler.coffee index ec5e6a8de5..6a43dec3e6 100644 --- a/services/web/app/coffee/Features/PasswordReset/PasswordResetHandler.coffee +++ b/services/web/app/coffee/Features/PasswordReset/PasswordResetHandler.coffee @@ -1,7 +1,7 @@ settings = require("settings-sharelatex") async = require("async") UserGetter = require("../User/UserGetter") -PasswordResetTokenHandler = require("./PasswordResetTokenHandler") +OneTimeTokenHandler = require("./OneTimeTokenHandler") EmailHandler = require("../Email/EmailHandler") AuthenticationManager = require("../Authentication/AuthenticationManager") logger = require("logger-sharelatex") @@ -14,7 +14,7 @@ module.exports = if !user? or user.holdingAccount logger.err email:email, "user could not be found for password reset" return callback(null, false) - PasswordResetTokenHandler.getNewToken user._id, (err, token)-> + OneTimeTokenHandler.getNewToken user._id, (err, token)-> if err then return callback(err) emailOptions = to : email @@ -24,7 +24,7 @@ module.exports = callback null, true setNewUserPassword: (token, password, callback = (error, found) ->)-> - PasswordResetTokenHandler.getUserIdFromTokenAndExpire token, (err, user_id)-> + OneTimeTokenHandler.getValueFromTokenAndExpire token, (err, user_id)-> if err then return callback(err) if !user_id? return callback null, false diff --git a/services/web/app/coffee/Features/PasswordReset/PasswordResetTokenHandler.coffee b/services/web/app/coffee/Features/Security/OneTimeTokenHandler.coffee similarity index 79% rename from services/web/app/coffee/Features/PasswordReset/PasswordResetTokenHandler.coffee rename to services/web/app/coffee/Features/Security/OneTimeTokenHandler.coffee index 1fc5680ec2..66581a02b3 100644 --- a/services/web/app/coffee/Features/PasswordReset/PasswordResetTokenHandler.coffee +++ b/services/web/app/coffee/Features/Security/OneTimeTokenHandler.coffee @@ -10,21 +10,21 @@ buildKey = (token)-> return "password_token:#{token}" module.exports = - getNewToken: (user_id, options = {}, callback)-> + getNewToken: (value, options = {}, callback)-> # options is optional if typeof options == "function" callback = options options = {} expiresIn = options.expiresIn or ONE_HOUR_IN_S - logger.log user_id:user_id, "generating token for password reset" + logger.log value:value, "generating token for password reset" token = crypto.randomBytes(32).toString("hex") multi = rclient.multi() - multi.set buildKey(token), user_id + multi.set buildKey(token), value multi.expire buildKey(token), expiresIn multi.exec (err)-> callback(err, token) - getUserIdFromTokenAndExpire: (token, callback)-> + getValueFromTokenAndExpire: (token, callback)-> logger.log token:token, "getting user id from password token" multi = rclient.multi() multi.get buildKey(token) diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee index fc8885563f..07b229b00b 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee @@ -3,7 +3,7 @@ logger = require("logger-sharelatex") SubscriptionLocator = require("./SubscriptionLocator") settings = require("settings-sharelatex") -PasswordResetTokenHandler = require("../PasswordReset/PasswordResetTokenHandler") +OneTimeTokenHandler = require("../Security/OneTimeTokenHandler") EmailHandler = require("../Email/EmailHandler") SubscriptionDomainAllocator = require("./SubscriptionDomainAllocator") @@ -53,7 +53,7 @@ module.exports = licence = SubscriptionDomainAllocator.findDomainLicenceBySubscriptionId(subscription_id) if !licence? res.send 500 - PasswordResetTokenHandler.getNewToken subscription_id, (err, token)-> + OneTimeTokenHandler.getNewToken subscription_id, (err, token)-> opts = to : req.session.user.email group_name: licence.name @@ -63,9 +63,9 @@ module.exports = completeJoin: (req, res)-> subscription_id = req.params.subscription_id - PasswordResetTokenHandler.getUserIdFromTokenAndExpire req.query.token, (err, token_subscription_id)-> + OneTimeTokenHandler.getValueFromTokenAndExpire req.query.token, (err, token_subscription_id)-> console.log token_subscription_id - if subscription_id != token_subscription_id + if err? or subscription_id != token_subscription_id return res.send 403 SubscriptionLocator.getSubscription subscription_id, (err, subscription)-> SubscriptionGroupHandler.addUserToGroup subscription.admin_id, req.user.email, (err, user)-> diff --git a/services/web/app/coffee/Features/User/UserController.coffee b/services/web/app/coffee/Features/User/UserController.coffee index 53ce50eb1b..1efe77e44c 100644 --- a/services/web/app/coffee/Features/User/UserController.coffee +++ b/services/web/app/coffee/Features/User/UserController.coffee @@ -10,7 +10,7 @@ AuthenticationManager = require("../Authentication/AuthenticationManager") UserUpdater = require("./UserUpdater") SubscriptionDomainAllocator = require("../Subscription/SubscriptionDomainAllocator") EmailHandler = require("../Email/EmailHandler") -PasswordResetTokenHandler = require "../PasswordReset/PasswordResetTokenHandler" +OneTimeTokenHandler = require "../Security/OneTimeTokenHandler" settings = require "settings-sharelatex" crypto = require "crypto" @@ -98,7 +98,7 @@ module.exports = logger.log {email}, "user already exists, resending welcome email" ONE_WEEK = 7 * 24 * 60 * 60 # seconds - PasswordResetTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)-> + OneTimeTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)-> return next(err) if err? setNewPasswordUrl = "#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}&email=#{encodeURIComponent(email)}" diff --git a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetHandlerTests.coffee b/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetHandlerTests.coffee index 65eb81bdcb..67a4688c58 100644 --- a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetHandlerTests.coffee @@ -12,9 +12,9 @@ describe "PasswordResetHandler", -> @settings = siteUrl: "www.sharelatex.com" - @PasswordResetTokenHandler = + @OneTimeTokenHandler = getNewToken:sinon.stub() - getUserIdFromTokenAndExpire:sinon.stub() + getValueFromTokenAndExpire:sinon.stub() @UserGetter = getUser:sinon.stub() @EmailHandler = @@ -23,7 +23,7 @@ describe "PasswordResetHandler", -> setUserPassword:sinon.stub() @PasswordResetHandler = SandboxedModule.require modulePath, requires: "../User/UserGetter": @UserGetter - "./PasswordResetTokenHandler": @PasswordResetTokenHandler + "./OneTimeTokenHandler": @OneTimeTokenHandler "../Email/EmailHandler":@EmailHandler "../Authentication/AuthenticationManager":@AuthenticationManager "settings-sharelatex": @settings @@ -41,7 +41,7 @@ describe "PasswordResetHandler", -> it "should check the user exists", (done)-> @UserGetter.getUser.callsArgWith(1) - @PasswordResetTokenHandler.getNewToken.callsArgWith(1) + @OneTimeTokenHandler.getNewToken.callsArgWith(1) @PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=> exists.should.equal false done() @@ -50,7 +50,7 @@ describe "PasswordResetHandler", -> it "should send the email with the token", (done)-> @UserGetter.getUser.callsArgWith(1, null, @user) - @PasswordResetTokenHandler.getNewToken.callsArgWith(1, null, @token) + @OneTimeTokenHandler.getNewToken.callsArgWith(1, null, @token) @EmailHandler.sendEmail.callsArgWith(2) @PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=> @EmailHandler.sendEmail.called.should.equal true @@ -63,7 +63,7 @@ describe "PasswordResetHandler", -> it "should return exists = false for a holdingAccount", (done) -> @user.holdingAccount = true @UserGetter.getUser.callsArgWith(1, null, @user) - @PasswordResetTokenHandler.getNewToken.callsArgWith(1) + @OneTimeTokenHandler.getNewToken.callsArgWith(1) @PasswordResetHandler.generateAndEmailResetToken @user.email, (err, exists)=> exists.should.equal false done() @@ -71,14 +71,14 @@ describe "PasswordResetHandler", -> describe "setNewUserPassword", -> it "should return false if no user id can be found", (done)-> - @PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1) + @OneTimeTokenHandler.getValueFromTokenAndExpire.callsArgWith(1) @PasswordResetHandler.setNewUserPassword @token, @password, (err, found) => found.should.equal false @AuthenticationManager.setUserPassword.called.should.equal false done() it "should set the user password", (done)-> - @PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1, null, @user_id) + @OneTimeTokenHandler.getValueFromTokenAndExpire.callsArgWith(1, null, @user_id) @AuthenticationManager.setUserPassword.callsArgWith(2) @PasswordResetHandler.setNewUserPassword @token, @password, (err, found) => found.should.equal true diff --git a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetTokenHandlerTests.coffee b/services/web/test/UnitTests/coffee/Security/OneTimeTokenHandlerTests.coffee similarity index 67% rename from services/web/test/UnitTests/coffee/PasswordReset/PasswordResetTokenHandlerTests.coffee rename to services/web/test/UnitTests/coffee/Security/OneTimeTokenHandlerTests.coffee index 7de011740c..a6305db27c 100644 --- a/services/web/test/UnitTests/coffee/PasswordReset/PasswordResetTokenHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Security/OneTimeTokenHandlerTests.coffee @@ -3,13 +3,13 @@ SandboxedModule = require('sandboxed-module') assert = require('assert') path = require('path') sinon = require('sinon') -modulePath = path.join __dirname, "../../../../app/js/Features/PasswordReset/PasswordResetTokenHandler" +modulePath = path.join __dirname, "../../../../app/js/Features/Security/OneTimeTokenHandler" expect = require("chai").expect -describe "PasswordResetTokenHandler", -> +describe "OneTimeTokenHandler", -> beforeEach -> - @user_id = "user id here" + @value = "user id here" @stubbedToken = require("crypto").randomBytes(32) @settings = @@ -22,7 +22,7 @@ describe "PasswordResetTokenHandler", -> expire:sinon.stub() exec:sinon.stub() self = @ - @PasswordResetTokenHandler = SandboxedModule.require modulePath, requires: + @OneTimeTokenHandler = SandboxedModule.require modulePath, requires: "redis-sharelatex" : createClient: => auth:-> @@ -37,30 +37,30 @@ describe "PasswordResetTokenHandler", -> it "should set a new token into redis with a ttl", (done)-> @redisMulti.exec.callsArgWith(0) - @PasswordResetTokenHandler.getNewToken @user_id, (err, token) => - @redisMulti.set.calledWith("password_token:#{@stubbedToken.toString("hex")}", @user_id).should.equal true + @OneTimeTokenHandler.getNewToken @value, (err, token) => + @redisMulti.set.calledWith("password_token:#{@stubbedToken.toString("hex")}", @value).should.equal true @redisMulti.expire.calledWith("password_token:#{@stubbedToken.toString("hex")}", 60 * 60).should.equal true done() it "should return if there was an error", (done)-> @redisMulti.exec.callsArgWith(0, "error") - @PasswordResetTokenHandler.getNewToken @user_id, (err, token)=> + @OneTimeTokenHandler.getNewToken @value, (err, token)=> err.should.exist done() it "should allow the expiry time to be overridden", (done) -> @redisMulti.exec.callsArgWith(0) @ttl = 42 - @PasswordResetTokenHandler.getNewToken @user_id, {expiresIn: @ttl}, (err, token) => + @OneTimeTokenHandler.getNewToken @value, {expiresIn: @ttl}, (err, token) => @redisMulti.expire.calledWith("password_token:#{@stubbedToken.toString("hex")}", @ttl).should.equal true done() - describe "getUserIdFromTokenAndExpire", -> + describe "getValueFromTokenAndExpire", -> it "should get and delete the token", (done)-> - @redisMulti.exec.callsArgWith(0, null, [@user_id]) - @PasswordResetTokenHandler.getUserIdFromTokenAndExpire @stubbedToken, (err, user_id)=> - user_id.should.equal @user_id + @redisMulti.exec.callsArgWith(0, null, [@value]) + @OneTimeTokenHandler.getValueFromTokenAndExpire @stubbedToken, (err, value)=> + value.should.equal @value @redisMulti.get.calledWith("password_token:#{@stubbedToken}").should.equal true @redisMulti.del.calledWith("password_token:#{@stubbedToken}").should.equal true done() diff --git a/services/web/test/UnitTests/coffee/User/UserControllerTests.coffee b/services/web/test/UnitTests/coffee/User/UserControllerTests.coffee index 3aad4b9c85..cb94bc8618 100644 --- a/services/web/test/UnitTests/coffee/User/UserControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/User/UserControllerTests.coffee @@ -42,7 +42,7 @@ describe "UserController", -> changeEmailAddress:sinon.stub() @EmailHandler = sendEmail:sinon.stub().callsArgWith(2) - @PasswordResetTokenHandler = + @OneTimeTokenHandler = getNewToken: sinon.stub() @settings = siteUrl: "sharelatex.example.com" @@ -58,7 +58,7 @@ describe "UserController", -> "../Referal/ReferalAllocator":@ReferalAllocator "../Subscription/SubscriptionDomainAllocator":@SubscriptionDomainAllocator "../Email/EmailHandler": @EmailHandler - "../PasswordReset/PasswordResetTokenHandler": @PasswordResetTokenHandler + "../Security/OneTimeTokenHandler": @OneTimeTokenHandler "crypto": @crypto = {} "settings-sharelatex": @settings "logger-sharelatex": {log:->} @@ -177,7 +177,7 @@ describe "UserController", -> beforeEach -> @req.body.email = @user.email = "email@example.com" @crypto.randomBytes = sinon.stub().returns({toString: () => @password = "mock-password"}) - @PasswordResetTokenHandler.getNewToken.callsArgWith(2, null, @token = "mock-token") + @OneTimeTokenHandler.getNewToken.callsArgWith(2, null, @token = "mock-token") describe "with a new user", -> beforeEach -> @@ -192,7 +192,7 @@ describe "UserController", -> }).should.equal true it "should generate a new password reset token", -> - @PasswordResetTokenHandler.getNewToken + @OneTimeTokenHandler.getNewToken .calledWith(@user_id, expiresIn: 7 * 24 * 60 * 60) .should.equal true @@ -218,7 +218,7 @@ describe "UserController", -> @UserController.register @req, @res it "should still generate a new password token and email", -> - @PasswordResetTokenHandler.getNewToken.called.should.equal true + @OneTimeTokenHandler.getNewToken.called.should.equal true @EmailHandler.sendEmail.called.should.equal true describe "changePassword", ->