diff --git a/backend/repository/peopleRepository.js b/backend/repository/peopleRepository.js index 8afe193903ffecc9c2e9b0f9249c4f69248e74cf..2dcb1379564fda80433aa8f835171bb62a3435e0 100644 --- a/backend/repository/peopleRepository.js +++ b/backend/repository/peopleRepository.js @@ -251,10 +251,69 @@ async function updatePerson(personId, personInfo) { } } +// Define a whitelist of allowed fields to prevent SQL injection +const allowedSearchFields = [ + "gradYear", + "degreeYear", + "city", + "stateId", + "contactNumber", + "firstName", + "lastName", +]; + +async function search(filters) { + //begin by selecting all people + let baseQuery = ` + SELECT p.*, pd.degreeYear, a.city, a.stateId, c.contactNumber + FROM people p + LEFT JOIN peopleDegree pd ON p.peopleId = pd.peopleId + LEFT JOIN peopleXAddress pa ON p.peopleId = pa.peopleId + LEFT JOIN address a ON pa.addressId = a.addressId + LEFT JOIN peopleContact c ON p.peopleId = c.peopleId + WHERE 1=1 +`; + + // add filters to the base query in order to keep refining the set of people returned + let queryValues = []; + for (const filter of filters) { + console.log("Filter: ", filter); + + // extract field or value + let field = filter.field ? filter.field : null; + let value = filter.value ? filter.value : null; + + // throw error if the field or value is missing + if (field == null || value == null) { + throw new Error( + `Missing field or value in filter: ${JSON.stringify(filter)}` + ); + } + + // make sure the search field is allowed + if (!allowedSearchFields.includes(field)) { + throw new Error(`Invalid field specified: ${field}`); + } + + // Add condition and value to the query + baseQuery += ` AND ${field} = ?`; + queryValues.push(value); + } + + try { + const [results] = await pool.query(baseQuery, queryValues); + return results; + } catch (error) { + console.log("Error in repository w/ base query"); + throw error; + } +} + module.exports = { addPerson, searchPeopleByName, deletePersonById, updatePerson, getPersonById, + search, }; diff --git a/backend/routes/peopleRoutes.js b/backend/routes/peopleRoutes.js index b8beaa87a76221a4bacc11886bc0407823e3f9be..4bd806f95813527e2d25025bfe6612c1e4810aa3 100644 --- a/backend/routes/peopleRoutes.js +++ b/backend/routes/peopleRoutes.js @@ -260,7 +260,7 @@ peopleRouter.get("/fullInfo/:id", async (req, res) => { peopleRouter.post("/search", async (req, res) => { // filters is an array of { field, value } pairs - const filters = req.body.filters; + let filters = req.body.filters; if (filters == null || !Array.isArray(filters) || filters.length == 0) { res.status(400).json({ message: "Missing filters." }); @@ -273,7 +273,7 @@ peopleRouter.post("/search", async (req, res) => { res.status(200).json({ people: results }); } catch (error) { console.log("Error in search route: ", error); - res.status(500).json({ message: "Internal service error." }); + res.status(500).json({ message: `Internal service error: ${error}` }); } }); diff --git a/backend/service/peopleService.js b/backend/service/peopleService.js index de7b0842ad649ec4921d9c2a54bd292165cf9cdc..940d5d753b4c9eb150f84ed1674abe2a5368c475 100644 --- a/backend/service/peopleService.js +++ b/backend/service/peopleService.js @@ -23,6 +23,7 @@ const { deletePersonById, updatePerson, getPersonById, + search, } = require("../repository/peopleRepository"); const { addPeopleInvolvement, @@ -344,10 +345,15 @@ async function updateInvolvementsForPerson(personId, involevements) { } } +async function generalSearch(filters) { + return search(filters); +} + module.exports = { createPerson, findByName, deleteById, updatePersonById, getPersonFullDetails, + generalSearch, };