mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-02 13:49:00 +02:00
Merge branch 'sk-sudo-mode'
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
logger = require 'logger-sharelatex'
|
||||
SudoModeHandler = require './SudoModeHandler'
|
||||
AuthenticationController = require '../Authentication/AuthenticationController'
|
||||
AuthenticationManager = require '../Authentication/AuthenticationManager'
|
||||
ObjectId = require('../../infrastructure/Mongoose').mongo.ObjectId
|
||||
UserGetter = require '../User/UserGetter'
|
||||
|
||||
|
||||
module.exports = SudoModeController =
|
||||
|
||||
sudoModePrompt: (req, res, next) ->
|
||||
if req.externalAuthenticationSystemUsed()
|
||||
logger.log {userId}, "[SudoMode] using external auth, redirecting"
|
||||
return res.redirect('/project')
|
||||
userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log {userId}, "[SudoMode] rendering sudo mode password page"
|
||||
SudoModeHandler.isSudoModeActive userId, (err, isActive) ->
|
||||
if err?
|
||||
logger.err {err, userId}, "[SudoMode] error checking if sudo mode is active"
|
||||
return next(err)
|
||||
if isActive
|
||||
logger.log {userId}, "[SudoMode] sudo mode already active, redirecting"
|
||||
return res.redirect('/project')
|
||||
res.render 'sudo_mode/sudo_mode_prompt', title: 'confirm_password_to_continue'
|
||||
|
||||
submitPassword: (req, res, next) ->
|
||||
userId = AuthenticationController.getLoggedInUserId(req)
|
||||
redir = AuthenticationController._getRedirectFromSession(req) || "/project"
|
||||
password = req.body.password
|
||||
if !password
|
||||
logger.log {userId}, "[SudoMode] no password supplied, failed authentication"
|
||||
return next(new Error('no password supplied'))
|
||||
logger.log {userId, redir}, "[SudoMode] checking user password"
|
||||
UserGetter.getUser ObjectId(userId), {email: 1}, (err, userRecord) ->
|
||||
if err?
|
||||
logger.err {err, userId}, "[SudoMode] error getting user"
|
||||
return next(err)
|
||||
if !userRecord?
|
||||
err = new Error('user not found')
|
||||
logger.err {err, userId}, "[SudoMode] user not found"
|
||||
return next(err)
|
||||
AuthenticationManager.authenticate email: userRecord.email, password, (err, user) ->
|
||||
if err?
|
||||
logger.err {err, userId}, "[SudoMode] error authenticating user"
|
||||
return next(err)
|
||||
if user?
|
||||
logger.log {userId}, "[SudoMode] authenticated user, activating sudo mode"
|
||||
SudoModeHandler.activateSudoMode userId, (err) ->
|
||||
if err?
|
||||
logger.err {err, userId}, "[SudoMode] error activating sudo mode"
|
||||
return next(err)
|
||||
return res.json {
|
||||
redir: redir
|
||||
}
|
||||
else
|
||||
logger.log {userId}, "[SudoMode] authentication failed for user"
|
||||
return res.json {
|
||||
message: {
|
||||
text: req.i18n.translate("invalid_password"),
|
||||
type: 'error'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
RedisWrapper = require('../../infrastructure/RedisWrapper')
|
||||
rclient = RedisWrapper.client('sudomode')
|
||||
logger = require('logger-sharelatex')
|
||||
|
||||
|
||||
TIMEOUT_IN_SECONDS = 60 * 60
|
||||
|
||||
|
||||
module.exports = SudoModeHandler =
|
||||
|
||||
_buildKey: (userId) ->
|
||||
"SudoMode:{#{userId}}"
|
||||
|
||||
activateSudoMode: (userId, callback=(err)->) ->
|
||||
if !userId?
|
||||
return callback(new Error('[SudoMode] user must be supplied'))
|
||||
duration = TIMEOUT_IN_SECONDS
|
||||
logger.log {userId, duration}, "[SudoMode] activating sudo mode for user"
|
||||
rclient.set SudoModeHandler._buildKey(userId), '1', 'EX', duration, callback
|
||||
|
||||
clearSudoMode: (userId, callback=(err)->) ->
|
||||
if !userId?
|
||||
return callback(new Error('[SudoMode] user must be supplied'))
|
||||
logger.log {userId}, "[SudoMode] clearing sudo mode for user"
|
||||
rclient.del SudoModeHandler._buildKey(userId), callback
|
||||
|
||||
isSudoModeActive: (userId, callback=(err, isActive)->) ->
|
||||
if !userId?
|
||||
return callback(new Error('[SudoMode] user must be supplied'))
|
||||
rclient.get SudoModeHandler._buildKey(userId), (err, result) ->
|
||||
if err?
|
||||
return callback(err)
|
||||
callback(null, result == '1')
|
||||
@@ -0,0 +1,24 @@
|
||||
logger = require 'logger-sharelatex'
|
||||
SudoModeHandler = require './SudoModeHandler'
|
||||
AuthenticationController = require '../Authentication/AuthenticationController'
|
||||
|
||||
|
||||
module.exports = SudoModeMiddlewear =
|
||||
|
||||
protectPage: (req, res, next) ->
|
||||
if req.externalAuthenticationSystemUsed()
|
||||
logger.log {userId}, "[SudoMode] using external auth, skipping sudo-mode check"
|
||||
return next()
|
||||
userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log {userId}, "[SudoMode] protecting endpoint, checking if sudo mode is active"
|
||||
SudoModeHandler.isSudoModeActive userId, (err, isActive) ->
|
||||
if err?
|
||||
logger.err {err, userId}, "[SudoMode] error checking if sudo mode is active"
|
||||
return next(err)
|
||||
if isActive
|
||||
logger.log {userId}, "[SudoMode] sudo mode active, continuing"
|
||||
return next()
|
||||
else
|
||||
logger.log {userId}, "[SudoMode] sudo mode not active, redirecting"
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
return res.redirect('/confirm-password')
|
||||
@@ -11,6 +11,7 @@ AuthenticationManager = require("../Authentication/AuthenticationManager")
|
||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
UserSessionsManager = require("./UserSessionsManager")
|
||||
UserUpdater = require("./UserUpdater")
|
||||
SudoModeHandler = require('../SudoMode/SudoModeHandler')
|
||||
settings = require "settings-sharelatex"
|
||||
|
||||
module.exports = UserController =
|
||||
@@ -118,6 +119,7 @@ module.exports = UserController =
|
||||
if err
|
||||
logger.err err: err, 'error destorying session'
|
||||
UserSessionsManager.untrackSession(user, sessionId)
|
||||
SudoModeHandler.clearSudoMode(user._id)
|
||||
res.redirect '/login'
|
||||
|
||||
register : (req, res, next = (error) ->)->
|
||||
|
||||
@@ -84,6 +84,11 @@ module.exports = (app, webRouter, apiRouter)->
|
||||
webRouter.use addSetContentDisposition
|
||||
apiRouter.use addSetContentDisposition
|
||||
|
||||
webRouter.use (req, res, next)->
|
||||
req.externalAuthenticationSystemUsed = res.locals.externalAuthenticationSystemUsed = ->
|
||||
Settings.ldap? or Settings.saml?
|
||||
next()
|
||||
|
||||
webRouter.use (req, res, next)->
|
||||
|
||||
cdnBlocked = req.query.nocdn == 'true' or req.session.cdnBlocked
|
||||
@@ -222,11 +227,6 @@ module.exports = (app, webRouter, apiRouter)->
|
||||
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
||||
next()
|
||||
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.externalAuthenticationSystemUsed = ->
|
||||
Settings.ldap? or Settings.saml?
|
||||
next()
|
||||
|
||||
webRouter.use (req, res, next)->
|
||||
currentUser = AuthenticationController.getSessionUser(req)
|
||||
if currentUser?
|
||||
|
||||
@@ -39,6 +39,8 @@ ContactRouter = require("./Features/Contacts/ContactRouter")
|
||||
ReferencesController = require('./Features/References/ReferencesController')
|
||||
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
|
||||
BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
|
||||
SudoModeController = require('./Features/SudoMode/SudoModeController')
|
||||
SudoModeMiddlewear = require('./Features/SudoMode/SudoModeMiddlewear')
|
||||
AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
|
||||
AnnouncementsController = require("./Features/Announcements/AnnouncementsController")
|
||||
|
||||
@@ -86,11 +88,17 @@ module.exports = class Router
|
||||
webRouter.get '/user/activate', UserPagesController.activateAccountPage
|
||||
AuthenticationController.addEndpointToLoginWhitelist '/user/activate'
|
||||
|
||||
webRouter.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
|
||||
webRouter.get '/user/settings',
|
||||
AuthenticationController.requireLogin(),
|
||||
SudoModeMiddlewear.protectPage,
|
||||
UserPagesController.settingsPage
|
||||
webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
|
||||
webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
|
||||
|
||||
webRouter.get '/user/sessions', AuthenticationController.requireLogin(), UserPagesController.sessionsPage
|
||||
webRouter.get '/user/sessions',
|
||||
AuthenticationController.requireLogin(),
|
||||
SudoModeMiddlewear.protectPage,
|
||||
UserPagesController.sessionsPage
|
||||
webRouter.post '/user/sessions/clear', AuthenticationController.requireLogin(), UserController.clearSessions
|
||||
|
||||
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||
@@ -239,6 +247,9 @@ module.exports = class Router
|
||||
webRouter.get "/beta/participate", AuthenticationController.requireLogin(), BetaProgramController.optInPage
|
||||
webRouter.post "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optIn
|
||||
webRouter.post "/beta/opt-out", AuthenticationController.requireLogin(), BetaProgramController.optOut
|
||||
webRouter.get "/confirm-password", AuthenticationController.requireLogin(), SudoModeController.sudoModePrompt
|
||||
webRouter.post "/confirm-password", AuthenticationController.requireLogin(), SudoModeController.submitPassword
|
||||
|
||||
|
||||
#Admin Stuff
|
||||
webRouter.get '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
.content.content-alt
|
||||
.container
|
||||
.row
|
||||
.col-md-8.col-md-offset-2.col-lg-6.col-lg-offset-3
|
||||
.card
|
||||
.page-header.text-centered
|
||||
h2 #{translate('confirm_password_to_continue')}
|
||||
div
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-12
|
||||
form(async-form="confirmPassword", name="confirmPassword",
|
||||
action='/confirm-password', method="POST", ng-cloak)
|
||||
input(name='_csrf', type='hidden', value=csrfToken)
|
||||
form-messages(for="confirmPassword")
|
||||
.form-group
|
||||
label
|
||||
| #{translate('password')}
|
||||
input.form-control(
|
||||
type='password',
|
||||
name='password',
|
||||
required,
|
||||
placeholder='********',
|
||||
ng-model="password"
|
||||
)
|
||||
span.small.text-primary(
|
||||
ng-show="confirmPassword.password.$invalid && confirmPassword.password.$dirty"
|
||||
)
|
||||
| #{translate("required")}
|
||||
.actions
|
||||
button.btn-primary.btn(
|
||||
style="width: 100%",
|
||||
type='submit',
|
||||
ng-disabled="confirmPassword.inflight"
|
||||
)
|
||||
span #{translate("confirm")}
|
||||
|
||||
.row.row-spaced-small
|
||||
.col-md-12
|
||||
p.text-centered
|
||||
small
|
||||
| #{translate('confirm_password_footer')}
|
||||
@@ -0,0 +1,297 @@
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
sinon = require 'sinon'
|
||||
should = require("chai").should()
|
||||
expect = require('chai').expect
|
||||
MockRequest = require "../helpers/MockRequest"
|
||||
MockResponse = require "../helpers/MockResponse"
|
||||
modulePath = '../../../../app/js/Features/SudoMode/SudoModeController'
|
||||
|
||||
describe 'SudoModeController', ->
|
||||
beforeEach ->
|
||||
@user =
|
||||
_id: 'abcd'
|
||||
email: 'user@example.com'
|
||||
@UserGetter =
|
||||
getUser: sinon.stub().callsArgWith(2, null, @user)
|
||||
@SudoModeHandler =
|
||||
isSudoModeActive: sinon.stub()
|
||||
activateSudoMode: sinon.stub()
|
||||
@AuthenticationController =
|
||||
getLoggedInUserId: sinon.stub().returns(@user._id)
|
||||
_getRediretFromSession: sinon.stub()
|
||||
@AuthenticationManager =
|
||||
authenticate: sinon.stub()
|
||||
@UserGetter =
|
||||
getUser: sinon.stub()
|
||||
@SudoModeController = SandboxedModule.require modulePath, requires:
|
||||
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
|
||||
'./SudoModeHandler': @SudoModeHandler
|
||||
'../Authentication/AuthenticationController': @AuthenticationController
|
||||
'../Authentication/AuthenticationManager': @AuthenticationManager
|
||||
'../../infrastructure/Mongoose': {mongo: {ObjectId: () -> 'some_object_id'}}
|
||||
'../User/UserGetter': @UserGetter
|
||||
|
||||
describe 'sudoModePrompt', ->
|
||||
beforeEach ->
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, false)
|
||||
@req = {externalAuthenticationSystemUsed: sinon.stub().returns(false)}
|
||||
@res = {redirect: sinon.stub(), render: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should get the logged in user id', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 1
|
||||
@AuthenticationController.getLoggedInUserId.calledWith(@req).should.equal true
|
||||
|
||||
it 'should check if sudo-mode is active', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 1
|
||||
@SudoModeHandler.isSudoModeActive.calledWith(@user._id).should.equal true
|
||||
|
||||
it 'should redirect when sudo-mode is active', ->
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, true)
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@res.redirect.callCount.should.equal 1
|
||||
@res.redirect.calledWith('/project').should.equal true
|
||||
|
||||
it 'should render the sudo_mode_prompt page when sudo mode is not active', ->
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, false)
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@res.render.callCount.should.equal 1
|
||||
@res.render.calledWith('sudo_mode/sudo_mode_prompt').should.equal true
|
||||
|
||||
describe 'when isSudoModeActive produces an error', ->
|
||||
beforeEach ->
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call next with an error', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not render page', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@res.render.callCount.should.equal 0
|
||||
|
||||
describe 'when external auth system is used', ->
|
||||
beforeEach ->
|
||||
@req.externalAuthenticationSystemUsed = sinon.stub().returns(true)
|
||||
|
||||
it 'should redirect', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@res.redirect.callCount.should.equal 1
|
||||
@res.redirect.calledWith('/project').should.equal true
|
||||
|
||||
it 'should not check if sudo mode is active', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 0
|
||||
|
||||
it 'should not render page', ->
|
||||
@SudoModeController.sudoModePrompt(@req, @res, @next)
|
||||
@res.render.callCount.should.equal 0
|
||||
|
||||
describe 'submitPassword', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController._getRedirectFromSession = sinon.stub().returns '/somewhere'
|
||||
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user)
|
||||
@AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, null, @user)
|
||||
@SudoModeHandler.activateSudoMode = sinon.stub().callsArgWith(1, null)
|
||||
@password = 'a_terrible_secret'
|
||||
@req = {body: {password: @password}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
describe 'when all goes well', ->
|
||||
beforeEach ->
|
||||
|
||||
it 'should get the logged in user id', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 1
|
||||
@AuthenticationController.getLoggedInUserId.calledWith(@req).should.equal true
|
||||
|
||||
it 'should get redirect from session', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationController._getRedirectFromSession.callCount.should.equal 1
|
||||
@AuthenticationController._getRedirectFromSession.calledWith(@req).should.equal true
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 1
|
||||
@AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true
|
||||
|
||||
it 'should activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 1
|
||||
@SudoModeHandler.activateSudoMode.calledWith(@user._id).should.equal true
|
||||
|
||||
it 'should send back a json response', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 1
|
||||
@res.json.calledWith({redir: '/somewhere'}).should.equal true
|
||||
|
||||
it 'should not call next', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 0
|
||||
|
||||
describe 'when no password is supplied', ->
|
||||
beforeEach ->
|
||||
@req.body.password = ''
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should return next with an error', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 0
|
||||
|
||||
it 'should not try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 0
|
||||
|
||||
it 'should not activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 0
|
||||
|
||||
it 'should not send back a json response', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
||||
|
||||
describe 'when getUser produces an error', ->
|
||||
beforeEach ->
|
||||
@UserGetter.getUser = sinon.stub().callsArgWith(2, new Error('woops'))
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should return next with an error', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should not try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 0
|
||||
|
||||
it 'should not activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 0
|
||||
|
||||
it 'should not send back a json response', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
||||
|
||||
describe 'when getUser does not find a user', ->
|
||||
beforeEach ->
|
||||
@UserGetter.getUser = sinon.stub().callsArgWith(2, null, null)
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should return next with an error', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should not try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 0
|
||||
|
||||
it 'should not activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 0
|
||||
|
||||
it 'should not send back a json response', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
||||
|
||||
describe 'when authentication fails', ->
|
||||
beforeEach ->
|
||||
@AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, null, null)
|
||||
@res.json = sinon.stub()
|
||||
@req.i18n = {translate: sinon.stub()}
|
||||
|
||||
it 'should send back a failure message', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 1
|
||||
expect(@res.json.lastCall.args[0]).to.have.keys ['message']
|
||||
expect(@res.json.lastCall.args[0].message).to.have.keys ['text', 'type']
|
||||
@req.i18n.translate.callCount.should.equal 1
|
||||
@req.i18n.translate.calledWith('invalid_password')
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 1
|
||||
@AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true
|
||||
|
||||
it 'should not activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 0
|
||||
|
||||
describe 'when authentication produces an error', ->
|
||||
beforeEach ->
|
||||
@AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, new Error('woops'))
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should return next with an error', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 1
|
||||
@AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true
|
||||
|
||||
it 'should not activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 0
|
||||
|
||||
describe 'when sudo mode activation produces an error', ->
|
||||
beforeEach ->
|
||||
@SudoModeHandler.activateSudoMode = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should return next with an error', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should get the user from storage', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@UserGetter.getUser.callCount.should.equal 1
|
||||
@UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true
|
||||
|
||||
it 'should try to authenticate the user with the password', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@AuthenticationManager.authenticate.callCount.should.equal 1
|
||||
@AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true
|
||||
|
||||
it 'should have tried to activate sudo mode', ->
|
||||
@SudoModeController.submitPassword(@req, @res, @next)
|
||||
@SudoModeHandler.activateSudoMode.callCount.should.equal 1
|
||||
@SudoModeHandler.activateSudoMode.calledWith(@user._id).should.equal true
|
||||
@@ -0,0 +1,188 @@
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
assert = require('assert')
|
||||
require('chai').should()
|
||||
expect = require('chai').expect
|
||||
sinon = require('sinon')
|
||||
modulePath = require('path').join __dirname, '../../../../app/js/Features/SudoMode/SudoModeHandler'
|
||||
|
||||
|
||||
describe 'SudoModeHandler', ->
|
||||
beforeEach ->
|
||||
@userId = 'some_user_id'
|
||||
@rclient = {get: sinon.stub(), set: sinon.stub(), del: sinon.stub()}
|
||||
@RedisWrapper =
|
||||
client: () => @rclient
|
||||
@SudoModeHandler = SandboxedModule.require modulePath, requires:
|
||||
'../../infrastructure/RedisWrapper': @RedisWrapper
|
||||
'logger-sharelatex': @logger = {log: sinon.stub(), err: sinon.stub()}
|
||||
|
||||
describe '_buildKey', ->
|
||||
|
||||
it 'should build a properly formed key', ->
|
||||
expect(@SudoModeHandler._buildKey('123')).to.equal 'SudoMode:{123}'
|
||||
|
||||
describe 'activateSudoMode', ->
|
||||
beforeEach ->
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.activateSudoMode @userId, cb
|
||||
|
||||
describe 'when all goes well', ->
|
||||
beforeEach ->
|
||||
@rclient.set = sinon.stub().callsArgWith(4, null)
|
||||
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should set a value in redis', (done) ->
|
||||
@call (err) =>
|
||||
expect(@rclient.set.callCount).to.equal 1
|
||||
expect(@rclient.set.calledWith(
|
||||
'SudoMode:{some_user_id}', '1', 'EX', 60*60
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when user id is not supplied', ->
|
||||
beforeEach ->
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.activateSudoMode null, cb
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
it 'should not set value in redis', (done) ->
|
||||
@call (err) =>
|
||||
expect(@rclient.set.callCount).to.equal 0
|
||||
done()
|
||||
|
||||
describe 'when rclient.set produces an error', ->
|
||||
beforeEach ->
|
||||
@rclient.set = sinon.stub().callsArgWith(4, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
describe 'clearSudoMode', ->
|
||||
beforeEach ->
|
||||
@rclient.del = sinon.stub().callsArgWith(1, null)
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.clearSudoMode @userId, cb
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should delete key from redis', (done) ->
|
||||
@call (err) =>
|
||||
expect(@rclient.del.callCount).to.equal 1
|
||||
expect(@rclient.del.calledWith(
|
||||
'SudoMode:{some_user_id}'
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when rclient.del produces an error', ->
|
||||
beforeEach ->
|
||||
@rclient.del = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
describe 'when user id is not supplied', ->
|
||||
beforeEach ->
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.clearSudoMode null, cb
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
it 'should not delete value in redis', (done) ->
|
||||
@call (err) =>
|
||||
expect(@rclient.del.callCount).to.equal 0
|
||||
done()
|
||||
|
||||
describe 'isSudoModeActive', ->
|
||||
beforeEach ->
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.isSudoModeActive @userId, cb
|
||||
|
||||
describe 'when sudo-mode is active for that user', ->
|
||||
beforeEach ->
|
||||
@rclient.get = sinon.stub().callsArgWith(1, null, '1')
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should get the value from redis', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(@rclient.get.callCount).to.equal 1
|
||||
expect(@rclient.get.calledWith('SudoMode:{some_user_id}')).to.equal true
|
||||
done()
|
||||
|
||||
it 'should produce a true result', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(isActive).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when sudo-mode is not active for that user', ->
|
||||
beforeEach ->
|
||||
@rclient.get = sinon.stub().callsArgWith(1, null, null)
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should get the value from redis', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(@rclient.get.callCount).to.equal 1
|
||||
expect(@rclient.get.calledWith('SudoMode:{some_user_id}')).to.equal true
|
||||
done()
|
||||
|
||||
it 'should produce a false result', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(isActive).to.equal false
|
||||
done()
|
||||
|
||||
describe 'when rclient.get produces an error', ->
|
||||
beforeEach ->
|
||||
@rclient.get = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err, isActive) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
expect(isActive).to.be.oneOf [null, undefined]
|
||||
done()
|
||||
|
||||
describe 'when user id is not supplied', ->
|
||||
beforeEach ->
|
||||
@call = (cb) =>
|
||||
@SudoModeHandler.isSudoModeActive null, cb
|
||||
|
||||
it 'should produce an error', (done) ->
|
||||
@call (err) =>
|
||||
expect(err).to.not.equal null
|
||||
expect(err).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
it 'should not get value in redis', (done) ->
|
||||
@call (err) =>
|
||||
expect(@rclient.get.callCount).to.equal 0
|
||||
done()
|
||||
@@ -0,0 +1,123 @@
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
assert = require('assert')
|
||||
require('chai').should()
|
||||
expect = require('chai').expect
|
||||
sinon = require('sinon')
|
||||
modulePath = require('path').join __dirname, '../../../../app/js/Features/SudoMode/SudoModeMiddlewear'
|
||||
|
||||
|
||||
describe 'SudoModeMiddlewear', ->
|
||||
beforeEach ->
|
||||
@userId = 'some_user_id'
|
||||
@SudoModeHandler =
|
||||
isSudoModeActive: sinon.stub()
|
||||
@AuthenticationController =
|
||||
getLoggedInUserId: sinon.stub().returns(@userId)
|
||||
_setRedirectInSession: sinon.stub()
|
||||
@SudoModeMiddlewear = SandboxedModule.require modulePath, requires:
|
||||
'./SudoModeHandler': @SudoModeHandler
|
||||
'../Authentication/AuthenticationController': @AuthenticationController
|
||||
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
|
||||
|
||||
describe 'protectPage', ->
|
||||
beforeEach ->
|
||||
@externalAuth = false
|
||||
@call = (cb) =>
|
||||
@req = {externalAuthenticationSystemUsed: sinon.stub().returns(@externalAuth)}
|
||||
@res = {redirect: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
@SudoModeMiddlewear.protectPage @req, @res, @next
|
||||
cb()
|
||||
|
||||
describe 'when sudo mode is active', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId)
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, true)
|
||||
|
||||
it 'should get the current user id', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 1
|
||||
done()
|
||||
|
||||
it 'should check if sudo-mode is active', (done) ->
|
||||
@call () =>
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 1
|
||||
@SudoModeHandler.isSudoModeActive.calledWith(@userId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call next', (done) ->
|
||||
@call () =>
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.equal undefined
|
||||
done()
|
||||
|
||||
describe 'when sudo mode is not active', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController._setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId)
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, false)
|
||||
|
||||
it 'should get the current user id', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 1
|
||||
done()
|
||||
|
||||
it 'should check if sudo-mode is active', (done) ->
|
||||
@call () =>
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 1
|
||||
@SudoModeHandler.isSudoModeActive.calledWith(@userId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should set redirect in session', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController._setRedirectInSession.callCount.should.equal 1
|
||||
@AuthenticationController._setRedirectInSession.calledWith(@req).should.equal true
|
||||
done()
|
||||
|
||||
it 'should redirect to the password-prompt page', (done) ->
|
||||
@call () =>
|
||||
@res.redirect.callCount.should.equal 1
|
||||
@res.redirect.calledWith('/confirm-password').should.equal true
|
||||
done()
|
||||
|
||||
describe 'when isSudoModeActive produces an error', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId)
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
|
||||
it 'should get the current user id', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 1
|
||||
done()
|
||||
|
||||
it 'should check if sudo-mode is active', (done) ->
|
||||
@call () =>
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 1
|
||||
@SudoModeHandler.isSudoModeActive.calledWith(@userId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call next with an error', (done) ->
|
||||
@call () =>
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
done()
|
||||
|
||||
describe 'when external auth is being used', ->
|
||||
beforeEach ->
|
||||
@externalAuth = true
|
||||
|
||||
it 'should immediately return next with no args', (done) ->
|
||||
@call () =>
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.not.exist
|
||||
done()
|
||||
|
||||
it 'should not get the current user id', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController.getLoggedInUserId.callCount.should.equal 0
|
||||
done()
|
||||
|
||||
it 'should not check if sudo-mode is active', (done) ->
|
||||
@call () =>
|
||||
@SudoModeHandler.isSudoModeActive.callCount.should.equal 0
|
||||
done()
|
||||
@@ -60,6 +60,8 @@ describe "UserController", ->
|
||||
trackSession: sinon.stub()
|
||||
untrackSession: sinon.stub()
|
||||
revokeAllUserSessions: sinon.stub().callsArgWith(2, null)
|
||||
@SudoModeHandler =
|
||||
clearSudoMode: sinon.stub()
|
||||
@UserController = SandboxedModule.require modulePath, requires:
|
||||
"./UserLocator": @UserLocator
|
||||
"./UserDeleter": @UserDeleter
|
||||
@@ -73,6 +75,7 @@ describe "UserController", ->
|
||||
"../Subscription/SubscriptionDomainHandler":@SubscriptionDomainHandler
|
||||
"./UserHandler":@UserHandler
|
||||
"./UserSessionsManager": @UserSessionsManager
|
||||
"../SudoMode/SudoModeHandler": @SudoModeHandler
|
||||
"settings-sharelatex": @settings
|
||||
"logger-sharelatex":
|
||||
log:->
|
||||
@@ -302,6 +305,17 @@ describe "UserController", ->
|
||||
|
||||
@UserController.logout @req, @res
|
||||
|
||||
it 'should clear sudo-mode', (done) ->
|
||||
@req.session.destroy = sinon.stub().callsArgWith(0)
|
||||
@SudoModeHandler.clearSudoMode = sinon.stub()
|
||||
@res.redirect = (url)=>
|
||||
url.should.equal "/login"
|
||||
@SudoModeHandler.clearSudoMode.callCount.should.equal 1
|
||||
@SudoModeHandler.clearSudoMode.calledWith(@user._id).should.equal true
|
||||
done()
|
||||
|
||||
@UserController.logout @req, @res
|
||||
|
||||
|
||||
describe "register", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -34,9 +34,9 @@ describe "Sessions", ->
|
||||
expect(sessions[0].slice(0, 5)).to.equal 'sess:'
|
||||
next()
|
||||
|
||||
# should be able to access settings page
|
||||
# should be able to access project list page
|
||||
, (next) =>
|
||||
@user1.getUserSettingsPage (err, statusCode) =>
|
||||
@user1.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
@@ -94,15 +94,15 @@ describe "Sessions", ->
|
||||
expect(sessions[1].slice(0, 5)).to.equal 'sess:'
|
||||
next()
|
||||
|
||||
# both should be able to access settings page
|
||||
# both should be able to access project list page
|
||||
, (next) =>
|
||||
@user1.getUserSettingsPage (err, statusCode) =>
|
||||
@user1.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
|
||||
, (next) =>
|
||||
@user2.getUserSettingsPage (err, statusCode) =>
|
||||
@user2.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
@@ -117,16 +117,16 @@ describe "Sessions", ->
|
||||
expect(sessions.length).to.equal 1
|
||||
next()
|
||||
|
||||
# first session should not have access to settings page
|
||||
# first session should not have access to project list page
|
||||
, (next) =>
|
||||
@user1.getUserSettingsPage (err, statusCode) =>
|
||||
@user1.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
|
||||
# second session should still have access to settings
|
||||
, (next) =>
|
||||
@user2.getUserSettingsPage (err, statusCode) =>
|
||||
@user2.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
@@ -141,9 +141,9 @@ describe "Sessions", ->
|
||||
expect(sessions.length).to.equal 0
|
||||
next()
|
||||
|
||||
# second session should not have access to settings page
|
||||
# second session should not have access to project list page
|
||||
, (next) =>
|
||||
@user2.getUserSettingsPage (err, statusCode) =>
|
||||
@user2.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
@@ -216,22 +216,22 @@ describe "Sessions", ->
|
||||
expect(sessions.length).to.equal 1
|
||||
next()
|
||||
|
||||
# users one and three should not be able to access settings page
|
||||
# users one and three should not be able to access project list page
|
||||
, (next) =>
|
||||
@user1.getUserSettingsPage (err, statusCode) =>
|
||||
@user1.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
|
||||
, (next) =>
|
||||
@user3.getUserSettingsPage (err, statusCode) =>
|
||||
@user3.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
|
||||
# user two should still be logged in, and able to access settings page
|
||||
# user two should still be logged in, and able to access project list page
|
||||
, (next) =>
|
||||
@user2.getUserSettingsPage (err, statusCode) =>
|
||||
@user2.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
@@ -305,6 +305,19 @@ describe "Sessions", ->
|
||||
expect(sessions[1].slice(0, 5)).to.equal 'sess:'
|
||||
next()
|
||||
|
||||
# enter sudo-mode
|
||||
, (next) =>
|
||||
@user2.getCsrfToken (err) =>
|
||||
expect(err).to.be.oneOf [null, undefined]
|
||||
@user2.request.post {
|
||||
uri: '/confirm-password',
|
||||
json:
|
||||
password: @user2.password
|
||||
}, (err, response, body) =>
|
||||
expect(err).to.be.oneOf [null, undefined]
|
||||
expect(response.statusCode).to.equal 200
|
||||
next()
|
||||
|
||||
# check the sessions page
|
||||
, (next) =>
|
||||
@user2.request.get {
|
||||
@@ -328,22 +341,22 @@ describe "Sessions", ->
|
||||
expect(sessions.length).to.equal 1
|
||||
next()
|
||||
|
||||
# users one and three should not be able to access settings page
|
||||
# users one and three should not be able to access project list page
|
||||
, (next) =>
|
||||
@user1.getUserSettingsPage (err, statusCode) =>
|
||||
@user1.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
|
||||
, (next) =>
|
||||
@user3.getUserSettingsPage (err, statusCode) =>
|
||||
@user3.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 302
|
||||
next()
|
||||
|
||||
# user two should still be logged in, and able to access settings page
|
||||
# user two should still be logged in, and able to access project list page
|
||||
, (next) =>
|
||||
@user2.getUserSettingsPage (err, statusCode) =>
|
||||
@user2.getProjectListPage (err, statusCode) =>
|
||||
expect(err).to.equal null
|
||||
expect(statusCode).to.equal 200
|
||||
next()
|
||||
|
||||
@@ -134,6 +134,15 @@ class User
|
||||
return callback(error) if error?
|
||||
callback(null, response.statusCode)
|
||||
|
||||
getProjectListPage: (callback=(error, statusCode)->) ->
|
||||
@getCsrfToken (error) =>
|
||||
return callback(error) if error?
|
||||
@request.get {
|
||||
url: "/project"
|
||||
}, (error, response, body) =>
|
||||
return callback(error) if error?
|
||||
callback(null, response.statusCode)
|
||||
|
||||
|
||||
|
||||
module.exports = User
|
||||
|
||||
Reference in New Issue
Block a user