From 8b61f4f69ff9eeebd8c3277b86ae9e73b83c4ad0 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 27 Feb 2021 12:58:30 +1300 Subject: [PATCH] api: add validators to endpoints --- api/src/executor/routes.js | 34 +++++++++++++++++------- api/src/index.js | 38 ++++++++++++++++----------- api/src/ppman/routes.js | 54 +++++++++++++++++++++++++++++--------- 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/api/src/executor/routes.js b/api/src/executor/routes.js index 07f7ace..440e290 100644 --- a/api/src/executor/routes.js +++ b/api/src/executor/routes.js @@ -3,19 +3,35 @@ const { get_latest_runtime_matching_language_version } = require('../runtime'); const { Job } = require('./job'); +const { body } = require('express-validator'); module.exports = { + run_job_validators: [ + body('language') + .isString(), + body('version') + .isSemVer(), + body('files') + .isArray(), + body('files.*.name') + .isString() + .bail() + .not() + .contains('/'), + body('files.*.content') + .isString(), + body('*_timeout') + .isNumeric(), + body('stdin') + .isString(), + body('args') + .isArray(), + body('args.*') + .isString() + ], async run_job(req, res){ // POST /jobs - var errored = false; - ['language', 'version', - 'files', 'main', - 'args', 'stdin', - 'compile_timeout', 'run_timeout', - ].forEach(key => { - if(req.body[key] == undefined) errored = errored || res.json_error(`${key} is required`, 400); - }); - if(errored) return errored; + const runtime = get_latest_runtime_matching_language_version(req.body.language, req.body.version); if(runtime == undefined) return res.json_error(`${req.body.language}-${req.body.version} runtime is unknown`, 400); diff --git a/api/src/index.js b/api/src/index.js index 7b295b3..5c0da73 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -11,6 +11,7 @@ const fs = require('fs/promises'); const fss = require('fs'); const body_parser = require('body-parser'); const runtime = require('./runtime'); +const {validationResult} = require('express-validator'); const logger = Logger.create('index'); const app = express(); @@ -58,11 +59,6 @@ const app = express(); logger.debug('Constructing Express App'); - logger.debug('Registering middleware'); - - app.use(body_parser.urlencoded({extended: true})); - app.use(body_parser.json()); - logger.debug('Registering custom message wrappers'); express.response.json_error = function(message, code) { @@ -74,23 +70,33 @@ const app = express(); return this.json({success: true, data: obj}); }; + logger.debug('Registering middleware'); + + app.use(body_parser.urlencoded({extended: true})); + app.use(body_parser.json()); + + + function validate(req, res, next) { + const errors = validationResult(req); + if (!errors.isEmpty()) + return res.json_error(errors.array(), 422); + next(); + } + logger.debug('Registering Routes'); const ppman_routes = require('./ppman/routes'); - app.get ('/repos', ppman_routes.repo_list); - app.post ('/repos', ppman_routes.repo_add); - app.get ('/repos/:repo_slug', ppman_routes.repo_info); - app.get ('/repos/:repo_slug/packages', ppman_routes.repo_packages); - app.get ('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_info); - app.post ('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_install); - app.delete('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_uninstall); //TODO + app.get ('/repos', validate, ppman_routes.repo_list); + app.post ('/repos', ppman_routes.repo_add_validators, validate, ppman_routes.repo_add); + app.get ('/repos/:repo_slug', ppman_routes.repo_info_validators, validate, ppman_routes.repo_info); + app.get ('/repos/:repo_slug/packages', ppman_routes.repo_packages_validators, validate, ppman_routes.repo_packages); + app.get ('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_info_validators, validate, ppman_routes.package_info); + app.post ('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_info_validators, validate, ppman_routes.package_install); + app.delete('/repos/:repo_slug/packages/:language/:version', ppman_routes.package_info_validators, validate, ppman_routes.package_uninstall); //TODO const executor_routes = require('./executor/routes'); - app.post ('/jobs', executor_routes.run_job); - - - + app.post ('/jobs', executor_routes.run_job_validators, validate, executor_routes.run_job); logger.debug('Calling app.listen'); const [address,port] = config.bind_address.split(':'); diff --git a/api/src/ppman/routes.js b/api/src/ppman/routes.js index c376172..01ea19c 100644 --- a/api/src/ppman/routes.js +++ b/api/src/ppman/routes.js @@ -3,6 +3,7 @@ const state = require('../state'); const logger = require('logplease').create('ppman/routes'); const {Repository} = require('./repo'); const semver = require('semver'); +const { body, param } = require('express-validator'); async function get_or_construct_repo(slug){ if(repos.has(slug))return repos.get(slug); @@ -39,31 +40,46 @@ module.exports = { })) }); }, + repo_add_validators: [ + body('slug') + .notEmpty() + .bail() + .isSlug() + .bail() + .not() + .custom(value=>state.state.get('repositories').keys().includes(value)) + .withMessage("slug is already in use"), + body('url') + .notEmpty() + .bail() + .isURL({require_protocol: true}) + + ], async repo_add(req, res){ // POST /repos logger.debug(`Request for repoAdd slug=${req.body.slug} url=${req.body.url}`); - if(!req.body.slug) - return res.json_error('slug is missing from request body', 400); - if(!req.body.url) - return res.json_error('url is missing from request body', 400); const repo_state = state.state.get('repositories'); - - if(repo_state.has(req.body.slug)) return res.json_error(`repository ${req.body.slug} already exists`, 409); repo_state.set(req.body.slug, req.body.url); logger.info(`Repository ${req.body.slug} added url=${req.body.url}`); return res.json_success(req.body.slug); }, + repo_info_validators: [ + param('repo_slug') + .isSlug() + .bail() + .custom(value=>state.state.get('repositories').has(value)) + .withMessage("repository does not exist") + .bail() + ], async repo_info(req, res){ // GET /repos/:slug logger.debug(`Request for repoInfo for ${req.params.repo_slug}`); const repo = await get_or_construct_repo(req.params.repo_slug); - - if(repo == null) return res.json_error(`Requested repo ${req.params.repo_slug} does not exist`, 404); res.json_success({ slug: repo.slug, @@ -71,6 +87,14 @@ module.exports = { packages: repo.packages.length }); }, + repo_packages_validators: [ + param('repo_slug') + .isSlug() + .bail() + .custom(value=>state.state.get('repositories').has(value)) + .withMessage("repository does not exist") + .bail() + ], async repo_packages(req, res){ // GET /repos/:slug/packages logger.debug('Request to repoPackages'); @@ -86,13 +110,20 @@ module.exports = { })) }); }, + package_info_validators: [ + param('repo_slug') + .isSlug() + .bail() + .custom(value=>state.state.get('repositories').has(value)) + .withMessage("repository does not exist") + .bail() + ], async package_info(req, res){ // GET /repos/:slug/packages/:language/:version logger.debug('Request to packageInfo'); const repo = await get_or_construct_repo(req.params.repo_slug); - if(repo == null) return res.json_error(`Requested repo ${req.params.repo_slug} does not exist`, 404); const pkg = await get_package(repo, req.params.language, req.params.version); if(pkg == null) return res.json_error(`Requested package ${req.params.language}-${req.params.version} does not exist`, 404); @@ -113,8 +144,6 @@ module.exports = { logger.debug('Request to packageInstall'); const repo = await get_or_construct_repo(req.params.repo_slug); - if(repo == null) return res.json_error(`Requested repo ${req.params.repo_slug} does not exist`, 404); - const pkg = await get_package(repo, req.params.language, req.params.version); if(pkg == null) return res.json_error(`Requested package ${req.params.language}-${req.params.version} does not exist`, 404); @@ -131,6 +160,7 @@ module.exports = { async package_uninstall(req,res){ // DELETE /repos/:slug/packages/:language/:version - res.json(req.body); //TODO + //res.json(req.body); //TODO + res.json_error("not implemented", 500) } }; \ No newline at end of file