Merge pull request #29968 from overleaf/acf-migration3-express-routes

(3) Create Express routes (replaces swagger-router)

GitOrigin-RevId: e27973b630509312b198be899148310e69b94777
This commit is contained in:
Anna Claire Fields
2025-12-05 14:06:45 +01:00
committed by Copybot
parent a45c39389c
commit 56c98ac99c
3 changed files with 215 additions and 44 deletions

View File

@@ -11,8 +11,6 @@ const schemas = require('../schema')
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')
@@ -50,53 +48,58 @@ function setupSSL(app) {
exports.setupSSL = setupSSL
function handleJWTAuth(req, res, next) {
if (hasValidBasicAuthCredentials(req)) {
return next()
}
function configureJWTAuth(mode = 'jwt') {
return function handleJWTAuth(req, res, next) {
if (hasValidBasicAuthCredentials(req)) {
return next()
}
let token
if (req.query.token) {
token = req.query.token
} else if (
req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'Bearer'
) {
token = req.headers.authorization.split(' ')[1]
}
if (!token) {
const 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
let token
if ((mode === 'either' || mode === 'token') && req.query.token) {
token = req.query.token
} else if (
(mode === 'either' || mode === 'jwt') &&
req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'Bearer'
) {
const err = new Error(error.message)
token = req.headers.authorization.split(' ')[1]
}
if (!token) {
const err = new Error('jwt missing')
err.statusCode = HTTPStatus.UNAUTHORIZED
err.headers = { 'WWW-Authenticate': 'Bearer error="invalid_token"' }
err.headers = { 'WWW-Authenticate': 'Bearer' }
return next(err)
}
throw error
}
const { params } = validateReq(req, schemas.projectId)
if (decoded.project_id.toString() !== params.project_id.toString()) {
const err = new Error('Wrong project_id')
err.statusCode = HTTPStatus.FORBIDDEN
return next(err)
}
let decoded
try {
decoded = decodeJWT(token)
} catch (error) {
if (
error instanceof jwt.JsonWebTokenError ||
error instanceof jwt.TokenExpiredError
) {
const err = new Error(error.message)
err.statusCode = HTTPStatus.UNAUTHORIZED
err.headers = {
'WWW-Authenticate': 'Bearer error="invalid_token"',
}
return next(err)
}
throw error
}
req.jwt = decoded
next()
const { params } = validateReq(req, schemas.projectId)
if (decoded.project_id.toString() !== params.project_id.toString()) {
const err = new Error('Wrong project_id')
err.statusCode = HTTPStatus.FORBIDDEN
return next(err)
}
req.jwt = decoded
next()
}
}
/**
@@ -134,11 +137,14 @@ function getAuthHandlers() {
}
const handlers = {}
handlers.jwt = handleJWTAuth
handlers.jwt = configureJWTAuth('jwt')
handlers.token = configureJWTAuth('token')
handlers.either = configureJWTAuth('either')
handlers.basic = handleBasicAuth
handlers.token = handleJWTAuth
return handlers
}
exports.hasValidBasicAuthCredentials = hasValidBasicAuthCredentials
exports.configureJWTAuth = configureJWTAuth
exports.handleBasicAuth = handleBasicAuth
exports.getAuthHandlers = getAuthHandlers

View File

@@ -0,0 +1,46 @@
'use strict'
const express = require('express')
const router = express.Router()
const { getAuthHandlers } = require('../middleware/security')
const projectImportController = require('../controllers/project_import')
const { basic: handleBasicAuth } = getAuthHandlers()
router.post(
'/projects/:project_id/import',
handleBasicAuth,
projectImportController.importSnapshot
)
router.post(
'/projects/:project_id/legacy_import',
handleBasicAuth,
projectImportController.importSnapshot
)
router.post(
'/projects/:project_id/changes',
handleBasicAuth,
projectImportController.importChanges
)
router.post(
'/projects/:project_id/legacy_changes',
handleBasicAuth,
projectImportController.importChanges
)
router.post(
'/projects/:project_id/flush',
handleBasicAuth,
projectImportController.flushChanges
)
router.post(
'/projects/:project_id/expire',
handleBasicAuth,
projectImportController.expireProject
)
module.exports = router

View File

@@ -0,0 +1,119 @@
'use strict'
const express = require('express')
const router = express.Router()
const { getAuthHandlers } = require('../middleware/security')
const projectsController = require('../controllers/projects')
const {
basic: handleBasicAuth,
jwt: handleJWTAuth,
token: handleTokenAuth,
either: handleJWTOrTokenAuth,
} = getAuthHandlers()
router.post('/projects', handleBasicAuth, projectsController.initializeProject)
router.post(
'/projects/blob-stats',
handleBasicAuth,
projectsController.getProjectBlobsStats
)
router.post(
'/projects/:project_id/blob-stats',
handleBasicAuth,
projectsController.getBlobStats
)
router.delete(
'/projects/:project_id',
handleBasicAuth,
projectsController.deleteProject
)
router.get(
'/projects/:project_id/blobs/:hash',
handleJWTOrTokenAuth,
projectsController.getProjectBlob
)
router.put(
'/projects/:project_id/blobs/:hash',
handleJWTAuth,
projectsController.createProjectBlob
)
router.post(
'/projects/:project_id/blobs/:hash',
handleJWTAuth,
projectsController.copyProjectBlob
)
router.get(
'/projects/:project_id/latest/content',
handleJWTAuth,
projectsController.getLatestContent
)
router.get(
'/projects/:project_id/latest/hashed_content',
handleBasicAuth,
projectsController.getLatestHashedContent
)
router.get(
'/projects/:project_id/latest/history',
handleJWTAuth,
projectsController.getLatestHistory
)
router.get(
'/projects/:project_id/latest/history/raw',
handleJWTAuth,
projectsController.getLatestHistoryRaw
)
router.get(
'/projects/:project_id/latest/persistedHistory',
handleJWTAuth,
projectsController.getLatestPersistedHistory
)
router.get(
'/projects/:project_id/versions/:version/history',
handleJWTAuth,
projectsController.getHistory
)
router.get(
'/projects/:project_id/versions/:version/content',
handleJWTAuth,
projectsController.getContentAtVersion
)
router.get(
'/projects/:project_id/timestamp/:timestamp/history',
handleJWTAuth,
projectsController.getHistoryBefore
)
router.get(
'/projects/:project_id/version/:version/zip',
handleTokenAuth,
projectsController.getZip
)
router.post(
'/projects/:project_id/version/:version/zip',
handleBasicAuth,
projectsController.createZip
)
router.get(
'/projects/:project_id/changes',
handleBasicAuth,
projectsController.getChanges
)
module.exports = router