diff --git a/services/history-v1/api/middleware/security.js b/services/history-v1/api/middleware/security.js index 79823c4018..582ffc62c4 100644 --- a/services/history-v1/api/middleware/security.js +++ b/services/history-v1/api/middleware/security.js @@ -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 diff --git a/services/history-v1/api/routes/project_import.js b/services/history-v1/api/routes/project_import.js new file mode 100644 index 0000000000..ad3f730711 --- /dev/null +++ b/services/history-v1/api/routes/project_import.js @@ -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 diff --git a/services/history-v1/api/routes/projects.js b/services/history-v1/api/routes/projects.js new file mode 100644 index 0000000000..86f61fe179 --- /dev/null +++ b/services/history-v1/api/routes/projects.js @@ -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