This commit is contained in:
Henry Oswald 2015-04-16 21:02:47 +01:00
commit c583903e04
6 changed files with 87 additions and 20 deletions

View file

@ -6,6 +6,7 @@ Metrics = require('../../infrastructure/Metrics')
logger = require("logger-sharelatex")
querystring = require('querystring')
Url = require("url")
Settings = require "settings-sharelatex"
module.exports = AuthenticationController =
login: (req, res, next = (error) ->) ->
@ -84,6 +85,26 @@ module.exports = AuthenticationController =
return doRequest
_globalLoginWhitelist: []
addEndpointToLoginWhitelist: (endpoint) ->
AuthenticationController._globalLoginWhitelist.push endpoint
requireGlobalLogin: (req, res, next) ->
if req.url in AuthenticationController._globalLoginWhitelist
return next()
if req.headers['authorization']?
return AuthenticationController.httpAuth(req, res, next)
else if req.session.user?
return next()
else
return res.redirect "/login"
httpAuth: require('express').basicAuth (user, pass)->
isValid = Settings.httpAuthUsers[user] == pass
if !isValid
logger.err user:user, pass:pass, "invalid login details"
return isValid
_redirectToLoginOrRegisterPage: (req, res)->
if req.query.zipUrl? or req.query.project_name?

View file

@ -1,8 +1,9 @@
EditorHttpController = require('./EditorHttpController')
SecurityManager = require('../../managers/SecurityManager')
AuthenticationController = require "../Authentication/AuthenticationController"
module.exports =
apply: (app, httpAuth) ->
apply: (app) ->
app.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
app.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
@ -18,5 +19,5 @@ module.exports =
# Called by the real-time API to load up the current project state.
# This is a post request because it's more than just a getting of data. We take actions
# whenever a user joins a project, like updating the deleted status.
app.post '/project/:Project_id/join', httpAuth, EditorHttpController.joinProject
app.post '/project/:Project_id/join', AuthenticationController.httpAuth, EditorHttpController.joinProject
app.ignoreCsrf('post', '/project/:Project_id/join')

View file

@ -39,25 +39,25 @@ RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
logger = require("logger-sharelatex")
_ = require("underscore")
httpAuth = require('express').basicAuth (user, pass)->
isValid = Settings.httpAuthUsers[user] == pass
if !isValid
logger.err user:user, pass:pass, "invalid login details"
return isValid
module.exports = class Router
constructor: (app)->
if !Settings.allowPublicAccess
app.all '*', AuthenticationController.requireGlobalLogin
app.use(app.router)
app.get '/login', UserPagesController.loginPage
AuthenticationController.addEndpointToLoginWhitelist '/login'
app.post '/login', AuthenticationController.login
app.get '/logout', UserController.logout
app.get '/restricted', SecurityManager.restricted
# Left as a placeholder for implementing a public register page
app.get '/register', UserPagesController.registerPage
AuthenticationController.addEndpointToLoginWhitelist '/register'
EditorRouter.apply(app, httpAuth)
EditorRouter.apply(app)
CollaboratorsRouter.apply(app)
SubscriptionRouter.apply(app)
UploadsRouter.apply(app)
@ -82,7 +82,7 @@ module.exports = class Router
app.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
app.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
app.get '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
app.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
@ -127,23 +127,23 @@ module.exports = class Router
app.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
app.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
app.get '/project/:project_id/details', httpAuth, ProjectApiController.getProjectDetails
app.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
app.get '/internal/project/:Project_id/zip', httpAuth, ProjectDownloadsController.downloadProject
app.get '/internal/project/:project_id/compile/pdf', httpAuth, CompileController.compileAndDownloadPdf
app.get '/internal/project/:Project_id/zip', AuthenticationController.httpAuth, ProjectDownloadsController.downloadProject
app.get '/internal/project/:project_id/compile/pdf', AuthenticationController.httpAuth, CompileController.compileAndDownloadPdf
app.get '/project/:Project_id/doc/:doc_id', httpAuth, DocumentController.getDocument
app.post '/project/:Project_id/doc/:doc_id', httpAuth, DocumentController.setDocument
app.get '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.getDocument
app.post '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.setDocument
app.ignoreCsrf('post', '/project/:Project_id/doc/:doc_id')
app.post '/user/:user_id/update/*', httpAuth, TpdsController.mergeUpdate
app.del '/user/:user_id/update/*', httpAuth, TpdsController.deleteUpdate
app.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
app.del '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
app.ignoreCsrf('post', '/user/:user_id/update/*')
app.ignoreCsrf('delete', '/user/:user_id/update/*')
app.post '/project/:project_id/contents/*', httpAuth, TpdsController.updateProjectContents
app.del '/project/:project_id/contents/*', httpAuth, TpdsController.deleteProjectContents
app.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
app.del '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
app.ignoreCsrf('post', '/project/:project_id/contents/*')
app.ignoreCsrf('delete', '/project/:project_id/contents/*')

View file

@ -232,6 +232,10 @@ module.exports =
# Cookie max age (in milliseconds). Set to false for a browser session.
cookieSessionLength: 5 * 24 * 60 * 60 * 1000 # 5 days
# Should we allow access to any page without logging in? This includes
# public projects, /learn, /templates, about pages, etc.
allowPublicAccess: false
# Internal configs
# ----------------

View file

@ -19,6 +19,7 @@ describe "AuthenticationController", ->
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
"../Security/LoginRateLimiter": @LoginRateLimiter = { processLoginRequest:sinon.stub(), recordSuccessfulLogin:sinon.stub() }
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
"settings-sharelatex": {}
@user =
_id: ObjectId()
email: @email = "USER@example.com"
@ -275,6 +276,46 @@ describe "AuthenticationController", ->
.calledWith(@req, {allow_auth_token: true})
.should.equal true
describe "requireGlobalLogin", ->
beforeEach ->
@req.headers = {}
@AuthenticationController.httpAuth = sinon.stub()
describe "with white listed url", ->
beforeEach ->
@AuthenticationController.addEndpointToLoginWhitelist "/login"
@req.url = "/login"
@AuthenticationController.requireGlobalLogin @req, @res, @next
it "should call next() directly", ->
@next.called.should.equal true
describe "with http auth", ->
beforeEach ->
@req.headers["authorization"] = "Mock Basic Auth"
@AuthenticationController.requireGlobalLogin @req, @res, @next
it "should pass the request onto httpAuth", ->
@AuthenticationController.httpAuth
.calledWith(@req, @res, @next)
.should.equal true
describe "with a user session", ->
beforeEach ->
@req.session =
user: {"mock": "user"}
@AuthenticationController.requireGlobalLogin @req, @res, @next
it "should call next() directly", ->
@next.called.should.equal true
describe "with no login credentials", ->
beforeEach ->
@AuthenticationController.requireGlobalLogin @req, @res, @next
it "should redirect to the /login page", ->
@res.redirectedTo.should.equal "/login"
describe "_redirectToLoginOrRegisterPage", ->
beforeEach ->
@middleware = @AuthenticationController.requireLogin(@options = { load_from_db: false })

View file

@ -7,6 +7,6 @@ class MockRequest
query: {}
i18n:
translate:->
module.exports = MockRequest