mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #1047 from sharelatex/ew-oauth-authorization
add oauth middlewear GitOrigin-RevId: b68360763e1060fdbcbb4348d3d691a803fbfa41
This commit is contained in:
parent
0cfb765501
commit
365158f283
5 changed files with 120 additions and 0 deletions
|
@ -13,6 +13,8 @@ Analytics = require "../Analytics/AnalyticsManager"
|
||||||
passport = require 'passport'
|
passport = require 'passport'
|
||||||
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
||||||
SudoModeHandler = require '../SudoMode/SudoModeHandler'
|
SudoModeHandler = require '../SudoMode/SudoModeHandler'
|
||||||
|
V1Api = require "../V1/V1Api"
|
||||||
|
{User} = require "../../models/User"
|
||||||
|
|
||||||
module.exports = AuthenticationController =
|
module.exports = AuthenticationController =
|
||||||
|
|
||||||
|
@ -169,6 +171,24 @@ module.exports = AuthenticationController =
|
||||||
|
|
||||||
return doRequest
|
return doRequest
|
||||||
|
|
||||||
|
requireOauth: () ->
|
||||||
|
return (req, res, next = (error) ->) ->
|
||||||
|
return res.status(401).send() unless req.token?
|
||||||
|
options =
|
||||||
|
expectedStatusCodes: [401]
|
||||||
|
json: token: req.token
|
||||||
|
method: "POST"
|
||||||
|
uri: "/api/v1/sharelatex/oauth_authorize"
|
||||||
|
V1Api.request options, (error, response, body) ->
|
||||||
|
return next(error) if error?
|
||||||
|
return res.status(401).json({error: "invalid_token"}) unless body?.user_profile?.id
|
||||||
|
User.findOne { "overleaf.id": body.user_profile.id }, (error, user) ->
|
||||||
|
return next(error) if error?
|
||||||
|
return res.status(401).send() unless user?
|
||||||
|
req.oauth = access_token: body.access_token
|
||||||
|
req.oauth_user = user
|
||||||
|
next()
|
||||||
|
|
||||||
_globalLoginWhitelist: []
|
_globalLoginWhitelist: []
|
||||||
addEndpointToLoginWhitelist: (endpoint) ->
|
addEndpointToLoginWhitelist: (endpoint) ->
|
||||||
AuthenticationController._globalLoginWhitelist.push endpoint
|
AuthenticationController._globalLoginWhitelist.push endpoint
|
||||||
|
|
|
@ -20,6 +20,7 @@ methodOverride = require('method-override')
|
||||||
csrf = require('csurf')
|
csrf = require('csurf')
|
||||||
csrfProtection = csrf()
|
csrfProtection = csrf()
|
||||||
cookieParser = require('cookie-parser')
|
cookieParser = require('cookie-parser')
|
||||||
|
bearerToken = require('express-bearer-token')
|
||||||
|
|
||||||
# Init the session store
|
# Init the session store
|
||||||
sessionStore = new RedisStore(client:sessionsRedisClient)
|
sessionStore = new RedisStore(client:sessionsRedisClient)
|
||||||
|
@ -71,6 +72,7 @@ app.use bodyParser.urlencoded({ extended: true, limit: "2mb"})
|
||||||
app.use bodyParser.json({limit: Settings.max_doc_length + 64 * 1024}) # 64kb overhead
|
app.use bodyParser.json({limit: Settings.max_doc_length + 64 * 1024}) # 64kb overhead
|
||||||
app.use multer(dest: Settings.path.uploadFolder)
|
app.use multer(dest: Settings.path.uploadFolder)
|
||||||
app.use methodOverride()
|
app.use methodOverride()
|
||||||
|
app.use bearerToken()
|
||||||
|
|
||||||
app.use metrics.http.monitor(logger)
|
app.use metrics.http.monitor(logger)
|
||||||
RedirectManager.apply(webRouter)
|
RedirectManager.apply(webRouter)
|
||||||
|
|
5
services/web/npm-shrinkwrap.json
generated
5
services/web/npm-shrinkwrap.json
generated
|
@ -3332,6 +3332,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express-bearer-token": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"from": "express-bearer-token@>=2.2.0 <3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-bearer-token/-/express-bearer-token-2.2.0.tgz"
|
||||||
|
},
|
||||||
"express-http-proxy": {
|
"express-http-proxy": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"from": "express-http-proxy@>=1.1.0 <2.0.0",
|
"from": "express-http-proxy@>=1.1.0 <2.0.0",
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"dateformat": "1.0.4-1.2.3",
|
"dateformat": "1.0.4-1.2.3",
|
||||||
"daterangepicker": "^2.1.27",
|
"daterangepicker": "^2.1.27",
|
||||||
"express": "4.13.0",
|
"express": "4.13.0",
|
||||||
|
"express-bearer-token": "^2.2.0",
|
||||||
"express-http-proxy": "^1.1.0",
|
"express-http-proxy": "^1.1.0",
|
||||||
"express-session": "^1.14.2",
|
"express-session": "^1.14.2",
|
||||||
"fs-extra": "^4.0.2",
|
"fs-extra": "^4.0.2",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
sinon = require('sinon')
|
sinon = require('sinon')
|
||||||
chai = require('chai')
|
chai = require('chai')
|
||||||
|
sinonChai = require "sinon-chai"
|
||||||
|
chai.use sinonChai
|
||||||
should = chai.should()
|
should = chai.should()
|
||||||
expect = chai.expect
|
expect = chai.expect
|
||||||
modulePath = "../../../../app/js/Features/Authentication/AuthenticationController.js"
|
modulePath = "../../../../app/js/Features/Authentication/AuthenticationController.js"
|
||||||
|
@ -13,6 +15,7 @@ ObjectId = require("mongojs").ObjectId
|
||||||
describe "AuthenticationController", ->
|
describe "AuthenticationController", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
tk.freeze(Date.now())
|
tk.freeze(Date.now())
|
||||||
|
@UserModel = findOne: sinon.stub()
|
||||||
@AuthenticationController = SandboxedModule.require modulePath, requires:
|
@AuthenticationController = SandboxedModule.require modulePath, requires:
|
||||||
"./AuthenticationManager": @AuthenticationManager = {}
|
"./AuthenticationManager": @AuthenticationManager = {}
|
||||||
"../User/UserUpdater" : @UserUpdater = {updateUser:sinon.stub()}
|
"../User/UserUpdater" : @UserUpdater = {updateUser:sinon.stub()}
|
||||||
|
@ -32,6 +35,8 @@ describe "AuthenticationController", ->
|
||||||
"../SudoMode/SudoModeHandler": @SudoModeHandler = {activateSudoMode: sinon.stub().callsArgWith(1, null)}
|
"../SudoMode/SudoModeHandler": @SudoModeHandler = {activateSudoMode: sinon.stub().callsArgWith(1, null)}
|
||||||
"../Notifications/NotificationsBuilder": @NotificationsBuilder =
|
"../Notifications/NotificationsBuilder": @NotificationsBuilder =
|
||||||
ipMatcherAffiliation: sinon.stub()
|
ipMatcherAffiliation: sinon.stub()
|
||||||
|
"../V1/V1Api": @V1Api = request: sinon.stub()
|
||||||
|
"../../models/User": { User: @UserModel }
|
||||||
@user =
|
@user =
|
||||||
_id: ObjectId()
|
_id: ObjectId()
|
||||||
email: @email = "USER@example.com"
|
email: @email = "USER@example.com"
|
||||||
|
@ -395,6 +400,93 @@ describe "AuthenticationController", ->
|
||||||
it "should redirect to the register or login page", ->
|
it "should redirect to the register or login page", ->
|
||||||
@AuthenticationController._redirectToLoginOrRegisterPage.calledWith(@req, @res).should.equal true
|
@AuthenticationController._redirectToLoginOrRegisterPage.calledWith(@req, @res).should.equal true
|
||||||
|
|
||||||
|
describe "requireOauth", ->
|
||||||
|
beforeEach ->
|
||||||
|
@res.send = sinon.stub()
|
||||||
|
@res.status = sinon.stub().returns(@res)
|
||||||
|
@middleware = @AuthenticationController.requireOauth()
|
||||||
|
|
||||||
|
describe "when token not provided", ->
|
||||||
|
beforeEach ->
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should return 401 error", ->
|
||||||
|
@res.status.should.have.been.calledWith 401
|
||||||
|
|
||||||
|
describe "when token provided", ->
|
||||||
|
beforeEach ->
|
||||||
|
@V1Api.request = sinon.stub().yields("error", {}, {})
|
||||||
|
@req.token = "foo"
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should make request to v1 api with token", ->
|
||||||
|
@V1Api.request.should.have.been.calledWith {
|
||||||
|
expectedStatusCodes: [401]
|
||||||
|
json: token: "foo"
|
||||||
|
method: "POST"
|
||||||
|
uri: "/api/v1/sharelatex/oauth_authorize"
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "when v1 api returns error", ->
|
||||||
|
beforeEach ->
|
||||||
|
@V1Api.request = sinon.stub().yields("error", {}, {})
|
||||||
|
@req.token = "foo"
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should return status", ->
|
||||||
|
@next.should.have.been.calledWith "error"
|
||||||
|
|
||||||
|
describe "when v1 api status code is not 200", ->
|
||||||
|
beforeEach ->
|
||||||
|
@V1Api.request = sinon.stub().yields(null, {statusCode: 401}, {})
|
||||||
|
@req.token = "foo"
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should return status", ->
|
||||||
|
@res.status.should.have.been.calledWith 401
|
||||||
|
|
||||||
|
describe "when v1 api returns authorized profile and access token", ->
|
||||||
|
beforeEach ->
|
||||||
|
@oauth_authorize =
|
||||||
|
access_token: "access_token"
|
||||||
|
user_profile: id: "overleaf-id"
|
||||||
|
@V1Api.request = sinon.stub().yields(null, {statusCode: 200}, @oauth_authorize)
|
||||||
|
@req.token = "foo"
|
||||||
|
|
||||||
|
describe "in all cases", ->
|
||||||
|
beforeEach ->
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should find user", ->
|
||||||
|
@UserModel.findOne.should.have.been.calledWithMatch { "overleaf.id": "overleaf-id" }
|
||||||
|
|
||||||
|
describe "when user find returns error", ->
|
||||||
|
beforeEach ->
|
||||||
|
@UserModel.findOne = sinon.stub().yields("error")
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should return error", ->
|
||||||
|
@next.should.have.been.calledWith "error"
|
||||||
|
|
||||||
|
describe "when user is not found", ->
|
||||||
|
beforeEach ->
|
||||||
|
@UserModel.findOne = sinon.stub().yields(null, null)
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should return unauthorized", ->
|
||||||
|
@res.status.should.have.been.calledWith 401
|
||||||
|
|
||||||
|
describe "when user is found", ->
|
||||||
|
beforeEach ->
|
||||||
|
@UserModel.findOne = sinon.stub().yields(null, "user")
|
||||||
|
@middleware(@req, @res, @next)
|
||||||
|
|
||||||
|
it "should add user to request", ->
|
||||||
|
@req.oauth_user.should.equal "user"
|
||||||
|
|
||||||
|
it "should add access_token to request", ->
|
||||||
|
@req.oauth.access_token.should.equal "access_token"
|
||||||
|
|
||||||
describe "requireGlobalLogin", ->
|
describe "requireGlobalLogin", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@req.headers = {}
|
@req.headers = {}
|
||||||
|
|
Loading…
Reference in a new issue