From af7e6ff70122c7bd2361ffeedd8901d930b4d3c6 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sun, 24 May 2020 23:51:17 +0200 Subject: [PATCH] - [API] Added getSchool, getSchools, and addSchools --- ...us_db_create.sql => auracle_db_create.sql} | 6 +- ...saurus_db_data.sql => auracle_db_data.sql} | 3 +- models/School.js | 4 +- routes/schools.js | 179 ++++++++++++++---- routes/spells.js | 2 +- routes/users.js | 2 +- 6 files changed, 151 insertions(+), 45 deletions(-) rename database/{spellsaurus_db_create.sql => auracle_db_create.sql} (96%) rename database/{spellsaurus_db_data.sql => auracle_db_data.sql} (99%) diff --git a/database/spellsaurus_db_create.sql b/database/auracle_db_create.sql similarity index 96% rename from database/spellsaurus_db_create.sql rename to database/auracle_db_create.sql index d09ead0..f36cf18 100644 --- a/database/spellsaurus_db_create.sql +++ b/database/auracle_db_create.sql @@ -1,6 +1,6 @@ -DROP DATABASE IF EXISTS spellsaurus; -CREATE DATABASE spellsaurus CHARACTER SET utf8 COLLATE utf8_bin; -USE spellsaurus; +DROP DATABASE IF EXISTS auracle; +CREATE DATABASE auracle CHARACTER SET utf8 COLLATE utf8_bin; +USE auracle; /* ==== PRIMARY TABLES ==== */ diff --git a/database/spellsaurus_db_data.sql b/database/auracle_db_data.sql similarity index 99% rename from database/spellsaurus_db_data.sql rename to database/auracle_db_data.sql index 73b4d92..350946c 100644 --- a/database/spellsaurus_db_data.sql +++ b/database/auracle_db_data.sql @@ -1,5 +1,5 @@ SET NAMES utf8; -USE spellsaurus; +USE auracle; -- META SCHOOLS INSERT INTO `meta_school` (name, description) VALUES @@ -219,6 +219,7 @@ INSERT INTO `variable` (description) VALUES /* SCHEMA (id_spell, id_school), + */ DELIMITER $$ diff --git a/models/School.js b/models/School.js index 48e1e56..fe1067c 100644 --- a/models/School.js +++ b/models/School.js @@ -4,9 +4,9 @@ const School = { "properties": { "name": { "type": "string" }, "description": { "type": "string" }, - "meta_school": { "type": "number" }, + "id_meta_school": { "type": "number" }, }, - "required": ["name", "description"] + "required": ["name", "description", "id_meta_school"] } module.exports = School \ No newline at end of file diff --git a/routes/schools.js b/routes/schools.js index 1c79359..cb5b825 100644 --- a/routes/schools.js +++ b/routes/schools.js @@ -8,6 +8,10 @@ let router = express.Router() const connection = require('../database/connection') const db = connection.db +// Validations +const regexInt = RegExp(/^[1-9]\d*$/) +const regexXSS = RegExp(/<[^>]*script/) + // Model validation const Validator = require('jsonschema').Validator const v = new Validator() @@ -21,62 +25,141 @@ const { HttpError } = require('../models/Errors') // ROUTES // GET ALL ------------------ const getSchools = () => { - let getSchoolsPromise = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let query = "SELECT DISTINCT * FROM school" db.query(query, async (err, result) => { if (err) { - reject(new HttpError(500, 'Error: Database error')) + reject(new HttpError(500, 'Database error')) } else if (result.length == 0) { - reject(new HttpError(404, 'Error: No ressource matching this id')) + reject(new HttpError(404, 'No schools were found')) } - result = await buildSchool(result[0]) - resolve(result); + + // Loops over the results to fetch the associated tables + for (let i = 0; i < result.length; i++) { + try { + result[i] = await buildSchool(result[i]) + } catch (err) { + reject(err) + } + } + resolve(result) }) }) - .catch(err => { throw err }) - - return getSchoolsPromise + .catch(err => { + throw err + }) } -router.get('/', async (req, res, next) => { +router.get('/', async (req, res) => { getSchools() .then(v => { res.setHeader('Content-Type', 'application/json;charset=utf-8') res.end(JSON.stringify(v)) }) .catch(err => { - res.status(err.code).send(err.message) + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) }) }) // GET ONE ------------------ const getSchool = (id) => { - let getSchoolPromise = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let query = "SELECT * FROM school WHERE id = " + db.escape(id) db.query(query, async (err, result) => { if (err) { - reject(new HttpError(500, 'Error: Database error')) + reject(new HttpError(500, 'Database error')) + } + try { + result = buildSchool(result[0]) + resolve(result) + } catch (err) { + reject(err) } - result = await buildSchool(result[0]) - resolve(result); }) }) - .catch(err => { throw err }) - - return getSchoolPromise + .catch(err => { + throw err + }) } -router.get('/:id/', async (req, res, next) => { +router.get('/:id/', async (req, res) => { getSchool(req.params.id) .then(v => { res.setHeader('Content-Type', 'application/json;charset=utf-8') res.end(JSON.stringify(v)) }) .catch(err => { - res.status(err.code).send(err.message) + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// CREATE ONE ------------------ +const addSchool = (s) => { + return new Promise(async (resolve, reject) => { + + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(s)) { + reject(new HttpError(403, "Error: School cannot be nothing !")) + } else if (!v.validate(s, School).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, School).errors)) + } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + let query = `INSERT INTO school (name, description, id_meta_school) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}, ${db.escape(s.id_meta_school)})` + + db.query(query, (err, result) => { + if (err) { + if (err.errno == 1452) { + reject(new HttpError(404, "Error: No meta school matching this ID")) + } else { + reject(new HttpError(500, 'Database error')) + } + } else { + console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`) + + const new_school_id = result.insertId + + getSchool(new_school_id) + .then(v => { + resolve(v) + }) + .catch(err => { + reject(err) + }) + } + }) + } + }).catch(err => { + throw err + }) +} +router.post('/', async (req, res) => { + addSchool(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) }) }) @@ -91,7 +174,12 @@ router.param('id', (req, res, next, id) => { throw new HttpError(403, 'Provided ID must be an integer') } } catch (err) { - res.status(err.code).send(err.message) + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) } }) @@ -101,30 +189,38 @@ router.param('id', (req, res, next, id) => { const buildSchool = async (school) => { // Fetches the school's parent school - let fetchMetaSchoolData = new Promise((resolve, reject) => { - let query = - "SELECT ms.id, ms.name " + - "FROM meta_school AS ms " + - "WHERE ms.id = " + school.id_meta_school + let fetchMetaSchoolData = (s) => { + return new Promise((resolve, reject) => { - db.query(query, (err, result) => { - if (err) { - reject(new HttpError(500, 'Error: Database error')) - } else { - resolve(result); - } + if (s == null) { reject(new HttpError(404, "Error: No school matching this ID"))} + + let query = + "SELECT ms.id, ms.name " + + "FROM meta_school AS ms " + + "WHERE ms.id = " + s.id_meta_school + + + db.query(query, (err, result) => { + if (err) { + reject(new HttpError(500, 'Database error')) + } else { + delete s.id_meta_school + s.meta_school = result + resolve(s) + } + }) }) - }) + } + + let s = fetchMetaSchoolData(school) .catch(err => { - res.status(403).send(err) + throw err }) - // Builds the school and returns it - school.meta_school = await fetchMetaSchoolData - return school + return s } -// Check if spell is null +// Check if object is null const isEmptyObject = (obj) => { if (Object.keys(obj).length === 0 && obj.constructor === Object) { return true @@ -133,4 +229,13 @@ const isEmptyObject = (obj) => { } } +// Check if script injection attempt +const isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } +} + module.exports = router \ No newline at end of file diff --git a/routes/spells.js b/routes/spells.js index 5232f3a..d208043 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -229,7 +229,7 @@ const addSpell = (s) => { } Promise.all([addSchoolsData(), addVariablesData(), addIngredientsData()]) - .then(v => { + .then(() => { resolve(getSpell(new_spell_id)) }) .catch(err => { diff --git a/routes/users.js b/routes/users.js index 744ee50..e62360a 100644 --- a/routes/users.js +++ b/routes/users.js @@ -240,4 +240,4 @@ const isXSSAttempt = (string) => { } } -module.exports = router +module.exports = router \ No newline at end of file