mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 12:43:39 -05:00
148 lines
4.1 KiB
JavaScript
148 lines
4.1 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const basicAuth = require('basic-auth')
|
||
|
const config = require('config')
|
||
|
const HTTPStatus = require('http-status')
|
||
|
const jwt = require('jsonwebtoken')
|
||
|
const tsscmp = require('tsscmp')
|
||
|
|
||
|
function setupBasicHttpAuthForSwaggerDocs(app) {
|
||
|
app.use('/docs', function (req, res, next) {
|
||
|
if (hasValidBasicAuthCredentials(req)) {
|
||
|
return next()
|
||
|
}
|
||
|
|
||
|
res.header('WWW-Authenticate', 'Basic realm="Application"')
|
||
|
res.status(HTTPStatus.UNAUTHORIZED).end()
|
||
|
})
|
||
|
}
|
||
|
|
||
|
exports.setupBasicHttpAuthForSwaggerDocs = setupBasicHttpAuthForSwaggerDocs
|
||
|
|
||
|
function hasValidBasicAuthCredentials(req) {
|
||
|
const credentials = basicAuth(req)
|
||
|
if (!credentials) return false
|
||
|
|
||
|
// No security in the name, so just use straight comparison.
|
||
|
if (credentials.name !== 'staging') return false
|
||
|
|
||
|
const password = config.get('basicHttpAuth.password')
|
||
|
if (password && tsscmp(credentials.pass, password)) return true
|
||
|
|
||
|
// Support an old password so we can change the password without downtime.
|
||
|
if (config.has('basicHttpAuth.oldPassword')) {
|
||
|
const oldPassword = config.get('basicHttpAuth.oldPassword')
|
||
|
if (oldPassword && tsscmp(credentials.pass, oldPassword)) return true
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
function setupSSL(app) {
|
||
|
const httpsOnly = config.get('httpsOnly') === 'true'
|
||
|
if (!httpsOnly) {
|
||
|
return
|
||
|
}
|
||
|
app.enable('trust proxy')
|
||
|
app.use(function (req, res, next) {
|
||
|
if (req.protocol === 'https') {
|
||
|
next()
|
||
|
return
|
||
|
}
|
||
|
if (req.method === 'GET' || req.method === 'HEAD') {
|
||
|
res.redirect('https://' + req.headers.host + req.url)
|
||
|
} else {
|
||
|
res
|
||
|
.status(HTTPStatus.FORBIDDEN)
|
||
|
.send('Please use HTTPS when submitting data to this server.')
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
exports.setupSSL = setupSSL
|
||
|
|
||
|
function handleJWTAuth(req, authOrSecDef, scopesOrApiKey, next) {
|
||
|
// as a temporary solution, to make the OT demo still work
|
||
|
// this handler will also check for basic authorization
|
||
|
if (hasValidBasicAuthCredentials(req)) {
|
||
|
return next()
|
||
|
}
|
||
|
let token, err
|
||
|
if (authOrSecDef.name === 'token') {
|
||
|
token = req.query.token
|
||
|
} else if (
|
||
|
req.headers.authorization &&
|
||
|
req.headers.authorization.split(' ')[0] === 'Bearer'
|
||
|
) {
|
||
|
token = req.headers.authorization.split(' ')[1]
|
||
|
}
|
||
|
if (!token) {
|
||
|
err = new Error('jwt missing')
|
||
|
err.statusCode = HTTPStatus.UNAUTHORIZED
|
||
|
err.headers = { 'WWW-Authenticate': 'Bearer' }
|
||
|
return next(err)
|
||
|
}
|
||
|
let decoded
|
||
|
try {
|
||
|
decoded = decodeJWT(token)
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof jwt.JsonWebTokenError ||
|
||
|
error instanceof jwt.TokenExpiredError
|
||
|
) {
|
||
|
err = new Error(error.message)
|
||
|
err.statusCode = HTTPStatus.UNAUTHORIZED
|
||
|
err.headers = { 'WWW-Authenticate': 'Bearer error="invalid_token"' }
|
||
|
return next(err)
|
||
|
}
|
||
|
throw error
|
||
|
}
|
||
|
if (decoded.project_id.toString() !== req.swagger.params.project_id.value) {
|
||
|
err = new Error('Wrong project_id')
|
||
|
err.statusCode = HTTPStatus.FORBIDDEN
|
||
|
return next(err)
|
||
|
}
|
||
|
next()
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Verify and decode the given JSON Web Token
|
||
|
*/
|
||
|
function decodeJWT(token) {
|
||
|
const key = config.get('jwtAuth.key')
|
||
|
const algorithm = config.get('jwtAuth.algorithm')
|
||
|
try {
|
||
|
return jwt.verify(token, key, { algorithms: [algorithm] })
|
||
|
} catch (err) {
|
||
|
// Support an old key so we can change the key without downtime.
|
||
|
if (config.has('jwtAuth.oldKey')) {
|
||
|
const oldKey = config.get('jwtAuth.oldKey')
|
||
|
return jwt.verify(token, oldKey, { algorithms: [algorithm] })
|
||
|
} else {
|
||
|
throw err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function handleBasicAuth(req, authOrSecDef, scopesOrApiKey, next) {
|
||
|
if (hasValidBasicAuthCredentials(req)) {
|
||
|
return next()
|
||
|
}
|
||
|
const error = new Error()
|
||
|
error.statusCode = HTTPStatus.UNAUTHORIZED
|
||
|
error.headers = { 'WWW-Authenticate': 'Basic realm="Application"' }
|
||
|
return next(error)
|
||
|
}
|
||
|
|
||
|
function getSwaggerHandlers() {
|
||
|
const handlers = {}
|
||
|
if (!config.has('jwtAuth.key') || !config.has('basicHttpAuth.password')) {
|
||
|
throw new Error('missing authentication env vars')
|
||
|
}
|
||
|
handlers.jwt = handleJWTAuth
|
||
|
handlers.basic = handleBasicAuth
|
||
|
handlers.token = handleJWTAuth
|
||
|
return handlers
|
||
|
}
|
||
|
|
||
|
exports.getSwaggerHandlers = getSwaggerHandlers
|